Files
Jyotisha/frontend/tests/home-bootstrap-fake-timers.ts
T
jesse-ux da35b3e4af refactor(home): split bootstrap, recovery polling, and shells
Move the home startup sequence and consultation recovery poll into
plain functions, and the error and onboarding screens into components.
Landing rules, the 20s fatal screen, retry, and the chart settings
deep link stay the same.
2026-09-25 02:59:11 +08:00

47 lines
1.6 KiB
TypeScript

export function installWindow() {
const host = globalThis as { window?: unknown };
if (host.window === undefined) host.window = globalThis;
}
export async function withFakeTimers(run: (tick: (ms: number) => void) => Promise<void>): Promise<void> {
const originalSet = globalThis.setTimeout;
const originalClear = globalThis.clearTimeout;
const host = globalThis as { window?: unknown };
const originalWindow = host.window;
host.window = globalThis;
let now = 0;
let sequence = 1;
const queue = new Map<number, { at: number; fn: () => void }>();
globalThis.setTimeout = ((fn: TimerHandler, ms?: number) => {
const id = sequence;
sequence += 1;
queue.set(id, {
at: now + (typeof ms === "number" ? ms : 0),
fn: () => { if (typeof fn === "function") fn(); },
});
return id as unknown as ReturnType<typeof setTimeout>;
}) as unknown as typeof setTimeout;
globalThis.clearTimeout = ((id?: ReturnType<typeof setTimeout>) => {
if (typeof id === "number") queue.delete(id);
}) as unknown as typeof clearTimeout;
const tick = (ms: number) => {
now += ms;
const due = [...queue.entries()]
.filter((entry) => entry[1].at <= now)
.sort((left, right) => left[1].at - right[1].at);
for (const [id, item] of due) {
if (!queue.has(id)) continue;
queue.delete(id);
item.fn();
}
};
try {
await run(tick);
} finally {
globalThis.setTimeout = originalSet;
globalThis.clearTimeout = originalClear;
if (originalWindow === undefined) delete host.window;
else host.window = originalWindow;
}
}