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
|
||||
} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
import {TimerWrapper} from '../src/facade/async';
|
||||
import {IS_DART} from '../src/facade/lang';
|
||||
import {PromiseWrapper} from '../src/facade/promise';
|
||||
import {dispatchEvent} from "@angular/platform-browser/testing";
|
||||
|
@ -123,6 +124,26 @@ class AsyncChangeComp {
|
|||
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 {
|
||||
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)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import {DirectiveResolver, ViewResolver} from '../index';
|
||||
|
||||
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 {ListWrapper, MapWrapper} from '../src/facade/collection';
|
||||
|
||||
|
@ -102,10 +102,16 @@ export class ComponentFixture<T> {
|
|||
});
|
||||
this._onStableSubscription = ObservableWrapper.subscribe(ngZone.onStable, (_) => {
|
||||
this._isStable = true;
|
||||
if (this._completer != null) {
|
||||
this._completer.resolve(true);
|
||||
this._completer = null;
|
||||
}
|
||||
// Check whether there are no pending macrotasks in a microtask so that ngZone gets a chance
|
||||
// to update the state of pending macrotasks.
|
||||
scheduleMicroTask(() => {
|
||||
if (!this.ngZone.hasPendingMacrotasks) {
|
||||
if (this._completer != null) {
|
||||
this._completer.resolve(true);
|
||||
this._completer = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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
|
||||
* yet.
|
||||
*/
|
||||
isStable(): boolean { return this._isStable; }
|
||||
isStable(): boolean { return this._isStable && !this.ngZone.hasPendingMacrotasks; }
|
||||
|
||||
/**
|
||||
* Get a promise that resolves when the fixture is stable.
|
||||
|
@ -165,8 +171,10 @@ export class ComponentFixture<T> {
|
|||
* asynchronous change detection.
|
||||
*/
|
||||
whenStable(): Promise<any> {
|
||||
if (this._isStable) {
|
||||
if (this.isStable()) {
|
||||
return PromiseWrapper.resolve(false);
|
||||
} else if (this._completer !== null) {
|
||||
return this._completer.promise;
|
||||
} else {
|
||||
this._completer = new PromiseCompleter<any>();
|
||||
return this._completer.promise;
|
||||
|
|
Loading…
Reference in New Issue