160 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @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, ignoreNestedTimeout = false): void {
 | 
						|
    _getFakeAsyncZoneSpec().tick(millis, null, ignoreNestedTimeout);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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};
 | 
						|
});
 |