/** * @license * Copyright Google Inc. 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 '../zone-spec/fake-async-test'; Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate) => { const FakeAsyncTestZoneSpec = Zone && (Zone as any)['FakeAsyncTestZoneSpec']; type ProxyZoneSpec = { setDelegate(delegateSpec: ZoneSpec): void; getDelegate(): ZoneSpec; resetDelegate(): void; }; const ProxyZoneSpec: {get(): ProxyZoneSpec; assertPresent: () => ProxyZoneSpec} = Zone && (Zone as any)['ProxyZoneSpec']; let _fakeAsyncTestZoneSpec: any = null; /** * Clears out the shared fake async zone for a test. * To be called in a global `beforeEach`. * * @experimental */ function resetFakeAsyncZone() { if (_fakeAsyncTestZoneSpec) { _fakeAsyncTestZoneSpec.unlockDatePatch(); } _fakeAsyncTestZoneSpec = null; // in node.js testing we may not have ProxyZoneSpec in which case there is nothing to reset. ProxyZoneSpec && ProxyZoneSpec.assertPresent().resetDelegate(); } /** * Wraps a function to be executed in the fakeAsync zone: * - microtasks are manually executed by calling `flushMicrotasks()`, * - timers are synchronous, `tick()` simulates the asynchronous passage of time. * * If there are any pending timers at the end of the function, an exception will be thrown. * * Can be used to wrap inject() calls. * * ## Example * * {@example core/testing/ts/fake_async.ts region='basic'} * * @param fn * @returns The function wrapped to be executed in the fakeAsync zone * * @experimental */ function fakeAsync(fn: Function): (...args: any[]) => any { // Not using an arrow function to preserve context passed from call site return function(this: unknown, ...args: any[]) { const proxyZoneSpec = ProxyZoneSpec.assertPresent(); if (Zone.current.get('FakeAsyncTestZoneSpec')) { throw new Error('fakeAsync() calls can not be nested'); } try { // in case jasmine.clock init a fakeAsyncTestZoneSpec if (!_fakeAsyncTestZoneSpec) { if (proxyZoneSpec.getDelegate() instanceof FakeAsyncTestZoneSpec) { throw new Error('fakeAsync() calls can not be nested'); } _fakeAsyncTestZoneSpec = new FakeAsyncTestZoneSpec(); } let res: any; const lastProxyZoneSpec = proxyZoneSpec.getDelegate(); proxyZoneSpec.setDelegate(_fakeAsyncTestZoneSpec); _fakeAsyncTestZoneSpec.lockDatePatch(); try { res = fn.apply(this, args); flushMicrotasks(); } finally { proxyZoneSpec.setDelegate(lastProxyZoneSpec); } if (_fakeAsyncTestZoneSpec.pendingPeriodicTimers.length > 0) { throw new Error( `${_fakeAsyncTestZoneSpec.pendingPeriodicTimers.length} ` + `periodic timer(s) still in the queue.`); } if (_fakeAsyncTestZoneSpec.pendingTimers.length > 0) { throw new Error( `${_fakeAsyncTestZoneSpec.pendingTimers.length} timer(s) still in the queue.`); } return res; } finally { resetFakeAsyncZone(); } }; } function _getFakeAsyncZoneSpec(): any { if (_fakeAsyncTestZoneSpec == null) { _fakeAsyncTestZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); if (_fakeAsyncTestZoneSpec == null) { throw new Error('The code should be running in the fakeAsync zone to call this function'); } } return _fakeAsyncTestZoneSpec; } /** * Simulates the asynchronous passage of time for the timers in the fakeAsync zone. * * The microtasks queue is drained at the very start of this function and after any timer callback * has been executed. * * ## Example * * {@example core/testing/ts/fake_async.ts region='basic'} * * @experimental */ function tick(millis: number = 0): void { _getFakeAsyncZoneSpec().tick(millis); } /** * Simulates the asynchronous passage of time for the timers in the fakeAsync zone by * draining the macrotask queue until it is empty. The returned value is the milliseconds * of time that would have been elapsed. * * @param maxTurns * @returns The simulated time elapsed, in millis. * * @experimental */ function flush(maxTurns?: number): number { return _getFakeAsyncZoneSpec().flush(maxTurns); } /** * Discard all remaining periodic tasks. * * @experimental */ function discardPeriodicTasks(): void { const zoneSpec = _getFakeAsyncZoneSpec(); const pendingTimers = zoneSpec.pendingPeriodicTimers; zoneSpec.pendingPeriodicTimers.length = 0; } /** * Flush any pending microtasks. * * @experimental */ function flushMicrotasks(): void { _getFakeAsyncZoneSpec().flushMicrotasks(); } (Zone as any)[api.symbol('fakeAsyncTest')] = { resetFakeAsyncZone, flushMicrotasks, discardPeriodicTasks, tick, flush, fakeAsync}; });