Reference
yieldless/test
Small async test helpers for deferred promises, manual clocks, and abort signals.
yieldless/test contains tiny helpers for testing async code that uses promises and AbortSignal. They are deliberately independent from Vitest, Jest, or Node's test runner.
Use these helpers to make async tests deterministic without changing application code or installing a fake runtime.
Exports
deferred<T>(): Deferred<T>flushMicrotasks(times): Promise<void>createTestSignal(): TestSignalcreateManualClock(start): ManualClocktype Deferred<T> = { promise, resolve, reject }type ManualClock = { now, pending, sleep, tick, runAll }type TestSignal = { controller, signal, abort }
Example
import { createManualClock, deferred, flushMicrotasks } from "yieldless/test";
const ready = deferred<string>();
ready.resolve("done");
await expect(ready.promise).resolves.toBe("done");
const clock = createManualClock();
let settled = false;
void clock.sleep(100).then(() => {
settled = true;
});
clock.tick(100);
await flushMicrotasks();
expect(settled).toBe(true);Behavior notes
deferred()exposes a promise plus itsresolveandrejectfunctions.flushMicrotasks()awaitsPromise.resolve()one or more times.createTestSignal()returns a controller, signal, and convenienceabort()method.createManualClock().sleep()is abort-aware and resolves only whentick()orrunAll()reaches its time.createManualClock()does not patch global timers.
Good
Use manual clocks for code that already accepts a sleep function or clock dependency.
const clock = createManualClock();
const running = waitForReady({ sleep: clock.sleep });
clock.runAll();
await running;Use controlled signals when testing cleanup.
const testSignal = createTestSignal();
testSignal.abort(new Error("stop"));Avoid
Do not mix the manual clock with real setTimeout() and expect it to control global time.
const clock = createManualClock();
setTimeout(resolve, 100);
clock.tick(100);Use it by dependency injection, not global monkey-patching.