import { expect, test } from "@playwright/test"; // P6 exit (§16 NFR): installable PWA — manifest, icons, and a live // service worker that never touches /api. test("the app is installable: manifest, icons, service worker", async ({ page, }) => { await page.goto("/login"); // Manifest is linked and serves the §2 identity. const href = await page .locator('link[rel="manifest"]') .getAttribute("href"); expect(href).toBeTruthy(); const manifest = await (await page.request.get(href!)).json(); expect(manifest.name).toBe("Clawmates"); expect(manifest.display).toBe("standalone"); expect(manifest.theme_color).toBe("#f96565"); // Icons are real PNGs. for (const icon of manifest.icons) { const res = await page.request.get(icon.src); expect(res.status()).toBe(200); expect((await res.body()).subarray(0, 4).toString("latin1")).toContain( "PNG", ); } // The service worker registers and activates (prod build in e2e). const state = await page.evaluate(async () => { const registration = await navigator.serviceWorker.ready; return registration.active?.state; }); expect(state).toBe("activated"); // The SW intercepts static assets but NEVER /api (live SSE). const swText = await (await page.request.get("/sw.js")).text(); expect(swText).toContain('url.pathname.startsWith("/api/")'); });