test(ivy): run forms tests with ivy on ci (#26968)

PR Close #26968
This commit is contained in:
Kara Erickson 2018-11-06 14:02:22 -08:00
parent 297c54ebb3
commit 3ca1a57f81
4 changed files with 388 additions and 362 deletions

View File

@ -10,6 +10,7 @@ ts_library(
"//packages/forms", "//packages/forms",
"//packages/platform-browser", "//packages/platform-browser",
"//packages/platform-browser/testing", "//packages/platform-browser/testing",
"//packages/private/testing",
"@rxjs", "@rxjs",
"@rxjs//operators", "@rxjs//operators",
], ],
@ -18,9 +19,6 @@ ts_library(
jasmine_node_test( jasmine_node_test(
name = "test", name = "test",
bootstrap = ["angular/tools/testing/init_node_spec.js"], bootstrap = ["angular/tools/testing/init_node_spec.js"],
tags = [
"fixme-ivy-aot",
],
deps = [ deps = [
":test_lib", ":test_lib",
"//tools/testing:node", "//tools/testing:node",
@ -29,9 +27,6 @@ jasmine_node_test(
ts_web_test_suite( ts_web_test_suite(
name = "test_web", name = "test_web",
tags = [
"fixme-ivy-aot",
],
deps = [ deps = [
":test_lib", ":test_lib",
], ],

View File

@ -12,6 +12,7 @@ import {AbstractControl, AsyncValidator, AsyncValidatorFn, COMPOSITION_BUFFER_MO
import {By} from '@angular/platform-browser/src/dom/debug/by'; import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'; import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
import {fixmeIvy} from '@angular/private/testing';
import {merge, timer} from 'rxjs'; import {merge, timer} from 'rxjs';
import {tap} from 'rxjs/operators'; import {tap} from 'rxjs/operators';
@ -712,127 +713,133 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
}); });
describe('setting status classes', () => { fixmeIvy('Host bindings to styles do not yet work') &&
it('should work with single fields', () => { describe('setting status classes', () => {
const fixture = initTest(FormControlComp); it('should work with single fields', () => {
const control = new FormControl('', Validators.required); const fixture = initTest(FormControlComp);
fixture.componentInstance.control = control; const control = new FormControl('', Validators.required);
fixture.detectChanges(); fixture.componentInstance.control = control;
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement; const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']); expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
input.value = 'updatedValue'; input.value = 'updatedValue';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
}); });
it('should work with single fields and async validators', fakeAsync(() => { it('should work with single fields and async validators', fakeAsync(() => {
const fixture = initTest(FormControlComp); const fixture = initTest(FormControlComp);
const control = new FormControl('', null !, uniqLoginAsyncValidator('good')); const control = new FormControl('', null !, uniqLoginAsyncValidator('good'));
fixture.debugElement.componentInstance.control = control; fixture.debugElement.componentInstance.control = control;
fixture.detectChanges(); fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement; const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(input)).toEqual([
'ng-pending', 'ng-pristine', 'ng-untouched'
]);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-touched']); expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-touched']);
input.value = 'good'; input.value = 'good';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
tick(); tick();
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
})); }));
it('should work with single fields that combines async and sync validators', fakeAsync(() => { it('should work with single fields that combines async and sync validators',
const fixture = initTest(FormControlComp); fakeAsync(() => {
const control = const fixture = initTest(FormControlComp);
new FormControl('', Validators.required, uniqLoginAsyncValidator('good')); const control =
fixture.debugElement.componentInstance.control = control; new FormControl('', Validators.required, uniqLoginAsyncValidator('good'));
fixture.detectChanges(); fixture.debugElement.componentInstance.control = control;
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement; const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(input)).toEqual([
'ng-invalid', 'ng-pristine', 'ng-untouched'
]);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']); expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
input.value = 'bad'; input.value = 'bad';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-pending', 'ng-touched']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-pending', 'ng-touched']);
tick(); tick();
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-invalid', 'ng-touched']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-invalid', 'ng-touched']);
input.value = 'good'; input.value = 'good';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
tick(); tick();
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
})); }));
it('should work with single fields in parent forms', () => { it('should work with single fields in parent forms', () => {
const fixture = initTest(FormGroupComp); const fixture = initTest(FormGroupComp);
const form = new FormGroup({'login': new FormControl('', Validators.required)}); const form = new FormGroup({'login': new FormControl('', Validators.required)});
fixture.componentInstance.form = form; fixture.componentInstance.form = form;
fixture.detectChanges(); fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement; const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']); expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
input.value = 'updatedValue'; input.value = 'updatedValue';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
}); });
it('should work with formGroup', () => { it('should work with formGroup', () => {
const fixture = initTest(FormGroupComp); const fixture = initTest(FormGroupComp);
const form = new FormGroup({'login': new FormControl('', Validators.required)}); const form = new FormGroup({'login': new FormControl('', Validators.required)});
fixture.componentInstance.form = form; fixture.componentInstance.form = form;
fixture.detectChanges(); fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement; const input = fixture.debugElement.query(By.css('input')).nativeElement;
const formEl = fixture.debugElement.query(By.css('form')).nativeElement; const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
expect(sortedClassList(formEl)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(formEl)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(formEl)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']); expect(sortedClassList(formEl)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
input.value = 'updatedValue'; input.value = 'updatedValue';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(formEl)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(formEl)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
}); });
}); });
describe('updateOn options', () => { describe('updateOn options', () => {
@ -1911,80 +1918,81 @@ import {MyInput, MyInputForm} from './value_accessor_integration_spec';
expect(form.valid).toEqual(true); expect(form.valid).toEqual(true);
}); });
it('changes on bound properties should change the validation state of the form', () => { fixmeIvy('host attribute instructions are not generated properly') &&
const fixture = initTest(ValidationBindingsForm); it('changes on bound properties should change the validation state of the form', () => {
const form = new FormGroup({ const fixture = initTest(ValidationBindingsForm);
'login': new FormControl(''), const form = new FormGroup({
'min': new FormControl(''), 'login': new FormControl(''),
'max': new FormControl(''), 'min': new FormControl(''),
'pattern': new FormControl('') 'max': new FormControl(''),
}); 'pattern': new FormControl('')
fixture.componentInstance.form = form; });
fixture.detectChanges(); fixture.componentInstance.form = form;
fixture.detectChanges();
const required = fixture.debugElement.query(By.css('[name=required]')); const required = fixture.debugElement.query(By.css('[name=required]'));
const minLength = fixture.debugElement.query(By.css('[name=minlength]')); const minLength = fixture.debugElement.query(By.css('[name=minlength]'));
const maxLength = fixture.debugElement.query(By.css('[name=maxlength]')); const maxLength = fixture.debugElement.query(By.css('[name=maxlength]'));
const pattern = fixture.debugElement.query(By.css('[name=pattern]')); const pattern = fixture.debugElement.query(By.css('[name=pattern]'));
required.nativeElement.value = ''; required.nativeElement.value = '';
minLength.nativeElement.value = '1'; minLength.nativeElement.value = '1';
maxLength.nativeElement.value = '1234'; maxLength.nativeElement.value = '1234';
pattern.nativeElement.value = '12'; pattern.nativeElement.value = '12';
dispatchEvent(required.nativeElement, 'input'); dispatchEvent(required.nativeElement, 'input');
dispatchEvent(minLength.nativeElement, 'input'); dispatchEvent(minLength.nativeElement, 'input');
dispatchEvent(maxLength.nativeElement, 'input'); dispatchEvent(maxLength.nativeElement, 'input');
dispatchEvent(pattern.nativeElement, 'input'); dispatchEvent(pattern.nativeElement, 'input');
expect(form.hasError('required', ['login'])).toEqual(false); expect(form.hasError('required', ['login'])).toEqual(false);
expect(form.hasError('minlength', ['min'])).toEqual(false); expect(form.hasError('minlength', ['min'])).toEqual(false);
expect(form.hasError('maxlength', ['max'])).toEqual(false); expect(form.hasError('maxlength', ['max'])).toEqual(false);
expect(form.hasError('pattern', ['pattern'])).toEqual(false); expect(form.hasError('pattern', ['pattern'])).toEqual(false);
expect(form.valid).toEqual(true); expect(form.valid).toEqual(true);
fixture.componentInstance.required = true; fixture.componentInstance.required = true;
fixture.componentInstance.minLen = 3; fixture.componentInstance.minLen = 3;
fixture.componentInstance.maxLen = 3; fixture.componentInstance.maxLen = 3;
fixture.componentInstance.pattern = '.{3,}'; fixture.componentInstance.pattern = '.{3,}';
fixture.detectChanges(); fixture.detectChanges();
dispatchEvent(required.nativeElement, 'input'); dispatchEvent(required.nativeElement, 'input');
dispatchEvent(minLength.nativeElement, 'input'); dispatchEvent(minLength.nativeElement, 'input');
dispatchEvent(maxLength.nativeElement, 'input'); dispatchEvent(maxLength.nativeElement, 'input');
dispatchEvent(pattern.nativeElement, 'input'); dispatchEvent(pattern.nativeElement, 'input');
expect(form.hasError('required', ['login'])).toEqual(true); expect(form.hasError('required', ['login'])).toEqual(true);
expect(form.hasError('minlength', ['min'])).toEqual(true); expect(form.hasError('minlength', ['min'])).toEqual(true);
expect(form.hasError('maxlength', ['max'])).toEqual(true); expect(form.hasError('maxlength', ['max'])).toEqual(true);
expect(form.hasError('pattern', ['pattern'])).toEqual(true); expect(form.hasError('pattern', ['pattern'])).toEqual(true);
expect(form.valid).toEqual(false); expect(form.valid).toEqual(false);
expect(required.nativeElement.getAttribute('required')).toEqual(''); expect(required.nativeElement.getAttribute('required')).toEqual('');
expect(fixture.componentInstance.minLen.toString()) expect(fixture.componentInstance.minLen.toString())
.toEqual(minLength.nativeElement.getAttribute('minlength')); .toEqual(minLength.nativeElement.getAttribute('minlength'));
expect(fixture.componentInstance.maxLen.toString()) expect(fixture.componentInstance.maxLen.toString())
.toEqual(maxLength.nativeElement.getAttribute('maxlength')); .toEqual(maxLength.nativeElement.getAttribute('maxlength'));
expect(fixture.componentInstance.pattern.toString()) expect(fixture.componentInstance.pattern.toString())
.toEqual(pattern.nativeElement.getAttribute('pattern')); .toEqual(pattern.nativeElement.getAttribute('pattern'));
fixture.componentInstance.required = false; fixture.componentInstance.required = false;
fixture.componentInstance.minLen = null !; fixture.componentInstance.minLen = null !;
fixture.componentInstance.maxLen = null !; fixture.componentInstance.maxLen = null !;
fixture.componentInstance.pattern = null !; fixture.componentInstance.pattern = null !;
fixture.detectChanges(); fixture.detectChanges();
expect(form.hasError('required', ['login'])).toEqual(false); expect(form.hasError('required', ['login'])).toEqual(false);
expect(form.hasError('minlength', ['min'])).toEqual(false); expect(form.hasError('minlength', ['min'])).toEqual(false);
expect(form.hasError('maxlength', ['max'])).toEqual(false); expect(form.hasError('maxlength', ['max'])).toEqual(false);
expect(form.hasError('pattern', ['pattern'])).toEqual(false); expect(form.hasError('pattern', ['pattern'])).toEqual(false);
expect(form.valid).toEqual(true); expect(form.valid).toEqual(true);
expect(required.nativeElement.getAttribute('required')).toEqual(null); expect(required.nativeElement.getAttribute('required')).toEqual(null);
expect(required.nativeElement.getAttribute('minlength')).toEqual(null); expect(required.nativeElement.getAttribute('minlength')).toEqual(null);
expect(required.nativeElement.getAttribute('maxlength')).toEqual(null); expect(required.nativeElement.getAttribute('maxlength')).toEqual(null);
expect(required.nativeElement.getAttribute('pattern')).toEqual(null); expect(required.nativeElement.getAttribute('pattern')).toEqual(null);
}); });
it('should support rebound controls with rebound validators', () => { it('should support rebound controls with rebound validators', () => {
const fixture = initTest(ValidationBindingsForm); const fixture = initTest(ValidationBindingsForm);

View File

@ -12,6 +12,7 @@ import {AbstractControl, AsyncValidator, COMPOSITION_BUFFER_MODE, FormControl, F
import {By} from '@angular/platform-browser/src/dom/debug/by'; import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'; import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
import {fixmeIvy} from '@angular/private/testing';
import {merge} from 'rxjs'; import {merge} from 'rxjs';
import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integration_spec'; import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integration_spec';
@ -148,85 +149,101 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
expect(form.value).toEqual({}); expect(form.value).toEqual({});
})); }));
it('should set status classes with ngModel', async(() => { fixmeIvy('whenStable not working') &&
const fixture = initTest(NgModelForm); it('should set status classes with ngModel', async(() => {
fixture.componentInstance.name = 'aa'; const fixture = initTest(NgModelForm);
fixture.detectChanges(); fixture.componentInstance.name = 'aa';
fixture.whenStable().then(() => { fixture.detectChanges();
fixture.detectChanges(); fixture.whenStable().then(() => {
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement; const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(input)).toEqual([
'ng-invalid', 'ng-pristine', 'ng-untouched'
]);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']); expect(sortedClassList(input)).toEqual([
'ng-invalid', 'ng-pristine', 'ng-touched'
]);
input.value = 'updatedValue'; input.value = 'updatedValue';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
}); });
})); }));
it('should set status classes with ngModel and async validators', fakeAsync(() => { fixmeIvy('whenStable not working') &&
it('should set status classes with ngModel and async validators', fakeAsync(() => {
const fixture = initTest(NgModelAsyncValidation, NgAsyncValidator); const fixture = initTest(NgModelAsyncValidation, NgAsyncValidator);
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement; const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(input)).toEqual([
'ng-pending', 'ng-pristine', 'ng-untouched'
]);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-touched']); expect(sortedClassList(input)).toEqual([
'ng-pending', 'ng-pristine', 'ng-touched'
]);
input.value = 'updatedValue'; input.value = 'updatedValue';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
tick(); tick();
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
}); });
})); }));
it('should set status classes with ngModelGroup and ngForm', async(() => { fixmeIvy('whenStable not working') &&
const fixture = initTest(NgModelGroupForm); it('should set status classes with ngModelGroup and ngForm', async(() => {
fixture.componentInstance.first = ''; const fixture = initTest(NgModelGroupForm);
fixture.detectChanges(); fixture.componentInstance.first = '';
fixture.detectChanges();
const form = fixture.debugElement.query(By.css('form')).nativeElement; const form = fixture.debugElement.query(By.css('form')).nativeElement;
const modelGroup = fixture.debugElement.query(By.css('[ngModelGroup]')).nativeElement; const modelGroup =
const input = fixture.debugElement.query(By.css('input')).nativeElement; fixture.debugElement.query(By.css('[ngModelGroup]')).nativeElement;
const input = fixture.debugElement.query(By.css('input')).nativeElement;
// ngModelGroup creates its control asynchronously // ngModelGroup creates its control asynchronously
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(modelGroup)).toEqual([ expect(sortedClassList(modelGroup)).toEqual([
'ng-invalid', 'ng-pristine', 'ng-untouched' 'ng-invalid', 'ng-pristine', 'ng-untouched'
]); ]);
expect(sortedClassList(form)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']); expect(sortedClassList(form)).toEqual([
'ng-invalid', 'ng-pristine', 'ng-untouched'
]);
dispatchEvent(input, 'blur'); dispatchEvent(input, 'blur');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(modelGroup)).toEqual([ expect(sortedClassList(modelGroup)).toEqual([
'ng-invalid', 'ng-pristine', 'ng-touched' 'ng-invalid', 'ng-pristine', 'ng-touched'
]); ]);
expect(sortedClassList(form)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']); expect(sortedClassList(form)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
input.value = 'updatedValue'; input.value = 'updatedValue';
dispatchEvent(input, 'input'); dispatchEvent(input, 'input');
fixture.detectChanges(); fixture.detectChanges();
expect(sortedClassList(modelGroup)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); expect(sortedClassList(modelGroup)).toEqual([
expect(sortedClassList(form)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); 'ng-dirty', 'ng-touched', 'ng-valid'
}); ]);
})); expect(sortedClassList(form)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
});
}));
it('should not create a template-driven form when ngNoForm is used', () => { it('should not create a template-driven form when ngNoForm is used', () => {
const fixture = initTest(NgNoFormComp); const fixture = initTest(NgNoFormComp);
@ -1167,22 +1184,23 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
expect(input.nativeElement.disabled).toBe(true); expect(input.nativeElement.disabled).toBe(true);
})); }));
it('should disable a custom control if disabled attr is added', async(() => { fixmeIvy('whenStable not working') &&
const fixture = initTest(NgModelCustomWrapper, NgModelCustomComp); it('should disable a custom control if disabled attr is added', async(() => {
fixture.componentInstance.name = 'Nancy'; const fixture = initTest(NgModelCustomWrapper, NgModelCustomComp);
fixture.componentInstance.isDisabled = true; fixture.componentInstance.name = 'Nancy';
fixture.detectChanges(); fixture.componentInstance.isDisabled = true;
fixture.whenStable().then(() => { fixture.detectChanges();
fixture.detectChanges(); fixture.whenStable().then(() => {
fixture.whenStable().then(() => { fixture.detectChanges();
const form = fixture.debugElement.children[0].injector.get(NgForm); fixture.whenStable().then(() => {
expect(form.control.get('name') !.disabled).toBe(true); const form = fixture.debugElement.children[0].injector.get(NgForm);
expect(form.control.get('name') !.disabled).toBe(true);
const customInput = fixture.debugElement.query(By.css('[name="custom"]')); const customInput = fixture.debugElement.query(By.css('[name="custom"]'));
expect(customInput.nativeElement.disabled).toEqual(true); expect(customInput.nativeElement.disabled).toEqual(true);
}); });
}); });
})); }));
it('should disable a control with unbound disabled attr', fakeAsync(() => { it('should disable a control with unbound disabled attr', fakeAsync(() => {
TestBed.overrideComponent(NgModelForm, { TestBed.overrideComponent(NgModelForm, {
@ -1213,41 +1231,42 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
describe('validation directives', () => { describe('validation directives', () => {
it('required validator should validate checkbox', fakeAsync(() => { fixmeIvy('RequiredValidator provided instead of CheckboxRequiredValidator') &&
const fixture = initTest(NgModelCheckboxRequiredValidator); it('required validator should validate checkbox', fakeAsync(() => {
fixture.detectChanges(); const fixture = initTest(NgModelCheckboxRequiredValidator);
tick(); fixture.detectChanges();
tick();
const control = const control =
fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !; fixture.debugElement.children[0].injector.get(NgForm).control.get('checkbox') !;
const input = fixture.debugElement.query(By.css('input')); const input = fixture.debugElement.query(By.css('input'));
expect(input.nativeElement.checked).toBe(false); expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(false); expect(control.hasError('required')).toBe(false);
fixture.componentInstance.required = true; fixture.componentInstance.required = true;
fixture.detectChanges(); fixture.detectChanges();
tick(); tick();
expect(input.nativeElement.checked).toBe(false); expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(true); expect(control.hasError('required')).toBe(true);
input.nativeElement.checked = true; input.nativeElement.checked = true;
dispatchEvent(input.nativeElement, 'change'); dispatchEvent(input.nativeElement, 'change');
fixture.detectChanges(); fixture.detectChanges();
tick(); tick();
expect(input.nativeElement.checked).toBe(true); expect(input.nativeElement.checked).toBe(true);
expect(control.hasError('required')).toBe(false); expect(control.hasError('required')).toBe(false);
input.nativeElement.checked = false; input.nativeElement.checked = false;
dispatchEvent(input.nativeElement, 'change'); dispatchEvent(input.nativeElement, 'change');
fixture.detectChanges(); fixture.detectChanges();
tick(); tick();
expect(input.nativeElement.checked).toBe(false); expect(input.nativeElement.checked).toBe(false);
expect(control.hasError('required')).toBe(true); expect(control.hasError('required')).toBe(true);
})); }));
it('should validate email', fakeAsync(() => { it('should validate email', fakeAsync(() => {
const fixture = initTest(NgModelEmailValidator); const fixture = initTest(NgModelEmailValidator);
@ -1401,106 +1420,108 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
expect(form.control.hasError('minlength', ['tovalidate'])).toBeTruthy(); expect(form.control.hasError('minlength', ['tovalidate'])).toBeTruthy();
})); }));
it('changes on bound properties should change the validation state of the form', fixmeIvy('host attribute instructions are not generated properly') &&
fakeAsync(() => { it('changes on bound properties should change the validation state of the form',
const fixture = initTest(NgModelValidationBindings); fakeAsync(() => {
fixture.detectChanges(); const fixture = initTest(NgModelValidationBindings);
tick(); fixture.detectChanges();
tick();
const required = fixture.debugElement.query(By.css('[name=required]')); const required = fixture.debugElement.query(By.css('[name=required]'));
const minLength = fixture.debugElement.query(By.css('[name=minlength]')); const minLength = fixture.debugElement.query(By.css('[name=minlength]'));
const maxLength = fixture.debugElement.query(By.css('[name=maxlength]')); const maxLength = fixture.debugElement.query(By.css('[name=maxlength]'));
const pattern = fixture.debugElement.query(By.css('[name=pattern]')); const pattern = fixture.debugElement.query(By.css('[name=pattern]'));
required.nativeElement.value = ''; required.nativeElement.value = '';
minLength.nativeElement.value = '1'; minLength.nativeElement.value = '1';
maxLength.nativeElement.value = '1234'; maxLength.nativeElement.value = '1234';
pattern.nativeElement.value = '12'; pattern.nativeElement.value = '12';
dispatchEvent(required.nativeElement, 'input'); dispatchEvent(required.nativeElement, 'input');
dispatchEvent(minLength.nativeElement, 'input'); dispatchEvent(minLength.nativeElement, 'input');
dispatchEvent(maxLength.nativeElement, 'input'); dispatchEvent(maxLength.nativeElement, 'input');
dispatchEvent(pattern.nativeElement, 'input'); dispatchEvent(pattern.nativeElement, 'input');
const form = fixture.debugElement.children[0].injector.get(NgForm); const form = fixture.debugElement.children[0].injector.get(NgForm);
expect(form.control.hasError('required', ['required'])).toEqual(false); expect(form.control.hasError('required', ['required'])).toEqual(false);
expect(form.control.hasError('minlength', ['minlength'])).toEqual(false); expect(form.control.hasError('minlength', ['minlength'])).toEqual(false);
expect(form.control.hasError('maxlength', ['maxlength'])).toEqual(false); expect(form.control.hasError('maxlength', ['maxlength'])).toEqual(false);
expect(form.control.hasError('pattern', ['pattern'])).toEqual(false); expect(form.control.hasError('pattern', ['pattern'])).toEqual(false);
expect(form.valid).toEqual(true); expect(form.valid).toEqual(true);
fixture.componentInstance.required = true; fixture.componentInstance.required = true;
fixture.componentInstance.minLen = 3; fixture.componentInstance.minLen = 3;
fixture.componentInstance.maxLen = 3; fixture.componentInstance.maxLen = 3;
fixture.componentInstance.pattern = '.{3,}'; fixture.componentInstance.pattern = '.{3,}';
fixture.detectChanges(); fixture.detectChanges();
dispatchEvent(required.nativeElement, 'input'); dispatchEvent(required.nativeElement, 'input');
dispatchEvent(minLength.nativeElement, 'input'); dispatchEvent(minLength.nativeElement, 'input');
dispatchEvent(maxLength.nativeElement, 'input'); dispatchEvent(maxLength.nativeElement, 'input');
dispatchEvent(pattern.nativeElement, 'input'); dispatchEvent(pattern.nativeElement, 'input');
expect(form.control.hasError('required', ['required'])).toEqual(true); expect(form.control.hasError('required', ['required'])).toEqual(true);
expect(form.control.hasError('minlength', ['minlength'])).toEqual(true); expect(form.control.hasError('minlength', ['minlength'])).toEqual(true);
expect(form.control.hasError('maxlength', ['maxlength'])).toEqual(true); expect(form.control.hasError('maxlength', ['maxlength'])).toEqual(true);
expect(form.control.hasError('pattern', ['pattern'])).toEqual(true); expect(form.control.hasError('pattern', ['pattern'])).toEqual(true);
expect(form.valid).toEqual(false); expect(form.valid).toEqual(false);
expect(required.nativeElement.getAttribute('required')).toEqual(''); expect(required.nativeElement.getAttribute('required')).toEqual('');
expect(fixture.componentInstance.minLen.toString()) expect(fixture.componentInstance.minLen.toString())
.toEqual(minLength.nativeElement.getAttribute('minlength')); .toEqual(minLength.nativeElement.getAttribute('minlength'));
expect(fixture.componentInstance.maxLen.toString()) expect(fixture.componentInstance.maxLen.toString())
.toEqual(maxLength.nativeElement.getAttribute('maxlength')); .toEqual(maxLength.nativeElement.getAttribute('maxlength'));
expect(fixture.componentInstance.pattern.toString()) expect(fixture.componentInstance.pattern.toString())
.toEqual(pattern.nativeElement.getAttribute('pattern')); .toEqual(pattern.nativeElement.getAttribute('pattern'));
fixture.componentInstance.required = false; fixture.componentInstance.required = false;
fixture.componentInstance.minLen = null !; fixture.componentInstance.minLen = null !;
fixture.componentInstance.maxLen = null !; fixture.componentInstance.maxLen = null !;
fixture.componentInstance.pattern = null !; fixture.componentInstance.pattern = null !;
fixture.detectChanges(); fixture.detectChanges();
expect(form.control.hasError('required', ['required'])).toEqual(false); expect(form.control.hasError('required', ['required'])).toEqual(false);
expect(form.control.hasError('minlength', ['minlength'])).toEqual(false); expect(form.control.hasError('minlength', ['minlength'])).toEqual(false);
expect(form.control.hasError('maxlength', ['maxlength'])).toEqual(false); expect(form.control.hasError('maxlength', ['maxlength'])).toEqual(false);
expect(form.control.hasError('pattern', ['pattern'])).toEqual(false); expect(form.control.hasError('pattern', ['pattern'])).toEqual(false);
expect(form.valid).toEqual(true); expect(form.valid).toEqual(true);
expect(required.nativeElement.getAttribute('required')).toEqual(null); expect(required.nativeElement.getAttribute('required')).toEqual(null);
expect(required.nativeElement.getAttribute('minlength')).toEqual(null); expect(required.nativeElement.getAttribute('minlength')).toEqual(null);
expect(required.nativeElement.getAttribute('maxlength')).toEqual(null); expect(required.nativeElement.getAttribute('maxlength')).toEqual(null);
expect(required.nativeElement.getAttribute('pattern')).toEqual(null); expect(required.nativeElement.getAttribute('pattern')).toEqual(null);
})); }));
it('should update control status', fakeAsync(() => { fixmeIvy('ngModelChange callback never called') &&
const fixture = initTest(NgModelChangeState); it('should update control status', fakeAsync(() => {
const inputEl = fixture.debugElement.query(By.css('input')); const fixture = initTest(NgModelChangeState);
const inputNativeEl = inputEl.nativeElement; const inputEl = fixture.debugElement.query(By.css('input'));
const onNgModelChange = jasmine.createSpy('onNgModelChange'); const inputNativeEl = inputEl.nativeElement;
fixture.componentInstance.onNgModelChange = onNgModelChange; const onNgModelChange = jasmine.createSpy('onNgModelChange');
fixture.detectChanges(); fixture.componentInstance.onNgModelChange = onNgModelChange;
tick(); fixture.detectChanges();
tick();
expect(onNgModelChange).not.toHaveBeenCalled(); expect(onNgModelChange).not.toHaveBeenCalled();
inputNativeEl.value = 'updated'; inputNativeEl.value = 'updated';
onNgModelChange.and.callFake((ngModel: NgModel) => { onNgModelChange.and.callFake((ngModel: NgModel) => {
expect(ngModel.invalid).toBe(true); expect(ngModel.invalid).toBe(true);
expect(ngModel.value).toBe('updated'); expect(ngModel.value).toBe('updated');
}); });
dispatchEvent(inputNativeEl, 'input'); dispatchEvent(inputNativeEl, 'input');
expect(onNgModelChange).toHaveBeenCalled(); expect(onNgModelChange).toHaveBeenCalled();
tick(); tick();
inputNativeEl.value = '333'; inputNativeEl.value = '333';
onNgModelChange.and.callFake((ngModel: NgModel) => { onNgModelChange.and.callFake((ngModel: NgModel) => {
expect(ngModel.invalid).toBe(false); expect(ngModel.invalid).toBe(false);
expect(ngModel.value).toBe('333'); expect(ngModel.value).toBe('333');
}); });
dispatchEvent(inputNativeEl, 'input'); dispatchEvent(inputNativeEl, 'input');
expect(onNgModelChange).toHaveBeenCalledTimes(2); expect(onNgModelChange).toHaveBeenCalledTimes(2);
tick(); tick();
})); }));
}); });

View File

@ -11,6 +11,7 @@ import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/t
import {AbstractControl, ControlValueAccessor, FormControl, FormGroup, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgForm, NgModel, ReactiveFormsModule, Validators} from '@angular/forms'; import {AbstractControl, ControlValueAccessor, FormControl, FormGroup, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgForm, NgModel, ReactiveFormsModule, Validators} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by'; import {By} from '@angular/platform-browser/src/dom/debug/by';
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'; import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
import {fixmeIvy} from '@angular/private/testing';
{ {
describe('value accessors', () => { describe('value accessors', () => {
@ -1058,26 +1059,27 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
}); });
describe('in template-driven forms', () => { describe('in template-driven forms', () => {
it('should support standard writing to view and model', async(() => { fixmeIvy('whenStable not working') &&
const fixture = initTest(NgModelCustomWrapper, NgModelCustomComp); it('should support standard writing to view and model', async(() => {
fixture.componentInstance.name = 'Nancy'; const fixture = initTest(NgModelCustomWrapper, NgModelCustomComp);
fixture.detectChanges(); fixture.componentInstance.name = 'Nancy';
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
// model -> view
const customInput = fixture.debugElement.query(By.css('[name="custom"]'));
expect(customInput.nativeElement.value).toEqual('Nancy');
customInput.nativeElement.value = 'Carson';
dispatchEvent(customInput.nativeElement, 'input');
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
// model -> view
const customInput = fixture.debugElement.query(By.css('[name="custom"]'));
expect(customInput.nativeElement.value).toEqual('Nancy');
// view -> model customInput.nativeElement.value = 'Carson';
expect(fixture.componentInstance.name).toEqual('Carson'); dispatchEvent(customInput.nativeElement, 'input');
}); fixture.detectChanges();
});
})); // view -> model
expect(fixture.componentInstance.name).toEqual('Carson');
});
});
}));
}); });