import { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { DisplaySetupApp } from "../src/display/DisplaySetupApp.js"; declare global { // eslint-disable-next-line no-var var IS_REACT_ACT_ENVIRONMENT: boolean | undefined; } /** * The platform's own display setup page, `/display/setup` (ADR 0065): a screen pairs here with no * module installed at all, then opens whatever module it is set to show — or waits, named, if * nothing is set yet. */ const apiBaseUrl = "https://ekklesia.example.test"; let container: HTMLDivElement; let root: Root; beforeEach(() => { globalThis.IS_REACT_ACT_ENVIRONMENT = true; vi.useFakeTimers(); container = document.createElement("div"); document.body.append(container); root = createRoot(container); }); afterEach(async () => { await act(async () => root.unmount()); container.remove(); vi.useRealTimers(); }); function json(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { headers: { "content-type": "application/json" }, status, }); } interface Self { readonly name: string; readonly displays: readonly { moduleId: string; url: string }[]; } /** * A fake API. `self` is what `/api/v1/device-pairing/self` answers right now: `undefined` for a * screen that is not paired (403), or the device as it sees itself. */ function api(initial: { self?: Self | "error" } = {}) { const state: { self: Self | "error" | undefined; claim: "pending" | "paired" } = { claim: "pending", self: initial.self, }; const requests: { path: string; body: unknown }[] = []; const fetch = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { const path = new URL(String(input)).pathname; const body = init?.body === undefined ? undefined : JSON.parse(String(init.body)); requests.push({ body, path }); if (path === "/api/v1/device-pairing/self") { if (state.self === "error") { return json({ code: "DEVICE_LOOKUP_UNAVAILABLE" }, 503); } return state.self === undefined ? json({ code: "AUTHORIZATION_DENIED" }, 403) : json({ displays: state.self.displays, name: state.self.name }); } if (path === "/api/v1/device-pairing/requests") { return json( { code: "482913", expiresAt: "2026-10-16T12:10:00.000Z", pollIntervalMs: 1000 }, 201, ); } if (path === "/api/v1/device-pairing/claims") { return json({ status: state.claim }); } return json({ code: "NOT_FOUND" }, 404); }) as unknown as typeof globalThis.fetch; return { fetch, requests, state }; } function query(testId: string): HTMLElement | null { return container.querySelector(`[data-testid="${testId}"]`); } async function flush(ms = 0): Promise { await act(async () => { await vi.advanceTimersByTimeAsync(ms); }); } async function renderSetup(server: ReturnType) { const openDisplay = vi.fn(); await act(async () => { root.render( , ); }); await flush(); return openDisplay; } describe("the platform display setup page", () => { it("pairs a screen with no module at all, then shows it is paired and waiting", async () => { const server = api(); const openDisplay = await renderSetup(server); // Not paired: it asks to be paired as a platform display — no module — and shows the code. expect(query("display-pairing-code")?.textContent).toBe("482913"); expect(server.requests.find((r) => r.path === "/api/v1/device-pairing/requests")?.body).toEqual( {}, ); expect(query("display-pairing")?.textContent).toContain("Settings → Displays → Add display"); // An administrator approves it, with nothing for it to show yet. server.state.claim = "paired"; server.state.self = { displays: [], name: "Foyer TV" }; await flush(1000); await flush(); expect(query("display-setup-waiting-headline")?.textContent).toBe("Foyer TV"); expect(query("display-setup-waiting")?.textContent).toContain("This screen is paired"); expect(openDisplay).not.toHaveBeenCalled(); }); it("opens the module display a paired screen is set to show", async () => { const server = api({ self: { displays: [{ moduleId: "signage", url: "/display/signage" }], name: "Hall" }, }); const openDisplay = await renderSetup(server); expect(openDisplay).toHaveBeenCalledWith("/display/signage"); expect(server.requests.some((r) => r.path === "/api/v1/device-pairing/requests")).toBe(false); }); it("keeps asking while it waits, and opens the module as soon as one is set", async () => { const server = api({ self: { displays: [], name: "Foyer TV" } }); const openDisplay = await renderSetup(server); expect(query("display-setup-waiting")).not.toBeNull(); server.state.self = { displays: [{ moduleId: "signage", url: "/display/signage" }], name: "Foyer TV", }; await flush(30_000); expect(openDisplay).toHaveBeenCalledWith("/display/signage"); }); it("returns to a pairing code when a waiting screen is revoked", async () => { const server = api({ self: { displays: [], name: "Foyer TV" } }); await renderSetup(server); server.state.self = undefined; await flush(30_000); await flush(); expect(query("display-pairing-code")?.textContent).toBe("482913"); }); it("never starts pairing on a server failure, and tries again", async () => { const server = api({ self: "error" }); await renderSetup(server); expect(query("display-setup-unreachable")).not.toBeNull(); expect(server.requests.some((r) => r.path === "/api/v1/device-pairing/requests")).toBe(false); server.state.self = { displays: [], name: "Foyer TV" }; await flush(5_000); expect(query("display-setup-waiting")).not.toBeNull(); }); });