import { describe, expect, it, vi } from "vitest"; import { type DisplayConnectivity, type DisplayConnectivitySource, DISPLAY_SERVICE_WORKER_URL, registerDisplayServiceWorker, watchDisplayConnectivity, } from "../src/display/offline-status.js"; /** * The display's connectivity signal (ADR 0043): what it reports, and what it deliberately refuses to * decide. */ interface FakeSource extends DisplayConnectivitySource { emitOnline(): void; emitOffline(): void; emitWorkerMessage(data: unknown): void; readonly attached: () => number; } function fakeSource(initialOnline = true): FakeSource { const windowListeners = new Map void>>(); const workerListeners = new Set<(event: MessageEvent) => void>(); return { addEventListener(type, listener) { const existing = windowListeners.get(type) ?? new Set(); existing.add(listener); windowListeners.set(type, existing); }, attached: () => [...windowListeners.values()].reduce((total, set) => total + set.size, 0) + workerListeners.size, emitOffline() { for (const listener of windowListeners.get("offline") ?? []) { listener(); } }, emitOnline() { for (const listener of windowListeners.get("online") ?? []) { listener(); } }, emitWorkerMessage(data: unknown) { for (const listener of workerListeners) { listener({ data } as MessageEvent); } }, initialOnline, removeEventListener(type, listener) { windowListeners.get(type)?.delete(listener); }, serviceWorkerMessages: { addEventListener(_type, listener) { workerListeners.add(listener); }, removeEventListener(_type, listener) { workerListeners.delete(listener); }, }, }; } describe("display connectivity monitor", () => { it("starts from what the browser believes and reports nothing else", () => { const monitor = watchDisplayConnectivity(fakeSource(false)); expect(monitor.current()).toEqual({ online: false, usingFallback: false }); monitor.close(); }); it("reports the browser's online and offline transitions", () => { const source = fakeSource(true); const monitor = watchDisplayConnectivity(source); const seen: DisplayConnectivity[] = []; monitor.subscribe((next) => seen.push(next)); source.emitOffline(); source.emitOnline(); expect(seen).toEqual([ { online: false, usingFallback: false }, { online: true, usingFallback: false }, ]); monitor.close(); }); it("reports a cache fallback the worker actually performed, with its kind", () => { const source = fakeSource(true); const monitor = watchDisplayConnectivity(source); const seen: DisplayConnectivity[] = []; monitor.subscribe((next) => seen.push(next)); // The browser still claims to be online — a captive portal, or a dead uplink behind a live // Wi-Fi association. Only the worker knows this application actually fell back. source.emitWorkerMessage({ ekklesiaDisplayOffline: { kind: "artifact", usingFallback: true } }); expect(seen).toEqual([{ fallbackKind: "artifact", online: true, usingFallback: true }]); monitor.close(); }); it("clears the fallback flag once the worker serves from the network again", () => { const source = fakeSource(true); const monitor = watchDisplayConnectivity(source); source.emitWorkerMessage({ ekklesiaDisplayOffline: { kind: "shell", usingFallback: true } }); source.emitWorkerMessage({ ekklesiaDisplayOffline: { usingFallback: false } }); expect(monitor.current()).toEqual({ online: true, usingFallback: false }); monitor.close(); }); it("ignores a message that is not the worker's own status", () => { const source = fakeSource(true); const monitor = watchDisplayConnectivity(source); const listener = vi.fn(); monitor.subscribe(listener); for (const data of [null, "offline", { other: true }, { ekklesiaDisplayOffline: "yes" }, 7]) { source.emitWorkerMessage(data); } expect(listener).not.toHaveBeenCalled(); monitor.close(); }); it("publishes only genuine changes, so an unattended screen is not re-rendered on every event", () => { const source = fakeSource(true); const monitor = watchDisplayConnectivity(source); const listener = vi.fn(); monitor.subscribe(listener); source.emitOnline(); source.emitOnline(); source.emitOffline(); source.emitOffline(); expect(listener).toHaveBeenCalledTimes(1); monitor.close(); }); it("detaches everything on close, idempotently", () => { const source = fakeSource(true); const monitor = watchDisplayConnectivity(source); monitor.subscribe(() => undefined); expect(source.attached()).toBeGreaterThan(0); monitor.close(); monitor.close(); expect(source.attached()).toBe(0); }); it("works with no sources at all, reporting a display that simply never knows", () => { const monitor = watchDisplayConnectivity(); expect(monitor.current()).toEqual({ online: true, usingFallback: false }); monitor.close(); }); }); describe("registering the display service worker", () => { it("registers the one fixed platform path as a module worker", async () => { const register = vi.fn().mockResolvedValue({}); const registered = await registerDisplayServiceWorker({ register, } as unknown as ServiceWorkerContainer); expect(registered).toBe(true); expect(register).toHaveBeenCalledWith(DISPLAY_SERVICE_WORKER_URL, { type: "module" }); // The path's *directory* is the scope: `/display/`, so the worker controls display pages and // nothing else. expect(DISPLAY_SERVICE_WORKER_URL).toBe("/display/sw.js"); }); it("never lets a missing or refused registration break the display", async () => { // Offline resilience is an enhancement. A browser without service workers, a page served over // plain HTTP in development, and a policy that refuses all leave a display that works exactly as // it did before — online only. expect(await registerDisplayServiceWorker(undefined)).toBe(false); expect( await registerDisplayServiceWorker({ register: vi.fn().mockRejectedValue(new Error("SecurityError")), } as unknown as ServiceWorkerContainer), ).toBe(false); }); });