fix(core): fix chained http call (#20924)

Fixes an issue where chained http calls would prematurely call
testability whenStable callbacks after the first http call.

Fixes #20921

PR Close #20924
This commit is contained in:
Benjamin Ingberg 2017-12-10 17:08:12 +01:00 committed by Alex Eagle
parent fb4d84d5b8
commit 7e3f9a482a
2 changed files with 44 additions and 37 deletions

View File

@ -98,13 +98,22 @@ export class Testability implements PublicTestability {
/** @internal */ /** @internal */
_runCallbacksIfReady(): void { _runCallbacksIfReady(): void {
if (this.isStable()) { if (this.isStable()) {
// Schedules the call backs in a new frame so that it is always async. if (this._callbacks.length !== 0) {
scheduleMicroTask(() => { // Schedules the call backs after a macro task run outside of the angular zone to make sure
while (this._callbacks.length !== 0) { // no new task are added
(this._callbacks.pop() !)(this._didWork); this._ngZone.runOutsideAngular(() => {
} setTimeout(() => {
if (this.isStable()) {
while (this._callbacks.length !== 0) {
(this._callbacks.pop() !)(this._didWork);
}
this._didWork = false;
}
});
});
} else {
this._didWork = false; this._didWork = false;
}); }
} else { } else {
// Not Ready // Not Ready
this._didWork = true; this._didWork = true;

View File

@ -16,13 +16,11 @@ import {scheduleMicroTask} from '../../src/util';
// Schedules a microtasks (using a resolved promise .then()) // Schedules a task to be run after Testability checks for oustanding tasks. Since Testability
function microTask(fn: Function): void { // uses a 0 second timeout to check for outstanding tasks we add our 0 second timeout after a
scheduleMicroTask(() => { // micro task (which ensures Testability's timeout is run first).
// We do double dispatch so that we can wait for scheduleMicrotask in the Testability when function afterTestabilityCheck(fn: Function): void {
// NgZone becomes stable. scheduleMicroTask(() => setTimeout(fn));
scheduleMicroTask(fn);
});
} }
@Injectable() @Injectable()
@ -65,7 +63,7 @@ class MockNgZone extends NgZone {
it('should fire whenstable callbacks if pending count is 0', it('should fire whenstable callbacks if pending count is 0',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalled(); expect(execute).toHaveBeenCalled();
async.done(); async.done();
}); });
@ -82,11 +80,11 @@ class MockNgZone extends NgZone {
testability.increasePendingRequestCount(); testability.increasePendingRequestCount();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
testability.decreasePendingRequestCount(); testability.decreasePendingRequestCount();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
async.done(); async.done();
}); });
@ -98,11 +96,11 @@ class MockNgZone extends NgZone {
testability.increasePendingRequestCount(); testability.increasePendingRequestCount();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
testability.decreasePendingRequestCount(); testability.decreasePendingRequestCount();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalled(); expect(execute).toHaveBeenCalled();
async.done(); async.done();
}); });
@ -120,7 +118,7 @@ class MockNgZone extends NgZone {
it('should fire whenstable callbacks with didWork if pending count is 0', it('should fire whenstable callbacks with didWork if pending count is 0',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalledWith(false); expect(execute).toHaveBeenCalledWith(false);
async.done(); async.done();
}); });
@ -131,14 +129,14 @@ class MockNgZone extends NgZone {
testability.increasePendingRequestCount(); testability.increasePendingRequestCount();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
testability.decreasePendingRequestCount(); testability.decreasePendingRequestCount();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalledWith(true); expect(execute).toHaveBeenCalledWith(true);
testability.whenStable(execute2); testability.whenStable(execute2);
microTask(() => { afterTestabilityCheck(() => {
expect(execute2).toHaveBeenCalledWith(false); expect(execute2).toHaveBeenCalledWith(false);
async.done(); async.done();
}); });
@ -154,7 +152,7 @@ class MockNgZone extends NgZone {
ngZone.stable(); ngZone.stable();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalled(); expect(execute).toHaveBeenCalled();
async.done(); async.done();
}); });
@ -173,11 +171,11 @@ class MockNgZone extends NgZone {
ngZone.unstable(); ngZone.unstable();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
ngZone.stable(); ngZone.stable();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalled(); expect(execute).toHaveBeenCalled();
async.done(); async.done();
}); });
@ -198,15 +196,15 @@ class MockNgZone extends NgZone {
testability.increasePendingRequestCount(); testability.increasePendingRequestCount();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
testability.decreasePendingRequestCount(); testability.decreasePendingRequestCount();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
ngZone.stable(); ngZone.stable();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalled(); expect(execute).toHaveBeenCalled();
async.done(); async.done();
}); });
@ -221,19 +219,19 @@ class MockNgZone extends NgZone {
testability.increasePendingRequestCount(); testability.increasePendingRequestCount();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
ngZone.stable(); ngZone.stable();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
testability.decreasePendingRequestCount(); testability.decreasePendingRequestCount();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).not.toHaveBeenCalled(); expect(execute).not.toHaveBeenCalled();
testability.decreasePendingRequestCount(); testability.decreasePendingRequestCount();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalled(); expect(execute).toHaveBeenCalled();
async.done(); async.done();
}); });
@ -248,11 +246,11 @@ class MockNgZone extends NgZone {
ngZone.stable(); ngZone.stable();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalledWith(true); expect(execute).toHaveBeenCalledWith(true);
testability.whenStable(execute2); testability.whenStable(execute2);
microTask(() => { afterTestabilityCheck(() => {
expect(execute2).toHaveBeenCalledWith(false); expect(execute2).toHaveBeenCalledWith(false);
async.done(); async.done();
}); });
@ -264,14 +262,14 @@ class MockNgZone extends NgZone {
ngZone.unstable(); ngZone.unstable();
testability.whenStable(execute); testability.whenStable(execute);
microTask(() => { afterTestabilityCheck(() => {
ngZone.stable(); ngZone.stable();
microTask(() => { afterTestabilityCheck(() => {
expect(execute).toHaveBeenCalledWith(true); expect(execute).toHaveBeenCalledWith(true);
testability.whenStable(execute2); testability.whenStable(execute2);
microTask(() => { afterTestabilityCheck(() => {
expect(execute2).toHaveBeenCalledWith(false); expect(execute2).toHaveBeenCalledWith(false);
async.done(); async.done();
}); });