import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { afterEach, describe, expect, it, vi } from "vitest"; import { MANASTORM_DEFAULT_PORTAL_READY_CUE_ID, MANASTORM_PORTAL_AUDIO_ROOT, MANASTORM_PORTAL_AUDIO_CUES, manastormCueForStatusTransition, manastormPortalAudioCue, playManastormClientCue, stopManastormClientCue, type ManastormAudioHandle, } from "./manastormClientCues"; import { useManastormStore } from "./manastormStore"; const projectDirectory = fileURLToPath(new URL("../..", import.meta.url)); describe("Manastorm imported client cues", () => { afterEach(() => { stopManastormClientCue(); useManastormStore.getState().resetRun(); }); it("catalogs every locally verified portal cue without inventing timing", () => { expect(MANASTORM_PORTAL_AUDIO_CUES).toHaveLength(5); expect(new Set(MANASTORM_PORTAL_AUDIO_CUES.map((cue) => cue.id)).size).toBe(5); expect(MANASTORM_PORTAL_AUDIO_CUES.filter((cue) => cue.signal === "portal-ready")) .toHaveLength(2); expect(MANASTORM_PORTAL_AUDIO_CUES.filter((cue) => cue.signal === "portal-coming-up")) .toHaveLength(3); for (const cue of MANASTORM_PORTAL_AUDIO_CUES) { expect(cue.fileName).toMatch(/^Manastorm_BossKill_.+\.wav$/); expect(cue.url).toBe(`${MANASTORM_PORTAL_AUDIO_ROOT}/${cue.fileName}`); const audio = readFileSync(path.join(projectDirectory, "public", cue.url.slice(1))); expect(audio.subarray(0, 4).toString("ascii")).toBe("RIFF"); expect(audio.subarray(8, 12).toString("ascii")).toBe("WAVE"); } }); it("keeps optional portal audio out of Vite source imports", () => { const source = readFileSync( new URL("./manastormClientCues.ts", import.meta.url), "utf8", ); expect(source).not.toContain("LadiksMPQEditor"); expect(source).not.toMatch(/new URL\s*\(/); }); it("maps only an actual portal-ready state edge to the automatic cue", () => { expect(manastormCueForStatusTransition("awaiting-loot", "portal-ready")) .toBe(MANASTORM_DEFAULT_PORTAL_READY_CUE_ID); expect(manastormCueForStatusTransition("portal-ready", "portal-ready")).toBeNull(); expect(manastormCueForStatusTransition("fighting", "awaiting-loot")).toBeNull(); expect(manastormCueForStatusTransition("transitioning", "entering")).toBeNull(); }); it("configures and plays a requested local cue through an injectable audio boundary", async () => { const play = vi.fn(async () => undefined); const pause = vi.fn(); const audio: ManastormAudioHandle = { currentTime: 12, preload: "", volume: 1, pause, play, }; const factory = vi.fn(() => audio); await expect(playManastormClientCue("generic-portals-ready", factory)) .resolves.toBe(true); expect(factory).toHaveBeenCalledWith( manastormPortalAudioCue("generic-portals-ready").url, ); expect(audio).toMatchObject({ currentTime: 0, preload: "auto", volume: 0.72 }); expect(play).toHaveBeenCalledOnce(); }); it("queues explicit cues with monotonic revisions for external server data", () => { const state = useManastormStore.getState(); expect(state.requestClientCue("single-portal-coming-up")).toBe(true); const first = useManastormStore.getState().clientCue; expect(first).toMatchObject({ cueId: "single-portal-coming-up" }); expect(useManastormStore.getState().requestClientCue("generic-portals-ready-alt")) .toBe(true); const second = useManastormStore.getState().clientCue; expect(second?.revision).toBe((first?.revision ?? 0) + 1); useManastormStore.getState().clearClientCue(); expect(useManastormStore.getState().clientCue).toBeNull(); }); it("queues the verified ready cue when the existing run state opens its portal", () => { const state = useManastormStore.getState(); state.startRun(() => 0); expect(useManastormStore.getState().enterStage()).toBe(true); useManastormStore.getState().observeTrash(true); useManastormStore.getState().observeBoss(true, true); expect(useManastormStore.getState()).toMatchObject({ status: "portal-ready", clientCue: { cueId: MANASTORM_DEFAULT_PORTAL_READY_CUE_ID, }, }); }); });