fix(testing): Check for pending macrotasks in ComponentFixture.whenStable() and ComponentFixture.isStable()
Closes #8389
This commit is contained in:
parent
27a7b51d99
commit
509f4ec611
|
@ -25,6 +25,7 @@ import {
|
||||||
ComponentResolver
|
ComponentResolver
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {NgIf} from '@angular/common';
|
import {NgIf} from '@angular/common';
|
||||||
|
import {TimerWrapper} from '../src/facade/async';
|
||||||
import {IS_DART} from '../src/facade/lang';
|
import {IS_DART} from '../src/facade/lang';
|
||||||
import {PromiseWrapper} from '../src/facade/promise';
|
import {PromiseWrapper} from '../src/facade/promise';
|
||||||
import {dispatchEvent} from "@angular/platform-browser/testing";
|
import {dispatchEvent} from "@angular/platform-browser/testing";
|
||||||
|
@ -123,6 +124,26 @@ class AsyncChangeComp {
|
||||||
click() { this.text += '1'; }
|
click() { this.text += '1'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'async-timeout-comp', template: `<span (click)='click()'>{{text}}</span>`})
|
||||||
|
class AsyncTimeoutComp {
|
||||||
|
text: string = '1';
|
||||||
|
|
||||||
|
click() {
|
||||||
|
TimerWrapper.setTimeout(() => { this.text += '1'; }, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
{selector: 'nested-async-timeout-comp', template: `<span (click)='click()'>{{text}}</span>`})
|
||||||
|
class NestedAsyncTimeoutComp {
|
||||||
|
text: string = '1';
|
||||||
|
|
||||||
|
click() {
|
||||||
|
TimerWrapper.setTimeout(() => { TimerWrapper.setTimeout(() => { this.text += '1'; }, 10); },
|
||||||
|
10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class FancyService {
|
class FancyService {
|
||||||
value: string = 'real value';
|
value: string = 'real value';
|
||||||
}
|
}
|
||||||
|
@ -378,6 +399,108 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should wait for macroTask(setTimeout) while checking for whenStable ' +
|
||||||
|
'(autoDetectChanges)',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
(tcb: TestComponentBuilder, async) => {
|
||||||
|
|
||||||
|
tcb.createAsync(AsyncTimeoutComp)
|
||||||
|
.then((componentFixture) => {
|
||||||
|
componentFixture.autoDetectChanges();
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
let element = componentFixture.debugElement.children[0];
|
||||||
|
dispatchEvent(element.nativeElement, 'click');
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
// Component is updated asynchronously. Wait for the fixture to become
|
||||||
|
// stable before checking for new value.
|
||||||
|
expect(componentFixture.isStable()).toBe(false);
|
||||||
|
componentFixture.whenStable().then((waited) => {
|
||||||
|
expect(waited).toBe(true);
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('11');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should wait for macroTask(setTimeout) while checking for whenStable ' +
|
||||||
|
'(no autoDetectChanges)',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
(tcb: TestComponentBuilder, async) => {
|
||||||
|
|
||||||
|
tcb.createAsync(AsyncTimeoutComp)
|
||||||
|
.then((componentFixture) => {
|
||||||
|
componentFixture.detectChanges();
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
let element = componentFixture.debugElement.children[0];
|
||||||
|
dispatchEvent(element.nativeElement, 'click');
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
// Component is updated asynchronously. Wait for the fixture to become
|
||||||
|
// stable before checking for new value.
|
||||||
|
expect(componentFixture.isStable()).toBe(false);
|
||||||
|
componentFixture.whenStable().then((waited) => {
|
||||||
|
expect(waited).toBe(true);
|
||||||
|
componentFixture.detectChanges();
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('11');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should wait for nested macroTasks(setTimeout) while checking for whenStable ' +
|
||||||
|
'(autoDetectChanges)',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
(tcb: TestComponentBuilder, async) => {
|
||||||
|
|
||||||
|
tcb.createAsync(NestedAsyncTimeoutComp)
|
||||||
|
.then((componentFixture) => {
|
||||||
|
componentFixture.autoDetectChanges();
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
let element = componentFixture.debugElement.children[0];
|
||||||
|
dispatchEvent(element.nativeElement, 'click');
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
// Component is updated asynchronously. Wait for the fixture to become
|
||||||
|
// stable before checking for new value.
|
||||||
|
expect(componentFixture.isStable()).toBe(false);
|
||||||
|
componentFixture.whenStable().then((waited) => {
|
||||||
|
expect(waited).toBe(true);
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('11');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should wait for nested macroTasks(setTimeout) while checking for whenStable ' +
|
||||||
|
'(no autoDetectChanges)',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
(tcb: TestComponentBuilder, async) => {
|
||||||
|
|
||||||
|
tcb.createAsync(NestedAsyncTimeoutComp)
|
||||||
|
.then((componentFixture) => {
|
||||||
|
componentFixture.detectChanges();
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
let element = componentFixture.debugElement.children[0];
|
||||||
|
dispatchEvent(element.nativeElement, 'click');
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('1');
|
||||||
|
|
||||||
|
// Component is updated asynchronously. Wait for the fixture to become
|
||||||
|
// stable before checking for new value.
|
||||||
|
expect(componentFixture.isStable()).toBe(false);
|
||||||
|
componentFixture.whenStable().then((waited) => {
|
||||||
|
expect(waited).toBe(true);
|
||||||
|
componentFixture.detectChanges();
|
||||||
|
expect(componentFixture.nativeElement).toHaveText('11');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should stabilize after async task in change detection (autoDetectChanges)',
|
it('should stabilize after async task in change detection (autoDetectChanges)',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||||
(tcb: TestComponentBuilder, async) => {
|
(tcb: TestComponentBuilder, async) => {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
import {DirectiveResolver, ViewResolver} from '../index';
|
import {DirectiveResolver, ViewResolver} from '../index';
|
||||||
|
|
||||||
import {BaseException} from '../src/facade/exceptions';
|
import {BaseException} from '../src/facade/exceptions';
|
||||||
import {Type, isPresent, isBlank, IS_DART} from '../src/facade/lang';
|
import {Type, isPresent, isBlank, IS_DART, scheduleMicroTask} from '../src/facade/lang';
|
||||||
import {PromiseWrapper, ObservableWrapper, PromiseCompleter} from '../src/facade/async';
|
import {PromiseWrapper, ObservableWrapper, PromiseCompleter} from '../src/facade/async';
|
||||||
import {ListWrapper, MapWrapper} from '../src/facade/collection';
|
import {ListWrapper, MapWrapper} from '../src/facade/collection';
|
||||||
|
|
||||||
|
@ -102,10 +102,16 @@ export class ComponentFixture<T> {
|
||||||
});
|
});
|
||||||
this._onStableSubscription = ObservableWrapper.subscribe(ngZone.onStable, (_) => {
|
this._onStableSubscription = ObservableWrapper.subscribe(ngZone.onStable, (_) => {
|
||||||
this._isStable = true;
|
this._isStable = true;
|
||||||
if (this._completer != null) {
|
// Check whether there are no pending macrotasks in a microtask so that ngZone gets a chance
|
||||||
this._completer.resolve(true);
|
// to update the state of pending macrotasks.
|
||||||
this._completer = null;
|
scheduleMicroTask(() => {
|
||||||
}
|
if (!this.ngZone.hasPendingMacrotasks) {
|
||||||
|
if (this._completer != null) {
|
||||||
|
this._completer.resolve(true);
|
||||||
|
this._completer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this._onErrorSubscription = ObservableWrapper.subscribe(
|
this._onErrorSubscription = ObservableWrapper.subscribe(
|
||||||
|
@ -156,7 +162,7 @@ export class ComponentFixture<T> {
|
||||||
* Return whether the fixture is currently stable or has async tasks that have not been completed
|
* Return whether the fixture is currently stable or has async tasks that have not been completed
|
||||||
* yet.
|
* yet.
|
||||||
*/
|
*/
|
||||||
isStable(): boolean { return this._isStable; }
|
isStable(): boolean { return this._isStable && !this.ngZone.hasPendingMacrotasks; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a promise that resolves when the fixture is stable.
|
* Get a promise that resolves when the fixture is stable.
|
||||||
|
@ -165,8 +171,10 @@ export class ComponentFixture<T> {
|
||||||
* asynchronous change detection.
|
* asynchronous change detection.
|
||||||
*/
|
*/
|
||||||
whenStable(): Promise<any> {
|
whenStable(): Promise<any> {
|
||||||
if (this._isStable) {
|
if (this.isStable()) {
|
||||||
return PromiseWrapper.resolve(false);
|
return PromiseWrapper.resolve(false);
|
||||||
|
} else if (this._completer !== null) {
|
||||||
|
return this._completer.promise;
|
||||||
} else {
|
} else {
|
||||||
this._completer = new PromiseCompleter<any>();
|
this._completer = new PromiseCompleter<any>();
|
||||||
return this._completer.promise;
|
return this._completer.promise;
|
||||||
|
|
Loading…
Reference in New Issue