diff --git a/lib/zone-spec/fake-async-test.ts b/lib/zone-spec/fake-async-test.ts index d6d6fcce2..239b1ca75 100644 --- a/lib/zone-spec/fake-async-test.ts +++ b/lib/zone-spec/fake-async-test.ts @@ -91,9 +91,10 @@ this._currentTime = finalTime; } - flush(limit: number = 20): number { + flush(limit: number = 20, flushPeriodic = false): number { const startTime = this._currentTime; let count = 0; + let seenTimers: number[] = []; while (this._schedulerQueue.length > 0) { count++; if (count > limit) { @@ -101,12 +102,28 @@ 'flush failed after reaching the limit of ' + limit + ' tasks. Does your code use a polling timeout?'); } - // If the only remaining tasks are periodic, finish flushing. - if (!(this._schedulerQueue.filter(task => !task.isPeriodic && !task.isRequestAnimationFrame) - .length)) { - break; + if (!flushPeriodic) { + // flush only non-periodic timers. + // If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing. + if (this._schedulerQueue.filter(task => !task.isPeriodic && !task.isRequestAnimationFrame) + .length === 0) { + break; + } + } else { + // flushPeriodic has been requested. + // Stop when all timer id-s have been seen at least once. + if (this._schedulerQueue + .filter( + task => + seenTimers.indexOf(task.id) === -1 || this._currentTime === task.endTime) + .length === 0) { + break; + } } let current = this._schedulerQueue.shift(); + if (seenTimers.indexOf(current.id) === -1) { + seenTimers.push(current.id); + } this._currentTime = current.endTime; let retval = current.func.apply(global, current.args); if (!retval) { @@ -254,10 +271,10 @@ flushErrors(); } - flush(limit?: number): number { + flush(limit?: number, flushPeriodic?: boolean): number { FakeAsyncTestZoneSpec.assertInZone(); this.flushMicrotasks(); - let elapsed = this._scheduler.flush(limit); + let elapsed = this._scheduler.flush(limit, flushPeriodic); if (this._lastError !== null) { this._resetLastErrorAndThrow(); } diff --git a/test/zone-spec/fake-async-test.spec.ts b/test/zone-spec/fake-async-test.spec.ts index 2eb1113bb..04813f0de 100644 --- a/test/zone-spec/fake-async-test.spec.ts +++ b/test/zone-spec/fake-async-test.spec.ts @@ -502,7 +502,7 @@ describe('FakeAsyncTestZoneSpec', () => { 'flush failed after reaching the limit of 20 tasks. Does your code use a polling timeout?'); }); - it('accepts a custom limit', function() { + it('accepts a custom limit', () => { expect(() => { fakeAsyncTestZone.run(() => { let z = 0; @@ -521,6 +521,42 @@ describe('FakeAsyncTestZoneSpec', () => { .toThrowError( 'flush failed after reaching the limit of 10 tasks. Does your code use a polling timeout?'); }); + + it('can flush periodic timers if flushPeriodic is true', () => { + fakeAsyncTestZone.run(() => { + let x = 0; + + setInterval(() => { + x++; + }, 10); + + let elapsed = testZoneSpec.flush(20, true); + + expect(elapsed).toEqual(10); + expect(x).toEqual(1); + }); + }); + + it('can flush multiple periodic timers if flushPeriodic is true', () => { + fakeAsyncTestZone.run(() => { + let x = 0; + let y = 0; + + setInterval(() => { + x++; + }, 10); + + setInterval(() => { + y++; + }, 100); + + let elapsed = testZoneSpec.flush(20, true); + + expect(elapsed).toEqual(100); + expect(x).toEqual(10); + expect(y).toEqual(1); + }); + }); }); describe('outside of FakeAsync Zone', () => { @@ -578,6 +614,27 @@ describe('FakeAsyncTestZoneSpec', () => { expect(ran).toEqual(false); }); }); + it('is not flushed when flushPeriodic is false', () => { + let ran = false; + fakeAsyncTestZone.run(() => { + requestAnimationFrame(() => { + ran = true; + }); + testZoneSpec.flush(20); + expect(ran).toEqual(false); + }); + }); + it('is flushed when flushPeriodic is true', () => { + let ran = false; + fakeAsyncTestZone.run(() => { + requestAnimationFrame(() => { + ran = true; + }); + const elapsed = testZoneSpec.flush(20, true); + expect(elapsed).toEqual(16); + expect(ran).toEqual(true); + }); + }); })); }); });