102 lines
3.1 KiB
JavaScript
102 lines
3.1 KiB
JavaScript
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);
|
|
});
|
|
});
|