Mock implementations of time-based services that provide synchronous control over asynchronous operations in tests. Angular Mocks decorates the $timeout and $interval services and provides a mock $browser.defer method for controlling timing in tests.
Mock $timeout service that provides synchronous control over timeout operations:
/**
* Schedule a function to be executed after a delay (works like native $timeout)
* @param {Function} fn - Function to execute
* @param {number} delay - Delay in milliseconds (optional, defaults to 0)
* @param {boolean} invokeApply - Whether to invoke $apply (optional, defaults to true)
* @param {...*} pass - Additional arguments to pass to the function
* @returns {Promise} Promise that resolves when the timeout executes
*/
$timeout(fn?, delay?, invokeApply?, ...pass);
/**
* Cancel a scheduled timeout
* @param {Promise} promise - Promise returned by $timeout()
* @returns {boolean} True if the timeout was successfully cancelled
*/
$timeout.cancel(promise);
/**
* Flush pending timeouts
* @param {number} delay - Specific delay to flush (optional, flushes all if not specified)
* @throws {Error} If there are no pending timeouts to flush
*/
$timeout.flush(delay?);
/**
* Flush the next pending timeout
* @param {number} expectedDelay - Expected delay of the next timeout (optional)
* @throws {Error} If there are no pending timeouts or delay doesn't match
*/
$timeout.flushNext(expectedDelay?);
/**
* Verify that no timeouts are pending
* @throws {Error} If there are pending timeouts
*/
$timeout.verifyNoPendingTasks();Mock $interval service that provides synchronous control over interval operations:
/**
* Create a repeating interval
* @param {Function} fn - Function to execute repeatedly
* @param {number} delay - Delay between executions in milliseconds
* @param {number} count - Number of times to execute (optional, infinite if not specified)
* @param {boolean} invokeApply - Whether to invoke $apply (optional, defaults to true)
* @param {...*} pass - Additional arguments to pass to the function
* @returns {Promise} Promise that resolves when the interval completes
*/
$interval(fn, delay, count?, invokeApply?, ...pass);
/**
* Cancel an active interval
* @param {Promise} promise - Promise returned by $interval()
* @returns {boolean} True if the interval was successfully cancelled
*/
$interval.cancel(promise);
/**
* Flush pending intervals
* @param {number} millis - Number of milliseconds to advance time (optional, flushes all if not specified)
* @returns {number} Number of interval callbacks that were executed
*/
$interval.flush(millis?);Mock browser defer method for controlling deferred execution:
/**
* Schedule a function for deferred execution
* @param {Function} fn - Function to execute
* @param {number} delay - Delay in milliseconds (optional, defaults to 0)
* @param {string} taskType - Type of task for tracking (optional)
* @returns {number} Timeout ID for cancellation
*/
$browser.defer(fn, delay?, taskType?);
/**
* Current mock time in milliseconds
* @type {number}
*/
$browser.defer.now;
/**
* Flush pending deferred functions
* @param {number} delay - Specific delay to flush (optional, flushes all if not specified)
*/
$browser.defer.flush(delay?);Services for managing and verifying asynchronous tasks:
/**
* Flush all pending tasks (timeouts, intervals, deferrals)
*/
$flushPendingTasks();
/**
* Verify that no tasks are pending
* @throws {Error} If there are pending tasks
*/
$verifyNoPendingTasks();
/**
* Register callback to be notified when no requests are outstanding
* @param {Function} callback - Function to call when no requests are pending
*/
$browser.notifyWhenNoOutstandingRequests(callback);Basic Timeout Testing:
describe('TimeoutService', function() {
var TimeoutService, $timeout;
beforeEach(module('myApp'));
beforeEach(inject(function(_TimeoutService_, _$timeout_) {
TimeoutService = _TimeoutService_;
$timeout = _$timeout_;
}));
it('should execute delayed function', function() {
var called = false;
TimeoutService.delayedAction(function() {
called = true;
}, 1000);
expect(called).toBe(false);
$timeout.flush(1000);
expect(called).toBe(true);
});
it('should handle timeout cancellation', function() {
var called = false;
var promise = $timeout(function() {
called = true;
}, 1000);
$timeout.cancel(promise);
$timeout.flush();
expect(called).toBe(false);
});
});Interval Testing:
describe('IntervalService', function() {
var $interval;
beforeEach(inject(function(_$interval_) {
$interval = _$interval_;
}));
it('should execute interval multiple times', function() {
var count = 0;
$interval(function() {
count++;
}, 100, 3);
expect(count).toBe(0);
$interval.flush(100);
expect(count).toBe(1);
$interval.flush(200);
expect(count).toBe(3);
});
it('should handle interval cancellation', function() {
var count = 0;
var promise = $interval(function() {
count++;
}, 100);
$interval.flush(200);
expect(count).toBe(2);
$interval.cancel(promise);
$interval.flush(100);
expect(count).toBe(2); // Should not increment after cancellation
});
});Complex Timing Scenarios:
it('should handle mixed timeout and interval operations', function() {
var timeoutCalled = false;
var intervalCount = 0;
// Set up timeout
$timeout(function() {
timeoutCalled = true;
}, 500);
// Set up interval
$interval(function() {
intervalCount++;
}, 200, 3);
// Flush intervals first
$interval.flush(400); // Should execute interval twice
expect(intervalCount).toBe(2);
expect(timeoutCalled).toBe(false);
// Flush timeout
$timeout.flush(500);
expect(timeoutCalled).toBe(true);
// Complete remaining interval
$interval.flush(200);
expect(intervalCount).toBe(3);
// Verify nothing is pending
$timeout.verifyNoPendingTasks();
});Task Verification:
describe('Task Management', function() {
var $flushPendingTasks, $verifyNoPendingTasks;
beforeEach(inject(function(_$flushPendingTasks_, _$verifyNoPendingTasks_) {
$flushPendingTasks = _$flushPendingTasks_;
$verifyNoPendingTasks = _$verifyNoPendingTasks_;
}));
it('should flush all pending tasks', function() {
var results = [];
$timeout(function() { results.push('timeout1'); }, 100);
$timeout(function() { results.push('timeout2'); }, 200);
$interval(function() { results.push('interval'); }, 50, 2);
$flushPendingTasks();
expect(results).toContain('timeout1');
expect(results).toContain('timeout2');
expect(results).toContain('interval');
$verifyNoPendingTasks(); // Should not throw
});
});// Timeout Promise Interface
interface TimeoutPromise extends Promise {
$$timeoutId: number;
}
// Interval Promise Interface
interface IntervalPromise extends Promise {
$$intervalId: number;
}
// Task Types
type TaskType = 'timeout' | 'interval' | 'defer' | string;