FakeTime
import { FakeTime } from "jsr:@ende/testing/time";class
class FakeTimeOverrides the real Date object and timer functions with fake ones that can be controlled through the fake time instance.
Note: there is no setter for the start property, as it cannot be changed
after initialization.
Example: Usage
import {
assertSpyCalls,
spy,
} from "@ende/testing/mock";
import { FakeTime } from "@ende/testing/time";
function secondInterval(cb: () => void) {
return setInterval(cb, 1000);
}
Ende.test("secondInterval calls callback every second and stops after being cleared", () => {
using time = new FakeTime();
const cb = spy();
const intervalId = secondInterval(cb);
assertSpyCalls(cb, 0);
time.tick(500);
assertSpyCalls(cb, 0);
time.tick(500);
assertSpyCalls(cb, 1);
time.tick(3500);
assertSpyCalls(cb, 4);
clearInterval(intervalId);
time.tick(1000);
assertSpyCalls(cb, 4);
});
Constructors
constructor
new FakeTime(start?: number | string | Date | null, options?: FakeTimeOptions)Construct a FakeTime object. This overrides the real Date object and timer functions with fake ones that can be controlled through the fake time instance.
start?: number | string | Date | nullThe time to simulate. The default is the current time.
options?: FakeTimeOptionsThe options
Static methods
restore
static restore(): voidRestores real time.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals, assertNotEquals } from "@ende/assert"
const setTimeout = globalThis.setTimeout;
const fakeTime = new FakeTime();
assertNotEquals(globalThis.setTimeout, setTimeout);
FakeTime.restore();
assertEquals(globalThis.setTimeout, setTimeout);
restoreFor
static restoreFor<T>(callback: (...args: any[]) => Promise<T> | T, ...args: any[]): Promise<T>Restores real time temporarily until callback returns and resolves.
callback: (...args: any[]) => Promise<T> | TThe callback to be called while FakeTime being restored
...args: any[]The arguments to pass to the callback
Returns
The returned value from the callback
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals, assertNotEquals } from "@ende/assert"
const setTimeout = globalThis.setTimeout;
const fakeTime = new FakeTime();
assertNotEquals(globalThis.setTimeout, setTimeout);
FakeTime.restoreFor(() => {
assertEquals(globalThis.setTimeout, setTimeout);
});
Methods
[Symbol.dispose]
[Symbol.dispose](): voidRestores real time.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals, assertNotEquals } from "@ende/assert";
const setTimeout = globalThis.setTimeout;
{
using fakeTime = new FakeTime();
assertNotEquals(globalThis.setTimeout, setTimeout);
// test timer related things.
// You don't need to call fakeTime.restore() explicitly
// as it's implicitly called via the [Symbol.dispose] method
// when declared with `using`.
}
assertEquals(globalThis.setTimeout, setTimeout);
now
get now(): numberThe number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time.
Returns
The number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
assertEquals(fakeTime.now, 15_000);
fakeTime.tick(5_000);
assertEquals(fakeTime.now, 20_000);
set now(value: number)Set the current time. It will call any functions waiting to be called between the current and new fake time. If the timer callback throws, time will stop advancing forward beyond that timer.
value: numberThe current time (in milliseconds)
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
assertEquals(fakeTime.now, 15_000);
fakeTime.now = 35_000;
assertEquals(fakeTime.now, 35_000);
start
get start(): numberThe initial number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time.
Returns
The initial number of milliseconds elapsed since the epoch (January 1, 1970 00:00:00 UTC) for the fake time.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
assertEquals(fakeTime.start, 15_000);
delay
delay(ms: number, options: DelayOptions = [UNSUPPORTED]): Promise<void>Resolves after the given number of milliseconds using real time.
ms: numberThe milliseconds to delay
options: DelayOptions = [UNSUPPORTED]The options
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
await fakeTime.delay(500); // wait 500 ms in real time.
assertEquals(fakeTime.now, 15_000); // The simulated time doesn't advance.
runMicrotasks
runMicrotasks(): Promise<void>Runs all pending microtasks.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assert } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
let called = false;
Promise.resolve().then(() => { called = true });
await fakeTime.runMicrotasks();
assert(called);
tick
tick(ms: number = 0): voidAdds the specified number of milliseconds to the fake time. This will call any functions waiting to be called between the current and new fake time.
ms: number = 0The milliseconds to advance
Example: Usage
import {
assertSpyCalls,
spy,
} from "@ende/testing/mock";
import { FakeTime } from "@ende/testing/time";
function secondInterval(cb: () => void) {
return setInterval(cb, 1000);
}
Ende.test("secondInterval calls callback every second and stops after being cleared", () => {
using time = new FakeTime();
const cb = spy();
const intervalId = secondInterval(cb);
assertSpyCalls(cb, 0);
time.tick(500);
assertSpyCalls(cb, 0);
time.tick(500);
assertSpyCalls(cb, 1);
time.tick(3500);
assertSpyCalls(cb, 4);
clearInterval(intervalId);
time.tick(1000);
assertSpyCalls(cb, 4);
});
tickAsync
tickAsync(ms: number = 0): Promise<void>Runs all pending microtasks then adds the specified number of milliseconds to the fake time. This will call any functions waiting to be called between the current and new fake time.
ms: number = 0The milliseconds to advance
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assert, assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
let called = false;
Promise.resolve().then(() => { called = true });
await fakeTime.tickAsync(5_000);
assert(called);
assertEquals(fakeTime.now, 20_000);
next
next(): booleanAdvances time to when the next scheduled timer is due. If there are no pending timers, time will not be changed.
Returns
true when there is a scheduled timer and false when there is not.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assert, assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
let called = false;
setTimeout(() => { called = true }, 5000);
fakeTime.next();
assert(called);
assertEquals(fakeTime.now, 20_000);
nextAsync
nextAsync(): Promise<boolean>Runs all pending microtasks then advances time to when the next scheduled timer is due. If there are no pending timers, time will not be changed.
Returns
true if the pending timers existed and the time advanced, false if there was no pending timer and the time didn't advance.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assert, assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
let called0 = false;
let called1 = false;
setTimeout(() => { called0 = true }, 5000);
Promise.resolve().then(() => { called1 = true });
await fakeTime.nextAsync();
assert(called0);
assert(called1);
assertEquals(fakeTime.now, 20_000);
runAll
runAll(): voidAdvances time forward to the next due timer until there are no pending timers remaining. If the timers create additional timers, they will be run too. If there is an interval, time will keep advancing forward until the interval is cleared.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
let count = 0;
setTimeout(() => { count++ }, 5_000);
setTimeout(() => { count++ }, 15_000);
setTimeout(() => { count++ }, 35_000);
fakeTime.runAll();
assertEquals(count, 3);
assertEquals(fakeTime.now, 50_000);
runAllAsync
runAllAsync(): Promise<void>Advances time forward to the next due timer until there are no pending timers remaining. If the timers create additional timers, they will be run too. If there is an interval, time will keep advancing forward until the interval is cleared. Runs all pending microtasks before each timer.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals } from "@ende/assert";
const fakeTime = new FakeTime(15_000);
let count = 0;
setTimeout(() => { count++ }, 5_000);
setTimeout(() => { count++ }, 15_000);
setTimeout(() => { count++ }, 35_000);
Promise.resolve().then(() => { count++ });
await fakeTime.runAllAsync();
assertEquals(count, 4);
assertEquals(fakeTime.now, 50_000);
restore
restore(): voidRestores time related global functions to their original state.
Example: Usage
import { FakeTime } from "@ende/testing/time";
import { assertEquals, assertNotEquals } from "@ende/assert";
const setTimeout = globalThis.setTimeout;
const fakeTime = new FakeTime(); // global timers are now faked
assertNotEquals(globalThis.setTimeout, setTimeout);
fakeTime.restore(); // timers are restored
assertEquals(globalThis.setTimeout, setTimeout);