CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-zone-js

Execution context library that persists across asynchronous operations for JavaScript applications

Overview
Eval results
Files

testing-utilities.mddocs/

Testing Utilities

Comprehensive testing framework with fakeAsync, async testing zones, and task tracking for testing asynchronous code in a controlled environment.

Capabilities

FakeAsync Testing

Testing utilities that allow control of time and microtask execution for deterministic async testing.

/**
 * Create a fakeAsync test function that controls time
 * @param fn - Test function to wrap
 * @param options - Configuration options
 * @returns Wrapped test function
 */
function fakeAsync(fn: Function, options?: {flush?: boolean}): (...args: any[]) => any;

/**
 * Advance virtual time by specified milliseconds
 * @param millis - Milliseconds to advance (default: 0)
 * @param ignoreNestedTimeout - Whether to ignore nested setTimeout calls
 */
function tick(millis?: number, ignoreNestedTimeout?: boolean): void;

/**
 * Flush all pending macrotasks and microtasks
 * @param maxTurns - Maximum number of task execution cycles
 * @returns Number of tasks flushed
 */
function flush(maxTurns?: number): number;

/**
 * Flush only pending microtasks
 */
function flushMicrotasks(): void;

/**
 * Discard all pending periodic tasks (setInterval)
 */
function discardPeriodicTasks(): void;

/**
 * Reset the fakeAsync zone to initial state
 */
function resetFakeAsyncZone(): void;

/**
 * Wrap a function to automatically use ProxyZoneSpec for test isolation
 * @param fn - Function to wrap with proxy zone behavior
 * @returns Wrapped function that runs in proxy zone context
 */
function withProxyZone<T extends Function>(fn: T): T;

Usage Examples:

import 'zone.js/testing';

describe('FakeAsync Tests', () => {
  it('should control time', fakeAsync(() => {
    let executed = false;
    
    setTimeout(() => {
      executed = true;
    }, 1000);
    
    // Time hasn't advanced yet
    expect(executed).toBe(false);
    
    // Advance time by 1000ms
    tick(1000);
    
    // Now the timeout has executed
    expect(executed).toBe(true);
  }));
  
  it('should handle promises', fakeAsync(() => {
    let result = '';
    
    Promise.resolve('async-value')
      .then(value => result = value);
    
    // Promise hasn't resolved yet
    expect(result).toBe('');
    
    // Flush microtasks
    flushMicrotasks();
    
    // Promise has resolved
    expect(result).toBe('async-value');
  }));
  
  it('should handle mixed async operations', fakeAsync(() => {
    const results: string[] = [];
    
    // Schedule various async operations
    setTimeout(() => results.push('timeout-100'), 100);
    setTimeout(() => results.push('timeout-200'), 200);
    
    Promise.resolve().then(() => results.push('promise-1'));
    Promise.resolve().then(() => results.push('promise-2'));
    
    setInterval(() => results.push('interval'), 50);
    
    // Flush microtasks first
    flushMicrotasks();
    expect(results).toEqual(['promise-1', 'promise-2']);
    
    // Advance time and check results
    tick(50);
    expect(results).toContain('interval');
    
    tick(50); // Total: 100ms
    expect(results).toContain('timeout-100');
    
    tick(100); // Total: 200ms
    expect(results).toContain('timeout-200');
    
    // Clean up periodic tasks
    discardPeriodicTasks();
  }));
  
  it('should use proxy zone for test isolation', withProxyZone(() => {
    let result = '';
    
    // This test runs in an isolated proxy zone
    setTimeout(() => {
      result = 'completed';
    }, 100);
    
    tick(100);
    expect(result).toBe('completed');
    
    // ProxyZoneSpec automatically handles cleanup
  }));
});

FakeAsyncTestZoneSpec

Zone specification for fakeAsync testing environment.

/**
 * Zone specification for controlling time in tests
 */
class FakeAsyncTestZoneSpec implements ZoneSpec {
  /** Zone name identifier */
  name: 'fakeAsync';
  
  /**
   * Advance virtual time by specified milliseconds
   * @param millis - Milliseconds to advance
   * @param doTick - Optional callback for each tick
   */
  tick(millis?: number, doTick?: (elapsed: number) => void): void;
  
  /**
   * Flush all pending tasks
   * @param maxTurns - Maximum execution cycles
   * @returns Number of tasks flushed
   */
  flush(maxTurns?: number): number;
  
  /**
   * Flush only microtasks
   */
  flushMicrotasks(): void;
  
  /**
   * Get current fake system time
   * @returns Current virtual time in milliseconds
   */
  getFakeSystemTime(): number;
}

Usage Examples:

import 'zone.js/testing';

// Direct use of FakeAsyncTestZoneSpec
const fakeAsyncZone = Zone.current.fork(new FakeAsyncTestZoneSpec());

fakeAsyncZone.run(() => {
  let value = 0;
  
  setTimeout(() => value = 1, 100);
  setTimeout(() => value = 2, 200);
  
  // Control time directly on the zone spec
  const zoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
  
  zoneSpec.tick(100);
  expect(value).toBe(1);
  
  zoneSpec.tick(100); // Total: 200ms
  expect(value).toBe(2);
});

Async Testing

Testing utilities for asynchronous operations that need to complete naturally.

/**
 * Zone specification for async testing
 */
class AsyncTestZoneSpec implements ZoneSpec {
  /** Zone name identifier */
  name: 'async-test';
  
  /**
   * Wait for all pending asynchronous operations to complete
   * @returns Promise that resolves when all async operations are done
   */
  whenStable(): Promise<any>;
  
  /**
   * Check if there are any pending asynchronous operations
   * @returns true if async operations are pending
   */
  hasPendingAsyncTasks(): boolean;
  
  /**
   * Check if there are any pending microtasks
   * @returns true if microtasks are pending
   */
  hasPendingMicrotasks(): boolean;
  
  /**
   * Check if there are any pending macrotasks
   * @returns true if macrotasks are pending
   */
  hasPendingMacrotasks(): boolean;
}

Usage Examples:

import 'zone.js/testing';

describe('Async Tests', () => {
  let asyncZone: Zone;
  let asyncZoneSpec: AsyncTestZoneSpec;
  
  beforeEach(() => {
    asyncZoneSpec = new AsyncTestZoneSpec();
    asyncZone = Zone.current.fork(asyncZoneSpec);
  });
  
  it('should wait for async operations', async () => {
    let completed = false;
    
    asyncZone.run(() => {
      setTimeout(() => {
        completed = true;
      }, 100);
      
      fetch('/api/data').then(() => {
        // Some async operation
      });
    });
    
    // Wait for all async operations to complete
    await asyncZoneSpec.whenStable();
    
    expect(completed).toBe(true);
  });
  
  it('should check pending tasks', () => {
    asyncZone.run(() => {
      setTimeout(() => {}, 100);
      
      expect(asyncZoneSpec.hasPendingMacrotasks()).toBe(true);
      expect(asyncZoneSpec.hasPendingAsyncTasks()).toBe(true);
    });
  });
});

Task Tracking

Zone specification for monitoring and tracking task execution.

/**
 * Zone specification for tracking task execution
 */
class TaskTrackingZoneSpec implements ZoneSpec {
  /** Zone name identifier */
  name: 'task-tracking';
  
  /** Array of scheduled microtasks */
  readonly microTasks: Task[];
  
  /** Array of scheduled macrotasks */
  readonly macroTasks: Task[];
  
  /** Array of scheduled event tasks */
  readonly eventTasks: Task[];
  
  /**
   * Clear all tracked tasks
   */
  clear(): void;
  
  /**
   * Get tasks by type
   * @param type - Task type to filter by
   * @returns Array of tasks of the specified type
   */
  getTasksByType(type: TaskType): Task[];
  
  /**
   * Get tasks by source
   * @param source - Source identifier to filter by
   * @returns Array of tasks from the specified source
   */
  getTasksBySource(source: string): Task[];
}

Usage Examples:

import 'zone.js/testing';

describe('Task Tracking Tests', () => {
  let trackingZone: Zone;
  let trackingSpec: TaskTrackingZoneSpec;
  
  beforeEach(() => {
    trackingSpec = new TaskTrackingZoneSpec();
    trackingZone = Zone.current.fork(trackingSpec);
  });
  
  it('should track scheduled tasks', () => {
    trackingZone.run(() => {
      setTimeout(() => {}, 100);
      setTimeout(() => {}, 200);
      
      Promise.resolve().then(() => {});
      
      document.addEventListener('click', () => {});
    });
    
    expect(trackingSpec.macroTasks).toHaveLength(2);
    expect(trackingSpec.microTasks).toHaveLength(1);
    expect(trackingSpec.eventTasks).toHaveLength(1);
    
    // Check specific task sources
    const timerTasks = trackingSpec.getTasksBySource('setTimeout');
    expect(timerTasks).toHaveLength(2);
    
    const promiseTasks = trackingSpec.getTasksByType('microTask');
    expect(promiseTasks).toHaveLength(1);
  });
  
  it('should clear tracked tasks', () => {
    trackingZone.run(() => {
      setTimeout(() => {}, 100);
      Promise.resolve().then(() => {});
    });
    
    expect(trackingSpec.macroTasks).toHaveLength(1);
    expect(trackingSpec.microTasks).toHaveLength(1);
    
    trackingSpec.clear();
    
    expect(trackingSpec.macroTasks).toHaveLength(0);
    expect(trackingSpec.microTasks).toHaveLength(0);
  });
});

Proxy Zone

Zone specification for test isolation and delegation control.

/**
 * Zone specification for test isolation and delegation
 */
class ProxyZoneSpec implements ZoneSpec {
  /** Zone name identifier */
  name: 'proxy';
  
  /**
   * Set the delegate zone specification
   * @param delegateSpec - Zone spec to delegate operations to
   */
  setDelegate(delegateSpec: ZoneSpec): void;
  
  /**
   * Get the current delegate zone specification
   * @returns Current delegate zone spec
   */
  getDelegate(): ZoneSpec;
  
  /**
   * Reset delegation to default behavior
   */
  resetDelegate(): void;
  
  /**
   * Assert that no tasks are pending
   * @param ignoreLeisure - Whether to ignore leisure tasks
   */
  assertNoPendingTasks(ignoreLeisure?: boolean): void;
}

Usage Examples:

import 'zone.js/testing';

describe('Proxy Zone Tests', () => {
  let proxyZone: Zone;
  let proxySpec: ProxyZoneSpec;
  
  beforeEach(() => {
    proxySpec = new ProxyZoneSpec();
    proxyZone = Zone.current.fork(proxySpec);
  });
  
  it('should isolate tests', () => {
    // Set up custom delegate for this test
    const customDelegate = {
      name: 'custom-test',
      onHandleError: (delegate, current, target, error) => {
        console.log('Custom error handling:', error);
        return true; // Handle the error
      }
    };
    
    proxySpec.setDelegate(customDelegate);
    
    proxyZone.run(() => {
      // Test code that might throw errors
      throw new Error('Test error');
      // Error will be handled by custom delegate
    });
    
    // Reset for next test
    proxySpec.resetDelegate();
  });
  
  it('should assert no pending tasks', () => {
    proxyZone.run(() => {
      // Synchronous operations only
      const result = 1 + 1;
      expect(result).toBe(2);
    });
    
    // Should not throw - no pending tasks
    proxySpec.assertNoPendingTasks();
  });
  
  it('should detect pending tasks', () => {
    proxyZone.run(() => {
      setTimeout(() => {}, 100);
    });
    
    // Should throw - there are pending tasks
    expect(() => {
      proxySpec.assertNoPendingTasks();
    }).toThrow();
  });
});

Long Stack Trace

Zone specification for enhanced error stack traces across async boundaries.

/**
 * Enable long stack traces across async boundaries
 * @param Zone - Zone constructor
 */
function patchLongStackTrace(Zone: ZoneType): void;

/**
 * Zone specification for enhanced stack traces
 */
class LongStackTraceZoneSpec implements ZoneSpec {
  /** Zone name identifier */
  name: 'long-stack-trace';
  
  /**
   * Get enhanced stack trace for an error
   * @param error - Error to get stack trace for
   * @returns Enhanced stack trace string
   */
  getLongStackTrace(error: Error): string;
}

Usage Examples:

import 'zone.js/testing';

// Enable long stack traces
Zone.__load_patch('longStackTraceZone', (global, Zone) => {
  patchLongStackTrace(Zone);
});

const longStackZone = Zone.current.fork(new LongStackTraceZoneSpec());

longStackZone.run(() => {
  function asyncFunction() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        someOtherAsyncFunction()
          .then(resolve)
          .catch(reject);
      }, 100);
    });
  }
  
  function someOtherAsyncFunction() {
    return Promise.reject(new Error('Async error'));
  }
  
  asyncFunction().catch(error => {
    // Error will have enhanced stack trace showing
    // the full async call chain
    console.log(error.stack);
  });
});

Install with Tessl CLI

npx tessl i tessl/npm-zone-js

docs

configuration-api.md

core-zone-api.md

index.md

patching-system.md

task-system.md

testing-utilities.md

zone-specifications.md

tile.json