Update the license headers throughout the repository to reference Google LLC rather than Google Inc, for the required license headers. PR Close #37205
		
			
				
	
	
		
			700 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			700 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google LLC All Rights Reserved.
 | |
|  *
 | |
|  * Use of this source code is governed by an MIT-style license that can be
 | |
|  * found in the LICENSE file at https://angular.io/license
 | |
|  */
 | |
| 
 | |
| import {isNode, zoneSymbol} from '../../lib/common/utils';
 | |
| import {ifEnvSupports} from '../test-util';
 | |
| 
 | |
| declare const global: any;
 | |
| 
 | |
| class MicroTaskQueueZoneSpec implements ZoneSpec {
 | |
|   name: string = 'MicroTaskQueue';
 | |
|   queue: MicroTask[] = [];
 | |
|   properties = {queue: this.queue, flush: this.flush.bind(this)};
 | |
| 
 | |
|   flush() {
 | |
|     while (this.queue.length) {
 | |
|       const task = this.queue.shift();
 | |
|       task!.invoke();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   onScheduleTask(delegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task): any {
 | |
|     this.queue.push(task as MicroTask);
 | |
|   }
 | |
| }
 | |
| 
 | |
| function flushMicrotasks() {
 | |
|   Zone.current.get('flush')();
 | |
| }
 | |
| 
 | |
| class TestRejection {
 | |
|   prop1?: string;
 | |
|   prop2?: string;
 | |
| }
 | |
| 
 | |
| describe(
 | |
|     'Promise', ifEnvSupports('Promise', function() {
 | |
|       if (!global.Promise) return;
 | |
|       let log: string[];
 | |
|       let queueZone: Zone;
 | |
|       let testZone: Zone;
 | |
|       let pZone: Zone;
 | |
| 
 | |
|       beforeEach(() => {
 | |
|         testZone = Zone.current.fork({name: 'TestZone'});
 | |
| 
 | |
|         pZone = Zone.current.fork({
 | |
|           name: 'promise-zone',
 | |
|           onScheduleTask:
 | |
|               (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task):
 | |
|                   any => {
 | |
|                     log.push('scheduleTask');
 | |
|                     parentZoneDelegate.scheduleTask(targetZone, task);
 | |
|                   }
 | |
|         });
 | |
| 
 | |
|         queueZone = Zone.current.fork(new MicroTaskQueueZoneSpec());
 | |
| 
 | |
|         log = [];
 | |
|       });
 | |
| 
 | |
|       xit('should allow set es6 Promise after load ZoneAwarePromise', (done) => {
 | |
|         const ES6Promise = require('es6-promise').Promise;
 | |
|         const NativePromise = global[zoneSymbol('Promise')];
 | |
| 
 | |
|         try {
 | |
|           global['Promise'] = ES6Promise;
 | |
|           Zone.assertZonePatched();
 | |
|           expect(global[zoneSymbol('Promise')]).toBe(ES6Promise);
 | |
|           const promise = Promise.resolve(0);
 | |
|           console.log('promise', promise);
 | |
|           promise
 | |
|               .then(value => {
 | |
|                 expect(value).toBe(0);
 | |
|                 done();
 | |
|               })
 | |
|               .catch(error => {
 | |
|                 fail(error);
 | |
|               });
 | |
|         } finally {
 | |
|           global['Promise'] = NativePromise;
 | |
|           Zone.assertZonePatched();
 | |
|           expect(global[zoneSymbol('Promise')]).toBe(NativePromise);
 | |
|         }
 | |
|       });
 | |
| 
 | |
|       it('should pretend to be a native code', () => {
 | |
|         expect(String(Promise).indexOf('[native code]') >= 0).toBe(true);
 | |
|       });
 | |
| 
 | |
|       it('should use native toString for promise instance', () => {
 | |
|         expect(Object.prototype.toString.call(Promise.resolve())).toEqual('[object Promise]');
 | |
|       });
 | |
| 
 | |
|       it('should make sure that new Promise is instance of Promise', () => {
 | |
|         expect(Promise.resolve(123) instanceof Promise).toBe(true);
 | |
|         expect(new Promise(() => null) instanceof Promise).toBe(true);
 | |
|       });
 | |
| 
 | |
|       xit('should ensure that Promise this is instanceof Promise', () => {
 | |
|         expect(() => {
 | |
|           Promise.call({} as any, () => null);
 | |
|         }).toThrowError('Must be an instanceof Promise.');
 | |
|       });
 | |
| 
 | |
|       it('should allow subclassing without Symbol.species', () => {
 | |
|         class MyPromise extends Promise<any> {
 | |
|           constructor(fn: any) {
 | |
|             super(fn);
 | |
|           }
 | |
|         }
 | |
|         expect(new MyPromise(() => {}).then(() => null) instanceof MyPromise).toBe(true);
 | |
|       });
 | |
| 
 | |
|       it('should allow subclassing with Symbol.species', () => {
 | |
|         class MyPromise extends Promise<any> {
 | |
|           constructor(fn: any) {
 | |
|             super(fn);
 | |
|           }
 | |
| 
 | |
|           static get[Symbol.species]() {
 | |
|             return MyPromise;
 | |
|           }
 | |
|         }
 | |
|         expect(new MyPromise(() => {}).then(() => null) instanceof MyPromise).toBe(true);
 | |
|       });
 | |
| 
 | |
|       it('Symbol.species should return ZoneAwarePromise', () => {
 | |
|         const empty = function() {};
 | |
|         const promise = Promise.resolve(1);
 | |
|         const FakePromise =
 | |
|             ((promise.constructor = {} as any) as any)[Symbol.species] = function(exec: any) {
 | |
|               exec(empty, empty);
 | |
|             };
 | |
|         expect(promise.then(empty) instanceof FakePromise).toBe(true);
 | |
|       });
 | |
| 
 | |
|       it('should intercept scheduling of resolution and then', (done) => {
 | |
|         pZone.run(() => {
 | |
|           let p: Promise<any> = new Promise(function(resolve, reject) {
 | |
|             expect(resolve('RValue')).toBe(undefined);
 | |
|           });
 | |
|           expect(log).toEqual([]);
 | |
|           expect(p instanceof Promise).toBe(true);
 | |
|           p = p.then((v) => {
 | |
|             log.push(v);
 | |
|             expect(v).toBe('RValue');
 | |
|             expect(log).toEqual(['scheduleTask', 'RValue']);
 | |
|             return 'second value';
 | |
|           });
 | |
|           expect(p instanceof Promise).toBe(true);
 | |
|           expect(log).toEqual(['scheduleTask']);
 | |
|           p = p.then((v) => {
 | |
|             log.push(v);
 | |
|             expect(log).toEqual(['scheduleTask', 'RValue', 'scheduleTask', 'second value']);
 | |
|             done();
 | |
|           });
 | |
|           expect(p instanceof Promise).toBe(true);
 | |
|           expect(log).toEqual(['scheduleTask']);
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       it('should allow sync resolution of promises', () => {
 | |
|         queueZone.run(() => {
 | |
|           const flush = Zone.current.get('flush');
 | |
|           const queue = Zone.current.get('queue');
 | |
|           const p = new Promise<string>(function(resolve, reject) {
 | |
|                       resolve('RValue');
 | |
|                     })
 | |
|                         .then((v: string) => {
 | |
|                           log.push(v);
 | |
|                           return 'second value';
 | |
|                         })
 | |
|                         .then((v: string) => {
 | |
|                           log.push(v);
 | |
|                         });
 | |
|           expect(queue.length).toEqual(1);
 | |
|           expect(log).toEqual([]);
 | |
|           flush();
 | |
|           expect(log).toEqual(['RValue', 'second value']);
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       it('should allow sync resolution of promises returning promises', () => {
 | |
|         queueZone.run(() => {
 | |
|           const flush = Zone.current.get('flush');
 | |
|           const queue = Zone.current.get('queue');
 | |
|           const p = new Promise<string>(function(resolve, reject) {
 | |
|                       resolve(Promise.resolve('RValue'));
 | |
|                     })
 | |
|                         .then((v: string) => {
 | |
|                           log.push(v);
 | |
|                           return Promise.resolve('second value');
 | |
|                         })
 | |
|                         .then((v: string) => {
 | |
|                           log.push(v);
 | |
|                         });
 | |
|           expect(queue.length).toEqual(1);
 | |
|           expect(log).toEqual([]);
 | |
|           flush();
 | |
|           expect(log).toEqual(['RValue', 'second value']);
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       describe('Promise API', function() {
 | |
|         it('should work with .then', function(done) {
 | |
|           let resolve: Function|null = null;
 | |
| 
 | |
|           testZone.run(function() {
 | |
|             new Promise(function(resolveFn) {
 | |
|               resolve = resolveFn;
 | |
|             }).then(function() {
 | |
|               expect(Zone.current).toBe(testZone);
 | |
|               done();
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           resolve!();
 | |
|         });
 | |
| 
 | |
|         it('should work with .catch', function(done) {
 | |
|           let reject: (() => void)|null = null;
 | |
| 
 | |
|           testZone.run(function() {
 | |
|             new Promise(function(resolveFn, rejectFn) {
 | |
|               reject = rejectFn;
 | |
|             })['catch'](function() {
 | |
|               expect(Zone.current).toBe(testZone);
 | |
|               done();
 | |
|             });
 | |
|           });
 | |
| 
 | |
| 
 | |
|           expect(reject!()).toBe(undefined);
 | |
|         });
 | |
| 
 | |
|         it('should work with .finally with resolved promise', function(done) {
 | |
|           let resolve: Function|null = null;
 | |
| 
 | |
|           testZone.run(function() {
 | |
|             (new Promise(function(resolveFn) {
 | |
|                resolve = resolveFn;
 | |
|              }) as any)
 | |
|                 .finally(function() {
 | |
|                   expect(arguments.length).toBe(0);
 | |
|                   expect(Zone.current).toBe(testZone);
 | |
|                   done();
 | |
|                 });
 | |
|           });
 | |
| 
 | |
|           resolve!('value');
 | |
|         });
 | |
| 
 | |
|         it('should work with .finally with rejected promise', function(done) {
 | |
|           let reject: Function|null = null;
 | |
| 
 | |
|           testZone.run(function() {
 | |
|             (new Promise(function(_, rejectFn) {
 | |
|                reject = rejectFn;
 | |
|              }) as any)
 | |
|                 .finally(function() {
 | |
|                   expect(arguments.length).toBe(0);
 | |
|                   expect(Zone.current).toBe(testZone);
 | |
|                   done();
 | |
|                 });
 | |
|           });
 | |
| 
 | |
|           reject!('error');
 | |
|         });
 | |
| 
 | |
|         it('should work with Promise.resolve', () => {
 | |
|           queueZone.run(() => {
 | |
|             let value: any = null;
 | |
|             Promise.resolve('resolveValue').then((v) => value = v);
 | |
|             expect(Zone.current.get('queue').length).toEqual(1);
 | |
|             flushMicrotasks();
 | |
|             expect(value).toEqual('resolveValue');
 | |
|           });
 | |
|         });
 | |
| 
 | |
|         it('should work with Promise.reject', () => {
 | |
|           queueZone.run(() => {
 | |
|             let value: any = null;
 | |
|             Promise.reject('rejectReason')['catch']((v) => value = v);
 | |
|             expect(Zone.current.get('queue').length).toEqual(1);
 | |
|             flushMicrotasks();
 | |
|             expect(value).toEqual('rejectReason');
 | |
|           });
 | |
|         });
 | |
| 
 | |
|         describe('reject', () => {
 | |
|           it('should reject promise', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               Promise.reject('rejectReason')['catch']((v) => value = v);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual('rejectReason');
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should re-reject promise', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               Promise.reject('rejectReason')['catch']((v) => {
 | |
|                 throw v;
 | |
|               })['catch']((v) => value = v);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual('rejectReason');
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should reject and recover promise', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               Promise.reject('rejectReason')['catch']((v) => v).then((v) => value = v);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual('rejectReason');
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should reject if chained promise does not catch promise', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               Promise.reject('rejectReason')
 | |
|                   .then((v) => fail('should not get here'))
 | |
|                   .then(null, (v) => value = v);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual('rejectReason');
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should output error to console if ignoreConsoleErrorUncaughtError is false',
 | |
|              (done) => {
 | |
|                Zone.current.fork({name: 'promise-error'}).run(() => {
 | |
|                  (Zone as any)[Zone.__symbol__('ignoreConsoleErrorUncaughtError')] = false;
 | |
|                  const originalConsoleError = console.error;
 | |
|                  console.error = jasmine.createSpy('consoleErr');
 | |
|                  const p = new Promise((resolve, reject) => {
 | |
|                    throw new Error('promise error');
 | |
|                  });
 | |
|                  setTimeout(() => {
 | |
|                    expect(console.error).toHaveBeenCalled();
 | |
|                    console.error = originalConsoleError;
 | |
|                    done();
 | |
|                  }, 10);
 | |
|                });
 | |
|              });
 | |
| 
 | |
|           it('should not output error to console if ignoreConsoleErrorUncaughtError is true',
 | |
|              (done) => {
 | |
|                Zone.current.fork({name: 'promise-error'}).run(() => {
 | |
|                  (Zone as any)[Zone.__symbol__('ignoreConsoleErrorUncaughtError')] = true;
 | |
|                  const originalConsoleError = console.error;
 | |
|                  console.error = jasmine.createSpy('consoleErr');
 | |
|                  const p = new Promise((resolve, reject) => {
 | |
|                    throw new Error('promise error');
 | |
|                  });
 | |
|                  setTimeout(() => {
 | |
|                    expect(console.error).not.toHaveBeenCalled();
 | |
|                    console.error = originalConsoleError;
 | |
|                    (Zone as any)[Zone.__symbol__('ignoreConsoleErrorUncaughtError')] = false;
 | |
|                    done();
 | |
|                  }, 10);
 | |
|                });
 | |
|              });
 | |
| 
 | |
|           it('should notify Zone.onHandleError if no one catches promise', (done) => {
 | |
|             let promiseError: Error|null = null;
 | |
|             let zone: Zone|null = null;
 | |
|             let task: Task|null = null;
 | |
|             let error: Error|null = null;
 | |
|             queueZone
 | |
|                 .fork({
 | |
|                   name: 'promise-error',
 | |
|                   onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any):
 | |
|                       boolean => {
 | |
|                         promiseError = error;
 | |
|                         delegate.handleError(target, error);
 | |
|                         return false;
 | |
|                       }
 | |
|                 })
 | |
|                 .run(() => {
 | |
|                   zone = Zone.current;
 | |
|                   task = Zone.currentTask;
 | |
|                   error = new Error('rejectedErrorShouldBeHandled');
 | |
|                   try {
 | |
|                     // throw so that the stack trace is captured
 | |
|                     throw error;
 | |
|                   } catch (e) {
 | |
|                   }
 | |
|                   Promise.reject(error);
 | |
|                   expect(promiseError).toBe(null);
 | |
|                 });
 | |
|             setTimeout((): any => null);
 | |
|             setTimeout(() => {
 | |
|               expect(promiseError!.message)
 | |
|                   .toBe(
 | |
|                       'Uncaught (in promise): ' + error +
 | |
|                       (error!.stack ? '\n' + error!.stack : ''));
 | |
|               expect((promiseError as any)['rejection']).toBe(error);
 | |
|               expect((promiseError as any)['zone']).toBe(zone);
 | |
|               expect((promiseError as any)['task']).toBe(task);
 | |
|               done();
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should print readable information when throw a not error object', (done) => {
 | |
|             let promiseError: Error|null = null;
 | |
|             let zone: Zone|null = null;
 | |
|             let task: Task|null = null;
 | |
|             let rejectObj: TestRejection;
 | |
|             queueZone
 | |
|                 .fork({
 | |
|                   name: 'promise-error',
 | |
|                   onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any):
 | |
|                       boolean => {
 | |
|                         promiseError = error;
 | |
|                         delegate.handleError(target, error);
 | |
|                         return false;
 | |
|                       }
 | |
|                 })
 | |
|                 .run(() => {
 | |
|                   zone = Zone.current;
 | |
|                   task = Zone.currentTask;
 | |
|                   rejectObj = new TestRejection();
 | |
|                   rejectObj.prop1 = 'value1';
 | |
|                   rejectObj.prop2 = 'value2';
 | |
|                   Promise.reject(rejectObj);
 | |
|                   expect(promiseError).toBe(null);
 | |
|                 });
 | |
|             setTimeout((): any => null);
 | |
|             setTimeout(() => {
 | |
|               expect(promiseError!.message)
 | |
|                   .toMatch(/Uncaught \(in promise\):.*: {"prop1":"value1","prop2":"value2"}/);
 | |
|               done();
 | |
|             });
 | |
|           });
 | |
|         });
 | |
| 
 | |
|         describe('Promise.race', () => {
 | |
|           it('should reject the value', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               (Promise as any).race([
 | |
|                 Promise.reject('rejection1'), 'v1'
 | |
|               ])['catch']((v: any) => value = v);
 | |
|               // expect(Zone.current.get('queue').length).toEqual(2);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual('rejection1');
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should resolve the value', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               (Promise as any)
 | |
|                   .race([Promise.resolve('resolution'), 'v1'])
 | |
|                   .then((v: any) => value = v);
 | |
|               // expect(Zone.current.get('queue').length).toEqual(2);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual('resolution');
 | |
|             });
 | |
|           });
 | |
|         });
 | |
| 
 | |
|         describe('Promise.all', () => {
 | |
|           it('should reject the value', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               Promise.all([Promise.reject('rejection'), 'v1'])['catch']((v: any) => value = v);
 | |
|               // expect(Zone.current.get('queue').length).toEqual(2);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual('rejection');
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should resolve the value', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               Promise.all([Promise.resolve('resolution'), 'v1']).then((v: any) => value = v);
 | |
|               // expect(Zone.current.get('queue').length).toEqual(2);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual(['resolution', 'v1']);
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should resolve with the sync then operation', () => {
 | |
|             queueZone.run(() => {
 | |
|               let value: any = null;
 | |
|               const p1 = {
 | |
|                 then: function(thenCallback: Function) {
 | |
|                   return thenCallback('p1');
 | |
|                 }
 | |
|               };
 | |
|               const p2 = {
 | |
|                 then: function(thenCallback: Function) {
 | |
|                   return thenCallback('p2');
 | |
|                 }
 | |
|               };
 | |
|               Promise.all([p1, 'v1', p2]).then((v: any) => value = v);
 | |
|               // expect(Zone.current.get('queue').length).toEqual(2);
 | |
|               flushMicrotasks();
 | |
|               expect(value).toEqual(['p1', 'v1', 'p2']);
 | |
|             });
 | |
|           });
 | |
| 
 | |
|           it('should resolve generators',
 | |
|              ifEnvSupports(
 | |
|                  () => {
 | |
|                    return isNode;
 | |
|                  },
 | |
|                  () => {
 | |
|                    const generators: any = function*() {
 | |
|                      yield Promise.resolve(1);
 | |
|                      yield Promise.resolve(2);
 | |
|                      return;
 | |
|                    };
 | |
|                    queueZone.run(() => {
 | |
|                      let value: any = null;
 | |
|                      Promise.all(generators()).then(val => {
 | |
|                        value = val;
 | |
|                      });
 | |
|                      // expect(Zone.current.get('queue').length).toEqual(2);
 | |
|                      flushMicrotasks();
 | |
|                      expect(value).toEqual([1, 2]);
 | |
|                    });
 | |
|                  }));
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       describe('Promise subclasses', function() {
 | |
|         class MyPromise<T> {
 | |
|           private _promise: Promise<any>;
 | |
|           constructor(init: any) {
 | |
|             this._promise = new Promise(init);
 | |
|           }
 | |
| 
 | |
|           catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>)|
 | |
|                                  undefined|null): Promise<T|TResult> {
 | |
|             return this._promise.catch.call(this._promise, onrejected);
 | |
|           };
 | |
| 
 | |
|           then<TResult1 = T, TResult2 = never>(
 | |
|               onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>)|undefined|null,
 | |
|               onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>)|undefined|
 | |
|               null): Promise<any> {
 | |
|             return this._promise.then.call(this._promise, onfulfilled, onrejected);
 | |
|           };
 | |
|         }
 | |
| 
 | |
|         const setPrototypeOf = (Object as any).setPrototypeOf || function(obj: any, proto: any) {
 | |
|           obj.__proto__ = proto;
 | |
|           return obj;
 | |
|         };
 | |
| 
 | |
|         setPrototypeOf(MyPromise.prototype, Promise.prototype);
 | |
| 
 | |
|         it('should reject if the Promise subclass rejects', function() {
 | |
|           const myPromise = new MyPromise(function(resolve: any, reject: any): void {
 | |
|             reject('foo');
 | |
|           });
 | |
| 
 | |
|           return Promise.resolve()
 | |
|               .then(function() {
 | |
|                 return myPromise;
 | |
|               })
 | |
|               .then(
 | |
|                   function() {
 | |
|                     throw new Error('Unexpected resolution');
 | |
|                   },
 | |
|                   function(result) {
 | |
|                     expect(result).toBe('foo');
 | |
|                   });
 | |
|         });
 | |
| 
 | |
|         function testPromiseSubClass(done?: Function) {
 | |
|           const myPromise = new MyPromise(function(resolve: any, reject: Function) {
 | |
|             resolve('foo');
 | |
|           });
 | |
| 
 | |
|           return Promise.resolve()
 | |
|               .then(function() {
 | |
|                 return myPromise;
 | |
|               })
 | |
|               .then(function(result) {
 | |
|                 expect(result).toBe('foo');
 | |
|                 done && done();
 | |
|               });
 | |
|         }
 | |
| 
 | |
|         it('should resolve if the Promise subclass resolves', jasmine ? function(done) {
 | |
|           testPromiseSubClass(done);
 | |
|         } : function() {
 | |
|           testPromiseSubClass();
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       describe('Promise.allSettled', () => {
 | |
|         const yes = function makeFulfilledResult(value: any) {
 | |
|           return {status: 'fulfilled', value: value};
 | |
|         };
 | |
|         const no = function makeRejectedResult(reason: any) {
 | |
|           return {status: 'rejected', reason: reason};
 | |
|         };
 | |
|         const a = {};
 | |
|         const b = {};
 | |
|         const c = {};
 | |
|         const allSettled = (Promise as any).allSettled;
 | |
|         it('no promise values', (done: DoneFn) => {
 | |
|           allSettled([a, b, c]).then((results: any[]) => {
 | |
|             expect(results).toEqual([yes(a), yes(b), yes(c)]);
 | |
|             done();
 | |
|           });
 | |
|         });
 | |
|         it('all fulfilled', (done: DoneFn) => {
 | |
|           allSettled([
 | |
|             Promise.resolve(a), Promise.resolve(b), Promise.resolve(c)
 | |
|           ]).then((results: any[]) => {
 | |
|             expect(results).toEqual([yes(a), yes(b), yes(c)]);
 | |
|             done();
 | |
|           });
 | |
|         });
 | |
|         it('all rejected', (done: DoneFn) => {
 | |
|           allSettled([
 | |
|             Promise.reject(a), Promise.reject(b), Promise.reject(c)
 | |
|           ]).then((results: any[]) => {
 | |
|             expect(results).toEqual([no(a), no(b), no(c)]);
 | |
|             done();
 | |
|           });
 | |
|         });
 | |
|         it('mixed', (done: DoneFn) => {
 | |
|           allSettled([a, Promise.resolve(b), Promise.reject(c)]).then((results: any[]) => {
 | |
|             expect(results).toEqual([yes(a), yes(b), no(c)]);
 | |
|             done();
 | |
|           });
 | |
|         });
 | |
|         it('mixed should in zone', (done: DoneFn) => {
 | |
|           const zone = Zone.current.fork({name: 'settled'});
 | |
|           const bPromise = Promise.resolve(b);
 | |
|           const cPromise = Promise.reject(c);
 | |
|           zone.run(() => {
 | |
|             allSettled([a, bPromise, cPromise]).then((results: any[]) => {
 | |
|               expect(results).toEqual([yes(a), yes(b), no(c)]);
 | |
|               expect(Zone.current.name).toEqual(zone.name);
 | |
|               done();
 | |
|             });
 | |
|           });
 | |
|         });
 | |
|         it('poisoned .then', (done: DoneFn) => {
 | |
|           const promise = new Promise(function() {});
 | |
|           promise.then = function() {
 | |
|             throw new EvalError();
 | |
|           };
 | |
|           allSettled([promise]).then(
 | |
|               () => {
 | |
|                 fail('should not reach here');
 | |
|               },
 | |
|               (reason: any) => {
 | |
|                 expect(reason instanceof EvalError).toBe(true);
 | |
|                 done();
 | |
|               });
 | |
|         });
 | |
|         const Subclass = (function() {
 | |
|           try {
 | |
|             // eslint-disable-next-line no-new-func
 | |
|             return Function(
 | |
|                 'class Subclass extends Promise { constructor(...args) { super(...args); this.thenArgs = []; } then(...args) { Subclass.thenArgs.push(args); this.thenArgs.push(args); return super.then(...args); } } Subclass.thenArgs = []; return Subclass;')();
 | |
|           } catch (e) { /**/
 | |
|           }
 | |
| 
 | |
|           return false;
 | |
|         }());
 | |
| 
 | |
|         describe('inheritance', () => {
 | |
|           it('preserves correct subclass', () => {
 | |
|             const promise = allSettled.call(Subclass, [1]);
 | |
|             expect(promise instanceof Subclass).toBe(true);
 | |
|             expect(promise.constructor).toEqual(Subclass);
 | |
|           });
 | |
| 
 | |
|           it('invoke the subclass', () => {
 | |
|             Subclass.thenArgs.length = 0;
 | |
| 
 | |
|             const original = Subclass.resolve();
 | |
|             expect(Subclass.thenArgs.length).toBe(0);
 | |
|             expect(original.thenArgs.length).toBe(0);
 | |
| 
 | |
|             allSettled.call(Subclass, [original]);
 | |
| 
 | |
|             expect(original.thenArgs.length).toBe(1);
 | |
|             expect(Subclass.thenArgs.length).toBe(1);
 | |
|           });
 | |
|         });
 | |
|       });
 | |
|     }));
 |