Перейти к содержимому
GitHubXDiscord

createFakeScheduler

Стабильный · с 0.1.0

Независящий от тест-раннера виртуальный clock. После install() globalThis.setTimeout и clearTimeout подменяются, так что ни один реальный таймер не выстреливает — вместо этого advance(ms) гоняет каждый обратный вызов, который становится готов в окне. Полезно для тестов цепочек actions.debounce / throttle / defer без флака await new Promise(setTimeout, …).

Зависимости от vi.useFakeTimers() Vitest’а нет — работает одинаково в чистом node:test, Jest и обычном Vitest.

import { createFakeScheduler } from '@triggery/testing';
function createFakeScheduler(): FakeScheduler;

interface FakeScheduler {
  install(): void;
  uninstall(): void;
  now(): number;
  advance(ms: number): Promise<void>;
  flushAll(): Promise<void>;
  pending(): number;
}
МетодОписание
install()Подменить globalThis.setTimeout / clearTimeout на фейковый контроллер. Идемпотентно.
uninstall()Восстановить оригиналы, очистить ожидающие таймеры, сбросить виртуальные часы в 0. Безопасно звать многократно.
now()Текущее значение виртуальных часов в мс с момента install.
advance(ms)Сдвинуть часы вперёд на ms и прогнать каждый таймер, ставший готовым в этом окне. После — сливает микротаски.
flushAll()Прогнать каждый ожидающий таймер независимо от запланированного времени. Полезно для “забить на часы, посмотреть, что в итоге произойдёт”.
pending()Сколько таймеров ещё ожидает.
import { createTriggerfunction createTrigger<S extends TriggerSchema>(config: CreateTriggerConfig<S>, runtime?: Runtime): Trigger<S>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
} from '@triggery/core';
import { createFakeSchedulerfunction createFakeScheduler(): FakeScheduler, createTestRuntimefunction createTestRuntime(options?: TestRuntimeOptions): TestRuntime } from '@triggery/testing'; import { afterEachfunction afterEach<ExtraContext = object>(fn: AfterEachListener<ExtraContext>, timeout?: number): void
Registers a callback function to be executed after each test within the current suite has completed. This hook is useful for scenarios where you need to clean up or reset the test environment after each test runs, such as deleting temporary files, clearing test-specific database entries, or resetting mocked functions. **Note:** The `afterEach` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
@paramfn - The callback function to be executed after each test. This function receives an `TestContext` parameter if additional test context is needed.@paramtimeout - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.@returns@example```ts // Example of using afterEach to delete temporary files created during a test afterEach(async () => { await fileSystem.deleteTempFiles(); }); ```
, beforeEachfunction beforeEach<ExtraContext = object>(fn: BeforeEachListener<ExtraContext>, timeout?: number): void
Registers a callback function to be executed before each test within the current suite. This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables. **Note:** The `beforeEach` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
@paramfn - The callback function to be executed before each test. This function receives an `TestContext` parameter if additional test context is needed.@paramtimeout - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.@returns@example```ts // Example of using beforeEach to reset a database state beforeEach(async () => { await database.reset(); }); ```
, expectconst expect: ExpectStatic, itconst it: TestAPI
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
, viconst vi: VitestUtils } from 'vitest';
const ftconst ft: FakeScheduler = createFakeSchedulerfunction createFakeScheduler(): FakeScheduler(); beforeEachbeforeEach<object>(fn: BeforeEachListener<object>, timeout?: number): void
Registers a callback function to be executed before each test within the current suite. This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables. **Note:** The `beforeEach` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
@paramfn - The callback function to be executed before each test. This function receives an `TestContext` parameter if additional test context is needed.@paramtimeout - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.@returns@example```ts // Example of using beforeEach to reset a database state beforeEach(async () => { await database.reset(); }); ```
(() => ftconst ft: FakeScheduler.installFakeScheduler.install(): void
Replace globalThis.setTimeout / clearTimeout with the fake controller.
());
afterEachafterEach<object>(fn: AfterEachListener<object>, timeout?: number): void
Registers a callback function to be executed after each test within the current suite has completed. This hook is useful for scenarios where you need to clean up or reset the test environment after each test runs, such as deleting temporary files, clearing test-specific database entries, or resetting mocked functions. **Note:** The `afterEach` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
@paramfn - The callback function to be executed after each test. This function receives an `TestContext` parameter if additional test context is needed.@paramtimeout - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.@returns@example```ts // Example of using afterEach to delete temporary files created during a test afterEach(async () => { await fileSystem.deleteTempFiles(); }); ```
(() => ftconst ft: FakeScheduler.uninstallFakeScheduler.uninstall(): void
Restore the real timer functions. Safe to call multiple times.
());
itit<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number): void (+1 overload)
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
('debounces keystrokes', async () => {
const rtconst rt: TestRuntime = createTestRuntimefunction createTestRuntime(options?: TestRuntimeOptions): TestRuntime(); const t
const t: Trigger<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>
= createTrigger
createTrigger<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>(config: CreateTriggerConfig<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
<{
events
events: {
    keystroke: string;
}
: { keystrokekeystroke: string: string };
actions
actions: {
    search: string;
}
: { searchsearch: string: string };
}>( { idid: string: 'demo', eventsevents: readonly "keystroke"[]: ['keystroke'], handler
handler: TriggerHandler<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}, never>
({ event
event: {
    readonly name: "keystroke";
    readonly payload: string;
}
, actions
actions: ActionsCtx<{
    search: string;
}>
}) {
actions
actions: ActionsCtx<{
    search: string;
}>
.debounce
function debounce(ms: number): ActionsCtx<{
    search: string;
}>
(300).searchsearch?: ((payload: string) => void) | undefined?.(event
event: {
    readonly name: "keystroke";
    readonly payload: string;
}
.payloadpayload: string);
}, }, rtconst rt: TestRuntime, ); const searchconst search: Mock<Procedure> = viconst vi: VitestUtils.fnVitestUtils.fn: <Procedure>(originalImplementation?: Procedure | undefined) => Mock<Procedure>
Creates a spy on a function, though can be initiated without one. Every time a function is invoked, it stores its call arguments, returns, and instances. Also, you can manipulate its behavior with [methods](https://vitest.dev/api/mock). If no function is given, mock will return `undefined`, when invoked.
@example```ts const getApples = vi.fn(() => 0) getApples() expect(getApples).toHaveBeenCalled() expect(getApples).toHaveReturnedWith(0) getApples.mockReturnValueOnce(5) expect(getApples()).toBe(5) expect(getApples).toHaveNthReturnedWith(2, 5) ```
();
rtconst rt: TestRuntime.mockAction
mockAction<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}, "search">(trigger: Trigger<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>, name: "search", handler: (payload: string) => void): RegistrationToken
Register an action handler — typically a `vi.fn()`.
(t
const t: Trigger<{
    events: {
        keystroke: string;
    };
    actions: {
        search: string;
    };
}>
, 'search', searchconst search: Mock<Procedure>);
rtconst rt: TestRuntime.fireSyncfunction fireSync(eventName: string, payload?: unknown): void
Run dispatch synchronously (for tests and benchmarks).
('keystroke', 'a');
rtconst rt: TestRuntime.fireSyncfunction fireSync(eventName: string, payload?: unknown): void
Run dispatch synchronously (for tests and benchmarks).
('keystroke', 'ab');
rtconst rt: TestRuntime.fireSyncfunction fireSync(eventName: string, payload?: unknown): void
Run dispatch synchronously (for tests and benchmarks).
('keystroke', 'abc');
await ftconst ft: FakeScheduler.advanceFakeScheduler.advance(ms: number): Promise<void>
Advance the virtual clock by `ms` and run every timer that becomes due in that window. Returns a promise that resolves after pending microtasks are drained, so callers can `await ft.advance(N)` and then assert.
(299);
expectexpect<Mock<Procedure>>(actual: Mock<Procedure>, message?: string): Assertion<Mock<Procedure>> (+1 overload)(searchconst search: Mock<Procedure>).notnot: Assertion<Mock<Procedure>>.toHaveBeenCalledJestAssertion<Mock<Procedure>>.toHaveBeenCalled: () => void
Ensures that a mock function is called. Also under the alias `expect.toBeCalled`.
@exampleexpect(mockFunc).toHaveBeenCalled();
();
await ftconst ft: FakeScheduler.advanceFakeScheduler.advance(ms: number): Promise<void>
Advance the virtual clock by `ms` and run every timer that becomes due in that window. Returns a promise that resolves after pending microtasks are drained, so callers can `await ft.advance(N)` and then assert.
(1);
expectexpect<Mock<Procedure>>(actual: Mock<Procedure>, message?: string): Assertion<Mock<Procedure>> (+1 overload)(searchconst search: Mock<Procedure>).toHaveBeenCalledExactlyOnceWithAssertion<Mock<Procedure>>.toHaveBeenCalledExactlyOnceWith: <[string]>(args_0: string) => void
Ensure that a mock function is called with specific arguments and called exactly once.
@exampleexpect(mockFunc).toHaveBeenCalledExactlyOnceWith('arg1', 42);
('abc');
});
import { createFakeSchedulerfunction createFakeScheduler(): FakeScheduler } from '@triggery/testing';
import { expectconst expect: ExpectStatic, itconst it: TestAPI
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
} from 'vitest';
itit<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number): void (+1 overload)
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
('runs at most one timer per window', async () => {
const ftconst ft: FakeScheduler = createFakeSchedulerfunction createFakeScheduler(): FakeScheduler(); ftconst ft: FakeScheduler.installFakeScheduler.install(): void
Replace globalThis.setTimeout / clearTimeout with the fake controller.
();
try { let callslet calls: number = 0; setTimeoutfunction setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout)
(() => callslet calls: number++, 100);
setTimeoutfunction setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout)
(() => callslet calls: number++, 200);
setTimeoutfunction setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout)
(() => callslet calls: number++, 300);
expectexpect<number>(actual: number, message?: string): Assertion<number> (+1 overload)(ftconst ft: FakeScheduler.pendingFakeScheduler.pending(): number
Number of timers still pending.
()).toBeJestAssertion<number>.toBe: <number>(expected: number) => void
Checks that a value is what you expect. It calls `Object.is` to compare values. Don't use `toBe` with floating-point numbers.
@exampleexpect(result).toBe(42); expect(status).toBe(true);
(3);
await ftconst ft: FakeScheduler.advanceFakeScheduler.advance(ms: number): Promise<void>
Advance the virtual clock by `ms` and run every timer that becomes due in that window. Returns a promise that resolves after pending microtasks are drained, so callers can `await ft.advance(N)` and then assert.
(150);
expectexpect<number>(actual: number, message?: string): Assertion<number> (+1 overload)(callslet calls: number).toBeJestAssertion<number>.toBe: <number>(expected: number) => void
Checks that a value is what you expect. It calls `Object.is` to compare values. Don't use `toBe` with floating-point numbers.
@exampleexpect(result).toBe(42); expect(status).toBe(true);
(1);
await ftconst ft: FakeScheduler.flushAllFakeScheduler.flushAll(): Promise<void>
Run every pending timer (regardless of its scheduled time). Useful for "give up on the clock, just see what eventually happens".
();
expectexpect<number>(actual: number, message?: string): Assertion<number> (+1 overload)(callslet calls: number).toBeJestAssertion<number>.toBe: <number>(expected: number) => void
Checks that a value is what you expect. It calls `Object.is` to compare values. Don't use `toBe` with floating-point numbers.
@exampleexpect(result).toBe(42); expect(status).toBe(true);
(3);
expectexpect<number>(actual: number, message?: string): Assertion<number> (+1 overload)(ftconst ft: FakeScheduler.pendingFakeScheduler.pending(): number
Number of timers still pending.
()).toBeJestAssertion<number>.toBe: <number>(expected: number) => void
Checks that a value is what you expect. It calls `Object.is` to compare values. Don't use `toBe` with floating-point numbers.
@exampleexpect(result).toBe(42); expect(status).toBe(true);
(0);
} finally { ftconst ft: FakeScheduler.uninstallFakeScheduler.uninstall(): void
Restore the real timer functions. Safe to call multiple times.
();
} });
import { createTriggerfunction createTrigger<S extends TriggerSchema>(config: CreateTriggerConfig<S>, runtime?: Runtime): Trigger<S>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
} from '@triggery/core';
import { createFakeSchedulerfunction createFakeScheduler(): FakeScheduler, createTestRuntimefunction createTestRuntime(options?: TestRuntimeOptions): TestRuntime } from '@triggery/testing'; import { expectconst expect: ExpectStatic, itconst it: TestAPI
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
, viconst vi: VitestUtils } from 'vitest';
itit<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number): void (+1 overload)
Defines a test case with a given name and test function. The test function can optionally be configured with test options.
@paramname - The name of the test or a function that will be used as a test name.@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.@throws{Error} If called inside another test function.@example```ts // Define a simple test it('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); ```@example```ts // Define a test with options it('subtracts two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); }); ```
('defers an action by a fixed delay', async () => {
const ftconst ft: FakeScheduler = createFakeSchedulerfunction createFakeScheduler(): FakeScheduler(); ftconst ft: FakeScheduler.installFakeScheduler.install(): void
Replace globalThis.setTimeout / clearTimeout with the fake controller.
();
try { const rtconst rt: TestRuntime = createTestRuntimefunction createTestRuntime(options?: TestRuntimeOptions): TestRuntime(); const t
const t: Trigger<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}>
= createTrigger
createTrigger<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}>(config: CreateTriggerConfig<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}>, runtime?: Runtime): Trigger<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}>
Create a trigger and register it in a runtime (the default runtime if none is passed).
@example```ts const messageTrigger = createTrigger<{ events: { 'new-message': { author: string; text: string } }; conditions: { user: { id: string }; settings: { sound: boolean } }; actions: { showToast: { title: string }; playSound: void }; }>({ id: 'message-received', events: ['new-message'], required: ['user', 'settings'], handler({ event, conditions, actions, check }) { if (!conditions.user || !conditions.settings) return; // V1: manual narrowing if (check.is('settings', (s) => s.sound)) actions.playSound?.(); actions.showToast?.({ title: event.payload.author }); }, }); ```
<{
events
events: {
    ping: void;
}
: { pingping: void: void };
actions
actions: {
    log: void;
}
: { loglog: void: void };
}>( { idid: string: 'demo', eventsevents: readonly "ping"[]: ['ping'], handler
handler: TriggerHandler<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}, never>
({ actions
actions: ActionsCtx<{
    log: void;
}>
}) {
actions
actions: ActionsCtx<{
    log: void;
}>
.defer
function defer(ms: number): ActionsCtx<{
    log: void;
}>
(500).loglog?: (() => void) | undefined?.();
}, }, rtconst rt: TestRuntime, ); const logconst log: Mock<Procedure> = viconst vi: VitestUtils.fnVitestUtils.fn: <Procedure>(originalImplementation?: Procedure | undefined) => Mock<Procedure>
Creates a spy on a function, though can be initiated without one. Every time a function is invoked, it stores its call arguments, returns, and instances. Also, you can manipulate its behavior with [methods](https://vitest.dev/api/mock). If no function is given, mock will return `undefined`, when invoked.
@example```ts const getApples = vi.fn(() => 0) getApples() expect(getApples).toHaveBeenCalled() expect(getApples).toHaveReturnedWith(0) getApples.mockReturnValueOnce(5) expect(getApples()).toBe(5) expect(getApples).toHaveNthReturnedWith(2, 5) ```
();
rtconst rt: TestRuntime.mockAction
mockAction<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}, "log">(trigger: Trigger<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}>, name: "log", handler: () => void): RegistrationToken
Register an action handler — typically a `vi.fn()`.
(t
const t: Trigger<{
    events: {
        ping: void;
    };
    actions: {
        log: void;
    };
}>
, 'log', logconst log: Mock<Procedure>);
rtconst rt: TestRuntime.fireSyncfunction fireSync(eventName: string, payload?: unknown): void
Run dispatch synchronously (for tests and benchmarks).
('ping');
await ftconst ft: FakeScheduler.advanceFakeScheduler.advance(ms: number): Promise<void>
Advance the virtual clock by `ms` and run every timer that becomes due in that window. Returns a promise that resolves after pending microtasks are drained, so callers can `await ft.advance(N)` and then assert.
(499);
expectexpect<Mock<Procedure>>(actual: Mock<Procedure>, message?: string): Assertion<Mock<Procedure>> (+1 overload)(logconst log: Mock<Procedure>).notnot: Assertion<Mock<Procedure>>.toHaveBeenCalledJestAssertion<Mock<Procedure>>.toHaveBeenCalled: () => void
Ensures that a mock function is called. Also under the alias `expect.toBeCalled`.
@exampleexpect(mockFunc).toHaveBeenCalled();
();
await ftconst ft: FakeScheduler.advanceFakeScheduler.advance(ms: number): Promise<void>
Advance the virtual clock by `ms` and run every timer that becomes due in that window. Returns a promise that resolves after pending microtasks are drained, so callers can `await ft.advance(N)` and then assert.
(1);
expectexpect<Mock<Procedure>>(actual: Mock<Procedure>, message?: string): Assertion<Mock<Procedure>> (+1 overload)(logconst log: Mock<Procedure>).toHaveBeenCalledOnceAssertion<Mock<Procedure>>.toHaveBeenCalledOnce: () => void
Asserts that a mock function was called exactly once.
@exampleexpect(mockFunc).toHaveBeenCalledOnce();
();
} finally { ftconst ft: FakeScheduler.uninstallFakeScheduler.uninstall(): void
Restore the real timer functions. Safe to call multiple times.
();
} });
import { createFakeSchedulerfunction createFakeScheduler(): FakeScheduler } from '@triggery/testing';

const ftconst ft: FakeScheduler = createFakeSchedulerfunction createFakeScheduler(): FakeScheduler();
ftconst ft: FakeScheduler.installFakeScheduler.install(): void
Replace globalThis.setTimeout / clearTimeout with the fake controller.
();
setTimeoutfunction setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+1 overload)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout)
(() => {}, 250);
consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
(ftconst ft: FakeScheduler.nowFakeScheduler.now(): number
Current virtual clock value, in ms since install.
()); // 0
consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
(ftconst ft: FakeScheduler.pendingFakeScheduler.pending(): number
Number of timers still pending.
()); // 1
await ftconst ft: FakeScheduler.advanceFakeScheduler.advance(ms: number): Promise<void>
Advance the virtual clock by `ms` and run every timer that becomes due in that window. Returns a promise that resolves after pending microtasks are drained, so callers can `await ft.advance(N)` and then assert.
(250);
consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
(ftconst ft: FakeScheduler.nowFakeScheduler.now(): number
Current virtual clock value, in ms since install.
()); // 250
consolevar console: Console.logConsole.log(...data: any[]): void
The **`console.log()`** static method outputs a message to the console. [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
(ftconst ft: FakeScheduler.pendingFakeScheduler.pending(): number
Number of timers still pending.
()); // 0
ftconst ft: FakeScheduler.uninstallFakeScheduler.uninstall(): void
Restore the real timer functions. Safe to call multiple times.
();