Files
planet/frontend/public/earth/js/cruise-sequencer.test.js
2026-05-10 22:06:01 +08:00

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);
});
});