/* Prüft die App-Installierbarkeit des Webdesign-Bereichs: - Manifest erreichbar, gültig, Icons laden - Service Worker registriert sich und wird aktiv - HTML wird NICHT zwischengespeichert (Sicherheitsvorgabe) - Bausteine unter /assets/ werden zwischengespeichert */ import { chromium } from "playwright"; const BASIS = "http://127.0.0.1:4199"; const browser = await chromium.launch(); const kontext = await browser.newContext(); const seite = await kontext.newPage(); const fehler = []; seite.on("console", (m) => { if (m.type() === "error") fehler.push(m.text()); }); seite.on("pageerror", (e) => fehler.push("pageerror: " + e.message)); let ok = 0, schlecht = 0; const pruefe = (name, b, zusatz = "") => { if (b) { ok++; console.log(" ok " + name); } else { schlecht++; console.log(" FEHLT " + name + " " + zusatz); } }; console.log("\n=== App-Installierbarkeit ===\n"); // --- Manifest ohne Anmeldung erreichbar (Installation von der Zugangswand) --- console.log("1. Manifest"); const mRes = await seite.request.get(BASIS + "/webdesign/app.webmanifest"); pruefe("ohne Anmeldung erreichbar", mRes.status() === 200, "(war " + mRes.status() + ")"); const manifest = await mRes.json().catch(() => null); pruefe("gueltiges JSON", !!manifest); if (manifest) { pruefe("start_url im Webdesign-Bereich", manifest.start_url === "/webdesign/"); pruefe("scope begrenzt auf /webdesign/", manifest.scope === "/webdesign/"); pruefe("display standalone", manifest.display === "standalone"); pruefe("192er-Symbol vorhanden", manifest.icons.some((i) => i.sizes === "192x192")); pruefe("512er-Symbol vorhanden", manifest.icons.some((i) => i.sizes === "512x512")); pruefe("maskierbares Symbol vorhanden", manifest.icons.some((i) => (i.purpose || "").includes("maskable"))); for (const symbol of manifest.icons) { const r = await seite.request.get(BASIS + symbol.src); pruefe("Symbol laedt: " + symbol.src, r.status() === 200); } } const swRes = await seite.request.get(BASIS + "/webdesign/sw.js"); pruefe("Service Worker ohne Anmeldung erreichbar", swRes.status() === 200, "(war " + swRes.status() + ")"); pruefe("wird als JavaScript ausgeliefert", (swRes.headers()["content-type"] || "").includes("javascript"), "(war " + swRes.headers()["content-type"] + ")"); // --- Registrierung im Browser --- console.log("\n2. Service Worker im Browser"); await seite.goto(BASIS + "/webdesign/zugang.html", { waitUntil: "networkidle" }); await seite.fill("#zg-code-dogfather", "DOGI-TEST-1"); await Promise.all([ seite.waitForURL((u) => !u.pathname.endsWith("zugang.html"), { timeout: 15000 }), seite.click("#zg-form-dogfather button[type=submit]"), ]); await seite.waitForLoadState("networkidle"); const aktiv = await seite.evaluate(async () => { const reg = await navigator.serviceWorker.ready.catch(() => null); return reg ? { scope: reg.scope, aktiv: !!reg.active } : null; }); pruefe("registriert und aktiv", !!(aktiv && aktiv.aktiv)); pruefe("Geltungsbereich ist /webdesign/", !!(aktiv && aktiv.scope.endsWith("/webdesign/")), aktiv ? aktiv.scope : ""); // --- Sicherheitsvorgabe: keine HTML-Seiten im Zwischenspeicher --- console.log("\n3. Sicherheit des Zwischenspeichers"); await seite.reload({ waitUntil: "networkidle" }); await seite.waitForTimeout(1200); const inhalt = await seite.evaluate(async () => { const namen = await caches.keys(); const alles = []; for (const n of namen) { const c = await caches.open(n); for (const anfrage of await c.keys()) alles.push(anfrage.url); } return alles; }); console.log(" Zwischenspeicher enthaelt " + inhalt.length + " Eintraege"); const htmlDrin = inhalt.filter((u) => u.endsWith(".html") || new URL(u).pathname === "/webdesign/"); pruefe("KEINE geschuetzte HTML-Seite zwischengespeichert", htmlDrin.length === 0, htmlDrin.join(", ")); pruefe("Stilvorlage ist zwischengespeichert", inhalt.some((u) => u.includes("webdesign.css"))); pruefe("Kern-Skript ist zwischengespeichert", inhalt.some((u) => u.includes("wd-core.js"))); console.log(fehler.length ? "\nKONSOLENFEHLER:\n" + fehler.join("\n") : "\nKeine Konsolenfehler."); await browser.close(); console.log(`\n=== ${ok} bestanden, ${schlecht} gescheitert ===\n`); process.exit(schlecht === 0 && fehler.length === 0 ? 0 : 1);