fix(forms): remove deprecated forms APIs (#10624)
BREAKING CHANGE: The deprecated forms APIs in @angular/common have been removed. Please update to the new forms API in @angular/forms. See angular.io for more information.
This commit is contained in:
@ -1,489 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CheckboxControlValueAccessor, Control, ControlGroup, ControlValueAccessor, DefaultValueAccessor, NgControl, NgControlGroup, NgControlName, NgForm, NgFormControl, NgFormModel, NgModel, SelectControlValueAccessor, Validator, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {composeValidators, selectValueAccessor} from '@angular/common/src/forms-deprecated/directives/shared';
|
||||
import {SimpleChange} from '@angular/core/src/change_detection';
|
||||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {SpyNgControl, SpyValueAccessor} from '../spies';
|
||||
|
||||
class DummyControlValueAccessor implements ControlValueAccessor {
|
||||
writtenValue: any /** TODO #9100 */;
|
||||
|
||||
registerOnChange(fn: any /** TODO #9100 */) {}
|
||||
registerOnTouched(fn: any /** TODO #9100 */) {}
|
||||
|
||||
writeValue(obj: any): void { this.writtenValue = obj; }
|
||||
}
|
||||
|
||||
class CustomValidatorDirective implements Validator {
|
||||
validate(c: Control): {[key: string]: any} { return {'custom': true}; }
|
||||
}
|
||||
|
||||
function asyncValidator(expected: any /** TODO #9100 */, timeout = 0) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
return new Promise((resolve) => {
|
||||
var res = c.value != expected ? {'async': true} : null;
|
||||
if (timeout == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => { resolve(res); }, timeout);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('Form Directives', () => {
|
||||
var defaultAccessor: DefaultValueAccessor;
|
||||
|
||||
beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null, null); });
|
||||
|
||||
describe('shared', () => {
|
||||
describe('selectValueAccessor', () => {
|
||||
var dir: NgControl;
|
||||
|
||||
beforeEach(() => { dir = <any>new SpyNgControl(); });
|
||||
|
||||
it('should throw when given an empty array',
|
||||
() => { expect(() => selectValueAccessor(dir, [])).toThrowError(); });
|
||||
|
||||
it('should return the default value accessor when no other provided',
|
||||
() => { expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor); });
|
||||
|
||||
it('should return checkbox accessor when provided', () => {
|
||||
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||
expect(selectValueAccessor(dir, [
|
||||
defaultAccessor, checkboxAccessor
|
||||
])).toEqual(checkboxAccessor);
|
||||
});
|
||||
|
||||
it('should return select accessor when provided', () => {
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null);
|
||||
expect(selectValueAccessor(dir, [
|
||||
defaultAccessor, selectAccessor
|
||||
])).toEqual(selectAccessor);
|
||||
});
|
||||
|
||||
it('should throw when more than one build-in accessor is provided', () => {
|
||||
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null);
|
||||
expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError();
|
||||
});
|
||||
|
||||
it('should return custom accessor when provided', () => {
|
||||
var customAccessor = new SpyValueAccessor();
|
||||
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||
expect(selectValueAccessor(dir, <any>[defaultAccessor, customAccessor, checkboxAccessor]))
|
||||
.toEqual(customAccessor);
|
||||
});
|
||||
|
||||
it('should throw when more than one custom accessor is provided', () => {
|
||||
var customAccessor: ControlValueAccessor = <any>new SpyValueAccessor();
|
||||
expect(() => selectValueAccessor(dir, [customAccessor, customAccessor])).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('composeValidators', () => {
|
||||
it('should compose functions', () => {
|
||||
var dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true});
|
||||
var dummy2 = (_: any /** TODO #9100 */) => ({'dummy2': true});
|
||||
var v = composeValidators([dummy1, dummy2]);
|
||||
expect(v(new Control(''))).toEqual({'dummy1': true, 'dummy2': true});
|
||||
});
|
||||
|
||||
it('should compose validator directives', () => {
|
||||
var dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true});
|
||||
var v = composeValidators([dummy1, new CustomValidatorDirective()]);
|
||||
expect(v(new Control(''))).toEqual({'dummy1': true, 'custom': true});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('NgFormModel', () => {
|
||||
var form: any /** TODO #9100 */;
|
||||
var formModel: ControlGroup;
|
||||
var loginControlDir: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
form = new NgFormModel([], []);
|
||||
formModel = new ControlGroup({
|
||||
'login': new Control(),
|
||||
'passwords':
|
||||
new ControlGroup({'password': new Control(), 'passwordConfirm': new Control()})
|
||||
});
|
||||
form.form = formModel;
|
||||
|
||||
loginControlDir = new NgControlName(
|
||||
form, [Validators.required], [asyncValidator('expected')], [defaultAccessor]);
|
||||
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, null, [defaultAccessor]);
|
||||
dir.name = 'invalidName';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
.toThrowError(new RegExp(`Cannot find control with name: 'invalidName'`));
|
||||
});
|
||||
|
||||
it('should throw when no value accessor', () => {
|
||||
var dir = new NgControlName(form, null, null, null);
|
||||
dir.name = 'login';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
.toThrowError(new RegExp(`No value accessor for form control with name: 'login'`));
|
||||
});
|
||||
|
||||
it('should throw when no value accessor with path', () => {
|
||||
const group = new NgControlGroup(form, null, null);
|
||||
const dir = new NgControlName(group, null, null, null);
|
||||
group.name = 'passwords';
|
||||
dir.name = 'password';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
.toThrowError(new RegExp(
|
||||
`No value accessor for form control with path: 'passwords -> password'`));
|
||||
});
|
||||
|
||||
it('should set up validators', fakeAsync(() => {
|
||||
form.addControl(loginControlDir);
|
||||
|
||||
// sync validators are set
|
||||
expect(formModel.hasError('required', ['login'])).toBe(true);
|
||||
expect(formModel.hasError('async', ['login'])).toBe(false);
|
||||
|
||||
(<Control>formModel.find(['login'])).updateValue('invalid value');
|
||||
|
||||
// sync validator passes, running async validators
|
||||
expect(formModel.pending).toBe(true);
|
||||
|
||||
tick();
|
||||
|
||||
expect(formModel.hasError('required', ['login'])).toBe(false);
|
||||
expect(formModel.hasError('async', ['login'])).toBe(true);
|
||||
}));
|
||||
|
||||
it('should write value to the DOM', () => {
|
||||
(<Control>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('addControlGroup', () => {
|
||||
var matchingPasswordsValidator = (g: any /** TODO #9100 */) => {
|
||||
if (g.controls['password'].value != g.controls['passwordConfirm'].value) {
|
||||
return {'differentPasswords': true};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
it('should set up validator', fakeAsync(() => {
|
||||
var group = new NgControlGroup(
|
||||
form, [matchingPasswordsValidator], [asyncValidator('expected')]);
|
||||
group.name = 'passwords';
|
||||
form.addControlGroup(group);
|
||||
|
||||
(<Control>formModel.find(['passwords', 'password'])).updateValue('somePassword');
|
||||
(<Control>formModel.find([
|
||||
'passwords', 'passwordConfirm'
|
||||
])).updateValue('someOtherPassword');
|
||||
|
||||
// sync validators are set
|
||||
expect(formModel.hasError('differentPasswords', ['passwords'])).toEqual(true);
|
||||
|
||||
(<Control>formModel.find([
|
||||
'passwords', 'passwordConfirm'
|
||||
])).updateValue('somePassword');
|
||||
|
||||
// sync validators pass, running async validators
|
||||
expect(formModel.pending).toBe(true);
|
||||
|
||||
tick();
|
||||
|
||||
expect(formModel.hasError('async', ['passwords'])).toBe(true);
|
||||
}));
|
||||
});
|
||||
|
||||
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('ngOnChanges', () => {
|
||||
it('should update dom values of all the directives', () => {
|
||||
form.addControl(loginControlDir);
|
||||
|
||||
(<Control>formModel.find(['login'])).updateValue('new value');
|
||||
|
||||
form.ngOnChanges({});
|
||||
|
||||
expect((<any>loginControlDir.valueAccessor).writtenValue).toEqual('new value');
|
||||
});
|
||||
|
||||
it('should set up a sync validator', () => {
|
||||
var formValidator = (c: any /** TODO #9100 */) => ({'custom': true});
|
||||
var f = new NgFormModel([formValidator], []);
|
||||
f.form = formModel;
|
||||
f.ngOnChanges({'form': new SimpleChange(null, null)});
|
||||
|
||||
expect(formModel.errors).toEqual({'custom': true});
|
||||
});
|
||||
|
||||
it('should set up an async validator', fakeAsync(() => {
|
||||
var f = new NgFormModel([], [asyncValidator('expected')]);
|
||||
f.form = formModel;
|
||||
f.ngOnChanges({'form': new SimpleChange(null, null)});
|
||||
|
||||
tick();
|
||||
|
||||
expect(formModel.errors).toEqual({'async': true});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('NgForm', () => {
|
||||
var form: any /** TODO #9100 */;
|
||||
var formModel: ControlGroup;
|
||||
var loginControlDir: any /** TODO #9100 */;
|
||||
var personControlGroupDir: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
form = new NgForm([], []);
|
||||
formModel = form.form;
|
||||
|
||||
personControlGroupDir = new NgControlGroup(form, [], []);
|
||||
personControlGroupDir.name = 'person';
|
||||
|
||||
loginControlDir = new NgControlName(personControlGroupDir, null, null, [defaultAccessor]);
|
||||
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
|
||||
});
|
||||
|
||||
it('should set up sync validator', fakeAsync(() => {
|
||||
var formValidator = (c: any /** TODO #9100 */) => ({'custom': true});
|
||||
var f = new NgForm([formValidator], []);
|
||||
|
||||
tick();
|
||||
|
||||
expect(f.form.errors).toEqual({'custom': true});
|
||||
}));
|
||||
|
||||
it('should set up async validator', fakeAsync(() => {
|
||||
var f = new NgForm([], [asyncValidator('expected')]);
|
||||
|
||||
tick();
|
||||
|
||||
expect(f.form.errors).toEqual({'async': true});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('NgControlGroup', () => {
|
||||
var formModel: any /** TODO #9100 */;
|
||||
var controlGroupDir: any /** TODO #9100 */;
|
||||
|
||||
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: any /** TODO #9100 */;
|
||||
var control: any /** TODO #9100 */;
|
||||
var checkProperties = function(control: any /** TODO #9100 */) {
|
||||
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);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
controlDir = new NgFormControl([Validators.required], [], [defaultAccessor]);
|
||||
controlDir.valueAccessor = new DummyControlValueAccessor();
|
||||
|
||||
control = new Control(null);
|
||||
controlDir.form = control;
|
||||
});
|
||||
|
||||
it('should reexport control properties', () => { checkProperties(control); });
|
||||
|
||||
it('should reexport new control properties', () => {
|
||||
var newControl = new Control(null);
|
||||
controlDir.form = newControl;
|
||||
controlDir.ngOnChanges({'form': new SimpleChange(control, newControl)});
|
||||
|
||||
checkProperties(newControl);
|
||||
});
|
||||
|
||||
it('should set up validator', () => {
|
||||
expect(control.valid).toBe(true);
|
||||
|
||||
// this will add the required validator and recalculate the validity
|
||||
controlDir.ngOnChanges({'form': new SimpleChange(null, control)});
|
||||
|
||||
expect(control.valid).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('NgModel', () => {
|
||||
var ngModel: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
ngModel =
|
||||
new NgModel([Validators.required], [asyncValidator('expected')], [defaultAccessor]);
|
||||
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 throw when no value accessor with unnamed control', () => {
|
||||
const unnamedDir = new NgModel(null, null, null);
|
||||
|
||||
expect(() => unnamedDir.ngOnChanges({}))
|
||||
.toThrowError(new RegExp(`No value accessor for form control with unspecified name`));
|
||||
});
|
||||
|
||||
|
||||
it('should set up validator', fakeAsync(() => {
|
||||
// this will add the required validator and recalculate the validity
|
||||
ngModel.ngOnChanges({});
|
||||
tick();
|
||||
|
||||
expect(ngModel.control.errors).toEqual({'required': true});
|
||||
|
||||
ngModel.control.updateValue('someValue');
|
||||
tick();
|
||||
|
||||
expect(ngModel.control.errors).toEqual({'async': true});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('NgControlName', () => {
|
||||
var formModel: any /** TODO #9100 */;
|
||||
var controlNameDir: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
formModel = new Control('name');
|
||||
|
||||
var parent = new NgFormModel([], []);
|
||||
parent.form = new ControlGroup({'name': formModel});
|
||||
controlNameDir = new NgControlName(parent, [], [], [defaultAccessor]);
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Control, FormBuilder} from '@angular/common/src/forms-deprecated';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
function syncValidator(_: any): any { return null; }
|
||||
function asyncValidator(_: any) { return Promise.resolve(null); }
|
||||
|
||||
describe('Form Builder', () => {
|
||||
var b: any /** TODO #9100 */;
|
||||
|
||||
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', syncValidator, asyncValidator]});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['password'].value).toEqual('some value');
|
||||
expect(g.controls['password'].validator).toEqual(syncValidator);
|
||||
expect(g.controls['password'].asyncValidator).toEqual(asyncValidator);
|
||||
});
|
||||
|
||||
it('should use controls', () => {
|
||||
var g = b.group({'login': b.control('some value', syncValidator, asyncValidator)});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
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': syncValidator, 'asyncValidator': asyncValidator});
|
||||
|
||||
expect(g.validator).toBe(syncValidator);
|
||||
expect(g.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create control arrays', () => {
|
||||
var c = b.control('three');
|
||||
var a = b.array(
|
||||
['one', ['two', syncValidator], c, b.array(['four'])], syncValidator, asyncValidator);
|
||||
|
||||
expect(a.value).toEqual(['one', 'two', 'three', ['four']]);
|
||||
expect(a.validator).toBe(syncValidator);
|
||||
expect(a.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
});
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,856 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
function asyncValidator(expected: any /** TODO #9100 */, timeouts = {}) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
return new Promise((resolve) => {
|
||||
var t = isPresent((timeouts as any /** TODO #9100 */)[c.value]) ?
|
||||
(timeouts as any /** TODO #9100 */)[c.value] :
|
||||
0;
|
||||
var res = c.value != expected ? {'async': true} : null;
|
||||
|
||||
if (t == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => { resolve(res); }, t);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function asyncValidatorReturningObservable(c: any /** TODO #9100 */) {
|
||||
var e = new EventEmitter();
|
||||
Promise.resolve(null).then(() => { e.emit({'async': true}); });
|
||||
return e;
|
||||
}
|
||||
|
||||
describe('Form Model', () => {
|
||||
describe('Control', () => {
|
||||
it('should default the value to null', () => {
|
||||
var c = new Control();
|
||||
expect(c.value).toBe(null);
|
||||
});
|
||||
|
||||
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('asyncValidator', () => {
|
||||
it('should run validator with the initial value', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
tick();
|
||||
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'async': true});
|
||||
}));
|
||||
|
||||
it('should support validators returning observables', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidatorReturningObservable);
|
||||
tick();
|
||||
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'async': true});
|
||||
}));
|
||||
|
||||
it('should rerun the validator when the value changes', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
|
||||
c.updateValue('expected');
|
||||
tick();
|
||||
|
||||
expect(c.valid).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should run the async validator only when the sync validator passes', fakeAsync(() => {
|
||||
var c = new Control('', Validators.required, asyncValidator('expected'));
|
||||
tick();
|
||||
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
|
||||
c.updateValue('some value');
|
||||
tick();
|
||||
|
||||
expect(c.errors).toEqual({'async': true});
|
||||
}));
|
||||
|
||||
it('should mark the control as pending while running the async validation',
|
||||
fakeAsync(() => {
|
||||
var c = new Control('', null, asyncValidator('expected'));
|
||||
|
||||
expect(c.pending).toEqual(true);
|
||||
|
||||
tick();
|
||||
|
||||
expect(c.pending).toEqual(false);
|
||||
}));
|
||||
|
||||
it('should only use the latest async validation run', fakeAsync(() => {
|
||||
var c =
|
||||
new Control('', null, asyncValidator('expected', {'long': 200, 'expected': 100}));
|
||||
|
||||
c.updateValue('long');
|
||||
c.updateValue('expected');
|
||||
|
||||
tick(300);
|
||||
|
||||
expect(c.valid).toEqual(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: any /** TODO #9100 */, c: any /** TODO #9100 */;
|
||||
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 ngOnChanges if it is present', () => {
|
||||
var ngOnChanges: any /** TODO #9100 */;
|
||||
c.registerOnChange((v: any /** TODO #9100 */) => ngOnChanges = ['invoked', v]);
|
||||
|
||||
c.updateValue('newValue');
|
||||
|
||||
expect(ngOnChanges).toEqual(['invoked', 'newValue']);
|
||||
});
|
||||
|
||||
it('should not invoke on change when explicitly specified', () => {
|
||||
var onChange: any /** TODO #9100 */ = null;
|
||||
c.registerOnChange((v: any /** TODO #9100 */) => 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(() => {
|
||||
|
||||
c.valueChanges.subscribe(
|
||||
{next: (value: any) => { expect(value).toEqual('newValue'); }});
|
||||
|
||||
c.updateValue('newValue');
|
||||
tick();
|
||||
}));
|
||||
|
||||
it('should not fire an event when explicitly specified', fakeAsync(() => {
|
||||
c.valueChanges.subscribe({next: (value: any) => { throw 'Should not happen'; }});
|
||||
|
||||
c.updateValue('newValue', {emitEvent: false});
|
||||
|
||||
tick();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('valueChanges & statusChanges', () => {
|
||||
var c: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => { c = new Control('old', Validators.required); });
|
||||
|
||||
it('should fire an event after the value has been updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(c.value).toEqual('new');
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
c.updateValue('new');
|
||||
}));
|
||||
|
||||
it('should fire an event after the status has been updated to invalid', fakeAsync(() => {
|
||||
c.statusChanges.subscribe({
|
||||
next: (status: any) => {
|
||||
expect(c.status).toEqual('INVALID');
|
||||
expect(status).toEqual('INVALID');
|
||||
}
|
||||
});
|
||||
|
||||
c.updateValue('');
|
||||
tick();
|
||||
}));
|
||||
|
||||
it('should fire an event after the status has been updated to pending', fakeAsync(() => {
|
||||
var c = new Control('old', Validators.required, asyncValidator('expected'));
|
||||
|
||||
var log: any[] /** TODO #9100 */ = [];
|
||||
c.valueChanges.subscribe({next: (value: any) => log.push(`value: '${value}'`)});
|
||||
|
||||
c.statusChanges.subscribe({next: (status: any) => log.push(`status: '${status}'`)});
|
||||
|
||||
c.updateValue('');
|
||||
tick();
|
||||
|
||||
c.updateValue('nonEmpty');
|
||||
tick();
|
||||
|
||||
c.updateValue('expected');
|
||||
tick();
|
||||
|
||||
expect(log).toEqual([
|
||||
'' +
|
||||
'value: \'\'',
|
||||
'status: \'INVALID\'',
|
||||
'value: \'nonEmpty\'',
|
||||
'status: \'PENDING\'',
|
||||
'status: \'INVALID\'',
|
||||
'value: \'expected\'',
|
||||
'status: \'PENDING\'',
|
||||
'status: \'VALID\'',
|
||||
]);
|
||||
}));
|
||||
|
||||
// TODO: remove the if statement after making observable delivery sync
|
||||
it('should update set errors and status before emitting an event',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.valueChanges.subscribe((value: any /** TODO #9100 */) => {
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
async.done();
|
||||
});
|
||||
c.updateValue('');
|
||||
}));
|
||||
|
||||
it('should return a cold observable',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.updateValue('will be ignored');
|
||||
c.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
c.updateValue('new');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('setErrors', () => {
|
||||
it('should set errors on a control', () => {
|
||||
var c = new Control('someValue');
|
||||
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'someError': true});
|
||||
});
|
||||
|
||||
it('should reset the errors and validity when the value changes', () => {
|
||||
var c = new Control('someValue', Validators.required);
|
||||
|
||||
c.setErrors({'someError': true});
|
||||
c.updateValue('');
|
||||
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
});
|
||||
|
||||
it('should update the parent group\'s validity', () => {
|
||||
var c = new Control('someValue');
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
expect(g.valid).toEqual(true);
|
||||
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
expect(g.valid).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not reset parent\'s errors', () => {
|
||||
var c = new Control('someValue');
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
g.setErrors({'someGroupError': true});
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
expect(g.errors).toEqual({'someGroupError': true});
|
||||
});
|
||||
|
||||
it('should reset errors when updating a value', () => {
|
||||
var c = new Control('oldValue');
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
g.setErrors({'someGroupError': true});
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
c.updateValue('newValue');
|
||||
|
||||
expect(c.errors).toEqual(null);
|
||||
expect(g.errors).toEqual(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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'}});
|
||||
|
||||
(<Control>(g.controls['nested'].find('two'))).updateValue('333');
|
||||
|
||||
expect(g.value).toEqual({'one': '111', 'nested': {'two': '333'}});
|
||||
});
|
||||
});
|
||||
|
||||
describe('adding and removing controls', () => {
|
||||
it('should update value and validity when control is added', () => {
|
||||
var g = new ControlGroup({'one': new Control('1')});
|
||||
expect(g.value).toEqual({'one': '1'});
|
||||
expect(g.valid).toBe(true);
|
||||
|
||||
g.addControl('two', new Control('2', Validators.minLength(10)));
|
||||
|
||||
expect(g.value).toEqual({'one': '1', 'two': '2'});
|
||||
expect(g.valid).toBe(false);
|
||||
});
|
||||
|
||||
it('should update value and validity when control is removed', () => {
|
||||
var g = new ControlGroup(
|
||||
{'one': new Control('1'), 'two': new Control('2', Validators.minLength(10))});
|
||||
expect(g.value).toEqual({'one': '1', 'two': '2'});
|
||||
expect(g.valid).toBe(false);
|
||||
|
||||
g.removeControl('two');
|
||||
|
||||
expect(g.value).toEqual({'one': '1'});
|
||||
expect(g.valid).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('should run the validator when the value changes', () => {
|
||||
var simpleValidator = (c: any /** TODO #9100 */) =>
|
||||
c.controls['one'].value != 'correct' ? {'broken': true} : null;
|
||||
|
||||
var c = new Control(null);
|
||||
var g = new ControlGroup({'one': c}, null, simpleValidator);
|
||||
|
||||
c.updateValue('correct');
|
||||
|
||||
expect(g.valid).toEqual(true);
|
||||
expect(g.errors).toEqual(null);
|
||||
|
||||
c.updateValue('incorrect');
|
||||
|
||||
expect(g.valid).toEqual(false);
|
||||
expect(g.errors).toEqual({'broken': true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dirty', () => {
|
||||
var c: any /** TODO #9100 */, g: any /** TODO #9100 */;
|
||||
|
||||
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: any /** TODO #9100 */;
|
||||
|
||||
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: any /** TODO #9100 */, c1: any /** TODO #9100 */, c2: any /** TODO #9100 */;
|
||||
|
||||
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: AsyncTestCompleter) => {
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
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: AsyncTestCompleter) => {
|
||||
var controlCallbackIsCalled = false;
|
||||
|
||||
|
||||
c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }});
|
||||
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is excluded',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual({'one': 'old1'});
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
g.exclude('two');
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is included',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
g.exclude('two');
|
||||
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
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: AsyncTestCompleter) => {
|
||||
var loggedValues: any[] /** TODO #9100 */ = [];
|
||||
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
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: AsyncTestCompleter) => {
|
||||
// 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('asyncValidator', () => {
|
||||
it('should run the async validator', fakeAsync(() => {
|
||||
var c = new Control('value');
|
||||
var g = new ControlGroup({'one': c}, null, null, asyncValidator('expected'));
|
||||
|
||||
expect(g.pending).toEqual(true);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.pending).toEqual(false);
|
||||
}));
|
||||
|
||||
it('should set the parent group\'s status to pending', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
expect(g.pending).toEqual(true);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.pending).toEqual(false);
|
||||
}));
|
||||
|
||||
it('should run the parent group\'s async validator when children are pending',
|
||||
fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
var g = new ControlGroup({'one': c}, null, null, asyncValidator('expected'));
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.find(['one']).errors).toEqual({'async': true});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('ControlArray', () => {
|
||||
describe('adding/removing', () => {
|
||||
var a: ControlArray;
|
||||
var c1: any /** TODO #9100 */, c2: any /** TODO #9100 */, c3: any /** TODO #9100 */;
|
||||
|
||||
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('errors', () => {
|
||||
it('should run the validator when the value changes', () => {
|
||||
var simpleValidator = (c: any /** TODO #9100 */) =>
|
||||
c.controls[0].value != 'correct' ? {'broken': true} : null;
|
||||
|
||||
var c = new Control(null);
|
||||
var g = new ControlArray([c], simpleValidator);
|
||||
|
||||
c.updateValue('correct');
|
||||
|
||||
expect(g.valid).toEqual(true);
|
||||
expect(g.errors).toEqual(null);
|
||||
|
||||
c.updateValue('incorrect');
|
||||
|
||||
expect(g.valid).toEqual(false);
|
||||
expect(g.errors).toEqual({'broken': true});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
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('pending', () => {
|
||||
var c: Control;
|
||||
var a: ControlArray;
|
||||
|
||||
beforeEach(() => {
|
||||
c = new Control('value');
|
||||
a = new ControlArray([c]);
|
||||
});
|
||||
|
||||
it('should be false after creating a control', () => {
|
||||
expect(c.pending).toEqual(false);
|
||||
expect(a.pending).toEqual(false);
|
||||
});
|
||||
|
||||
it('should be true after changing the value of the control', () => {
|
||||
c.markAsPending();
|
||||
|
||||
expect(c.pending).toEqual(true);
|
||||
expect(a.pending).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not update the parent when onlySelf = true', () => {
|
||||
c.markAsPending({onlySelf: true});
|
||||
|
||||
expect(c.pending).toEqual(true);
|
||||
expect(a.pending).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueChanges', () => {
|
||||
var a: ControlArray;
|
||||
var c1: any /** TODO #9100 */, c2: any /** TODO #9100 */;
|
||||
|
||||
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: AsyncTestCompleter) => {
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
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: AsyncTestCompleter) => {
|
||||
var controlCallbackIsCalled = false;
|
||||
|
||||
|
||||
c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }});
|
||||
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is removed',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual(['old1']);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
a.removeAt(1);
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is added',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
a.removeAt(1);
|
||||
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
describe('asyncValidator', () => {
|
||||
it('should run the async validator', fakeAsync(() => {
|
||||
var c = new Control('value');
|
||||
var g = new ControlArray([c], null, asyncValidator('expected'));
|
||||
|
||||
expect(g.pending).toEqual(true);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.pending).toEqual(false);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbstractControl, Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
import {normalizeAsyncValidator} from '../../src/forms-deprecated/directives/normalize_validator';
|
||||
|
||||
export function main() {
|
||||
function validator(key: string, error: any) {
|
||||
return function(c: AbstractControl) {
|
||||
var r = {};
|
||||
(r as any)[key] = error;
|
||||
return r;
|
||||
};
|
||||
}
|
||||
|
||||
class AsyncValidatorDirective {
|
||||
constructor(private expected: string, private error: any) {}
|
||||
|
||||
validate(c: any): {[key: string]: any;} {
|
||||
return Observable.create((obs: any) => {
|
||||
const error = this.expected !== c.value ? this.error : null;
|
||||
obs.next(error);
|
||||
obs.complete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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); });
|
||||
|
||||
it('should accept zero as valid',
|
||||
() => { expect(Validators.required(new Control(0))).toEqual(null); });
|
||||
});
|
||||
|
||||
describe('minLength', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.minLength(2)(new Control(''))).toEqual(null); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.minLength(2)(new Control(null))).toEqual(null); });
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.minLength(2)(new Control('aa'))).toEqual(null); });
|
||||
|
||||
it('should error on short strings', () => {
|
||||
expect(Validators.minLength(2)(new Control('a'))).toEqual({
|
||||
'minlength': {'requiredLength': 2, 'actualLength': 1}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('maxLength', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.maxLength(2)(new Control(''))).toEqual(null); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.maxLength(2)(new Control(null))).toEqual(null); });
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.maxLength(2)(new Control('aa'))).toEqual(null); });
|
||||
|
||||
it('should error on long strings', () => {
|
||||
expect(Validators.maxLength(2)(new Control('aaa'))).toEqual({
|
||||
'maxlength': {'requiredLength': 2, 'actualLength': 3}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('pattern', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control(''))).toEqual(null); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control(null))).toEqual(null); });
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control('aaAA'))).toEqual(null); });
|
||||
|
||||
it('should error on failure to match string', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]*')(new Control('aaa0'))).toEqual({
|
||||
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
|
||||
const c = Validators.composeAsync(
|
||||
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]);
|
||||
|
||||
let value: any = null;
|
||||
c(new Control()).then((v: any) => value = v);
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual({'one': true});
|
||||
}));
|
||||
|
||||
describe('compose', () => {
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.compose(null)).toBe(null); });
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('should ignore nulls', () => {
|
||||
var c = Validators.compose([null, Validators.required]);
|
||||
expect(c(new Control(''))).toEqual({'required': true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('composeAsync', () => {
|
||||
function asyncValidator(expected: any /** TODO #9100 */, response: any /** TODO #9100 */) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
var emitter = new EventEmitter();
|
||||
var res = c.value != expected ? response : null;
|
||||
Promise.resolve(null).then(() => {
|
||||
emitter.emit(res);
|
||||
// this is required because of a bug in ObservableWrapper
|
||||
// where callComplete can fire before callEmit
|
||||
// remove this one the bug is fixed
|
||||
setTimeout(() => { emitter.complete(); }, 0);
|
||||
});
|
||||
|
||||
return emitter;
|
||||
};
|
||||
}
|
||||
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.composeAsync(null)).toEqual(null); });
|
||||
|
||||
it('should collect errors from all the validators', fakeAsync(() => {
|
||||
var c = Validators.composeAsync([
|
||||
asyncValidator('expected', {'one': true}), asyncValidator('expected', {'two': true})
|
||||
]);
|
||||
|
||||
var value: any /** TODO #9100 */ = null;
|
||||
(<Promise<any>>c(new Control('invalid'))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual({'one': true, 'two': true});
|
||||
}));
|
||||
|
||||
it('should return null when no errors', fakeAsync(() => {
|
||||
var c = Validators.composeAsync([asyncValidator('expected', {'one': true})]);
|
||||
|
||||
var value: any /** TODO #9100 */ = null;
|
||||
(<Promise<any>>c(new Control('expected'))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should ignore nulls', fakeAsync(() => {
|
||||
var c = Validators.composeAsync([asyncValidator('expected', {'one': true}), null]);
|
||||
|
||||
var value: any /** TODO #9100 */ = null;
|
||||
(<Promise<any>>c(new Control('invalid'))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual({'one': true});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user