diff --git a/lib/zone-spec/fake-async-test.ts b/lib/zone-spec/fake-async-test.ts index f6b3f829f..78382a8ab 100644 --- a/lib/zone-spec/fake-async-test.ts +++ b/lib/zone-spec/fake-async-test.ts @@ -80,6 +80,7 @@ private _scheduler: Scheduler = new Scheduler(); private _microtasks: Function[] = []; private _lastError: Error = null; + private _uncaughtPromiseErrors: {rejection: any}[] = Promise[Zone['__symbol__']('uncaughtPromiseErrors')]; pendingPeriodicTimers: number[] = []; pendingTimers: number[] = []; @@ -172,7 +173,8 @@ } private _resetLastErrorAndThrow(): void { - let error = this._lastError; + let error = this._lastError || this._uncaughtPromiseErrors[0]; + this._uncaughtPromiseErrors.length = 0; this._lastError = null; throw error; } @@ -188,14 +190,17 @@ flushMicrotasks(): void { FakeAsyncTestZoneSpec.assertInZone(); - while (this._microtasks.length > 0) { - let microtask = this._microtasks.shift(); - microtask(); - if (this._lastError !== null) { + const flushErrors = () => { + if (this._lastError !== null || this._uncaughtPromiseErrors.length) { // If there is an error stop processing the microtask queue and rethrow the error. this._resetLastErrorAndThrow(); } } + while (this._microtasks.length > 0) { + let microtask = this._microtasks.shift(); + microtask(); + } + flushErrors(); } // ZoneSpec implementation below. diff --git a/lib/zone.ts b/lib/zone.ts index 888e73bdc..f5a318f97 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -868,7 +868,7 @@ const Zone: ZoneType = (function(global: any) { let _currentTask: Task = null; let _microTaskQueue: Task[] = []; let _isDrainingMicrotaskQueue: boolean = false; - let _uncaughtPromiseErrors: UncaughtPromiseError[] = []; + const _uncaughtPromiseErrors: UncaughtPromiseError[] = []; let _drainScheduled: boolean = false; function scheduleQueueDrain() { @@ -894,7 +894,8 @@ const Zone: ZoneType = (function(global: any) { 'Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, '; Zone:', (e.zone).name, '; Task:', e.task && (e.task).source, - '; Value:', rejection + '; Value:', rejection, + rejection instanceof Error ? rejection.stack : undefined ); } console.error(e); @@ -916,10 +917,8 @@ const Zone: ZoneType = (function(global: any) { } } while(_uncaughtPromiseErrors.length) { - const uncaughtPromiseErrors = _uncaughtPromiseErrors; - _uncaughtPromiseErrors = []; - for (let i = 0; i < uncaughtPromiseErrors.length; i++) { - const uncaughtPromiseError: UncaughtPromiseError = uncaughtPromiseErrors[i]; + while(_uncaughtPromiseErrors.length) { + const uncaughtPromiseError: UncaughtPromiseError = _uncaughtPromiseErrors.shift(); try { uncaughtPromiseError.zone.runGuarded(() => { throw uncaughtPromiseError; }); } catch (e) { @@ -1119,5 +1118,7 @@ const Zone: ZoneType = (function(global: any) { } } + // This is not part of public API, but it is usefull for tests, so we expose it. + Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; return global.Zone = Zone; })(typeof window === 'undefined' ? global : window); diff --git a/test/zone-spec/fake-async-test.spec.ts b/test/zone-spec/fake-async-test.spec.ts index 29f1b5019..96e491ab1 100644 --- a/test/zone-spec/fake-async-test.spec.ts +++ b/test/zone-spec/fake-async-test.spec.ts @@ -30,6 +30,15 @@ describe('FakeAsyncTestZoneSpec', () => { fakeAsyncTestZone.run(() => { throw new Error('sync'); }); }).toThrowError('sync'); }); + + it('should throw error on Rejected promise', () => { + expect(() => { + fakeAsyncTestZone.run(() => { + Promise.reject('myError') + testZoneSpec.flushMicrotasks(); + }); + }).toThrowError('Uncaught (in promise): myError'); + }); }); describe('asynchronous code', () => {