import { afterEach, expect, test } from "bun:test"; import { holdDecision, reducedMotion, viewTransition } from "../src/motion"; const g = globalThis as Record; afterEach(() => { delete g.document; delete g.window; }); function env({ reduce = false, vt = true, hidden = false } = {}) { const calls: string[] = []; g.window = { matchMedia: (q: string) => ({ matches: q.includes("prefers-reduced-motion: reduce") && reduce }) }; g.document = { visibilityState: hidden ? "hidden" : "visible", ...(vt ? { startViewTransition: (cb: () => void) => { calls.push("vt"); cb(); return { finished: Promise.resolve() }; } } : {}), }; return calls; } test("reducedMotion follows the media query and is false without a window", () => { expect(reducedMotion()).toBe(false); env({ reduce: true }); expect(reducedMotion()).toBe(true); env({ reduce: false }); expect(reducedMotion()).toBe(false); }); test("viewTransition runs the update inside startViewTransition when it can", () => { const calls = env(); viewTransition(() => calls.push("update")); expect(calls).toEqual(["vt", "update"]); }); test("viewTransition runs the update directly with reduced motion, without support, or in a hidden tab", () => { for (const o of [{ reduce: true }, { vt: false }, { hidden: true }]) { const calls = env(o); viewTransition(() => calls.push("update")); expect([o, calls]).toEqual([o, ["update"]]); } delete g.document; delete g.window; const plain: string[] = []; viewTransition(() => plain.push("update")); expect(plain).toEqual(["update"]); }); test("holdDecision: show the first value, hold while loading, swap to a new key, replace the same key", () => { type L = { id: string; course: string }; const key = (l: L) => l.id; const sameCourse = (c: string) => (l: L) => l.course === c; const a = { id: "a", course: "x" }, b = { id: "b", course: "x" }, a2 = { id: "a", course: "x" }; expect(holdDecision(null, null, key)).toBe("show-next"); expect(holdDecision(null, a, key)).toBe("show-next"); expect(holdDecision(a, a, key)).toBe("show-next"); expect(holdDecision(a, null, key, sameCourse("x"))).toBe("hold"); expect(holdDecision(a, null, key, sameCourse("y"))).toBe("replace"); expect(holdDecision(a, b, key)).toBe("swap"); expect(holdDecision(a, a2, key)).toBe("replace"); expect(holdDecision(a, b, key, sameCourse("y"))).toBe("replace"); });