refactor(core): move more modules into core

This commit is contained in:
Jeff Cross
2015-09-03 22:01:17 -07:00
parent 841aa1af5b
commit 6d13cf9b8f
47 changed files with 0 additions and 79 deletions

View File

@ -0,0 +1,308 @@
import {
ddescribe,
describe,
fakeAsync,
flushMicrotasks,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
el,
AsyncTestCompleter,
inject
} from 'angular2/test_lib';
import {
ControlGroup,
Control,
NgControlName,
NgControlGroup,
NgFormModel,
ControlValueAccessor,
Validators,
NgForm,
NgModel,
NgFormControl,
DefaultValidators
} from 'angular2/forms';
class DummyControlValueAccessor implements ControlValueAccessor {
writtenValue;
registerOnChange(fn) {}
registerOnTouched(fn) {}
writeValue(obj: any): void { this.writtenValue = obj; }
}
export function main() {
describe("Form Directives", () => {
describe("NgFormModel", () => {
var form;
var formModel;
var loginControlDir;
beforeEach(() => {
form = new NgFormModel();
formModel = new ControlGroup({"login": new Control(null)});
form.form = formModel;
loginControlDir = new NgControlName(form, []);
loginControlDir.name = "login";
loginControlDir.valueAccessor = new DummyControlValueAccessor();
});
it("should reexport control properties", () => {
expect(form.control).toBe(formModel);
expect(form.value).toBe(formModel.value);
expect(form.valid).toBe(formModel.valid);
expect(form.errors).toBe(formModel.errors);
expect(form.pristine).toBe(formModel.pristine);
expect(form.dirty).toBe(formModel.dirty);
expect(form.touched).toBe(formModel.touched);
expect(form.untouched).toBe(formModel.untouched);
});
describe("addControl", () => {
it("should throw when no control found", () => {
var dir = new NgControlName(form, null);
dir.name = "invalidName";
expect(() => form.addControl(dir))
.toThrowError(new RegExp("Cannot find control 'invalidName'"));
});
it("should throw when no value accessor", () => {
var dir = new NgControlName(form, null);
dir.name = "login";
expect(() => form.addControl(dir))
.toThrowError(new RegExp("No value accessor for 'login'"));
});
it("should set up validator", () => {
loginControlDir.validators = [Validators.required];
expect(formModel.find(["login"]).valid).toBe(true);
// this will add the required validator and recalculate the validity
form.addControl(loginControlDir);
expect(formModel.find(["login"]).valid).toBe(false);
});
it("should write value to the DOM", () => {
formModel.find(["login"]).updateValue("initValue");
form.addControl(loginControlDir);
expect((<any>loginControlDir.valueAccessor).writtenValue).toEqual("initValue");
});
it("should add the directive to the list of directives included in the form", () => {
form.addControl(loginControlDir);
expect(form.directives).toEqual([loginControlDir]);
});
});
describe("removeControl", () => {
it("should remove the directive to the list of directives included in the form", () => {
form.addControl(loginControlDir);
form.removeControl(loginControlDir);
expect(form.directives).toEqual([]);
});
});
describe("onChanges", () => {
it("should update dom values of all the directives", () => {
form.addControl(loginControlDir);
formModel.find(["login"]).updateValue("new value");
form.onChanges(null);
expect((<any>loginControlDir.valueAccessor).writtenValue).toEqual("new value");
});
});
});
describe("NgForm", () => {
var form;
var formModel;
var loginControlDir;
var personControlGroupDir;
beforeEach(() => {
form = new NgForm();
formModel = form.form;
personControlGroupDir = new NgControlGroup(form);
personControlGroupDir.name = "person";
loginControlDir = new NgControlName(personControlGroupDir, null);
loginControlDir.name = "login";
loginControlDir.valueAccessor = new DummyControlValueAccessor();
});
it("should reexport control properties", () => {
expect(form.control).toBe(formModel);
expect(form.value).toBe(formModel.value);
expect(form.valid).toBe(formModel.valid);
expect(form.errors).toBe(formModel.errors);
expect(form.pristine).toBe(formModel.pristine);
expect(form.dirty).toBe(formModel.dirty);
expect(form.touched).toBe(formModel.touched);
expect(form.untouched).toBe(formModel.untouched);
});
describe("addControl & addControlGroup", () => {
it("should create a control with the given name", fakeAsync(() => {
form.addControlGroup(personControlGroupDir);
form.addControl(loginControlDir);
flushMicrotasks();
expect(formModel.find(["person", "login"])).not.toBeNull;
}));
// should update the form's value and validity
});
describe("removeControl & removeControlGroup", () => {
it("should remove control", fakeAsync(() => {
form.addControlGroup(personControlGroupDir);
form.addControl(loginControlDir);
form.removeControlGroup(personControlGroupDir);
form.removeControl(loginControlDir);
flushMicrotasks();
expect(formModel.find(["person"])).toBeNull();
expect(formModel.find(["person", "login"])).toBeNull();
}));
// should update the form's value and validity
});
});
describe("NgControlGroup", () => {
var formModel;
var controlGroupDir;
beforeEach(() => {
formModel = new ControlGroup({"login": new Control(null)});
var parent = new NgFormModel();
parent.form = new ControlGroup({"group": formModel});
controlGroupDir = new NgControlGroup(parent);
controlGroupDir.name = "group";
});
it("should reexport control properties", () => {
expect(controlGroupDir.control).toBe(formModel);
expect(controlGroupDir.value).toBe(formModel.value);
expect(controlGroupDir.valid).toBe(formModel.valid);
expect(controlGroupDir.errors).toBe(formModel.errors);
expect(controlGroupDir.pristine).toBe(formModel.pristine);
expect(controlGroupDir.dirty).toBe(formModel.dirty);
expect(controlGroupDir.touched).toBe(formModel.touched);
expect(controlGroupDir.untouched).toBe(formModel.untouched);
});
});
describe("NgFormControl", () => {
var controlDir;
var control;
beforeEach(() => {
controlDir = new NgFormControl([]);
controlDir.valueAccessor = new DummyControlValueAccessor();
control = new Control(null);
controlDir.form = control;
});
it("should reexport control properties", () => {
expect(controlDir.control).toBe(control);
expect(controlDir.value).toBe(control.value);
expect(controlDir.valid).toBe(control.valid);
expect(controlDir.errors).toBe(control.errors);
expect(controlDir.pristine).toBe(control.pristine);
expect(controlDir.dirty).toBe(control.dirty);
expect(controlDir.touched).toBe(control.touched);
expect(controlDir.untouched).toBe(control.untouched);
});
it("should set up validator", () => {
controlDir.validators = [Validators.required];
expect(control.valid).toBe(true);
// this will add the required validator and recalculate the validity
controlDir.onChanges({});
expect(control.valid).toBe(false);
});
});
describe("NgModel", () => {
var ngModel;
beforeEach(() => {
ngModel = new NgModel([]);
ngModel.valueAccessor = new DummyControlValueAccessor();
});
it("should reexport control properties", () => {
var control = ngModel.control;
expect(ngModel.control).toBe(control);
expect(ngModel.value).toBe(control.value);
expect(ngModel.valid).toBe(control.valid);
expect(ngModel.errors).toBe(control.errors);
expect(ngModel.pristine).toBe(control.pristine);
expect(ngModel.dirty).toBe(control.dirty);
expect(ngModel.touched).toBe(control.touched);
expect(ngModel.untouched).toBe(control.untouched);
});
it("should set up validator", () => {
ngModel.validators = [Validators.required];
expect(ngModel.control.valid).toBe(true);
// this will add the required validator and recalculate the validity
ngModel.onChanges({});
expect(ngModel.control.valid).toBe(false);
});
});
describe("NgControlName", () => {
var formModel;
var controlNameDir;
beforeEach(() => {
formModel = new Control("name");
var parent = new NgFormModel();
parent.form = new ControlGroup({"name": formModel});
controlNameDir = new NgControlName(parent, []);
controlNameDir.name = "name";
});
it("should reexport control properties", () => {
expect(controlNameDir.control).toBe(formModel);
expect(controlNameDir.value).toBe(formModel.value);
expect(controlNameDir.valid).toBe(formModel.valid);
expect(controlNameDir.errors).toBe(formModel.errors);
expect(controlNameDir.pristine).toBe(formModel.pristine);
expect(controlNameDir.dirty).toBe(formModel.dirty);
expect(controlNameDir.touched).toBe(formModel.touched);
expect(controlNameDir.untouched).toBe(formModel.untouched);
});
});
});
}

View File

@ -0,0 +1,66 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
el
} from 'angular2/test_lib';
import {Control, FormBuilder, Validators} from 'angular2/forms';
export function main() {
describe("Form Builder", () => {
var b;
beforeEach(() => { b = new FormBuilder(); });
it("should create controls from a value", () => {
var g = b.group({"login": "some value"});
expect(g.controls["login"].value).toEqual("some value");
});
it("should create controls from an array", () => {
var g = b.group({"login": ["some value"], "password": ["some value", Validators.required]});
expect(g.controls["login"].value).toEqual("some value");
expect(g.controls["password"].value).toEqual("some value");
expect(g.controls["password"].validator).toEqual(Validators.required);
});
it("should use controls", () => {
var g = b.group({"login": b.control("some value", Validators.required)});
expect(g.controls["login"].value).toEqual("some value");
expect(g.controls["login"].validator).toBe(Validators.required);
});
it("should create groups with optional controls", () => {
var g = b.group({"login": "some value"}, {"optionals": {"login": false}});
expect(g.contains("login")).toEqual(false);
});
it("should create groups with a custom validator", () => {
var g = b.group({"login": "some value"}, {"validator": Validators.nullValidator});
expect(g.validator).toBe(Validators.nullValidator);
});
it("should use default validators when no validators are provided", () => {
var g = b.group({"login": "some value"});
expect(g.controls["login"].validator).toBe(Validators.nullValidator);
expect(g.validator).toBe(Validators.group);
});
it("should create control arrays", () => {
var c = b.control("three");
var a = b.array(["one", ["two", Validators.required], c, b.array(['four'])]);
expect(a.value).toEqual(['one', 'two', 'three', ['four']]);
});
});
}

View File

@ -0,0 +1,787 @@
import {Component, Directive, View} from 'angular2/angular2';
import {
afterEach,
AsyncTestCompleter,
TestComponentBuilder,
By,
beforeEach,
ddescribe,
describe,
dispatchEvent,
fakeAsync,
tick,
expect,
it,
inject,
iit,
xit,
browserDetection
} from 'angular2/test_lib';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {NgIf, NgFor} from 'angular2/directives';
import {
Control,
ControlGroup,
NgForm,
FORM_DIRECTIVES,
Validators,
NgControl,
ControlValueAccessor
} from 'angular2/forms';
export function main() {
describe("integration tests", () => {
it("should initialize DOM elements with the given form object",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="login">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"login": new Control("loginValue")});
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("loginValue");
async.done();
});
}));
it("should update the control group values on DOM change",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var form = new ControlGroup({"login": new Control("oldValue")});
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="login">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
input.nativeElement.value = "updatedValue";
dispatchEvent(input.nativeElement, "change");
expect(form.value).toEqual({"login": "updatedValue"});
async.done();
});
}));
it("should emit ng-submit event on submit",
inject(
[TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t =
`<div><form [ng-form-model]="form" (ng-submit)="name='updated'"></form><span>{{name}}</span></div>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((root) => { rootTC = root; });
tick();
rootTC.componentInstance.form = new ControlGroup({});
rootTC.componentInstance.name = 'old';
tick();
var form = rootTC.query(By.css("form"));
dispatchEvent(form.nativeElement, "submit");
tick();
expect(rootTC.componentInstance.name).toEqual('updated');
})));
it("should work with single controls",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var control = new Control("loginValue");
var t = `<div><input type="text" [ng-form-control]="form"></div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = control;
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("loginValue");
input.nativeElement.value = "updatedValue";
dispatchEvent(input.nativeElement, "change");
expect(control.value).toEqual("updatedValue");
async.done();
});
}));
it("should update DOM elements when rebinding the control group",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="login">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"login": new Control("oldValue")});
rootTC.detectChanges();
rootTC.componentInstance.form = new ControlGroup({"login": new Control("newValue")});
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("newValue");
async.done();
});
}));
it("should update DOM elements when updating the value of a control",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var login = new Control("oldValue");
var form = new ControlGroup({"login": login});
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="login">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
login.updateValue("newValue");
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("newValue");
async.done();
});
}));
it("should mark controls as touched after interacting with the DOM control",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var login = new Control("oldValue");
var form = new ControlGroup({"login": login});
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="login">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
var loginEl = rootTC.query(By.css("input"));
expect(login.touched).toBe(false);
dispatchEvent(loginEl.nativeElement, "blur");
expect(login.touched).toBe(true);
async.done();
});
}));
describe("different control types", () => {
it("should support <input type=text>",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="text">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"text": new Control("old")});
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("old");
input.nativeElement.value = "new";
dispatchEvent(input.nativeElement, "input");
expect(rootTC.componentInstance.form.value).toEqual({"text": "new"});
async.done();
});
}));
it("should support <input> without type",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<input ng-control="text">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"text": new Control("old")});
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("old");
input.nativeElement.value = "new";
dispatchEvent(input.nativeElement, "input");
expect(rootTC.componentInstance.form.value).toEqual({"text": "new"});
async.done();
});
}));
it("should support <textarea>",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<textarea ng-control="text"></textarea>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"text": new Control('old')});
rootTC.detectChanges();
var textarea = rootTC.query(By.css("textarea"));
expect(textarea.nativeElement.value).toEqual("old");
textarea.nativeElement.value = "new";
dispatchEvent(textarea.nativeElement, "input");
expect(rootTC.componentInstance.form.value).toEqual({"text": 'new'});
async.done();
});
}));
it("should support <type=checkbox>",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<input type="checkbox" ng-control="checkbox">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"checkbox": new Control(true)});
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.checked).toBe(true);
input.nativeElement.checked = false;
dispatchEvent(input.nativeElement, "change");
expect(rootTC.componentInstance.form.value).toEqual({"checkbox": false});
async.done();
});
}));
it("should support <select>",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<select ng-control="city">
<option value="SF"></option>
<option value="NYC"></option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"city": new Control("SF")});
rootTC.detectChanges();
var select = rootTC.query(By.css("select"));
var sfOption = rootTC.query(By.css("option"));
expect(select.nativeElement.value).toEqual('SF');
expect(sfOption.nativeElement.selected).toBe(true);
select.nativeElement.value = 'NYC';
dispatchEvent(select.nativeElement, "change");
expect(rootTC.componentInstance.form.value).toEqual({"city": 'NYC'});
expect(sfOption.nativeElement.selected).toBe(false);
async.done();
});
}));
it("should support <select> with a dynamic list of options",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<select ng-control="city">
<option *ng-for="#c of data" [value]="c"></option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"city": new Control("NYC")});
rootTC.componentInstance.data = ['SF', 'NYC'];
rootTC.detectChanges();
var select = rootTC.query(By.css('select'));
expect(select.nativeElement.value).toEqual('NYC');
async.done();
});
}));
it("should support custom value accessors",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="name" wrapped-value>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = new ControlGroup({"name": new Control("aa")});
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("!aa!");
input.nativeElement.value = "!bb!";
dispatchEvent(input.nativeElement, "change");
expect(rootTC.componentInstance.form.value).toEqual({"name": "bb"});
async.done();
});
}));
});
describe("validations", () => {
it("should use validators defined in html",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var form = new ControlGroup({"login": new Control("aa")});
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="login" required>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
expect(form.valid).toEqual(true);
var input = rootTC.query(By.css("input"));
input.nativeElement.value = "";
dispatchEvent(input.nativeElement, "change");
expect(form.valid).toEqual(false);
async.done();
});
}));
it("should use validators defined in the model",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var form = new ControlGroup({"login": new Control("aa", Validators.required)});
var t = `<div [ng-form-model]="form">
<input type="text" ng-control="login">
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
expect(form.valid).toEqual(true);
var input = rootTC.query(By.css("input"));
input.nativeElement.value = "";
dispatchEvent(input.nativeElement, "change");
expect(form.valid).toEqual(false);
async.done();
});
}));
});
describe("nested forms", () => {
it("should init DOM with the given form object",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var form =
new ControlGroup({"nested": new ControlGroup({"login": new Control("value")})});
var t = `<div [ng-form-model]="form">
<div ng-control-group="nested">
<input type="text" ng-control="login">
</div>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
expect(input.nativeElement.value).toEqual("value");
async.done();
});
}));
it("should update the control group values on DOM change",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var form =
new ControlGroup({"nested": new ControlGroup({"login": new Control("value")})});
var t = `<div [ng-form-model]="form">
<div ng-control-group="nested">
<input type="text" ng-control="login">
</div>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
var input = rootTC.query(By.css("input"));
input.nativeElement.value = "updatedValue";
dispatchEvent(input.nativeElement, "change");
expect(form.value).toEqual({"nested": {"login": "updatedValue"}});
async.done();
});
}));
});
it("should support ng-model for complex forms",
inject(
[TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var form = new ControlGroup({"name": new Control("")});
var t =
`<div [ng-form-model]="form"><input type="text" ng-control="name" [(ng-model)]="name"></div>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((root) => { rootTC = root; });
tick();
rootTC.componentInstance.name = 'oldValue';
rootTC.componentInstance.form = form;
rootTC.detectChanges();
var input = rootTC.query(By.css("input")).nativeElement;
expect(input.value).toEqual("oldValue");
input.value = "updatedValue";
dispatchEvent(input, "change");
tick();
expect(rootTC.componentInstance.name).toEqual("updatedValue");
})));
it("should support ng-model for single fields",
inject(
[TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var form = new Control("");
var t = `<div><input type="text" [ng-form-control]="form" [(ng-model)]="name"></div>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((root) => { rootTC = root; });
tick();
rootTC.componentInstance.form = form;
rootTC.componentInstance.name = "oldValue";
rootTC.detectChanges();
var input = rootTC.query(By.css("input")).nativeElement;
expect(input.value).toEqual("oldValue");
input.value = "updatedValue";
dispatchEvent(input, "change");
tick();
expect(rootTC.componentInstance.name).toEqual("updatedValue");
})));
describe("template-driven forms", () => {
it("should add new controls and control groups",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t = `<form>
<div ng-control-group="user">
<input type="text" ng-control="login">
</div>
</form>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
(root) => { rootTC = root; });
tick();
rootTC.componentInstance.name = null;
rootTC.detectChanges();
var form = rootTC.componentViewChildren[0].inject(NgForm);
expect(form.controls['user']).not.toBeDefined();
tick();
expect(form.controls['user']).toBeDefined();
expect(form.controls['user'].controls['login']).toBeDefined();
})));
it("should emit ng-submit event on submit",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t = `<div><form (ng-submit)="name='updated'"></form></div>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
(root) => { rootTC = root; });
tick();
rootTC.componentInstance.name = 'old';
var form = rootTC.query(By.css("form"));
dispatchEvent(form.nativeElement, "submit");
tick();
expect(rootTC.componentInstance.name).toEqual("updated");
})));
it("should not create a template-driven form when ng-no-form is used",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<form ng-no-form>
</form>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.name = null;
rootTC.detectChanges();
expect(rootTC.componentViewChildren.length).toEqual(0);
async.done();
});
}));
it("should remove controls",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t = `<form>
<div *ng-if="name == 'show'">
<input type="text" ng-control="login">
</div>
</form>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
(root) => { rootTC = root; });
tick();
rootTC.componentInstance.name = 'show';
rootTC.detectChanges();
tick();
var form = rootTC.componentViewChildren[0].inject(NgForm);
expect(form.controls['login']).toBeDefined();
rootTC.componentInstance.name = 'hide';
rootTC.detectChanges();
tick();
expect(form.controls['login']).not.toBeDefined();
})));
it("should remove control groups",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t = `<form>
<div *ng-if="name=='show'" ng-control-group="user">
<input type="text" ng-control="login">
</div>
</form>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
(root) => { rootTC = root; });
tick();
rootTC.componentInstance.name = 'show';
rootTC.detectChanges();
tick();
var form = rootTC.componentViewChildren[0].inject(NgForm);
expect(form.controls['user']).toBeDefined();
rootTC.componentInstance.name = 'hide';
rootTC.detectChanges();
tick();
expect(form.controls['user']).not.toBeDefined();
})));
it("should support ng-model for complex forms",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t = `<form>
<input type="text" ng-control="name" [(ng-model)]="name">
</form>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
(root) => { rootTC = root; });
tick();
rootTC.componentInstance.name = "oldValue";
rootTC.detectChanges();
tick();
var input = rootTC.query(By.css("input")).nativeElement;
expect(input.value).toEqual("oldValue");
input.value = "updatedValue";
dispatchEvent(input, "change");
tick();
expect(rootTC.componentInstance.name).toEqual("updatedValue");
})));
it("should support ng-model for single fields",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t = `<div><input type="text" [(ng-model)]="name"></div>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
(root) => { rootTC = root; });
tick();
rootTC.componentInstance.name = "oldValue";
rootTC.detectChanges();
var input = rootTC.query(By.css("input")).nativeElement;
expect(input.value).toEqual("oldValue");
input.value = "updatedValue";
dispatchEvent(input, "change");
tick();
expect(rootTC.componentInstance.name).toEqual("updatedValue");
})));
});
describe("setting status classes", () => {
it("should work with single fields",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var form = new Control("", Validators.required);
var t = `<div><input type="text" [ng-form-control]="form"></div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
var input = rootTC.query(By.css("input")).nativeElement;
expect(DOM.classList(input))
.toEqual(['ng-binding', 'ng-invalid', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, "blur");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-touched"]);
input.value = "updatedValue";
dispatchEvent(input, "change");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-touched", "ng-dirty", "ng-valid"]);
async.done();
});
}));
it("should work with complex model-driven forms",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var form = new ControlGroup({"name": new Control("", Validators.required)});
var t = `<form [ng-form-model]="form"><input type="text" ng-control="name"></form>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.form = form;
rootTC.detectChanges();
var input = rootTC.query(By.css("input")).nativeElement;
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-untouched"]);
dispatchEvent(input, "blur");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-touched"]);
input.value = "updatedValue";
dispatchEvent(input, "change");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-touched", "ng-dirty", "ng-valid"]);
async.done();
});
}));
it("should work with ng-model",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div><input [(ng-model)]="name" required></div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
rootTC.componentInstance.name = "";
rootTC.detectChanges();
var input = rootTC.query(By.css("input")).nativeElement;
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-untouched"]);
dispatchEvent(input, "blur");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-touched"]);
input.value = "updatedValue";
dispatchEvent(input, "change");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-touched", "ng-dirty", "ng-valid"]);
async.done();
});
}));
});
describe("ng-model corner cases", () => {
it("should not update the view when the value initially came from the view",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var form = new Control("");
var t =
`<div><input type="text" [ng-form-control]="form" [(ng-model)]="name"></div>`;
var rootTC;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
(root) => { rootTC = root; });
tick();
rootTC.componentInstance.form = form;
rootTC.detectChanges();
// In Firefox, effective text selection in the real DOM requires an actual focus
// of the field. This is not an issue in a new HTML document.
if (browserDetection.isFirefox) {
var fakeDoc = DOM.createHtmlDocument();
DOM.appendChild(fakeDoc.body, rootTC.nativeElement);
}
var input = rootTC.query(By.css("input")).nativeElement;
input.value = "aa";
input.selectionStart = 1;
dispatchEvent(input, "change");
tick();
rootTC.detectChanges();
// selection start has not changed because we did not reset the value
expect(input.selectionStart).toEqual(1);
})));
});
});
}
@Directive({
selector: '[wrapped-value]',
host: {'(change)': 'handleOnChange($event.target.value)', '[value]': 'value'}
})
class WrappedValue implements ControlValueAccessor {
value;
onChange: Function;
constructor(cd: NgControl) { cd.valueAccessor = this; }
writeValue(value) { this.value = `!${value}!`; }
registerOnChange(fn) { this.onChange = fn; }
registerOnTouched(fn) {}
handleOnChange(value) { this.onChange(value.substring(1, value.length - 1)); }
}
@Component({selector: "my-comp"})
@View({directives: [FORM_DIRECTIVES, WrappedValue, NgIf, NgFor]})
class MyComp {
form: any;
name: string;
data: any;
}

View File

@ -0,0 +1,559 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
el,
AsyncTestCompleter,
fakeAsync,
tick,
inject
} from 'angular2/test_lib';
import {ControlGroup, Control, ControlArray, Validators} from 'angular2/forms';
import {ObservableWrapper} from 'angular2/src/core/facade/async';
export function main() {
describe("Form Model", () => {
describe("Control", () => {
it("should default the value to null", () => {
var c = new Control();
expect(c.value).toBe(null);
expect(c.validator).toBe(Validators.nullValidator);
});
describe("validator", () => {
it("should run validator with the initial value", () => {
var c = new Control("value", Validators.required);
expect(c.valid).toEqual(true);
});
it("should rerun the validator when the value changes", () => {
var c = new Control("value", Validators.required);
c.updateValue(null);
expect(c.valid).toEqual(false);
});
it("should return errors", () => {
var c = new Control(null, Validators.required);
expect(c.errors).toEqual({"required": true});
});
});
describe("dirty", () => {
it("should be false after creating a control", () => {
var c = new Control("value");
expect(c.dirty).toEqual(false);
});
it("should be true after changing the value of the control", () => {
var c = new Control("value");
c.markAsDirty();
expect(c.dirty).toEqual(true);
});
});
describe("updateValue", () => {
var g, c;
beforeEach(() => {
c = new Control("oldValue");
g = new ControlGroup({"one": c});
});
it("should update the value of the control", () => {
c.updateValue("newValue");
expect(c.value).toEqual("newValue");
});
it("should invoke onChanges if it is present", () => {
var onChanges;
c.registerOnChange((v) => onChanges = ["invoked", v]);
c.updateValue("newValue");
expect(onChanges).toEqual(["invoked", "newValue"]);
});
it("should not invoke on change when explicitly specified", () => {
var onChange = null;
c.registerOnChange((v) => onChange = ["invoked", v]);
c.updateValue("newValue", {emitModelToViewChange: false});
expect(onChange).toBeNull();
});
it("should update the parent", () => {
c.updateValue("newValue");
expect(g.value).toEqual({"one": "newValue"});
});
it("should not update the parent when explicitly specified", () => {
c.updateValue("newValue", {onlySelf: true});
expect(g.value).toEqual({"one": "oldValue"});
});
it("should fire an event", fakeAsync(() => {
ObservableWrapper.subscribe(c.valueChanges,
(value) => { expect(value).toEqual("newValue"); });
c.updateValue("newValue");
tick();
}));
it("should not fire an event when explicitly specified", fakeAsync(() => {
ObservableWrapper.subscribe(c.valueChanges, (value) => { throw "Should not happen"; });
c.updateValue("newValue", {emitEvent: false});
tick();
}));
});
describe("valueChanges", () => {
var c;
beforeEach(() => { c = new Control("old"); });
it("should fire an event after the value has been updated",
inject([AsyncTestCompleter], (async) => {
ObservableWrapper.subscribe(c.valueChanges, (value) => {
expect(c.value).toEqual('new');
expect(value).toEqual('new');
async.done();
});
c.updateValue("new");
}));
it("should return a cold observable", inject([AsyncTestCompleter], (async) => {
c.updateValue("will be ignored");
ObservableWrapper.subscribe(c.valueChanges, (value) => {
expect(value).toEqual('new');
async.done();
});
c.updateValue("new");
}));
});
});
describe("ControlGroup", () => {
describe("value", () => {
it("should be the reduced value of the child controls", () => {
var g = new ControlGroup({"one": new Control("111"), "two": new Control("222")});
expect(g.value).toEqual({"one": "111", "two": "222"});
});
it("should be empty when there are no child controls", () => {
var g = new ControlGroup({});
expect(g.value).toEqual({});
});
it("should support nested groups", () => {
var g = new ControlGroup(
{"one": new Control("111"), "nested": new ControlGroup({"two": new Control("222")})});
expect(g.value).toEqual({"one": "111", "nested": {"two": "222"}});
g.controls["nested"].controls["two"].updateValue("333");
expect(g.value).toEqual({"one": "111", "nested": {"two": "333"}});
});
});
describe("validator", () => {
it("should run the validator with the initial value (valid)", () => {
var g = new ControlGroup({"one": new Control('value', Validators.required)});
expect(g.valid).toEqual(true);
expect(g.errors).toEqual(null);
});
it("should run the validator with the initial value (invalid)", () => {
var one = new Control(null, Validators.required);
var g = new ControlGroup({"one": one});
expect(g.valid).toEqual(false);
expect(g.errors).toEqual({"required": [one]});
});
it("should run the validator with the value changes", () => {
var c = new Control(null, Validators.required);
var g = new ControlGroup({"one": c});
c.updateValue("some value");
expect(g.valid).toEqual(true);
expect(g.errors).toEqual(null);
});
});
describe("dirty", () => {
var c, g;
beforeEach(() => {
c = new Control('value');
g = new ControlGroup({"one": c});
});
it("should be false after creating a control", () => { expect(g.dirty).toEqual(false); });
it("should be false after changing the value of the control", () => {
c.markAsDirty();
expect(g.dirty).toEqual(true);
});
});
describe("optional components", () => {
describe("contains", () => {
var group;
beforeEach(() => {
group = new ControlGroup(
{
"required": new Control("requiredValue"),
"optional": new Control("optionalValue")
},
{"optional": false});
});
// rename contains into has
it("should return false when the component is not included",
() => { expect(group.contains("optional")).toEqual(false); })
it("should return false when there is no component with the given name",
() => { expect(group.contains("something else")).toEqual(false); });
it("should return true when the component is included", () => {
expect(group.contains("required")).toEqual(true);
group.include("optional");
expect(group.contains("optional")).toEqual(true);
});
});
it("should not include an inactive component into the group value", () => {
var group = new ControlGroup(
{"required": new Control("requiredValue"), "optional": new Control("optionalValue")},
{"optional": false});
expect(group.value).toEqual({"required": "requiredValue"});
group.include("optional");
expect(group.value).toEqual({"required": "requiredValue", "optional": "optionalValue"});
});
it("should not run Validators on an inactive component", () => {
var group = new ControlGroup(
{
"required": new Control("requiredValue", Validators.required),
"optional": new Control("", Validators.required)
},
{"optional": false});
expect(group.valid).toEqual(true);
group.include("optional");
expect(group.valid).toEqual(false);
});
describe("valueChanges", () => {
var g, c1, c2;
beforeEach(() => {
c1 = new Control("old1");
c2 = new Control("old2");
g = new ControlGroup({"one": c1, "two": c2}, {"two": true});
});
it("should fire an event after the value has been updated",
inject([AsyncTestCompleter], (async) => {
ObservableWrapper.subscribe(g.valueChanges, (value) => {
expect(g.value).toEqual({'one': 'new1', 'two': 'old2'});
expect(value).toEqual({'one': 'new1', 'two': 'old2'});
async.done();
});
c1.updateValue("new1");
}));
it("should fire an event after the control's observable fired an event",
inject([AsyncTestCompleter], (async) => {
var controlCallbackIsCalled = false;
ObservableWrapper.subscribe(c1.valueChanges,
(value) => { controlCallbackIsCalled = true; });
ObservableWrapper.subscribe(g.valueChanges, (value) => {
expect(controlCallbackIsCalled).toBe(true);
async.done();
});
c1.updateValue("new1");
}));
it("should fire an event when a control is excluded",
inject([AsyncTestCompleter], (async) => {
ObservableWrapper.subscribe(g.valueChanges, (value) => {
expect(value).toEqual({'one': 'old1'});
async.done();
});
g.exclude("two");
}));
it("should fire an event when a control is included",
inject([AsyncTestCompleter], (async) => {
g.exclude("two");
ObservableWrapper.subscribe(g.valueChanges, (value) => {
expect(value).toEqual({'one': 'old1', 'two': 'old2'});
async.done();
});
g.include("two");
}));
it("should fire an event every time a control is updated",
inject([AsyncTestCompleter], (async) => {
var loggedValues = [];
ObservableWrapper.subscribe(g.valueChanges, (value) => {
loggedValues.push(value);
if (loggedValues.length == 2) {
expect(loggedValues)
.toEqual([{"one": "new1", "two": "old2"}, {"one": "new1", "two": "new2"}]);
async.done();
}
});
c1.updateValue("new1");
c2.updateValue("new2");
}));
xit("should not fire an event when an excluded control is updated",
inject([AsyncTestCompleter], (async) => {
// hard to test without hacking zones
}));
});
describe("getError", () => {
it("should return the error when it is present", () => {
var c = new Control("", Validators.required);
var g = new ControlGroup({"one": c});
expect(c.getError("required")).toEqual(true);
expect(g.getError("required", ["one"])).toEqual(true);
});
it("should return null otherwise", () => {
var c = new Control("not empty", Validators.required);
var g = new ControlGroup({"one": c});
expect(c.getError("invalid")).toEqual(null);
expect(g.getError("required", ["one"])).toEqual(null);
expect(g.getError("required", ["invalid"])).toEqual(null);
});
});
});
});
describe("ControlArray", () => {
describe("adding/removing", () => {
var a: ControlArray;
var c1, c2, c3;
beforeEach(() => {
a = new ControlArray([]);
c1 = new Control(1);
c2 = new Control(2);
c3 = new Control(3);
});
it("should support pushing", () => {
a.push(c1);
expect(a.length).toEqual(1);
expect(a.controls).toEqual([c1]);
});
it("should support removing", () => {
a.push(c1);
a.push(c2);
a.push(c3);
a.removeAt(1);
expect(a.controls).toEqual([c1, c3]);
});
it("should support inserting", () => {
a.push(c1);
a.push(c3);
a.insert(1, c2);
expect(a.controls).toEqual([c1, c2, c3]);
});
});
describe("value", () => {
it("should be the reduced value of the child controls", () => {
var a = new ControlArray([new Control(1), new Control(2)]);
expect(a.value).toEqual([1, 2]);
});
it("should be an empty array when there are no child controls", () => {
var a = new ControlArray([]);
expect(a.value).toEqual([]);
});
});
describe("validator", () => {
it("should run the validator with the initial value (valid)", () => {
var a = new ControlArray(
[new Control(1, Validators.required), new Control(2, Validators.required)]);
expect(a.valid).toBe(true);
expect(a.errors).toBe(null);
});
it("should run the validator with the initial value (invalid)", () => {
var a = new ControlArray([
new Control(1, Validators.required),
new Control(null, Validators.required),
new Control(2, Validators.required)
]);
expect(a.valid).toBe(false);
expect(a.errors).toEqual({"required": [a.controls[1]]});
});
it("should run the validator when the value changes", () => {
var a = new ControlArray([]);
var c = new Control(null, Validators.required);
a.push(c);
expect(a.valid).toBe(false);
c.updateValue("some value");
expect(a.valid).toBe(true);
expect(a.errors).toBe(null);
});
});
describe("dirty", () => {
var c: Control;
var a: ControlArray;
beforeEach(() => {
c = new Control('value');
a = new ControlArray([c]);
});
it("should be false after creating a control", () => { expect(a.dirty).toEqual(false); });
it("should be false after changing the value of the control", () => {
c.markAsDirty();
expect(a.dirty).toEqual(true);
});
});
describe("valueChanges", () => {
var a: ControlArray;
var c1, c2;
beforeEach(() => {
c1 = new Control("old1");
c2 = new Control("old2");
a = new ControlArray([c1, c2]);
});
it("should fire an event after the value has been updated",
inject([AsyncTestCompleter], (async) => {
ObservableWrapper.subscribe(a.valueChanges, (value) => {
expect(a.value).toEqual(['new1', 'old2']);
expect(value).toEqual(['new1', 'old2']);
async.done();
});
c1.updateValue("new1");
}));
it("should fire an event after the control's observable fired an event",
inject([AsyncTestCompleter], (async) => {
var controlCallbackIsCalled = false;
ObservableWrapper.subscribe(c1.valueChanges,
(value) => { controlCallbackIsCalled = true; });
ObservableWrapper.subscribe(a.valueChanges, (value) => {
expect(controlCallbackIsCalled).toBe(true);
async.done();
});
c1.updateValue("new1");
}));
it("should fire an event when a control is removed",
inject([AsyncTestCompleter], (async) => {
ObservableWrapper.subscribe(a.valueChanges, (value) => {
expect(value).toEqual(['old1']);
async.done();
});
a.removeAt(1);
}));
it("should fire an event when a control is added", inject([AsyncTestCompleter], (async) => {
a.removeAt(1);
ObservableWrapper.subscribe(a.valueChanges, (value) => {
expect(value).toEqual(['old1', 'old2']);
async.done();
});
a.push(c2);
}));
});
});
describe("find", () => {
it("should return null when path is null", () => {
var g = new ControlGroup({});
expect(g.find(null)).toEqual(null);
});
it("should return null when path is empty", () => {
var g = new ControlGroup({});
expect(g.find([])).toEqual(null);
});
it("should return null when path is invalid", () => {
var g = new ControlGroup({});
expect(g.find(["one", "two"])).toEqual(null);
});
it("should return a child of a control group", () => {
var g = new ControlGroup(
{"one": new Control("111"), "nested": new ControlGroup({"two": new Control("222")})});
expect(g.find(["nested", "two"]).value).toEqual("222");
expect(g.find(["one"]).value).toEqual("111");
expect(g.find("nested/two").value).toEqual("222");
expect(g.find("one").value).toEqual("111");
});
it("should return an element of an array", () => {
var g = new ControlGroup({"array": new ControlArray([new Control("111")])});
expect(g.find(["array", 0]).value).toEqual("111");
});
});
});
}

View File

@ -0,0 +1,79 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
el
} from 'angular2/test_lib';
import {ControlGroup, Control, Validators} from 'angular2/forms';
export function main() {
function validator(key: string, error: any) {
return function(c: Control) {
var r = {};
r[key] = error;
return r;
}
}
describe("Validators", () => {
describe("required", () => {
it("should error on an empty string",
() => { expect(Validators.required(new Control(""))).toEqual({"required": true}); });
it("should error on null",
() => { expect(Validators.required(new Control(null))).toEqual({"required": true}); });
it("should not error on a non-empty string",
() => { expect(Validators.required(new Control("not empty"))).toEqual(null); });
});
describe("compose", () => {
it("should return a null validator when given null",
() => { expect(Validators.compose(null)).toBe(Validators.nullValidator); });
it("should collect errors from all the validators", () => {
var c = Validators.compose([validator("a", true), validator("b", true)]);
expect(c(new Control(""))).toEqual({"a": true, "b": true});
});
it("should run validators left to right", () => {
var c = Validators.compose([validator("a", 1), validator("a", 2)]);
expect(c(new Control(""))).toEqual({"a": 2});
});
it("should return null when no errors", () => {
var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
expect(c(new Control(""))).toEqual(null);
});
});
describe("controlGroupValidator", () => {
it("should collect errors from the child controls", () => {
var one = new Control("one", validator("a", true));
var two = new Control("one", validator("b", true));
var g = new ControlGroup({"one": one, "two": two});
expect(Validators.group(g)).toEqual({"a": [one], "b": [two]});
});
it("should not include controls that have no errors", () => {
var one = new Control("one", validator("a", true));
var two = new Control("two");
var g = new ControlGroup({"one": one, "two": two});
expect(Validators.group(g)).toEqual({"a": [one]});
});
it("should return null when no errors", () => {
var g = new ControlGroup({"one": new Control("one")});
expect(Validators.group(g)).toEqual(null);
});
});
});
}

View File

@ -0,0 +1,221 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
SpyObject,
browserDetection
} from 'angular2/test_lib';
import {SpyChangeDetectorRef} from './spies';
import {isBlank} from 'angular2/src/core/facade/lang';
import {WrappedValue} from 'angular2/change_detection';
import {AsyncPipe} from 'angular2/pipes';
import {
EventEmitter,
ObservableWrapper,
PromiseWrapper,
TimerWrapper
} from 'angular2/src/core/facade/async';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
export function main() {
describe("AsyncPipe", () => {
describe('Observable', () => {
var emitter;
var pipe;
var ref;
var message = new Object();
beforeEach(() => {
emitter = new EventEmitter();
ref = new SpyChangeDetectorRef();
pipe = new AsyncPipe(ref);
});
describe("transform", () => {
it("should return null when subscribing to an observable",
() => { expect(pipe.transform(emitter)).toBe(null); });
it("should return the latest available value wrapped",
inject([AsyncTestCompleter], (async) => {
pipe.transform(emitter);
ObservableWrapper.callNext(emitter, message);
TimerWrapper.setTimeout(() => {
expect(pipe.transform(emitter)).toEqual(new WrappedValue(message));
async.done();
}, 0)
}));
it("should return same value when nothing has changed since the last call",
inject([AsyncTestCompleter], (async) => {
pipe.transform(emitter);
ObservableWrapper.callNext(emitter, message);
TimerWrapper.setTimeout(() => {
pipe.transform(emitter);
expect(pipe.transform(emitter)).toBe(message);
async.done();
}, 0)
}));
it("should dispose of the existing subscription when subscribing to a new observable",
inject([AsyncTestCompleter], (async) => {
pipe.transform(emitter);
var newEmitter = new EventEmitter();
expect(pipe.transform(newEmitter)).toBe(null);
// this should not affect the pipe
ObservableWrapper.callNext(emitter, message);
TimerWrapper.setTimeout(() => {
expect(pipe.transform(newEmitter)).toBe(null);
async.done();
}, 0)
}));
it("should request a change detection check upon receiving a new value",
inject([AsyncTestCompleter], (async) => {
pipe.transform(emitter);
ObservableWrapper.callNext(emitter, message);
TimerWrapper.setTimeout(() => {
expect(ref.spy('markForCheck')).toHaveBeenCalled();
async.done();
}, 0)
}));
});
describe("onDestroy", () => {
it("should do nothing when no subscription",
() => { expect(() => pipe.onDestroy()).not.toThrow(); });
it("should dispose of the existing subscription", inject([AsyncTestCompleter], (async) => {
pipe.transform(emitter);
pipe.onDestroy();
ObservableWrapper.callNext(emitter, message);
TimerWrapper.setTimeout(() => {
expect(pipe.transform(emitter)).toBe(null);
async.done();
}, 0)
}));
});
});
describe("Promise", () => {
var message = new Object();
var pipe;
var completer;
var ref;
// adds longer timers for passing tests in IE
var timer = (!isBlank(DOM) && browserDetection.isIE) ? 50 : 0;
beforeEach(() => {
completer = PromiseWrapper.completer();
ref = new SpyChangeDetectorRef();
pipe = new AsyncPipe(ref);
});
describe("transform", () => {
it("should return null when subscribing to a promise",
() => { expect(pipe.transform(completer.promise)).toBe(null); });
it("should return the latest available value", inject([AsyncTestCompleter], (async) => {
pipe.transform(completer.promise);
completer.resolve(message);
TimerWrapper.setTimeout(() => {
expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message));
async.done();
}, timer)
}));
it("should return unwrapped value when nothing has changed since the last call",
inject([AsyncTestCompleter], (async) => {
pipe.transform(completer.promise);
completer.resolve(message);
TimerWrapper.setTimeout(() => {
pipe.transform(completer.promise);
expect(pipe.transform(completer.promise)).toBe(message);
async.done();
}, timer)
}));
it("should dispose of the existing subscription when subscribing to a new promise",
inject([AsyncTestCompleter], (async) => {
pipe.transform(completer.promise);
var newCompleter = PromiseWrapper.completer();
expect(pipe.transform(newCompleter.promise)).toBe(null);
// this should not affect the pipe, so it should return WrappedValue
completer.resolve(message);
TimerWrapper.setTimeout(() => {
expect(pipe.transform(newCompleter.promise)).toBe(null);
async.done();
}, timer)
}));
it("should request a change detection check upon receiving a new value",
inject([AsyncTestCompleter], (async) => {
pipe.transform(completer.promise);
completer.resolve(message);
TimerWrapper.setTimeout(() => {
expect(ref.spy('markForCheck')).toHaveBeenCalled();
async.done();
}, timer)
}));
describe("onDestroy", () => {
it("should do nothing when no source",
() => { expect(() => pipe.onDestroy()).not.toThrow(); });
it("should dispose of the existing source", inject([AsyncTestCompleter], (async) => {
pipe.transform(completer.promise);
expect(pipe.transform(completer.promise)).toBe(null);
completer.resolve(message)
TimerWrapper.setTimeout(() => {
expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message));
pipe.onDestroy();
expect(pipe.transform(completer.promise)).toBe(null);
async.done();
}, timer);
}));
});
});
});
describe('null', () => {
it('should return null when given null', () => {
var pipe = new AsyncPipe(null);
expect(pipe.transform(null, [])).toEqual(null);
});
});
describe('other types', () => {
it('should throw when given an invalid object', () => {
var pipe = new AsyncPipe(null);
expect(() => pipe.transform(<any>"some bogus object", [])).toThrowError();
});
});
});
}

View File

@ -0,0 +1,78 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
browserDetection
} from 'angular2/test_lib';
import {DatePipe} from 'angular2/pipes';
import {DateWrapper} from 'angular2/src/core/facade/lang';
export function main() {
describe("DatePipe", () => {
var date;
var pipe;
beforeEach(() => {
date = DateWrapper.create(2015, 6, 15, 21, 43, 11);
pipe = new DatePipe();
});
describe("supports", () => {
it("should support date", () => { expect(pipe.supports(date)).toBe(true); });
it("should support int", () => { expect(pipe.supports(123456789)).toBe(true); });
it("should not support other objects", () => {
expect(pipe.supports(new Object())).toBe(false);
expect(pipe.supports(null)).toBe(false);
});
});
// TODO(mlaval): enable tests when Intl API is no longer used, see
// https://github.com/angular/angular/issues/3333
if (browserDetection.supportsIntlApi) {
describe("transform", () => {
it('should format each component correctly', () => {
expect(pipe.transform(date, ['y'])).toEqual('2015');
expect(pipe.transform(date, ['yy'])).toEqual('15');
expect(pipe.transform(date, ['M'])).toEqual('6');
expect(pipe.transform(date, ['MM'])).toEqual('06');
expect(pipe.transform(date, ['MMM'])).toEqual('Jun');
expect(pipe.transform(date, ['MMMM'])).toEqual('June');
expect(pipe.transform(date, ['d'])).toEqual('15');
expect(pipe.transform(date, ['E'])).toEqual('Mon');
expect(pipe.transform(date, ['EEEE'])).toEqual('Monday');
expect(pipe.transform(date, ['H'])).toEqual('21');
expect(pipe.transform(date, ['j'])).toEqual('9 PM');
expect(pipe.transform(date, ['m'])).toEqual('43');
expect(pipe.transform(date, ['s'])).toEqual('11');
});
it('should format common multi component patterns', () => {
expect(pipe.transform(date, ['yMEd'])).toEqual('Mon, 6/15/2015');
expect(pipe.transform(date, ['MEd'])).toEqual('Mon, 6/15');
expect(pipe.transform(date, ['MMMd'])).toEqual('Jun 15');
expect(pipe.transform(date, ['yMMMMEEEEd'])).toEqual('Monday, June 15, 2015');
expect(pipe.transform(date, ['jms'])).toEqual('9:43:11 PM');
expect(pipe.transform(date, ['ms'])).toEqual('43:11');
});
it('should format with pattern aliases', () => {
expect(pipe.transform(date, ['medium'])).toEqual('Jun 15, 2015, 9:43:11 PM');
expect(pipe.transform(date, ['short'])).toEqual('6/15/2015, 9:43 PM');
expect(pipe.transform(date, ['fullDate'])).toEqual('Monday, June 15, 2015');
expect(pipe.transform(date, ['longDate'])).toEqual('June 15, 2015');
expect(pipe.transform(date, ['mediumDate'])).toEqual('Jun 15, 2015');
expect(pipe.transform(date, ['shortDate'])).toEqual('6/15/2015');
expect(pipe.transform(date, ['mediumTime'])).toEqual('9:43:11 PM');
expect(pipe.transform(date, ['shortTime'])).toEqual('9:43 PM');
});
});
}
});
}

View File

@ -0,0 +1,76 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
proxy,
SpyObject
} from 'angular2/test_lib';
import {Json, RegExp, NumberWrapper, StringWrapper} from 'angular2/src/core/facade/lang';
import {JsonPipe} from 'angular2/pipes';
export function main() {
describe("JsonPipe", () => {
var regNewLine = '\n';
var inceptionObj;
var inceptionObjString;
var pipe;
var collection: number[];
function normalize(obj: string): string { return StringWrapper.replace(obj, regNewLine, ''); }
beforeEach(() => {
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};
inceptionObjString = "{\n" + " \"dream\": {\n" + " \"dream\": {\n" +
" \"dream\": \"Limbo\"\n" + " }\n" + " }\n" + "}";
pipe = new JsonPipe();
collection = [];
});
describe("transform", () => {
it("should return JSON-formatted string",
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
it("should return JSON-formatted string even when normalized", () => {
var dream1 = normalize(pipe.transform(inceptionObj));
var dream2 = normalize(inceptionObjString);
expect(dream1).toEqual(dream2);
});
it("should return JSON-formatted string similar to Json.stringify", () => {
var dream1 = normalize(pipe.transform(inceptionObj));
var dream2 = normalize(Json.stringify(inceptionObj));
expect(dream1).toEqual(dream2);
});
it("should return same ref when nothing has changed since the last call", () => {
expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString);
expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString);
});
it("should return a new value when something changed but the ref hasn't", () => {
var stringCollection = '[]';
var stringCollectionWith1 = '[\n' +
' 1' +
'\n]';
expect(pipe.transform(collection)).toEqual(stringCollection);
collection.push(1);
expect(pipe.transform(collection)).toEqual(stringCollectionWith1);
});
});
});
}

View File

@ -0,0 +1,57 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
import {LimitToPipe} from 'angular2/pipes';
export function main() {
describe("LimitToPipe", () => {
var list;
var str;
var pipe;
beforeEach(() => {
list = [1, 2, 3, 4, 5];
str = 'tuvwxyz';
pipe = new LimitToPipe();
});
describe("supports", () => {
it("should support strings", () => { expect(pipe.supports(str)).toBe(true); });
it("should support lists", () => { expect(pipe.supports(list)).toBe(true); });
it("should not support other objects", () => {
expect(pipe.supports(new Object())).toBe(false);
expect(pipe.supports(null)).toBe(false);
});
});
describe("transform", () => {
it('should return the first X items when X is positive', () => {
expect(pipe.transform(list, [3])).toEqual([1, 2, 3]);
expect(pipe.transform(str, [3])).toEqual('tuv');
});
it('should return the last X items when X is negative', () => {
expect(pipe.transform(list, [-3])).toEqual([3, 4, 5]);
expect(pipe.transform(str, [-3])).toEqual('xyz');
});
it('should return a copy of input array if X is exceeds array length', () => {
expect(pipe.transform(list, [20])).toEqual(list);
expect(pipe.transform(list, [-20])).toEqual(list);
});
it('should return the entire string if X exceeds input length', () => {
expect(pipe.transform(str, [20])).toEqual(str);
expect(pipe.transform(str, [-20])).toEqual(str);
});
it('should not modify the input list', () => {
expect(pipe.transform(list, [3])).toEqual([1, 2, 3]);
expect(list).toEqual([1, 2, 3, 4, 5]);
});
});
});
}

View File

@ -0,0 +1,35 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
import {LowerCasePipe} from 'angular2/pipes';
export function main() {
describe("LowerCasePipe", () => {
var upper;
var lower;
var pipe;
beforeEach(() => {
lower = 'something';
upper = 'SOMETHING';
pipe = new LowerCasePipe();
});
describe("transform", () => {
it("should return lowercase", () => {
var val = pipe.transform(upper);
expect(val).toEqual(lower);
});
it("should lowercase when there is a new value", () => {
var val = pipe.transform(upper);
expect(val).toEqual(lower);
var val2 = pipe.transform('WAT');
expect(val2).toEqual('wat');
});
it("should not support other objects",
() => { expect(() => pipe.transform(new Object())).toThrowError(); });
});
});
}

View File

@ -0,0 +1,72 @@
import {
ddescribe,
describe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
browserDetection
} from 'angular2/test_lib';
import {DecimalPipe, PercentPipe, CurrencyPipe} from 'angular2/pipes';
export function main() {
// TODO(mlaval): enable tests when Intl API is no longer used, see
// https://github.com/angular/angular/issues/3333
if (browserDetection.supportsIntlApi) {
describe("DecimalPipe", () => {
var pipe;
beforeEach(() => { pipe = new DecimalPipe(); });
describe("transform", () => {
it('should return correct value for numbers', () => {
expect(pipe.transform(12345, [])).toEqual('12,345');
expect(pipe.transform(123, ['.2'])).toEqual('123.00');
expect(pipe.transform(1, ['3.'])).toEqual('001');
expect(pipe.transform(1.1, ['3.4-5'])).toEqual('001.1000');
expect(pipe.transform(1.123456, ['3.4-5'])).toEqual('001.12346');
expect(pipe.transform(1.1234, [])).toEqual('1.123');
});
it("should not support other objects",
() => { expect(() => pipe.transform(new Object(), [])).toThrowError(); });
});
});
describe("PercentPipe", () => {
var pipe;
beforeEach(() => { pipe = new PercentPipe(); });
describe("transform", () => {
it('should return correct value for numbers', () => {
expect(pipe.transform(1.23, [])).toEqual('123%');
expect(pipe.transform(1.2, ['.2'])).toEqual('120.00%');
});
it("should not support other objects",
() => { expect(() => pipe.transform(new Object(), [])).toThrowError(); });
});
});
describe("CurrencyPipe", () => {
var pipe;
beforeEach(() => { pipe = new CurrencyPipe(); });
describe("transform", () => {
it('should return correct value for numbers', () => {
expect(pipe.transform(123, [])).toEqual('USD123');
expect(pipe.transform(12, ['EUR', false, '.2'])).toEqual('EUR12.00');
});
it("should not support other objects",
() => { expect(() => pipe.transform(new Object(), [])).toThrowError(); });
});
});
}
}

View File

@ -0,0 +1,9 @@
library pipes.spies;
import 'package:angular2/src/core/change_detection/change_detection.dart';
import 'package:angular2/test_lib.dart';
@proxy
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {
noSuchMethod(m) => super.noSuchMethod(m);
}

View File

@ -0,0 +1,7 @@
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detection';
import {SpyObject, proxy} from 'angular2/test_lib';
export class SpyChangeDetectorRef extends SpyObject {
constructor() { super(ChangeDetectorRef); }
}

View File

@ -0,0 +1,36 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
import {UpperCasePipe} from 'angular2/pipes';
export function main() {
describe("UpperCasePipe", () => {
var upper;
var lower;
var pipe;
beforeEach(() => {
lower = 'something';
upper = 'SOMETHING';
pipe = new UpperCasePipe();
});
describe("transform", () => {
it("should return uppercase", () => {
var val = pipe.transform(lower);
expect(val).toEqual(upper);
});
it("should uppercase when there is a new value", () => {
var val = pipe.transform(lower);
expect(val).toEqual(upper);
var val2 = pipe.transform('wat');
expect(val2).toEqual('WAT');
});
it("should not support other objects",
() => { expect(() => pipe.transform(new Object())).toThrowError(); });
});
});
}