1669 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			1669 lines
		
	
	
		
			48 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 '../../lib/rxjs/rxjs-fake-async';
 | |
| 
 | |
| import {Observable} from 'rxjs';
 | |
| import {delay} from 'rxjs/operators';
 | |
| 
 | |
| import {isNode, patchMacroTask, zoneSymbol} from '../../lib/common/utils';
 | |
| import {ifEnvSupports} from '../test-util';
 | |
| 
 | |
| function supportNode() {
 | |
|   return isNode;
 | |
| }
 | |
| 
 | |
| (supportNode as any).message = 'support node';
 | |
| 
 | |
| function supportClock() {
 | |
|   const _global: any = typeof window === 'undefined' ? global : window;
 | |
|   return typeof jasmine.clock === 'function' &&
 | |
|       _global[zoneSymbol('fakeAsyncAutoFakeAsyncWhenClockPatched')];
 | |
| }
 | |
| 
 | |
| (supportClock as any).message = 'support patch clock';
 | |
| 
 | |
| describe('FakeAsyncTestZoneSpec', () => {
 | |
|   let FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
 | |
|   let testZoneSpec: any;
 | |
|   let fakeAsyncTestZone: Zone;
 | |
| 
 | |
|   beforeEach(() => {
 | |
|     testZoneSpec = new FakeAsyncTestZoneSpec('name');
 | |
|     fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
 | |
|   });
 | |
| 
 | |
|   it('sets the FakeAsyncTestZoneSpec property', () => {
 | |
|     fakeAsyncTestZone.run(() => {
 | |
|       expect(Zone.current.get('FakeAsyncTestZoneSpec')).toEqual(testZoneSpec);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('synchronous code', () => {
 | |
|     it('should run', () => {
 | |
|       let ran = false;
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         ran = true;
 | |
|       });
 | |
| 
 | |
|       expect(ran).toEqual(true);
 | |
|     });
 | |
| 
 | |
|     it('should throw the error in the code', () => {
 | |
|       expect(() => {
 | |
|         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', () => {
 | |
|     it('should run', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let thenRan = false;
 | |
|         Promise.resolve(null).then((_) => {
 | |
|           thenRan = true;
 | |
|         });
 | |
| 
 | |
|         expect(thenRan).toEqual(false);
 | |
| 
 | |
|         testZoneSpec.flushMicrotasks();
 | |
|         expect(thenRan).toEqual(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should rethrow the exception on flushMicroTasks for error thrown in Promise callback',
 | |
|        () => {
 | |
|          fakeAsyncTestZone.run(() => {
 | |
|            Promise.resolve(null).then((_) => {
 | |
|              throw new Error('async');
 | |
|            });
 | |
|            expect(() => {
 | |
|              testZoneSpec.flushMicrotasks();
 | |
|            }).toThrowError(/Uncaught \(in promise\): Error: async/);
 | |
|          });
 | |
|        });
 | |
| 
 | |
|     it('should run chained thens', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let log: number[] = [];
 | |
| 
 | |
|         Promise.resolve(null).then((_) => log.push(1)).then((_) => log.push(2));
 | |
| 
 | |
|         expect(log).toEqual([]);
 | |
| 
 | |
|         testZoneSpec.flushMicrotasks();
 | |
|         expect(log).toEqual([1, 2]);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should run Promise created in Promise', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let log: number[] = [];
 | |
| 
 | |
|         Promise.resolve(null).then((_) => {
 | |
|           log.push(1);
 | |
|           Promise.resolve(null).then((_) => log.push(2));
 | |
|         });
 | |
| 
 | |
|         expect(log).toEqual([]);
 | |
| 
 | |
|         testZoneSpec.flushMicrotasks();
 | |
|         expect(log).toEqual([1, 2]);
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('timers', () => {
 | |
|     it('should run queued zero duration timer on zero tick', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let ran = false;
 | |
|         setTimeout(() => {
 | |
|           ran = true;
 | |
|         }, 0);
 | |
| 
 | |
|         expect(ran).toEqual(false);
 | |
| 
 | |
|         testZoneSpec.tick();
 | |
|         expect(ran).toEqual(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should run queued immediate timer on zero tick', ifEnvSupports('setImmediate', () => {
 | |
|          fakeAsyncTestZone.run(() => {
 | |
|            let ran = false;
 | |
|            setImmediate(() => {
 | |
|              ran = true;
 | |
|            });
 | |
| 
 | |
|            expect(ran).toEqual(false);
 | |
| 
 | |
|            testZoneSpec.tick();
 | |
|            expect(ran).toEqual(true);
 | |
|          });
 | |
|        }));
 | |
| 
 | |
|     it('should default to processNewMacroTasksSynchronously if providing other flags', () => {
 | |
|       function nestedTimer(callback: () => any): void {
 | |
|         setTimeout(() => setTimeout(() => callback()));
 | |
|       }
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         const callback = jasmine.createSpy('callback');
 | |
|         nestedTimer(callback);
 | |
|         expect(callback).not.toHaveBeenCalled();
 | |
|         testZoneSpec.tick(0, null, {});
 | |
|         expect(callback).toHaveBeenCalled();
 | |
|       });
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should not queue new macro task on tick with processNewMacroTasksSynchronously=false',
 | |
|        () => {
 | |
|          function nestedTimer(callback: () => any): void {
 | |
|            setTimeout(() => setTimeout(() => callback()));
 | |
|          }
 | |
|          fakeAsyncTestZone.run(() => {
 | |
|            const callback = jasmine.createSpy('callback');
 | |
|            nestedTimer(callback);
 | |
|            expect(callback).not.toHaveBeenCalled();
 | |
|            testZoneSpec.tick(0, null, {processNewMacroTasksSynchronously: false});
 | |
|            expect(callback).not.toHaveBeenCalled();
 | |
|            testZoneSpec.flush();
 | |
|            expect(callback).toHaveBeenCalled();
 | |
|          });
 | |
|        });
 | |
| 
 | |
|     it('should run queued timer after sufficient clock ticks', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let ran = false;
 | |
|         setTimeout(() => {
 | |
|           ran = true;
 | |
|         }, 10);
 | |
| 
 | |
|         testZoneSpec.tick(6);
 | |
|         expect(ran).toEqual(false);
 | |
| 
 | |
|         testZoneSpec.tick(4);
 | |
|         expect(ran).toEqual(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should run doTick callback even if no work ran', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let totalElapsed = 0;
 | |
|         function doTick(elapsed: number) {
 | |
|           totalElapsed += elapsed;
 | |
|         }
 | |
|         setTimeout(() => {}, 10);
 | |
| 
 | |
|         testZoneSpec.tick(6, doTick);
 | |
|         expect(totalElapsed).toEqual(6);
 | |
| 
 | |
|         testZoneSpec.tick(6, doTick);
 | |
|         expect(totalElapsed).toEqual(12);
 | |
| 
 | |
|         testZoneSpec.tick(6, doTick);
 | |
|         expect(totalElapsed).toEqual(18);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should run queued timer created by timer callback', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let counter = 0;
 | |
|         const startCounterLoop = () => {
 | |
|           counter++;
 | |
|           setTimeout(startCounterLoop, 10);
 | |
|         };
 | |
| 
 | |
|         startCounterLoop();
 | |
| 
 | |
|         expect(counter).toEqual(1);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(counter).toEqual(2);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(counter).toEqual(3);
 | |
| 
 | |
|         testZoneSpec.tick(30);
 | |
|         expect(counter).toEqual(6);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should run queued timer only once', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let cycles = 0;
 | |
|         setTimeout(() => {
 | |
|           cycles++;
 | |
|         }, 10);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(1);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(1);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(1);
 | |
|       });
 | |
|       expect(testZoneSpec.pendingTimers.length).toBe(0);
 | |
|     });
 | |
| 
 | |
|     it('should not run cancelled timer', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let ran = false;
 | |
|         let id: any = setTimeout(() => {
 | |
|           ran = true;
 | |
|         }, 10);
 | |
|         clearTimeout(id);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(ran).toEqual(false);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should pass arguments to times', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let value = 'genuine value';
 | |
|         let id = setTimeout((arg1, arg2) => {
 | |
|           value = arg1 + arg2;
 | |
|         }, 0, 'expected', ' value');
 | |
| 
 | |
|         testZoneSpec.tick();
 | |
|         expect(value).toEqual('expected value');
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should pass arguments to setImmediate', ifEnvSupports('setImmediate', () => {
 | |
|          fakeAsyncTestZone.run(() => {
 | |
|            let value = 'genuine value';
 | |
|            let id = setImmediate((arg1, arg2) => {
 | |
|              value = arg1 + arg2;
 | |
|            }, 'expected', ' value');
 | |
| 
 | |
|            testZoneSpec.tick();
 | |
|            expect(value).toEqual('expected value');
 | |
|          });
 | |
|        }));
 | |
| 
 | |
|     it('should run periodic timers', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let cycles = 0;
 | |
|         let id = setInterval(() => {
 | |
|           cycles++;
 | |
|         }, 10);
 | |
| 
 | |
|         expect(id).toBeGreaterThan(0);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(1);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(2);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(3);
 | |
| 
 | |
|         testZoneSpec.tick(30);
 | |
|         expect(cycles).toEqual(6);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should pass arguments to periodic timers', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let value = 'genuine value';
 | |
|         let id = setInterval((arg1, arg2) => {
 | |
|           value = arg1 + arg2;
 | |
|         }, 10, 'expected', ' value');
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(value).toEqual('expected value');
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should not run cancelled periodic timer', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let ran = false;
 | |
|         let id = setInterval(() => {
 | |
|           ran = true;
 | |
|         }, 10);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(ran).toEqual(true);
 | |
| 
 | |
|         ran = false;
 | |
|         clearInterval(id);
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(ran).toEqual(false);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should be able to cancel periodic timers from a callback', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let cycles = 0;
 | |
|         let id: number;
 | |
| 
 | |
|         id = setInterval(() => {
 | |
|                cycles++;
 | |
|                clearInterval(id);
 | |
|              }, 10) as any as number;
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(1);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycles).toEqual(1);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should process microtasks before timers', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let log: string[] = [];
 | |
| 
 | |
|         Promise.resolve(null).then((_) => log.push('microtask'));
 | |
| 
 | |
|         setTimeout(() => log.push('timer'), 9);
 | |
| 
 | |
|         setInterval(() => log.push('periodic timer'), 10);
 | |
| 
 | |
|         expect(log).toEqual([]);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(log).toEqual(['microtask', 'timer', 'periodic timer']);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should process micro-tasks created in timers before next timers', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let log: string[] = [];
 | |
| 
 | |
|         Promise.resolve(null).then((_) => log.push('microtask'));
 | |
| 
 | |
|         setTimeout(() => {
 | |
|           log.push('timer');
 | |
|           Promise.resolve(null).then((_) => log.push('t microtask'));
 | |
|         }, 9);
 | |
| 
 | |
|         let id = setInterval(() => {
 | |
|           log.push('periodic timer');
 | |
|           Promise.resolve(null).then((_) => log.push('pt microtask'));
 | |
|         }, 10);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(log).toEqual(
 | |
|             ['microtask', 'timer', 't microtask', 'periodic timer', 'pt microtask']);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(log).toEqual([
 | |
|           'microtask', 'timer', 't microtask', 'periodic timer', 'pt microtask', 'periodic timer',
 | |
|           'pt microtask'
 | |
|         ]);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should throw the exception from tick for error thrown in timer callback', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         setTimeout(() => {
 | |
|           throw new Error('timer');
 | |
|         }, 10);
 | |
|         expect(() => {
 | |
|           testZoneSpec.tick(10);
 | |
|         }).toThrowError('timer');
 | |
|       });
 | |
|       // There should be no pending timers after the error in timer callback.
 | |
|       expect(testZoneSpec.pendingTimers.length).toBe(0);
 | |
|     });
 | |
| 
 | |
|     it('should throw the exception from tick for error thrown in periodic timer callback', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let count = 0;
 | |
|         setInterval(() => {
 | |
|           count++;
 | |
|           throw new Error(count.toString());
 | |
|         }, 10);
 | |
| 
 | |
|         expect(() => {
 | |
|           testZoneSpec.tick(10);
 | |
|         }).toThrowError('1');
 | |
| 
 | |
|         // Periodic timer is cancelled on first error.
 | |
|         expect(count).toBe(1);
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(count).toBe(1);
 | |
|       });
 | |
|       // Periodic timer is removed from pending queue on error.
 | |
|       expect(testZoneSpec.pendingPeriodicTimers.length).toBe(0);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   it('should be able to resume processing timer callbacks after handling an error', () => {
 | |
|     fakeAsyncTestZone.run(() => {
 | |
|       let ran = false;
 | |
|       setTimeout(() => {
 | |
|         throw new Error('timer');
 | |
|       }, 10);
 | |
|       setTimeout(() => {
 | |
|         ran = true;
 | |
|       }, 10);
 | |
|       expect(() => {
 | |
|         testZoneSpec.tick(10);
 | |
|       }).toThrowError('timer');
 | |
|       expect(ran).toBe(false);
 | |
| 
 | |
|       // Restart timer queue processing.
 | |
|       testZoneSpec.tick(0);
 | |
|       expect(ran).toBe(true);
 | |
|     });
 | |
|     // There should be no pending timers after the error in timer callback.
 | |
|     expect(testZoneSpec.pendingTimers.length).toBe(0);
 | |
|   });
 | |
| 
 | |
|   describe('flushing all tasks', () => {
 | |
|     it('should flush all pending timers', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let x = false;
 | |
|         let y = false;
 | |
|         let z = false;
 | |
| 
 | |
|         setTimeout(() => {
 | |
|           x = true;
 | |
|         }, 10);
 | |
|         setTimeout(() => {
 | |
|           y = true;
 | |
|         }, 100);
 | |
|         setTimeout(() => {
 | |
|           z = true;
 | |
|         }, 70);
 | |
| 
 | |
|         let elapsed = testZoneSpec.flush();
 | |
| 
 | |
|         expect(elapsed).toEqual(100);
 | |
|         expect(x).toBe(true);
 | |
|         expect(y).toBe(true);
 | |
|         expect(z).toBe(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should flush nested timers', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let x = true;
 | |
|         let y = true;
 | |
|         setTimeout(() => {
 | |
|           x = true;
 | |
|           setTimeout(() => {
 | |
|             y = true;
 | |
|           }, 100);
 | |
|         }, 200);
 | |
| 
 | |
|         let elapsed = testZoneSpec.flush();
 | |
| 
 | |
|         expect(elapsed).toEqual(300);
 | |
|         expect(x).toBe(true);
 | |
|         expect(y).toBe(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should advance intervals', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let x = false;
 | |
|         let y = false;
 | |
|         let z = 0;
 | |
| 
 | |
|         setTimeout(() => {
 | |
|           x = true;
 | |
|         }, 50);
 | |
|         setTimeout(() => {
 | |
|           y = true;
 | |
|         }, 141);
 | |
|         setInterval(() => {
 | |
|           z++;
 | |
|         }, 10);
 | |
| 
 | |
|         let elapsed = testZoneSpec.flush();
 | |
| 
 | |
|         expect(elapsed).toEqual(141);
 | |
|         expect(x).toBe(true);
 | |
|         expect(y).toBe(true);
 | |
|         expect(z).toEqual(14);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should not wait for intervals', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let z = 0;
 | |
| 
 | |
|         setInterval(() => {
 | |
|           z++;
 | |
|         }, 10);
 | |
| 
 | |
|         let elapsed = testZoneSpec.flush();
 | |
| 
 | |
|         expect(elapsed).toEqual(0);
 | |
|         expect(z).toEqual(0);
 | |
|       });
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should process micro-tasks created in timers before next timers', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let log: string[] = [];
 | |
| 
 | |
|         Promise.resolve(null).then((_) => log.push('microtask'));
 | |
| 
 | |
|         setTimeout(() => {
 | |
|           log.push('timer');
 | |
|           Promise.resolve(null).then((_) => log.push('t microtask'));
 | |
|         }, 20);
 | |
| 
 | |
|         let id = setInterval(() => {
 | |
|           log.push('periodic timer');
 | |
|           Promise.resolve(null).then((_) => log.push('pt microtask'));
 | |
|         }, 10);
 | |
| 
 | |
|         testZoneSpec.flush();
 | |
|         expect(log).toEqual(
 | |
|             ['microtask', 'periodic timer', 'pt microtask', 'timer', 't microtask']);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should throw the exception from tick for error thrown in timer callback', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         setTimeout(() => {
 | |
|           throw new Error('timer');
 | |
|         }, 10);
 | |
|         expect(() => {
 | |
|           testZoneSpec.flush();
 | |
|         }).toThrowError('timer');
 | |
|       });
 | |
|       // There should be no pending timers after the error in timer callback.
 | |
|       expect(testZoneSpec.pendingTimers.length).toBe(0);
 | |
|     });
 | |
| 
 | |
|     it('should do something reasonable with polling timeouts', () => {
 | |
|       expect(() => {
 | |
|         fakeAsyncTestZone.run(() => {
 | |
|           let z = 0;
 | |
| 
 | |
|           let poll = () => {
 | |
|             setTimeout(() => {
 | |
|               z++;
 | |
|               poll();
 | |
|             }, 10);
 | |
|           };
 | |
| 
 | |
|           poll();
 | |
|           testZoneSpec.flush();
 | |
|         });
 | |
|       })
 | |
|           .toThrowError(
 | |
|               'flush failed after reaching the limit of 20 tasks. Does your code use a polling timeout?');
 | |
|     });
 | |
| 
 | |
|     it('accepts a custom limit', () => {
 | |
|       expect(() => {
 | |
|         fakeAsyncTestZone.run(() => {
 | |
|           let z = 0;
 | |
| 
 | |
|           let poll = () => {
 | |
|             setTimeout(() => {
 | |
|               z++;
 | |
|               poll();
 | |
|             }, 10);
 | |
|           };
 | |
| 
 | |
|           poll();
 | |
|           testZoneSpec.flush(10);
 | |
|         });
 | |
|       })
 | |
|           .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);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('can flush till the last periodic task is processed', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let x = 0;
 | |
|         let y = 0;
 | |
| 
 | |
|         setInterval(() => {
 | |
|           x++;
 | |
|         }, 10);
 | |
| 
 | |
|         // This shouldn't cause the flush to throw an exception even though
 | |
|         // it would require 100 iterations of the shorter timer.
 | |
|         setInterval(() => {
 | |
|           y++;
 | |
|         }, 1000);
 | |
| 
 | |
|         let elapsed = testZoneSpec.flush(20, true);
 | |
| 
 | |
|         // Should stop right after the longer timer has been processed.
 | |
|         expect(elapsed).toEqual(1000);
 | |
| 
 | |
|         expect(x).toEqual(100);
 | |
|         expect(y).toEqual(1);
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('outside of FakeAsync Zone', () => {
 | |
|     it('calling flushMicrotasks should throw exception', () => {
 | |
|       expect(() => {
 | |
|         testZoneSpec.flushMicrotasks();
 | |
|       }).toThrowError('The code should be running in the fakeAsync zone to call this function');
 | |
|     });
 | |
|     it('calling tick should throw exception', () => {
 | |
|       expect(() => {
 | |
|         testZoneSpec.tick();
 | |
|       }).toThrowError('The code should be running in the fakeAsync zone to call this function');
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('requestAnimationFrame', () => {
 | |
|     const functions =
 | |
|         ['requestAnimationFrame', 'webkitRequestAnimationFrame', 'mozRequestAnimationFrame'];
 | |
|     functions.forEach((fnName) => {
 | |
|       describe(fnName, ifEnvSupports(fnName, () => {
 | |
|                  it('should schedule a requestAnimationFrame with timeout of 16ms', () => {
 | |
|                    fakeAsyncTestZone.run(() => {
 | |
|                      let ran = false;
 | |
|                      requestAnimationFrame(() => {
 | |
|                        ran = true;
 | |
|                      });
 | |
| 
 | |
|                      testZoneSpec.tick(6);
 | |
|                      expect(ran).toEqual(false);
 | |
| 
 | |
|                      testZoneSpec.tick(10);
 | |
|                      expect(ran).toEqual(true);
 | |
|                    });
 | |
|                  });
 | |
|                  it('does not count as a pending timer', () => {
 | |
|                    fakeAsyncTestZone.run(() => {
 | |
|                      requestAnimationFrame(() => {});
 | |
|                    });
 | |
|                    expect(testZoneSpec.pendingTimers.length).toBe(0);
 | |
|                    expect(testZoneSpec.pendingPeriodicTimers.length).toBe(0);
 | |
|                  });
 | |
|                  it('should cancel a scheduled requestAnimatiomFrame', () => {
 | |
|                    fakeAsyncTestZone.run(() => {
 | |
|                      let ran = false;
 | |
|                      const id = requestAnimationFrame(() => {
 | |
|                        ran = true;
 | |
|                      });
 | |
| 
 | |
|                      testZoneSpec.tick(6);
 | |
|                      expect(ran).toEqual(false);
 | |
| 
 | |
|                      cancelAnimationFrame(id);
 | |
| 
 | |
|                      testZoneSpec.tick(10);
 | |
|                      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);
 | |
|                    });
 | |
|                  });
 | |
|                  it('should pass timestamp as parameter', () => {
 | |
|                    let timestamp = 0;
 | |
|                    let timestamp1 = 0;
 | |
|                    fakeAsyncTestZone.run(() => {
 | |
|                      requestAnimationFrame((ts) => {
 | |
|                        timestamp = ts;
 | |
|                        requestAnimationFrame(ts1 => {
 | |
|                          timestamp1 = ts1;
 | |
|                        });
 | |
|                      });
 | |
|                      const elapsed = testZoneSpec.flush(20, true);
 | |
|                      const elapsed1 = testZoneSpec.flush(20, true);
 | |
|                      expect(elapsed).toEqual(16);
 | |
|                      expect(elapsed1).toEqual(16);
 | |
|                      expect(timestamp).toEqual(16);
 | |
|                      expect(timestamp1).toEqual(32);
 | |
|                    });
 | |
|                  });
 | |
|                }));
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe(
 | |
|       'XHRs', ifEnvSupports('XMLHttpRequest', () => {
 | |
|         it('should throw an exception if an XHR is initiated in the zone', () => {
 | |
|           expect(() => {
 | |
|             fakeAsyncTestZone.run(() => {
 | |
|               let finished = false;
 | |
|               let req = new XMLHttpRequest();
 | |
| 
 | |
|               req.onreadystatechange = () => {
 | |
|                 if (req.readyState === XMLHttpRequest.DONE) {
 | |
|                   finished = true;
 | |
|                 }
 | |
|               };
 | |
| 
 | |
|               req.open('GET', '/test', true);
 | |
|               req.send();
 | |
|             });
 | |
|           }).toThrowError('Cannot make XHRs from within a fake async test. Request URL: /test');
 | |
|         });
 | |
|       }));
 | |
| 
 | |
|   describe('node process', ifEnvSupports(supportNode, () => {
 | |
|              it('should be able to schedule microTask with additional arguments', () => {
 | |
|                const process = global['process'];
 | |
|                const nextTick = process && process['nextTick'];
 | |
|                if (!nextTick) {
 | |
|                  return;
 | |
|                }
 | |
|                fakeAsyncTestZone.run(() => {
 | |
|                  let tickRun = false;
 | |
|                  let cbArgRun = false;
 | |
|                  nextTick(
 | |
|                      (strArg: string, cbArg: Function) => {
 | |
|                        tickRun = true;
 | |
|                        expect(strArg).toEqual('stringArg');
 | |
|                        cbArg();
 | |
|                      },
 | |
|                      'stringArg',
 | |
|                      () => {
 | |
|                        cbArgRun = true;
 | |
|                      });
 | |
| 
 | |
|                  expect(tickRun).toEqual(false);
 | |
| 
 | |
|                  testZoneSpec.flushMicrotasks();
 | |
|                  expect(tickRun).toEqual(true);
 | |
|                  expect(cbArgRun).toEqual(true);
 | |
|                });
 | |
|              });
 | |
|            }));
 | |
| 
 | |
|   describe('should allow user define which macroTask fakeAsyncTest', () => {
 | |
|     let FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
 | |
|     let testZoneSpec: any;
 | |
|     let fakeAsyncTestZone: Zone;
 | |
|     it('should support custom non perodic macroTask', () => {
 | |
|       testZoneSpec = new FakeAsyncTestZoneSpec(
 | |
|           'name', false, [{source: 'TestClass.myTimeout', callbackArgs: ['test']}]);
 | |
|       class TestClass {
 | |
|         myTimeout(callback: Function) {}
 | |
|       }
 | |
|       fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let ran = false;
 | |
|         patchMacroTask(
 | |
|             TestClass.prototype, 'myTimeout',
 | |
|             (self: any, args: any[]) =>
 | |
|                 ({name: 'TestClass.myTimeout', target: self, cbIdx: 0, args: args}));
 | |
| 
 | |
|         const testClass = new TestClass();
 | |
|         testClass.myTimeout(function(callbackArgs: any) {
 | |
|           ran = true;
 | |
|           expect(callbackArgs).toEqual('test');
 | |
|         });
 | |
| 
 | |
|         expect(ran).toEqual(false);
 | |
| 
 | |
|         testZoneSpec.tick();
 | |
|         expect(ran).toEqual(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should support custom non perodic macroTask by global flag', () => {
 | |
|       testZoneSpec = new FakeAsyncTestZoneSpec('name');
 | |
|       class TestClass {
 | |
|         myTimeout(callback: Function) {}
 | |
|       }
 | |
|       fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let ran = false;
 | |
|         patchMacroTask(
 | |
|             TestClass.prototype, 'myTimeout',
 | |
|             (self: any, args: any[]) =>
 | |
|                 ({name: 'TestClass.myTimeout', target: self, cbIdx: 0, args: args}));
 | |
| 
 | |
|         const testClass = new TestClass();
 | |
|         testClass.myTimeout(() => {
 | |
|           ran = true;
 | |
|         });
 | |
| 
 | |
|         expect(ran).toEqual(false);
 | |
| 
 | |
|         testZoneSpec.tick();
 | |
|         expect(ran).toEqual(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should support custom perodic macroTask', () => {
 | |
|       testZoneSpec = new FakeAsyncTestZoneSpec(
 | |
|           'name', false, [{source: 'TestClass.myInterval', isPeriodic: true}]);
 | |
|       fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         let cycle = 0;
 | |
|         class TestClass {
 | |
|           myInterval(callback: Function, interval: number): any {
 | |
|             return null;
 | |
|           }
 | |
|         }
 | |
|         patchMacroTask(
 | |
|             TestClass.prototype, 'myInterval',
 | |
|             (self: any, args: any[]) =>
 | |
|                 ({name: 'TestClass.myInterval', target: self, cbIdx: 0, args: args}));
 | |
| 
 | |
|         const testClass = new TestClass();
 | |
|         const id = testClass.myInterval(() => {
 | |
|           cycle++;
 | |
|         }, 10);
 | |
| 
 | |
|         expect(cycle).toEqual(0);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycle).toEqual(1);
 | |
| 
 | |
|         testZoneSpec.tick(10);
 | |
|         expect(cycle).toEqual(2);
 | |
|         clearInterval(id);
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('return promise', () => {
 | |
|     let log: string[];
 | |
|     beforeEach(() => {
 | |
|       log = [];
 | |
|     });
 | |
| 
 | |
|     it('should wait for promise to resolve', () => {
 | |
|       return new Promise<void>((res, _) => {
 | |
|         setTimeout(() => {
 | |
|           log.push('resolved');
 | |
|           res();
 | |
|         }, 100);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     afterEach(() => {
 | |
|       expect(log).toEqual(['resolved']);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('fakeAsyncTest should patch Date', () => {
 | |
|     let FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
 | |
|     let testZoneSpec: any;
 | |
|     let fakeAsyncTestZone: Zone;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       testZoneSpec = new FakeAsyncTestZoneSpec('name', false);
 | |
|       fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
 | |
|     });
 | |
| 
 | |
|     it('should get date diff correctly', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         const start = Date.now();
 | |
|         testZoneSpec.tick(100);
 | |
|         const end = Date.now();
 | |
|         expect(end - start).toBe(100);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should check date type correctly', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         const d: any = new Date();
 | |
|         expect(d instanceof Date).toBe(true);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should new Date with parameter correctly', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         const d: Date = new Date(0);
 | |
|         expect(d.getFullYear()).toBeLessThan(1971);
 | |
|         const d1: Date = new Date('December 17, 1995 03:24:00');
 | |
|         expect(d1.getFullYear()).toEqual(1995);
 | |
|         const d2: Date = new Date(1995, 11, 17, 3, 24, 0);
 | |
|         expect(d2.getFullYear()).toEqual(1995);
 | |
| 
 | |
|         d2.setFullYear(1985);
 | |
|         expect(isNaN(d2.getTime())).toBeFalsy();
 | |
|         expect(d2.getFullYear()).toBe(1985);
 | |
|         expect(d2.getMonth()).toBe(11);
 | |
|         expect(d2.getDate()).toBe(17);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should get Date.UTC() correctly', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         const utcDate = new Date(Date.UTC(96, 11, 1, 0, 0, 0));
 | |
|         expect(utcDate.getFullYear()).toBe(1996);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should call Date.parse() correctly', () => {
 | |
|       fakeAsyncTestZone.run(() => {
 | |
|         const unixTimeZero = Date.parse('01 Jan 1970 00:00:00 GMT');
 | |
|         expect(unixTimeZero).toBe(0);
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe(
 | |
|       'fakeAsyncTest should work without patch jasmine.clock',
 | |
|       ifEnvSupports(
 | |
|           () => {
 | |
|             return !supportClock() && supportNode();
 | |
|           },
 | |
|           () => {
 | |
|             const fakeAsync = (Zone as any)[Zone.__symbol__('fakeAsyncTest')].fakeAsync;
 | |
|             let spy: any;
 | |
|             beforeEach(() => {
 | |
|               spy = jasmine.createSpy('timer');
 | |
|               jasmine.clock().install();
 | |
|             });
 | |
| 
 | |
|             afterEach(() => {
 | |
|               jasmine.clock().uninstall();
 | |
|             });
 | |
| 
 | |
|             it('should check date type correctly', fakeAsync(() => {
 | |
|                  const d: any = new Date();
 | |
|                  expect(d instanceof Date).toBe(true);
 | |
|                }));
 | |
| 
 | |
|             it('should check date type correctly without fakeAsync', () => {
 | |
|               const d: any = new Date();
 | |
|               expect(d instanceof Date).toBe(true);
 | |
|             });
 | |
| 
 | |
|             it('should tick correctly', fakeAsync(() => {
 | |
|                  jasmine.clock().mockDate();
 | |
|                  const start = Date.now();
 | |
|                  jasmine.clock().tick(100);
 | |
|                  const end = Date.now();
 | |
|                  expect(end - start).toBe(100);
 | |
|                }));
 | |
| 
 | |
|             it('should tick correctly without fakeAsync', () => {
 | |
|               jasmine.clock().mockDate();
 | |
|               const start = Date.now();
 | |
|               jasmine.clock().tick(100);
 | |
|               const end = Date.now();
 | |
|               expect(end - start).toBe(100);
 | |
|             });
 | |
| 
 | |
|             it('should mock date correctly', fakeAsync(() => {
 | |
|                  const baseTime = new Date(2013, 9, 23);
 | |
|                  jasmine.clock().mockDate(baseTime);
 | |
|                  const start = Date.now();
 | |
|                  expect(start).toBe(baseTime.getTime());
 | |
|                  jasmine.clock().tick(100);
 | |
|                  const end = Date.now();
 | |
|                  expect(end - start).toBe(100);
 | |
|                  expect(end).toBe(baseTime.getTime() + 100);
 | |
|                  expect(new Date().getFullYear()).toEqual(2013);
 | |
|                }));
 | |
| 
 | |
|             it('should mock date correctly without fakeAsync', () => {
 | |
|               const baseTime = new Date(2013, 9, 23);
 | |
|               jasmine.clock().mockDate(baseTime);
 | |
|               const start = Date.now();
 | |
|               expect(start).toBe(baseTime.getTime());
 | |
|               jasmine.clock().tick(100);
 | |
|               const end = Date.now();
 | |
|               expect(end - start).toBe(100);
 | |
|               expect(end).toBe(baseTime.getTime() + 100);
 | |
|               expect(new Date().getFullYear()).toEqual(2013);
 | |
|             });
 | |
| 
 | |
|             it('should handle new Date correctly', fakeAsync(() => {
 | |
|                  const baseTime = new Date(2013, 9, 23);
 | |
|                  jasmine.clock().mockDate(baseTime);
 | |
|                  const start = new Date();
 | |
|                  expect(start.getTime()).toBe(baseTime.getTime());
 | |
|                  jasmine.clock().tick(100);
 | |
|                  const end = new Date();
 | |
|                  expect(end.getTime() - start.getTime()).toBe(100);
 | |
|                  expect(end.getTime()).toBe(baseTime.getTime() + 100);
 | |
|                }));
 | |
| 
 | |
|             it('should handle new Date correctly without fakeAsync', () => {
 | |
|               const baseTime = new Date(2013, 9, 23);
 | |
|               jasmine.clock().mockDate(baseTime);
 | |
|               const start = new Date();
 | |
|               expect(start.getTime()).toBe(baseTime.getTime());
 | |
|               jasmine.clock().tick(100);
 | |
|               const end = new Date();
 | |
|               expect(end.getTime() - start.getTime()).toBe(100);
 | |
|               expect(end.getTime()).toBe(baseTime.getTime() + 100);
 | |
|             });
 | |
| 
 | |
|             it('should handle setTimeout correctly', fakeAsync(() => {
 | |
|                  setTimeout(spy, 100);
 | |
|                  expect(spy).not.toHaveBeenCalled();
 | |
|                  jasmine.clock().tick(100);
 | |
|                  expect(spy).toHaveBeenCalled();
 | |
|                }));
 | |
| 
 | |
|             it('should handle setTimeout correctly without fakeAsync', () => {
 | |
|               setTimeout(spy, 100);
 | |
|               expect(spy).not.toHaveBeenCalled();
 | |
|               jasmine.clock().tick(100);
 | |
|               expect(spy).toHaveBeenCalled();
 | |
|             });
 | |
|           }));
 | |
| 
 | |
|   describe('fakeAsyncTest should patch jasmine.clock', ifEnvSupports(supportClock, () => {
 | |
|              let spy: any;
 | |
|              beforeEach(() => {
 | |
|                spy = jasmine.createSpy('timer');
 | |
|                jasmine.clock().install();
 | |
|              });
 | |
| 
 | |
|              afterEach(() => {
 | |
|                jasmine.clock().uninstall();
 | |
|              });
 | |
| 
 | |
|              it('should check date type correctly', () => {
 | |
|                const d: any = new Date();
 | |
|                expect(d instanceof Date).toBe(true);
 | |
|              });
 | |
| 
 | |
|              it('should get date diff correctly', () => {
 | |
|                const start = Date.now();
 | |
|                jasmine.clock().tick(100);
 | |
|                const end = Date.now();
 | |
|                expect(end - start).toBe(100);
 | |
|              });
 | |
| 
 | |
|              it('should tick correctly', () => {
 | |
|                const start = Date.now();
 | |
|                jasmine.clock().tick(100);
 | |
|                const end = Date.now();
 | |
|                expect(end - start).toBe(100);
 | |
|              });
 | |
| 
 | |
|              it('should mock date correctly', () => {
 | |
|                const baseTime = new Date(2013, 9, 23);
 | |
|                jasmine.clock().mockDate(baseTime);
 | |
|                const start = Date.now();
 | |
|                expect(start).toBe(baseTime.getTime());
 | |
|                jasmine.clock().tick(100);
 | |
|                const end = Date.now();
 | |
|                expect(end - start).toBe(100);
 | |
|                expect(end).toBe(baseTime.getTime() + 100);
 | |
|              });
 | |
| 
 | |
|              it('should handle new Date correctly', () => {
 | |
|                const baseTime = new Date(2013, 9, 23);
 | |
|                jasmine.clock().mockDate(baseTime);
 | |
|                const start = new Date();
 | |
|                expect(start.getTime()).toBe(baseTime.getTime());
 | |
|                jasmine.clock().tick(100);
 | |
|                const end = new Date();
 | |
|                expect(end.getTime() - start.getTime()).toBe(100);
 | |
|                expect(end.getTime()).toBe(baseTime.getTime() + 100);
 | |
|              });
 | |
| 
 | |
|              it('should handle setTimeout correctly', () => {
 | |
|                setTimeout(spy, 100);
 | |
|                expect(spy).not.toHaveBeenCalled();
 | |
|                jasmine.clock().tick(100);
 | |
|                expect(spy).toHaveBeenCalled();
 | |
|              });
 | |
|            }));
 | |
| 
 | |
|   describe('fakeAsyncTest should patch rxjs scheduler', ifEnvSupports(supportClock, () => {
 | |
|              let FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
 | |
|              let testZoneSpec: any;
 | |
|              let fakeAsyncTestZone: Zone;
 | |
| 
 | |
|              beforeEach(() => {
 | |
|                testZoneSpec = new FakeAsyncTestZoneSpec('name', false);
 | |
|                fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
 | |
|              });
 | |
| 
 | |
|              it('should get date diff correctly', (done) => {
 | |
|                fakeAsyncTestZone.run(() => {
 | |
|                  let result: any = null;
 | |
|                  const observable = new Observable((subscribe: any) => {
 | |
|                    subscribe.next('hello');
 | |
|                    subscribe.complete();
 | |
|                  });
 | |
|                  observable.pipe(delay(1000)).subscribe((v: any) => {
 | |
|                    result = v;
 | |
|                  });
 | |
|                  expect(result).toBe(null);
 | |
|                  testZoneSpec.tick(1000);
 | |
|                  expect(result).toBe('hello');
 | |
|                  done();
 | |
|                });
 | |
|              });
 | |
|            }));
 | |
| });
 | |
| 
 | |
| class Log {
 | |
|   logItems: any[];
 | |
| 
 | |
|   constructor() {
 | |
|     this.logItems = [];
 | |
|   }
 | |
| 
 | |
|   add(value: any /** TODO #9100 */): void {
 | |
|     this.logItems.push(value);
 | |
|   }
 | |
| 
 | |
|   fn(value: any /** TODO #9100 */) {
 | |
|     return (a1: any = null, a2: any = null, a3: any = null, a4: any = null, a5: any = null) => {
 | |
|       this.logItems.push(value);
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   clear(): void {
 | |
|     this.logItems = [];
 | |
|   }
 | |
| 
 | |
|   result(): string {
 | |
|     return this.logItems.join('; ');
 | |
|   }
 | |
| }
 | |
| 
 | |
| const resolvedPromise = Promise.resolve(null);
 | |
| const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec'];
 | |
| const fakeAsyncTestModule = (Zone as any)[Zone.__symbol__('fakeAsyncTest')];
 | |
| const {fakeAsync, tick, discardPeriodicTasks, flush, flushMicrotasks} = fakeAsyncTestModule;
 | |
| 
 | |
| {
 | |
|   describe('fake async', () => {
 | |
|     it('should run synchronous code', () => {
 | |
|       let ran = false;
 | |
|       fakeAsync(() => {
 | |
|         ran = true;
 | |
|       })();
 | |
| 
 | |
|       expect(ran).toEqual(true);
 | |
|     });
 | |
| 
 | |
|     it('should pass arguments to the wrapped function', () => {
 | |
|       fakeAsync((foo: any /** TODO #9100 */, bar: any /** TODO #9100 */) => {
 | |
|         expect(foo).toEqual('foo');
 | |
|         expect(bar).toEqual('bar');
 | |
|       })('foo', 'bar');
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should throw on nested calls', () => {
 | |
|       expect(() => {
 | |
|         fakeAsync(() => {
 | |
|           fakeAsync((): any /** TODO #9100 */ => null)();
 | |
|         })();
 | |
|       }).toThrowError('fakeAsync() calls can not be nested');
 | |
|     });
 | |
| 
 | |
|     it('should flush microtasks before returning', () => {
 | |
|       let thenRan = false;
 | |
| 
 | |
|       fakeAsync(() => {
 | |
|         resolvedPromise.then(_ => {
 | |
|           thenRan = true;
 | |
|         });
 | |
|       })();
 | |
| 
 | |
|       expect(thenRan).toEqual(true);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should propagate the return value', () => {
 | |
|       expect(fakeAsync(() => 'foo')()).toEqual('foo');
 | |
|     });
 | |
| 
 | |
|     describe('Promise', () => {
 | |
|       it('should run asynchronous code', fakeAsync(() => {
 | |
|            let thenRan = false;
 | |
|            resolvedPromise.then((_) => {
 | |
|              thenRan = true;
 | |
|            });
 | |
| 
 | |
|            expect(thenRan).toEqual(false);
 | |
| 
 | |
|            flushMicrotasks();
 | |
|            expect(thenRan).toEqual(true);
 | |
|          }));
 | |
| 
 | |
|       it('should run chained thens', fakeAsync(() => {
 | |
|            const log = new Log();
 | |
| 
 | |
|            resolvedPromise.then((_) => log.add(1)).then((_) => log.add(2));
 | |
| 
 | |
|            expect(log.result()).toEqual('');
 | |
| 
 | |
|            flushMicrotasks();
 | |
|            expect(log.result()).toEqual('1; 2');
 | |
|          }));
 | |
| 
 | |
|       it('should run Promise created in Promise', fakeAsync(() => {
 | |
|            const log = new Log();
 | |
| 
 | |
|            resolvedPromise.then((_) => {
 | |
|              log.add(1);
 | |
|              resolvedPromise.then((_) => log.add(2));
 | |
|            });
 | |
| 
 | |
|            expect(log.result()).toEqual('');
 | |
| 
 | |
|            flushMicrotasks();
 | |
|            expect(log.result()).toEqual('1; 2');
 | |
|          }));
 | |
| 
 | |
|       it('should complain if the test throws an exception during async calls', () => {
 | |
|         expect(() => {
 | |
|           fakeAsync(() => {
 | |
|             resolvedPromise.then((_) => {
 | |
|               throw new Error('async');
 | |
|             });
 | |
|             flushMicrotasks();
 | |
|           })();
 | |
|         }).toThrowError(/Uncaught \(in promise\): Error: async/);
 | |
|       });
 | |
| 
 | |
|       it('should complain if a test throws an exception', () => {
 | |
|         expect(() => {
 | |
|           fakeAsync(() => {
 | |
|             throw new Error('sync');
 | |
|           })();
 | |
|         }).toThrowError('sync');
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     describe('timers', () => {
 | |
|       it('should run queued zero duration timer on zero tick', fakeAsync(() => {
 | |
|            let ran = false;
 | |
|            setTimeout(() => {
 | |
|              ran = true;
 | |
|            }, 0);
 | |
| 
 | |
|            expect(ran).toEqual(false);
 | |
| 
 | |
|            tick();
 | |
|            expect(ran).toEqual(true);
 | |
|          }));
 | |
| 
 | |
| 
 | |
|       it('should run queued timer after sufficient clock ticks', fakeAsync(() => {
 | |
|            let ran = false;
 | |
|            setTimeout(() => {
 | |
|              ran = true;
 | |
|            }, 10);
 | |
| 
 | |
|            tick(6);
 | |
|            expect(ran).toEqual(false);
 | |
| 
 | |
|            tick(6);
 | |
|            expect(ran).toEqual(true);
 | |
|          }));
 | |
| 
 | |
|       it('should run queued timer only once', fakeAsync(() => {
 | |
|            let cycles = 0;
 | |
|            setTimeout(() => {
 | |
|              cycles++;
 | |
|            }, 10);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(1);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(1);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(1);
 | |
|          }));
 | |
| 
 | |
|       it('should not run cancelled timer', fakeAsync(() => {
 | |
|            let ran = false;
 | |
|            const id = setTimeout(() => {
 | |
|              ran = true;
 | |
|            }, 10);
 | |
|            clearTimeout(id);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(ran).toEqual(false);
 | |
|          }));
 | |
| 
 | |
|       it('should throw an error on dangling timers', () => {
 | |
|         expect(() => {
 | |
|           fakeAsync(() => {
 | |
|             setTimeout(() => {}, 10);
 | |
|           })();
 | |
|         }).toThrowError('1 timer(s) still in the queue.');
 | |
|       });
 | |
| 
 | |
|       it('should throw an error on dangling periodic timers', () => {
 | |
|         expect(() => {
 | |
|           fakeAsync(() => {
 | |
|             setInterval(() => {}, 10);
 | |
|           })();
 | |
|         }).toThrowError('1 periodic timer(s) still in the queue.');
 | |
|       });
 | |
| 
 | |
|       it('should run periodic timers', fakeAsync(() => {
 | |
|            let cycles = 0;
 | |
|            const id = setInterval(() => {
 | |
|              cycles++;
 | |
|            }, 10);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(1);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(2);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(3);
 | |
|            clearInterval(id);
 | |
|          }));
 | |
| 
 | |
|       it('should not run cancelled periodic timer', fakeAsync(() => {
 | |
|            let ran = false;
 | |
|            const id = setInterval(() => {
 | |
|              ran = true;
 | |
|            }, 10);
 | |
|            clearInterval(id);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(ran).toEqual(false);
 | |
|          }));
 | |
| 
 | |
|       it('should be able to cancel periodic timers from a callback', fakeAsync(() => {
 | |
|            let cycles = 0;
 | |
|            let id: any /** TODO #9100 */;
 | |
| 
 | |
|            id = setInterval(() => {
 | |
|              cycles++;
 | |
|              clearInterval(id);
 | |
|            }, 10);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(1);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(1);
 | |
|          }));
 | |
| 
 | |
|       it('should clear periodic timers', fakeAsync(() => {
 | |
|            let cycles = 0;
 | |
|            const id = setInterval(() => {
 | |
|              cycles++;
 | |
|            }, 10);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(1);
 | |
| 
 | |
|            discardPeriodicTasks();
 | |
| 
 | |
|            // Tick once to clear out the timer which already started.
 | |
|            tick(10);
 | |
|            expect(cycles).toEqual(2);
 | |
| 
 | |
|            tick(10);
 | |
|            // Nothing should change
 | |
|            expect(cycles).toEqual(2);
 | |
|          }));
 | |
| 
 | |
|       it('should process microtasks before timers', fakeAsync(() => {
 | |
|            const log = new Log();
 | |
| 
 | |
|            resolvedPromise.then((_) => log.add('microtask'));
 | |
| 
 | |
|            setTimeout(() => log.add('timer'), 9);
 | |
| 
 | |
|            const id = setInterval(() => log.add('periodic timer'), 10);
 | |
| 
 | |
|            expect(log.result()).toEqual('');
 | |
| 
 | |
|            tick(10);
 | |
|            expect(log.result()).toEqual('microtask; timer; periodic timer');
 | |
|            clearInterval(id);
 | |
|          }));
 | |
| 
 | |
|       it('should process micro-tasks created in timers before next timers', fakeAsync(() => {
 | |
|            const log = new Log();
 | |
| 
 | |
|            resolvedPromise.then((_) => log.add('microtask'));
 | |
| 
 | |
|            setTimeout(() => {
 | |
|              log.add('timer');
 | |
|              resolvedPromise.then((_) => log.add('t microtask'));
 | |
|            }, 9);
 | |
| 
 | |
|            const id = setInterval(() => {
 | |
|              log.add('periodic timer');
 | |
|              resolvedPromise.then((_) => log.add('pt microtask'));
 | |
|            }, 10);
 | |
| 
 | |
|            tick(10);
 | |
|            expect(log.result())
 | |
|                .toEqual('microtask; timer; t microtask; periodic timer; pt microtask');
 | |
| 
 | |
|            tick(10);
 | |
|            expect(log.result())
 | |
|                .toEqual(
 | |
|                    'microtask; timer; t microtask; periodic timer; pt microtask; periodic timer; pt microtask');
 | |
|            clearInterval(id);
 | |
|          }));
 | |
| 
 | |
|       it('should flush tasks', fakeAsync(() => {
 | |
|            let ran = false;
 | |
|            setTimeout(() => {
 | |
|              ran = true;
 | |
|            }, 10);
 | |
| 
 | |
|            flush();
 | |
|            expect(ran).toEqual(true);
 | |
|          }));
 | |
| 
 | |
|       it('should flush multiple tasks', fakeAsync(() => {
 | |
|            let ran = false;
 | |
|            let ran2 = false;
 | |
|            setTimeout(() => {
 | |
|              ran = true;
 | |
|            }, 10);
 | |
|            setTimeout(() => {
 | |
|              ran2 = true;
 | |
|            }, 30);
 | |
| 
 | |
|            let elapsed = flush();
 | |
| 
 | |
|            expect(ran).toEqual(true);
 | |
|            expect(ran2).toEqual(true);
 | |
|            expect(elapsed).toEqual(30);
 | |
|          }));
 | |
| 
 | |
|       it('should move periodic tasks', fakeAsync(() => {
 | |
|            let ran = false;
 | |
|            let count = 0;
 | |
|            setInterval(() => {
 | |
|              count++;
 | |
|            }, 10);
 | |
|            setTimeout(() => {
 | |
|              ran = true;
 | |
|            }, 35);
 | |
| 
 | |
|            let elapsed = flush();
 | |
| 
 | |
|            expect(count).toEqual(3);
 | |
|            expect(ran).toEqual(true);
 | |
|            expect(elapsed).toEqual(35);
 | |
| 
 | |
|            discardPeriodicTasks();
 | |
|          }));
 | |
|     });
 | |
| 
 | |
|     describe('outside of the fakeAsync zone', () => {
 | |
|       it('calling flushMicrotasks should throw', () => {
 | |
|         expect(() => {
 | |
|           flushMicrotasks();
 | |
|         }).toThrowError('The code should be running in the fakeAsync zone to call this function');
 | |
|       });
 | |
| 
 | |
|       it('calling tick should throw', () => {
 | |
|         expect(() => {
 | |
|           tick();
 | |
|         }).toThrowError('The code should be running in the fakeAsync zone to call this function');
 | |
|       });
 | |
| 
 | |
|       it('calling flush should throw', () => {
 | |
|         expect(() => {
 | |
|           flush();
 | |
|         }).toThrowError('The code should be running in the fakeAsync zone to call this function');
 | |
|       });
 | |
| 
 | |
|       it('calling discardPeriodicTasks should throw', () => {
 | |
|         expect(() => {
 | |
|           discardPeriodicTasks();
 | |
|         }).toThrowError('The code should be running in the fakeAsync zone to call this function');
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     describe('only one `fakeAsync` zone per test', () => {
 | |
|       let zoneInBeforeEach: Zone;
 | |
|       let zoneInTest1: Zone;
 | |
|       beforeEach(fakeAsync(() => {
 | |
|         zoneInBeforeEach = Zone.current;
 | |
|       }));
 | |
| 
 | |
|       it('should use the same zone as in beforeEach', fakeAsync(() => {
 | |
|            zoneInTest1 = Zone.current;
 | |
|            expect(zoneInTest1).toBe(zoneInBeforeEach);
 | |
|          }));
 | |
|     });
 | |
| 
 | |
|     describe('fakeAsync should work with Date', () => {
 | |
|       it('should get date diff correctly', fakeAsync(() => {
 | |
|            const start = Date.now();
 | |
|            tick(100);
 | |
|            const end = Date.now();
 | |
|            expect(end - start).toBe(100);
 | |
|          }));
 | |
| 
 | |
|       it('should check date type correctly', fakeAsync(() => {
 | |
|            const d: any = new Date();
 | |
|            expect(d instanceof Date).toBe(true);
 | |
|          }));
 | |
| 
 | |
|       it('should new Date with parameter correctly', fakeAsync(() => {
 | |
|            const d: Date = new Date(0);
 | |
|            expect(d.getFullYear()).toBeLessThan(1971);
 | |
|            const d1: Date = new Date('December 17, 1995 03:24:00');
 | |
|            expect(d1.getFullYear()).toEqual(1995);
 | |
|            const d2: Date = new Date(1995, 11, 17, 3, 24, 0);
 | |
|            expect(isNaN(d2.getTime())).toBeFalsy();
 | |
|            expect(d2.getFullYear()).toEqual(1995);
 | |
|            d2.setFullYear(1985);
 | |
|            expect(d2.getFullYear()).toBe(1985);
 | |
|            expect(d2.getMonth()).toBe(11);
 | |
|            expect(d2.getDate()).toBe(17);
 | |
|          }));
 | |
| 
 | |
|       it('should get Date.UTC() correctly', fakeAsync(() => {
 | |
|            const utcDate = new Date(Date.UTC(96, 11, 1, 0, 0, 0));
 | |
|            expect(utcDate.getFullYear()).toBe(1996);
 | |
|          }));
 | |
| 
 | |
|       it('should call Date.parse() correctly', fakeAsync(() => {
 | |
|            const unixTimeZero = Date.parse('01 Jan 1970 00:00:00 GMT');
 | |
|            expect(unixTimeZero).toBe(0);
 | |
|          }));
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('ProxyZone', () => {
 | |
|     beforeEach(() => {
 | |
|       ProxyZoneSpec.assertPresent();
 | |
|     });
 | |
| 
 | |
|     afterEach(() => {
 | |
|       ProxyZoneSpec.assertPresent();
 | |
|     });
 | |
| 
 | |
|     it('should allow fakeAsync zone to retroactively set a zoneSpec outside of fakeAsync', () => {
 | |
|       ProxyZoneSpec.assertPresent();
 | |
|       let state: string = 'not run';
 | |
|       const testZone = Zone.current.fork({name: 'test-zone'});
 | |
|       (fakeAsync(() => {
 | |
|         testZone.run(() => {
 | |
|           Promise.resolve('works').then((v) => state = v);
 | |
|           expect(state).toEqual('not run');
 | |
|           flushMicrotasks();
 | |
|           expect(state).toEqual('works');
 | |
|         });
 | |
|       }))();
 | |
|       expect(state).toEqual('works');
 | |
|     });
 | |
|   });
 | |
| }
 |