// F5 — PWA behaviour against a production preview build (see // playwright.pwa.config.ts). Covers the manifest, service-worker // registration, offline-from-cache load, and the version-keyed cache // (a new deploy's `version` makes a new cache and `activate` drops the // old one — verified here as "exactly one galaxy cache, version-keyed"). // The single-URL app-shell boots at the app base (`/`); with no seeded // session the dispatcher renders the login screen, so the shell's // `#main-content` region is the boot signal here. import { expect, test } from "@playwright/test"; test.describe("PWA", () => { test("links a web manifest with installable icons", async ({ page }) => { await page.goto("/"); const href = await page .locator('head link[rel="manifest"]') .getAttribute("href"); expect(href).toMatch(/manifest\.webmanifest$/); const manifest = await page.request .get(href!) .then((r) => r.json()); expect(manifest.name).toBe("Galaxy"); // Relative so the manifest is base-agnostic: served under the app // base (`/` at the root, `/game/` in the single-origin deploy) it // resolves to the app root either way. expect(manifest.start_url).toBe("./"); expect(manifest.display).toBe("standalone"); const sizes = (manifest.icons as { sizes: string }[]).map((i) => i.sizes); expect(sizes).toContain("192x192"); expect(sizes).toContain("512x512"); const purposes = (manifest.icons as { purpose?: string }[]).map( (i) => i.purpose, ); expect(purposes).toContain("maskable"); }); test("registers a service worker that controls the page", async ({ page }) => { await page.goto("/"); await page.waitForFunction( () => navigator.serviceWorker.controller !== null, null, { timeout: 20_000 }, ); const cacheNames: string[] = await page.evaluate(() => caches.keys()); const galaxy = cacheNames.filter((n) => n.startsWith("galaxy-cache-")); // Exactly one, version-keyed cache (old versions purged on activate). expect(galaxy).toHaveLength(1); expect(galaxy[0]).toMatch(/^galaxy-cache-.+/); }); test("serves the app shell offline from the cache", async ({ page, context, }) => { await page.goto("/"); await page.waitForFunction( () => navigator.serviceWorker.controller !== null, null, { timeout: 20_000 }, ); await expect(page.locator("#main-content")).toBeVisible(); await context.setOffline(true); try { await page.reload(); // The cached shell boots offline — the login main region renders. await expect(page.locator("#main-content")).toBeVisible({ timeout: 15_000, }); } finally { await context.setOffline(false); } }); });