From 55dfa1b69db31034b3dee7603a432f1e6181e978 Mon Sep 17 00:00:00 2001 From: Marc Laval Date: Fri, 16 Dec 2016 02:07:26 +0100 Subject: [PATCH] test(forms): refactor integration tests to improve speed (#13500) --- .../compiler/src/metadata_resolver.ts | 2 +- .../forms/test/reactive_integration_spec.ts | 205 ++++++++---------- .../forms/test/template_integration_spec.ts | 130 +++++------ 3 files changed, 146 insertions(+), 191 deletions(-) diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index a898ad8ef0..7e34ab09ac 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -987,4 +987,4 @@ function stringifyType(type: any): string { } else { return stringify(type); } -} \ No newline at end of file +} diff --git a/modules/@angular/forms/test/reactive_integration_spec.ts b/modules/@angular/forms/test/reactive_integration_spec.ts index a40217f634..79587ea696 100644 --- a/modules/@angular/forms/test/reactive_integration_spec.ts +++ b/modules/@angular/forms/test/reactive_integration_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, EventEmitter, Input, Output, forwardRef} from '@angular/core'; -import {TestBed, fakeAsync, tick} from '@angular/core/testing'; +import {Component, Directive, EventEmitter, Input, Output, Type, forwardRef} from '@angular/core'; +import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; import {AbstractControl, ControlValueAccessor, FormArray, FormControl, FormGroup, FormGroupDirective, FormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule, Validator, Validators} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; @@ -16,38 +16,15 @@ import {dispatchEvent} from '@angular/platform-browser/testing/browser_util'; export function main() { describe('reactive forms integration tests', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule], - declarations: [ - FormControlComp, - FormGroupComp, - FormArrayComp, - FormArrayNestedGroup, - FormControlNameSelect, - FormControlNumberInput, - FormControlRangeInput, - FormControlRadioButtons, - WrappedValue, - WrappedValueForm, - MyInput, - MyInputForm, - FormGroupNgModel, - FormControlNgModel, - LoginIsEmptyValidator, - LoginIsEmptyWrapper, - ValidationBindingsForm, - UniqLoginValidator, - UniqLoginWrapper, - NestedFormGroupComp, - FormControlCheckboxRequiredValidator, - ] - }); - }); + function initTest(component: Type, ...directives: Type[]): ComponentFixture { + TestBed.configureTestingModule( + {declarations: [component, ...directives], imports: [FormsModule, ReactiveFormsModule]}); + return TestBed.createComponent(component); + } describe('basic functionality', () => { it('should work with single controls', () => { - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl('old value'); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -64,7 +41,7 @@ export function main() { }); it('should work with formGroups (model -> view)', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')}); fixture.detectChanges(); @@ -73,7 +50,7 @@ export function main() { }); it('should add novalidate by default to form', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')}); fixture.detectChanges(); @@ -82,7 +59,7 @@ export function main() { }); it('work with formGroups (view -> model)', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const form = new FormGroup({'login': new FormControl('oldValue')}); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -99,7 +76,7 @@ export function main() { describe('rebound form groups', () => { it('should update DOM elements initially', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('oldValue')}); fixture.detectChanges(); @@ -111,7 +88,7 @@ export function main() { }); it('should update model when UI changes', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('oldValue')}); fixture.detectChanges(); @@ -132,7 +109,7 @@ export function main() { }); it('should work with radio buttons when reusing control', () => { - const fixture = TestBed.createComponent(FormControlRadioButtons); + const fixture = initTest(FormControlRadioButtons); const food = new FormControl('chicken'); fixture.componentInstance.form = new FormGroup({'food': food, 'drink': new FormControl('')}); @@ -150,7 +127,7 @@ export function main() { }); it('should update nested form group model when UI changes', () => { - const fixture = TestBed.createComponent(NestedFormGroupComp); + const fixture = initTest(NestedFormGroupComp); fixture.componentInstance.form = new FormGroup( {'signin': new FormGroup({'login': new FormControl(), 'password': new FormControl()})}); fixture.detectChanges(); @@ -178,7 +155,7 @@ export function main() { }); it('should pick up dir validators from form controls', () => { - const fixture = TestBed.createComponent(LoginIsEmptyWrapper); + const fixture = initTest(LoginIsEmptyWrapper, LoginIsEmptyValidator); const form = new FormGroup({ 'login': new FormControl(''), 'min': new FormControl(''), @@ -202,7 +179,7 @@ export function main() { }); it('should pick up dir validators from nested form groups', () => { - const fixture = TestBed.createComponent(NestedFormGroupComp); + const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); const form = new FormGroup({ 'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) @@ -222,7 +199,7 @@ export function main() { }); it('should strip named controls that are not found', () => { - const fixture = TestBed.createComponent(NestedFormGroupComp); + const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); const form = new FormGroup({ 'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) @@ -248,7 +225,7 @@ export function main() { }); it('should strip array controls that are not found', () => { - const fixture = TestBed.createComponent(FormArrayComp); + const fixture = initTest(FormArrayComp); const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]); const form = new FormGroup({cities: cityArray}); fixture.componentInstance.form = form; @@ -277,7 +254,7 @@ export function main() { it('should attach dir to control when leaf control changes', () => { const form = new FormGroup({'login': new FormControl('oldValue')}); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -300,7 +277,7 @@ export function main() { }); it('should attach dirs to all child controls when group control changes', () => { - const fixture = TestBed.createComponent(NestedFormGroupComp); + const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); const form = new FormGroup({ signin: new FormGroup( {login: new FormControl('oldLogin'), password: new FormControl('oldPassword')}) @@ -332,7 +309,7 @@ export function main() { }); it('should attach dirs to all present child controls when array control changes', () => { - const fixture = TestBed.createComponent(FormArrayComp); + const fixture = initTest(FormArrayComp); const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]); const form = new FormGroup({cities: cityArray}); fixture.componentInstance.form = form; @@ -363,7 +340,7 @@ export function main() { describe('form arrays', () => { it('should support form arrays', () => { - const fixture = TestBed.createComponent(FormArrayComp); + const fixture = initTest(FormArrayComp); const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]); const form = new FormGroup({cities: cityArray}); fixture.componentInstance.form = form; @@ -386,7 +363,7 @@ export function main() { }); it('should support pushing new controls to form arrays', () => { - const fixture = TestBed.createComponent(FormArrayComp); + const fixture = initTest(FormArrayComp); const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]); const form = new FormGroup({cities: cityArray}); fixture.componentInstance.form = form; @@ -402,7 +379,7 @@ export function main() { }); it('should support form groups nested in form arrays', () => { - const fixture = TestBed.createComponent(FormArrayNestedGroup); + const fixture = initTest(FormArrayNestedGroup); const cityArray = new FormArray([ new FormGroup({town: new FormControl('SF'), state: new FormControl('CA')}), new FormGroup({town: new FormControl('NY'), state: new FormControl('NY')}) @@ -434,7 +411,7 @@ export function main() { describe('programmatic changes', () => { it('should update the value in the DOM when setValue() is called', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const login = new FormControl('oldValue'); const form = new FormGroup({'login': login}); fixture.componentInstance.form = form; @@ -450,7 +427,7 @@ export function main() { describe('disabled controls', () => { it('should add disabled attribute to an individual control when instantiated as disabled', () => { - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl({value: 'some value', disabled: true}); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -464,7 +441,7 @@ export function main() { }); it('should add disabled attribute to formControlName when instantiated as disabled', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const control = new FormControl({value: 'some value', disabled: true}); fixture.componentInstance.form = new FormGroup({login: control}); fixture.componentInstance.control = control; @@ -480,7 +457,7 @@ export function main() { it('should add disabled attribute to an individual control when disable() is called', () => { - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl('some value'); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -498,7 +475,7 @@ export function main() { it('should add disabled attribute to child controls when disable() is called on group', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const form = new FormGroup({'login': new FormControl('login')}); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -516,7 +493,7 @@ export function main() { it('should not add disabled attribute to custom controls when disable() is called', () => { - const fixture = TestBed.createComponent(MyInputForm); + const fixture = initTest(MyInputForm, MyInput); const control = new FormControl('some value'); fixture.componentInstance.form = new FormGroup({login: control}); fixture.detectChanges(); @@ -535,7 +512,7 @@ export function main() { describe('user input', () => { it('should mark controls as touched after interacting with the DOM control', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const login = new FormControl('oldValue'); const form = new FormGroup({'login': login}); fixture.componentInstance.form = form; @@ -553,7 +530,7 @@ export function main() { describe('submit and reset events', () => { it('should emit ngSubmit event with the original submit event on submit', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')}); fixture.componentInstance.event = null; fixture.detectChanges(); @@ -566,7 +543,7 @@ export function main() { }); it('should mark formGroup as submitted on submit event', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')}); fixture.detectChanges(); @@ -581,7 +558,7 @@ export function main() { }); it('should set value in UI when form resets to that value programmatically', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const login = new FormControl('some value'); const form = new FormGroup({'login': login}); fixture.componentInstance.form = form; @@ -595,7 +572,7 @@ export function main() { }); it('should clear value in UI when form resets programmatically', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const login = new FormControl('some value'); const form = new FormGroup({'login': login}); fixture.componentInstance.form = form; @@ -613,7 +590,7 @@ export function main() { describe('value changes and status changes', () => { it('should mark controls as dirty before emitting a value change event', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const login = new FormControl('oldValue'); fixture.componentInstance.form = new FormGroup({'login': login}); fixture.detectChanges(); @@ -628,7 +605,7 @@ export function main() { it('should mark control as pristine before emitting a value change event when resetting ', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const login = new FormControl('oldValue'); const form = new FormGroup({'login': login}); fixture.componentInstance.form = form; @@ -649,7 +626,7 @@ export function main() { describe('setting status classes', () => { it('should work with single fields', () => { - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl('', Validators.required); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -670,7 +647,7 @@ export function main() { }); it('should work with single fields and async validators', fakeAsync(() => { - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl('', null, uniqLoginAsyncValidator('good')); fixture.debugElement.componentInstance.control = control; fixture.detectChanges(); @@ -691,7 +668,7 @@ export function main() { })); it('should work with single fields that combines async and sync validators', fakeAsync(() => { - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl('', Validators.required, uniqLoginAsyncValidator('good')); fixture.debugElement.componentInstance.control = control; @@ -724,7 +701,7 @@ export function main() { })); it('should work with single fields in parent forms', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const form = new FormGroup({'login': new FormControl('', Validators.required)}); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -745,7 +722,7 @@ export function main() { }); it('should work with formGroup', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const form = new FormGroup({'login': new FormControl('', Validators.required)}); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -774,7 +751,7 @@ export function main() { it('should support without type', () => { TestBed.overrideComponent( FormControlComp, {set: {template: ``}}); - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl('old'); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -791,7 +768,7 @@ export function main() { }); it('should support ', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const form = new FormGroup({'login': new FormControl('old')}); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -808,7 +785,7 @@ export function main() { }); it('should ignore the change event for ', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const form = new FormGroup({'login': new FormControl('oldValue')}); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -823,7 +800,7 @@ export function main() { it('should support `}}); - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl('old'); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -842,7 +819,7 @@ export function main() { it('should support ', () => { TestBed.overrideComponent( FormControlComp, {set: {template: ``}}); - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); const control = new FormControl(true); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -859,7 +836,7 @@ export function main() { }); it('should support `}}); - const fixture = TestBed.createComponent(FormControlComp); + const fixture = initTest(FormControlComp); fixture.componentInstance.control = new FormControl({value: 'aa', disabled: true}); fixture.detectChanges(); expect(fixture.componentInstance.control.status).toEqual('DISABLED'); @@ -1267,7 +1244,7 @@ export function main() { describe('ngModel interactions', () => { it('should support ngModel for complex forms', fakeAsync(() => { - const fixture = TestBed.createComponent(FormGroupNgModel); + const fixture = initTest(FormGroupNgModel); fixture.componentInstance.form = new FormGroup({'login': new FormControl('')}); fixture.componentInstance.login = 'oldValue'; fixture.detectChanges(); @@ -1284,7 +1261,7 @@ export function main() { })); it('should support ngModel for single fields', fakeAsync(() => { - const fixture = TestBed.createComponent(FormControlNgModel); + const fixture = initTest(FormControlNgModel); fixture.componentInstance.control = new FormControl(''); fixture.componentInstance.login = 'oldValue'; fixture.detectChanges(); @@ -1301,7 +1278,7 @@ export function main() { })); it('should not update the view when the value initially came from the view', fakeAsync(() => { - const fixture = TestBed.createComponent(FormControlNgModel); + const fixture = initTest(FormControlNgModel); fixture.componentInstance.control = new FormControl(''); fixture.detectChanges(); tick(); @@ -1322,7 +1299,7 @@ export function main() { describe('validations', () => { it('required validator should validate checkbox', () => { - const fixture = TestBed.createComponent(FormControlCheckboxRequiredValidator); + const fixture = initTest(FormControlCheckboxRequiredValidator); const control = new FormControl(false, Validators.requiredTrue); fixture.componentInstance.control = control; fixture.detectChanges(); @@ -1340,7 +1317,7 @@ export function main() { }); it('should use sync validators defined in html', () => { - const fixture = TestBed.createComponent(LoginIsEmptyWrapper); + const fixture = initTest(LoginIsEmptyWrapper, LoginIsEmptyValidator); const form = new FormGroup({ 'login': new FormControl(''), 'min': new FormControl(''), @@ -1385,7 +1362,7 @@ export function main() { }); it('should use sync validators using bindings', () => { - const fixture = TestBed.createComponent(ValidationBindingsForm); + const fixture = initTest(ValidationBindingsForm); const form = new FormGroup({ 'login': new FormControl(''), 'min': new FormControl(''), @@ -1433,7 +1410,7 @@ export function main() { }); it('changes on bound properties should change the validation state of the form', () => { - const fixture = TestBed.createComponent(ValidationBindingsForm); + const fixture = initTest(ValidationBindingsForm); const form = new FormGroup({ 'login': new FormControl(''), 'min': new FormControl(''), @@ -1508,7 +1485,7 @@ export function main() { }); it('should support rebound controls with rebound validators', () => { - const fixture = TestBed.createComponent(ValidationBindingsForm); + const fixture = initTest(ValidationBindingsForm); const form = new FormGroup({ 'login': new FormControl(''), 'min': new FormControl(''), @@ -1545,7 +1522,7 @@ export function main() { }); it('should use async validators defined in the html', fakeAsync(() => { - const fixture = TestBed.createComponent(UniqLoginWrapper); + const fixture = initTest(UniqLoginWrapper, UniqLoginValidator); const form = new FormGroup({'login': new FormControl('')}); tick(); fixture.componentInstance.form = form; @@ -1565,7 +1542,7 @@ export function main() { })); it('should use sync validators defined in the model', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const form = new FormGroup({'login': new FormControl('aa', Validators.required)}); fixture.componentInstance.form = form; fixture.detectChanges(); @@ -1579,7 +1556,7 @@ export function main() { }); it('should use async validators defined in the model', fakeAsync(() => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const control = new FormControl('', Validators.required, uniqLoginAsyncValidator('expected')); const form = new FormGroup({'login': control}); @@ -1610,7 +1587,7 @@ export function main() { describe('errors', () => { it('should throw if a form isn\'t passed into formGroup', () => { - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); expect(() => fixture.detectChanges()) .toThrowError(new RegExp(`formGroup expects a FormGroup instance`)); @@ -1624,7 +1601,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); expect(() => fixture.detectChanges()) .toThrowError( @@ -1641,7 +1618,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); expect(() => fixture.detectChanges()) .toThrowError( @@ -1660,7 +1637,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); expect(() => fixture.detectChanges()) .toThrowError( @@ -1677,7 +1654,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); expect(() => fixture.detectChanges()) .toThrowError( @@ -1696,7 +1673,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); expect(() => fixture.detectChanges()) .toThrowError( @@ -1712,7 +1689,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); expect(() => fixture.detectChanges()) .toThrowError( @@ -1729,7 +1706,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.myGroup = new FormGroup({}); expect(() => fixture.detectChanges()) @@ -1747,7 +1724,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.myGroup = new FormGroup({}); expect(() => fixture.detectChanges()).not.toThrowError(); @@ -1765,7 +1742,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); const myGroup = new FormGroup({person: new FormGroup({})}); fixture.componentInstance.myGroup = new FormGroup({person: new FormGroup({})}); @@ -1786,7 +1763,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.myGroup = new FormGroup({}); expect(() => fixture.detectChanges()) @@ -1803,7 +1780,7 @@ export function main() { ` } }); - const fixture = TestBed.createComponent(FormGroupComp); + const fixture = initTest(FormGroupComp); fixture.componentInstance.form = new FormGroup({'food': new FormControl('fish')}); expect(() => fixture.detectChanges()) diff --git a/modules/@angular/forms/test/template_integration_spec.ts b/modules/@angular/forms/test/template_integration_spec.ts index 02e7bc88ab..c16b871b62 100644 --- a/modules/@angular/forms/test/template_integration_spec.ts +++ b/modules/@angular/forms/test/template_integration_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, Input, forwardRef} from '@angular/core'; +import {Component, Directive, Input, Type, forwardRef} from '@angular/core'; import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing'; import {AbstractControl, ControlValueAccessor, FormsModule, NG_ASYNC_VALIDATORS, NG_VALUE_ACCESSOR, NgForm, Validator} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; @@ -16,37 +16,15 @@ import {dispatchEvent} from '@angular/platform-browser/testing/browser_util'; export function main() { describe('template-driven forms integration tests', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [ - StandaloneNgModel, - NgModelForm, - NgModelGroupForm, - NgModelValidBinding, - NgModelNgIfForm, - NgModelRadioForm, - NgModelRangeForm, - NgModelSelectForm, - NgNoFormComp, - InvalidNgModelNoName, - NgModelOptionsStandalone, - NgModelCustomComp, - NgModelCustomWrapper, - NgModelValidationBindings, - NgModelMultipleValidators, - NgAsyncValidator, - NgModelAsyncValidation, - NgModelSelectMultipleForm, - NgModelSelectWithNullForm, - NgModelCheckboxRequiredValidator, - ], - imports: [FormsModule] - }); - }); + function initTest(component: Type, ...directives: Type[]): ComponentFixture { + TestBed.configureTestingModule( + {declarations: [component, ...directives], imports: [FormsModule]}); + return TestBed.createComponent(component); + } describe('basic functionality', () => { it('should support ngModel for standalone fields', fakeAsync(() => { - const fixture = TestBed.createComponent(StandaloneNgModel); + const fixture = initTest(StandaloneNgModel); fixture.componentInstance.name = 'oldValue'; fixture.detectChanges(); @@ -65,7 +43,7 @@ export function main() { })); it('should support ngModel registration with a parent form', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.componentInstance.name = 'Nancy'; fixture.detectChanges(); @@ -77,7 +55,7 @@ export function main() { })); it('should add novalidate by default to form element', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.detectChanges(); tick(); @@ -87,7 +65,7 @@ export function main() { })); it('should support ngModelGroup', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelGroupForm); + const fixture = initTest(NgModelGroupForm); fixture.componentInstance.first = 'Nancy'; fixture.componentInstance.last = 'Drew'; fixture.componentInstance.email = 'some email'; @@ -110,7 +88,7 @@ export function main() { })); it('should add controls and control groups to form control model', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelGroupForm); + const fixture = initTest(NgModelGroupForm); fixture.componentInstance.first = 'Nancy'; fixture.componentInstance.last = 'Drew'; fixture.componentInstance.email = 'some email'; @@ -125,7 +103,7 @@ export function main() { })); it('should remove controls and control groups from form control model', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelNgIfForm); + const fixture = initTest(NgModelNgIfForm); fixture.componentInstance.emailShowing = true; fixture.componentInstance.first = 'Nancy'; fixture.componentInstance.email = 'some email'; @@ -158,7 +136,7 @@ export function main() { })); it('should set status classes with ngModel', async(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.componentInstance.name = 'aa'; fixture.detectChanges(); fixture.whenStable().then(() => { @@ -181,7 +159,7 @@ export function main() { it('should set status classes with ngModel and async validators', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelAsyncValidation); + const fixture = initTest(NgModelAsyncValidation, NgAsyncValidator); fixture.whenStable().then(() => { fixture.detectChanges(); @@ -203,7 +181,7 @@ export function main() { })); it('should set status classes with ngModelGroup and ngForm', async(() => { - const fixture = TestBed.createComponent(NgModelGroupForm); + const fixture = initTest(NgModelGroupForm); fixture.componentInstance.first = ''; fixture.detectChanges(); @@ -238,13 +216,13 @@ export function main() { })); it('should not create a template-driven form when ngNoForm is used', () => { - const fixture = TestBed.createComponent(NgNoFormComp); + const fixture = initTest(NgNoFormComp); fixture.detectChanges(); expect(fixture.debugElement.children[0].providerTokens.length).toEqual(0); }); it('should not add novalidate when ngNoForm is used', () => { - const fixture = TestBed.createComponent(NgNoFormComp); + const fixture = initTest(NgNoFormComp); fixture.detectChanges(); const form = fixture.debugElement.query(By.css('form')); expect(form.nativeElement.hasAttribute('novalidate')).toBeFalsy(); @@ -253,19 +231,19 @@ export function main() { describe('name and ngModelOptions', () => { it('should throw if ngModel has a parent form but no name attr or standalone label', () => { - const fixture = TestBed.createComponent(InvalidNgModelNoName); + const fixture = initTest(InvalidNgModelNoName); expect(() => fixture.detectChanges()) .toThrowError(new RegExp(`name attribute must be set`)); }); it('should not throw if ngModel has a parent form, no name attr, and a standalone label', () => { - const fixture = TestBed.createComponent(NgModelOptionsStandalone); + const fixture = initTest(NgModelOptionsStandalone); expect(() => fixture.detectChanges()).not.toThrow(); }); it('should not register standalone ngModels with parent form', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelOptionsStandalone); + const fixture = initTest(NgModelOptionsStandalone); fixture.componentInstance.one = 'some data'; fixture.componentInstance.two = 'should not show'; fixture.detectChanges(); @@ -280,7 +258,7 @@ export function main() { })); it('should override name attribute with ngModelOptions name if provided', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.componentInstance.options = {name: 'override'}; fixture.componentInstance.name = 'some data'; fixture.detectChanges(); @@ -293,7 +271,7 @@ export function main() { describe('submit and reset events', () => { it('should emit ngSubmit event with the original submit event on submit', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.componentInstance.event = null; const form = fixture.debugElement.query(By.css('form')); @@ -304,7 +282,7 @@ export function main() { })); it('should mark NgForm as submitted on submit event', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); @@ -318,7 +296,7 @@ export function main() { })); it('should reset the form to empty when reset event is fired', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.componentInstance.name = 'should be cleared'; fixture.detectChanges(); tick(); @@ -341,7 +319,7 @@ export function main() { })); it('should reset the form submit state when reset button is clicked', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); const form = fixture.debugElement.children[0].injector.get(NgForm); const formEl = fixture.debugElement.query(By.css('form')); @@ -359,7 +337,7 @@ export function main() { describe('valueChange and statusChange events', () => { it('should emit valueChanges and statusChanges on init', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); const form = fixture.debugElement.children[0].injector.get(NgForm); fixture.componentInstance.name = 'aa'; fixture.detectChanges(); @@ -380,7 +358,7 @@ export function main() { })); it('should mark controls dirty before emitting the value change event', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); const form = fixture.debugElement.children[0].injector.get(NgForm).form; fixture.detectChanges(); @@ -397,7 +375,7 @@ export function main() { it('should mark controls pristine before emitting the value change event when resetting ', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.detectChanges(); tick(); @@ -419,7 +397,7 @@ export function main() { describe('disabled controls', () => { it('should not consider disabled controls in value or validation', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelGroupForm); + const fixture = initTest(NgModelGroupForm); fixture.componentInstance.isDisabled = false; fixture.componentInstance.first = ''; fixture.componentInstance.last = 'Drew'; @@ -443,7 +421,7 @@ export function main() { it('should add disabled attribute in the UI if disable() is called programmatically', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelGroupForm); + const fixture = initTest(NgModelGroupForm); fixture.componentInstance.isDisabled = false; fixture.componentInstance.first = 'Nancy'; fixture.detectChanges(); @@ -459,7 +437,7 @@ export function main() { })); it('should disable a custom control if disabled attr is added', async(() => { - const fixture = TestBed.createComponent(NgModelCustomWrapper); + const fixture = initTest(NgModelCustomWrapper, NgModelCustomComp); fixture.componentInstance.name = 'Nancy'; fixture.componentInstance.isDisabled = true; fixture.detectChanges(); @@ -485,7 +463,7 @@ export function main() { `, } }); - const fixture = TestBed.createComponent(NgModelForm); + const fixture = initTest(NgModelForm); fixture.detectChanges(); tick(); const form = fixture.debugElement.children[0].injector.get(NgForm); @@ -501,7 +479,7 @@ export function main() { })); it('should disable radio controls properly with programmatic call', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelRadioForm); + const fixture = initTest(NgModelRadioForm); fixture.componentInstance.food = 'fish'; fixture.detectChanges(); tick(); @@ -537,7 +515,7 @@ export function main() { describe('range control', () => { it('should support ', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelRangeForm); + const fixture = initTest(NgModelRangeForm); // model -> view fixture.componentInstance.val = 4; fixture.detectChanges(); @@ -557,7 +535,7 @@ export function main() { describe('radio controls', () => { it('should support ', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelRadioForm); + const fixture = initTest(NgModelRadioForm); fixture.componentInstance.food = 'fish'; fixture.detectChanges(); tick(); @@ -576,7 +554,7 @@ export function main() { })); it('should support multiple named groups', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelRadioForm); + const fixture = initTest(NgModelRadioForm); fixture.componentInstance.food = 'fish'; fixture.componentInstance.drink = 'sprite'; fixture.detectChanges(); @@ -599,7 +577,7 @@ export function main() { })); it('should support initial undefined value', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelRadioForm); + const fixture = initTest(NgModelRadioForm); fixture.detectChanges(); tick(); @@ -611,7 +589,7 @@ export function main() { })); it('should support resetting properly', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelRadioForm); + const fixture = initTest(NgModelRadioForm); fixture.componentInstance.food = 'chicken'; fixture.detectChanges(); tick(); @@ -627,7 +605,7 @@ export function main() { })); it('should support setting value to null and undefined', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelRadioForm); + const fixture = initTest(NgModelRadioForm); fixture.componentInstance.food = 'chicken'; fixture.detectChanges(); tick(); @@ -655,7 +633,7 @@ export function main() { describe('select controls', () => { it('with option values that are objects', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelSelectForm); + const fixture = initTest(NgModelSelectForm); const comp = fixture.componentInstance; comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}]; comp.selectedCity = comp.cities[1]; @@ -679,7 +657,7 @@ export function main() { })); it('when new options are added', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelSelectForm); + const fixture = initTest(NgModelSelectForm); const comp = fixture.componentInstance; comp.cities = [{'name': 'SF'}, {'name': 'NYC'}]; comp.selectedCity = comp.cities[1]; @@ -698,7 +676,7 @@ export function main() { })); it('when options are removed', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelSelectForm); + const fixture = initTest(NgModelSelectForm); const comp = fixture.componentInstance; comp.cities = [{'name': 'SF'}, {'name': 'NYC'}]; comp.selectedCity = comp.cities[1]; @@ -716,7 +694,7 @@ export function main() { })); it('when option values have same content, but different identities', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelSelectForm); + const fixture = initTest(NgModelSelectForm); const comp = fixture.componentInstance; comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'NYC'}]; comp.selectedCity = comp.cities[0]; @@ -733,7 +711,7 @@ export function main() { })); it('should work with null option', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelSelectWithNullForm); + const fixture = initTest(NgModelSelectWithNullForm); const comp = fixture.componentInstance; comp.cities = [{'name': 'SF'}, {'name': 'NYC'}]; comp.selectedCity = null; @@ -760,7 +738,7 @@ export function main() { let comp: NgModelSelectMultipleForm; beforeEach(() => { - fixture = TestBed.createComponent(NgModelSelectMultipleForm); + fixture = initTest(NgModelSelectMultipleForm); comp = fixture.componentInstance; comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}]; }); @@ -821,7 +799,7 @@ export function main() { describe('custom value accessors', () => { it('should support standard writing to view and model', async(() => { - const fixture = TestBed.createComponent(NgModelCustomWrapper); + const fixture = initTest(NgModelCustomWrapper, NgModelCustomComp); fixture.componentInstance.name = 'Nancy'; fixture.detectChanges(); fixture.whenStable().then(() => { @@ -846,7 +824,7 @@ export function main() { describe('validation directives', () => { it('required validator should validate checkbox', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelCheckboxRequiredValidator); + const fixture = initTest(NgModelCheckboxRequiredValidator); fixture.detectChanges(); tick(); @@ -882,7 +860,7 @@ export function main() { })); it('should support dir validators using bindings', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelValidationBindings); + const fixture = initTest(NgModelValidationBindings); fixture.componentInstance.required = true; fixture.componentInstance.minLen = 3; fixture.componentInstance.maxLen = 3; @@ -926,7 +904,7 @@ export function main() { })); it('should support optional fields with string pattern validator', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelMultipleValidators); + const fixture = initTest(NgModelMultipleValidators); fixture.componentInstance.required = false; fixture.componentInstance.pattern = '[a-z]+'; fixture.detectChanges(); @@ -948,7 +926,7 @@ export function main() { })); it('should support optional fields with RegExp pattern validator', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelMultipleValidators); + const fixture = initTest(NgModelMultipleValidators); fixture.componentInstance.required = false; fixture.componentInstance.pattern = /^[a-z]+$/; fixture.detectChanges(); @@ -970,7 +948,7 @@ export function main() { })); it('should support optional fields with minlength validator', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelMultipleValidators); + const fixture = initTest(NgModelMultipleValidators); fixture.componentInstance.required = false; fixture.componentInstance.minLen = 2; fixture.detectChanges(); @@ -993,7 +971,7 @@ export function main() { it('changes on bound properties should change the validation state of the form', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelValidationBindings); + const fixture = initTest(NgModelValidationBindings); fixture.detectChanges(); tick(); @@ -1067,7 +1045,7 @@ export function main() { describe('ngModel corner cases', () => { it('should update the view when the model is set back to what used to be in the view', fakeAsync(() => { - const fixture = TestBed.createComponent(StandaloneNgModel); + const fixture = initTest(StandaloneNgModel); fixture.componentInstance.name = ''; fixture.detectChanges(); tick(); @@ -1095,7 +1073,7 @@ export function main() { })); it('should not crash when validity is checked from a binding', fakeAsync(() => { - const fixture = TestBed.createComponent(NgModelValidBinding); + const fixture = initTest(NgModelValidBinding); tick(); expect(() => fixture.detectChanges()).not.toThrowError(); }));