import { describe, expect, test } from "bun:test"; import { CruiseSequencer } from "./cruise-sequencer.js"; function installWindow() { globalThis.window = { requestAnimationFrame: (callback) => setTimeout(callback, 0), setTimeout, clearTimeout, }; } function wait(ms = 8) { return new Promise((resolve) => setTimeout(resolve, ms)); } function createSequencer(options = {}) { installWindow(); const calls = []; let active = true; let items = options.items || [{ id: "one" }, { id: "two" }]; const sequencer = new CruiseSequencer({ isActive: () => active, getItems: () => items, getItemId: (item) => item.id, dwellMs: options.dwellMs ?? 1, transitionGapMs: options.transitionGapMs ?? 1, presentationMode: options.presentationMode, clearCurrent: () => calls.push("clear"), focusItem: async (item) => calls.push(`focus:${item.id}`), presentItem: async (item) => { calls.push(`present:${item.id}`); return true; }, hideItem: async (item) => { calls.push(`hide:${item.id}`); if (options.stopAfterHide) active = false; }, }); return { calls, items, sequencer, setItems: (nextItems) => { items = nextItems; }, }; } describe("CruiseSequencer presentation modes", () => { test("auto_advance keeps existing dwell, hide, and advance behavior", async () => { const { calls, sequencer } = createSequencer({ stopAfterHide: true }); await sequencer.advance(); await wait(); expect(calls).toContain("focus:one"); expect(calls).toContain("present:one"); expect(calls).toContain("hide:one"); }); test("pinned mode presents without auto hiding or advancing", async () => { const { calls, sequencer } = createSequencer({ presentationMode: "pinned" }); await sequencer.advance(); await wait(); expect(calls).toEqual(["clear", "focus:one", "present:one"]); expect(sequencer.isPresentationPinned()).toBe(true); }); test("presentSpecificItem directly presents the requested item", async () => { const { calls, items, sequencer } = createSequencer({ presentationMode: "pinned" }); const presented = await sequencer.presentSpecificItem(items[1], { interrupt: true }); expect(presented).toBe(true); expect(calls).toEqual(["clear", "focus:two", "present:two"]); expect(sequencer.getCurrentItemId()).toBe("two"); }); test("pinned mode keeps the presented item even when the live queue no longer contains it", async () => { const { items, sequencer, setItems } = createSequencer({ presentationMode: "pinned" }); await sequencer.presentSpecificItem(items[1], { interrupt: true }); setItems([]); expect(sequencer.getCurrentItem()).toEqual({ id: "two" }); }); test("stop can preserve or clear a pinned presentation", async () => { const { sequencer } = createSequencer({ presentationMode: "pinned" }); await sequencer.advance(); sequencer.stop({ preservePresentation: true }); expect(sequencer.isPresentationPinned()).toBe(true); sequencer.stop(); expect(sequencer.isPresentationPinned()).toBe(false); }); });