test: fix memory leak when running test campaign (#11072)
This commit is contained in:
parent
566d4361e2
commit
d7c82f5c0f
|
@ -57,6 +57,7 @@ export class ComponentFixture<T> {
|
|||
private _autoDetect: boolean;
|
||||
|
||||
private _isStable: boolean = true;
|
||||
private _isDestroyed: boolean = false;
|
||||
private _resolve: (result: any) => void;
|
||||
private _promise: Promise<any> = null;
|
||||
private _onUnstableSubscription: any /** TODO #9100 */ = null;
|
||||
|
@ -178,22 +179,25 @@ export class ComponentFixture<T> {
|
|||
* Trigger component destruction.
|
||||
*/
|
||||
destroy(): void {
|
||||
this.componentRef.destroy();
|
||||
if (this._onUnstableSubscription != null) {
|
||||
this._onUnstableSubscription.unsubscribe();
|
||||
this._onUnstableSubscription = null;
|
||||
}
|
||||
if (this._onStableSubscription != null) {
|
||||
this._onStableSubscription.unsubscribe();
|
||||
this._onStableSubscription = null;
|
||||
}
|
||||
if (this._onMicrotaskEmptySubscription != null) {
|
||||
this._onMicrotaskEmptySubscription.unsubscribe();
|
||||
this._onMicrotaskEmptySubscription = null;
|
||||
}
|
||||
if (this._onErrorSubscription != null) {
|
||||
this._onErrorSubscription.unsubscribe();
|
||||
this._onErrorSubscription = null;
|
||||
if (!this._isDestroyed) {
|
||||
this.componentRef.destroy();
|
||||
if (this._onUnstableSubscription != null) {
|
||||
this._onUnstableSubscription.unsubscribe();
|
||||
this._onUnstableSubscription = null;
|
||||
}
|
||||
if (this._onStableSubscription != null) {
|
||||
this._onStableSubscription.unsubscribe();
|
||||
this._onStableSubscription = null;
|
||||
}
|
||||
if (this._onMicrotaskEmptySubscription != null) {
|
||||
this._onMicrotaskEmptySubscription.unsubscribe();
|
||||
this._onMicrotaskEmptySubscription = null;
|
||||
}
|
||||
if (this._onErrorSubscription != null) {
|
||||
this._onErrorSubscription.unsubscribe();
|
||||
this._onErrorSubscription = null;
|
||||
}
|
||||
this._isDestroyed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import {ListWrapper} from '../src/facade/collection';
|
|||
import {BaseException} from '../src/facade/exceptions';
|
||||
import {FunctionWrapper, stringify} from '../src/facade/lang';
|
||||
import {Type} from '../src/type';
|
||||
|
||||
import {AsyncTestCompleter} from './async_test_completer';
|
||||
import {ComponentFixture} from './component_fixture';
|
||||
import {MetadataOverride} from './metadata_override';
|
||||
|
@ -155,6 +156,7 @@ export class TestBed implements Injector {
|
|||
private _declarations: Array<Type<any>|any[]|any> = [];
|
||||
private _imports: Array<Type<any>|any[]|any> = [];
|
||||
private _schemas: Array<SchemaMetadata|any[]> = [];
|
||||
private _activeFixtures: ComponentFixture<any>[] = [];
|
||||
|
||||
/**
|
||||
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
|
||||
|
@ -203,6 +205,8 @@ export class TestBed implements Injector {
|
|||
this._imports = [];
|
||||
this._schemas = [];
|
||||
this._instantiated = false;
|
||||
this._activeFixtures.forEach((fixture) => fixture.destroy());
|
||||
this._activeFixtures = [];
|
||||
}
|
||||
|
||||
platform: PlatformRef = null;
|
||||
|
@ -355,7 +359,9 @@ export class TestBed implements Injector {
|
|||
return new ComponentFixture<T>(componentRef, ngZone, autoDetect);
|
||||
};
|
||||
|
||||
return ngZone == null ? initComponent() : ngZone.run(initComponent);
|
||||
const fixture = ngZone == null ? initComponent() : ngZone.run(initComponent);
|
||||
this._activeFixtures.push(fixture);
|
||||
return fixture;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,11 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn
|
|||
this.formDirective.addFormGroup(this);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this.formDirective.removeFormGroup(this); }
|
||||
ngOnDestroy(): void {
|
||||
if (this.formDirective) {
|
||||
this.formDirective.removeFormGroup(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link FormGroup} backing this binding.
|
||||
|
@ -52,7 +56,7 @@ export class AbstractFormGroupDirective extends ControlContainer implements OnIn
|
|||
/**
|
||||
* Get the {@link Form} to which this group belongs.
|
||||
*/
|
||||
get formDirective(): Form { return this._parent.formDirective; }
|
||||
get formDirective(): Form { return this._parent ? this._parent.formDirective : null; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
|
|
|
@ -105,7 +105,6 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
|||
// TODO(kara): Replace ngModel with reactive API
|
||||
@Input('ngModel') model: any;
|
||||
@Output('ngModelChange') update = new EventEmitter();
|
||||
|
||||
@Input('disabled')
|
||||
set disabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
|
||||
|
||||
|
@ -133,7 +132,11 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this.formDirective.removeControl(this); }
|
||||
ngOnDestroy(): void {
|
||||
if (this.formDirective) {
|
||||
this.formDirective.removeControl(this);
|
||||
}
|
||||
}
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
|
@ -142,7 +145,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
|||
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
||||
get formDirective(): any { return this._parent.formDirective; }
|
||||
get formDirective(): any { return this._parent ? this._parent.formDirective : null; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
|
|
|
@ -161,11 +161,17 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy
|
|||
this.formDirective.addFormArray(this);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this.formDirective.removeFormArray(this); }
|
||||
ngOnDestroy(): void {
|
||||
if (this.formDirective) {
|
||||
this.formDirective.removeFormArray(this);
|
||||
}
|
||||
}
|
||||
|
||||
get control(): FormArray { return this.formDirective.getFormArray(this); }
|
||||
|
||||
get formDirective(): FormGroupDirective { return <FormGroupDirective>this._parent.formDirective; }
|
||||
get formDirective(): FormGroupDirective {
|
||||
return this._parent ? <FormGroupDirective>this._parent.formDirective : null;
|
||||
}
|
||||
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
||||
|
|
|
@ -135,61 +135,63 @@ export function main() {
|
|||
expect(form.value).toEqual({});
|
||||
}));
|
||||
|
||||
it('should set status classes with ngModel', () => {
|
||||
const fixture = TestBed.createComponent(NgModelForm);
|
||||
fixture.debugElement.componentInstance.name = 'aa';
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
it('should set status classes with ngModel', async(() => {
|
||||
const fixture = TestBed.createComponent(NgModelForm);
|
||||
fixture.debugElement.componentInstance.name = 'aa';
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
|
||||
const input = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
|
||||
|
||||
dispatchEvent(input, 'blur');
|
||||
fixture.detectChanges();
|
||||
dispatchEvent(input, 'blur');
|
||||
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';
|
||||
dispatchEvent(input, 'input');
|
||||
fixture.detectChanges();
|
||||
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
|
||||
});
|
||||
});
|
||||
input.value = 'updatedValue';
|
||||
dispatchEvent(input, 'input');
|
||||
fixture.detectChanges();
|
||||
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should set status classes with ngModelGroup and ngForm', () => {
|
||||
const fixture = TestBed.createComponent(NgModelGroupForm);
|
||||
fixture.debugElement.componentInstance.first = '';
|
||||
fixture.detectChanges();
|
||||
it('should set status classes with ngModelGroup and ngForm', async(() => {
|
||||
const fixture = TestBed.createComponent(NgModelGroupForm);
|
||||
fixture.debugElement.componentInstance.first = '';
|
||||
fixture.detectChanges();
|
||||
|
||||
const form = fixture.debugElement.query(By.css('form')).nativeElement;
|
||||
const modelGroup = fixture.debugElement.query(By.css('[ngModelGroup]')).nativeElement;
|
||||
const input = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
const form = fixture.debugElement.query(By.css('form')).nativeElement;
|
||||
const modelGroup = fixture.debugElement.query(By.css('[ngModelGroup]')).nativeElement;
|
||||
const input = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||
|
||||
// ngModelGroup creates its control asynchronously
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(sortedClassList(modelGroup)).toEqual([
|
||||
'ng-invalid', 'ng-pristine', 'ng-untouched'
|
||||
]);
|
||||
// ngModelGroup creates its control asynchronously
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(sortedClassList(modelGroup)).toEqual([
|
||||
'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');
|
||||
fixture.detectChanges();
|
||||
dispatchEvent(input, 'blur');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(sortedClassList(modelGroup)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
|
||||
expect(sortedClassList(form)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
|
||||
expect(sortedClassList(modelGroup)).toEqual([
|
||||
'ng-invalid', 'ng-pristine', 'ng-touched'
|
||||
]);
|
||||
expect(sortedClassList(form)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
|
||||
|
||||
input.value = 'updatedValue';
|
||||
dispatchEvent(input, 'input');
|
||||
fixture.detectChanges();
|
||||
input.value = 'updatedValue';
|
||||
dispatchEvent(input, 'input');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(sortedClassList(modelGroup)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
|
||||
expect(sortedClassList(form)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
|
||||
});
|
||||
});
|
||||
expect(sortedClassList(modelGroup)).toEqual(['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', () => {
|
||||
const fixture = TestBed.createComponent(NgNoFormComp);
|
||||
|
|
Loading…
Reference in New Issue