import { describe, expect, it } from "vitest"; import { createPreviewSchedule, PREVIEW_MIN_GAP_MS } from "./previewSchedule.js"; /** Hand-driven clock + timer queue, so the schedule is asserted without waiting. */ function harness() { let now = 1_000; const timers = new Map void }>(); let nextHandle = 1; const emitted: (string | null)[] = []; const schedule = createPreviewSchedule((id) => emitted.push(id), { now: () => now, setTimer: (fn, ms) => { const h = nextHandle++; timers.set(h, { at: now + ms, fn }); return h; }, clearTimer: (h) => { timers.delete(h); }, }); /** Advance the clock, firing any timer that comes due. */ const advance = (ms: number) => { now += ms; for (const [h, t] of [...timers]) { if (t.at <= now) { timers.delete(h); t.fn(); } } }; return { schedule, emitted, advance }; } describe("preview schedule", () => { it("shows the first preview immediately", () => { const { schedule, emitted } = harness(); schedule("s1"); expect(emitted).toEqual(["s1"]); }); it("coalesces a burst into one trailing preview", () => { const { schedule, emitted, advance } = harness(); // A pointer sweep across the list, or j held down: every row in between // is walked well inside one gap. schedule("s1"); schedule("s2"); schedule("s3"); expect(emitted).toEqual(["s1"]); advance(PREVIEW_MIN_GAP_MS); expect(emitted).toEqual(["s1", "s3"]); }); it("shows the next preview immediately once the gap has passed", () => { const { schedule, emitted, advance } = harness(); schedule("s1"); advance(PREVIEW_MIN_GAP_MS); schedule("s2"); expect(emitted).toEqual(["s1", "s2"]); }); it("drops a superseded trailing preview rather than replaying it", () => { const { schedule, emitted, advance } = harness(); schedule("s1"); schedule("s2"); schedule("s1"); // back where we started inside the same gap advance(PREVIEW_MIN_GAP_MS); // s1 is already on screen, so the trailing edge has nothing to do. expect(emitted).toEqual(["s1"]); }); it("clears through the same gap, so leaving and re-entering does not blink", () => { const { schedule, emitted, advance } = harness(); schedule("s1"); schedule(null); // pointer crosses the gap between two rows schedule("s2"); advance(PREVIEW_MIN_GAP_MS); expect(emitted).toEqual(["s1", "s2"]); }); it("cancel drops a pending trailing preview", () => { const { schedule, emitted, advance } = harness(); schedule("s1"); schedule("s2"); schedule.cancel(); advance(PREVIEW_MIN_GAP_MS); expect(emitted).toEqual(["s1"]); }); });