import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { sagas, songsInSaga } from "../src/data/album"; import { friezeSvg } from "../src/app/frieze/sagas"; const escapeXml = (s: string) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]!); describe("saga friezes", () => { test.each(sagas.map((s) => [s.id, s] as const))("%s: names the saga and inscribes every song", (_id, saga) => { const svg = friezeSvg(saga.id); expect(svg.startsWith(" { const svg = friezeSvg("wisdom"); expect(svg).toContain("We'll Be Fine"); expect(svg).not.toContain("We'll Be Fine"); }); test("every saga has its own frieze", () => { const all = sagas.map((s) => friezeSvg(s.id)); expect(new Set(all).size).toBe(9); }); test("landing sagas have a doorway to go ashore through; the others do not", () => { for (const s of sagas) { const has = friezeSvg(s.id).includes('class="door-fill '); expect(has).toBe(s.id === "cyclops" || s.id === "wisdom" || s.id === "ithaca"); } }); }); describe("the curtain (happy-dom, reduced motion: 200ms crossfades)", () => { const { GlobalRegistrator } = require("@happy-dom/global-registrator") as typeof import("@happy-dom/global-registrator"); let createCurtain: typeof import("../src/app/frieze/curtain").createCurtain; let createWorld: typeof import("../src/app/world/world").createWorld; beforeAll(async () => { GlobalRegistrator.register(); window.matchMedia = ((q: string) => ({ matches: q.includes("reduce"), media: q, addEventListener() {}, removeEventListener() {} })) as unknown as typeof window.matchMedia; ({ createCurtain } = await import("../src/app/frieze/curtain")); ({ createWorld } = await import("../src/app/world/world")); }); afterAll(() => GlobalRegistrator.unregister()); test("drop shows the saga's frieze; lift clears it", async () => { const layer = document.createElement("div"); document.body.append(layer); const c = createCurtain(layer); await c.drop("cyclops", { landing: "cave" }); const host = layer.querySelector(".fz-host")!; expect(host.hidden).toBe(false); expect(host.innerHTML).toContain("The Cyclops Saga"); await c.lift(); expect(host.hidden).toBe(true); expect(Number.isFinite(c.groundY())).toBe(true); }); test("rapid calls never leave the curtain stuck: the last request wins", async () => { const layer = document.createElement("div"); const c = createCurtain(layer); const all = [c.drop("troy"), c.fade(), c.drop("ocean"), c.drop("circe")]; const last = c.lift(); await Promise.all([...all, last]); expect(layer.querySelector(".fz-host")!.hidden).toBe(true); expect(layer.querySelector(".fz-cover")!.hidden).toBe(true); }); test("fade covers the stage in the curtain colour; lift uncovers it", async () => { const layer = document.createElement("div"); const c = createCurtain(layer); await c.fade(); const cover = layer.querySelector(".fz-cover")!; expect(cover.hidden).toBe(false); await c.lift(); expect(cover.hidden).toBe(true); }); test("createWorld returns null without WebGL", () => { const canvas = document.createElement("canvas"); canvas.getContext = (() => null) as typeof canvas.getContext; expect(createWorld(canvas)).toBeNull(); }); });