Skip to content

Commit edb6279

Browse files
Lukas Geigerclaude
andcommitted
feat(web_companion): PWA-Haertung -- manifest id/scope, sw v2, Tests erweitert
- manifest.webmanifest: id und scope ergaenzt (PWA-Installierbarkeit Best-Practice) - sw.js: CACHE_NAME v2, clients.claim() in waitUntil-Kette verschoben - pwa.test.mjs: von 2 auf 15 Tests erweitert (20/20 gruen mit library-Tests) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 262f524 commit edb6279

4 files changed

Lines changed: 107 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.1.0/).
1515
- `llms.txt` mit maschinenlesbarer Projektpositionierung, Datenschutzgrenzen und relevanten Suchbegriffen.
1616
- Redigierter Workspace-Export `devcenter-workspace-v1.json` als Desktop-Funktion unter `Datei -> Arbeitsstand exportieren...`.
1717
- Statischer `web_companion/`-MVP mit lokalem JSON-Import, Demo-Fixture, read-only Dashboard, `manifest.webmanifest`, Service Worker und Node-Tests.
18+
- `web_companion/manifest.webmanifest`: `id` und `scope` ergänzt (PWA-Installierbarkeits-Best-Practice)
19+
- `web_companion/sw.js`: CACHE_NAME v2, `clients.claim()` in `waitUntil`-Kette verschoben
20+
- `web_companion/tests/pwa.test.mjs`: auf 15 PWA-Tests erweitert (20/20 grün mit library-Tests)
1821

1922
### Geändert / Changed
2023
- README, Contribution- und Security-Dokumentation auf `dev-bricks/DevCenter` aktualisiert.

web_companion/manifest.webmanifest

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
2+
"id": "./",
3+
"scope": "./",
24
"name": "DevCenter Companion",
35
"short_name": "DevCenter",
46
"description": "Liest redigierte DevCenter-Arbeitsstände lokal und offline im Browser.",

web_companion/sw.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const CACHE_NAME = "devcenter-companion-v1";
1+
const CACHE_NAME = "devcenter-companion-v2";
22
const APP_SHELL = [
33
"./",
44
"./index.html",
@@ -20,15 +20,17 @@ self.addEventListener("install", (event) => {
2020

2121
self.addEventListener("activate", (event) => {
2222
event.waitUntil(
23-
caches.keys().then((keys) =>
24-
Promise.all(
25-
keys
26-
.filter((key) => key !== CACHE_NAME)
27-
.map((key) => caches.delete(key)),
28-
),
29-
),
23+
caches
24+
.keys()
25+
.then((keys) =>
26+
Promise.all(
27+
keys
28+
.filter((key) => key !== CACHE_NAME)
29+
.map((key) => caches.delete(key)),
30+
),
31+
)
32+
.then(() => self.clients.claim()),
3033
);
31-
self.clients.claim();
3234
});
3335

3436
self.addEventListener("fetch", (event) => {

web_companion/tests/pwa.test.mjs

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,95 @@ import path from "node:path";
55
import { fileURLToPath } from "node:url";
66

77
const __dirname = path.dirname(fileURLToPath(import.meta.url));
8-
const manifestPath = path.join(__dirname, "..", "manifest.webmanifest");
9-
const swPath = path.join(__dirname, "..", "sw.js");
10-
11-
test("manifest definiert standalone-PWA mit SVG-Icons", () => {
12-
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
13-
assert.equal(manifest.display, "standalone");
14-
assert.equal(manifest.start_url, "./index.html");
15-
assert.ok(Array.isArray(manifest.icons));
16-
assert.ok(manifest.icons.some((icon) => icon.src.endsWith("app.svg")));
17-
});
18-
19-
test("service worker cached den Demo-Export und die Shell-Dateien", () => {
20-
const source = fs.readFileSync(swPath, "utf8");
21-
assert.match(source, /demo-workspace\.json/);
22-
assert.match(source, /manifest\.webmanifest/);
23-
assert.match(source, /cache\.addAll/);
8+
const root = path.join(__dirname, "..");
9+
const manifestPath = path.join(root, "manifest.webmanifest");
10+
const swPath = path.join(root, "sw.js");
11+
const indexPath = path.join(root, "index.html");
12+
const appJsPath = path.join(root, "app.js");
13+
14+
// --- Manifest ---
15+
16+
test("manifest: display ist standalone", () => {
17+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
18+
assert.equal(m.display, "standalone");
19+
});
20+
21+
test("manifest: start_url vorhanden", () => {
22+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
23+
assert.equal(m.start_url, "./index.html");
24+
});
25+
26+
test("manifest: id-Feld vorhanden (PWA-Installierbarkeit)", () => {
27+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
28+
assert.ok(typeof m.id === "string" && m.id.length > 0, "id fehlt oder leer");
29+
});
30+
31+
test("manifest: scope-Feld vorhanden", () => {
32+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
33+
assert.ok(typeof m.scope === "string" && m.scope.length > 0, "scope fehlt oder leer");
34+
});
35+
36+
test("manifest: name und short_name vorhanden", () => {
37+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
38+
assert.ok(m.name && m.short_name);
39+
});
40+
41+
test("manifest: SVG-Icon mit purpose any vorhanden", () => {
42+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
43+
assert.ok(Array.isArray(m.icons));
44+
assert.ok(m.icons.some((i) => i.src.endsWith("app.svg") && i.purpose === "any"));
45+
});
46+
47+
test("manifest: maskable Icon vorhanden", () => {
48+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
49+
assert.ok(m.icons.some((i) => i.purpose === "maskable"));
50+
});
51+
52+
test("manifest: Icon-SVG-Dateien physisch vorhanden", () => {
53+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
54+
for (const icon of m.icons) {
55+
const iconPath = path.join(root, icon.src.replace(/^\.\//, ""));
56+
assert.ok(fs.existsSync(iconPath), `Icon fehlt: ${icon.src}`);
57+
}
58+
});
59+
60+
// --- Service Worker ---
61+
62+
test("sw.js: CACHE_NAME enthält 'devcenter'", () => {
63+
const src = fs.readFileSync(swPath, "utf8");
64+
assert.match(src, /devcenter/);
65+
});
66+
67+
test("sw.js: CACHE_NAME ist v2 (aktuell)", () => {
68+
const src = fs.readFileSync(swPath, "utf8");
69+
assert.match(src, /devcenter-companion-v2/);
70+
});
71+
72+
test("sw.js: skipWaiting() im install-Handler", () => {
73+
const src = fs.readFileSync(swPath, "utf8");
74+
assert.match(src, /self\.skipWaiting\(\)/);
75+
});
76+
77+
test("sw.js: clients.claim() im activate-Handler", () => {
78+
const src = fs.readFileSync(swPath, "utf8");
79+
assert.match(src, /self\.clients\.claim\(\)/);
80+
});
81+
82+
test("sw.js: Demo-Export und Shell-Dateien gecacht", () => {
83+
const src = fs.readFileSync(swPath, "utf8");
84+
assert.match(src, /demo-workspace\.json/);
85+
assert.match(src, /manifest\.webmanifest/);
86+
assert.match(src, /cache\.addAll/);
87+
});
88+
89+
// --- HTML-Integration ---
90+
91+
test("index.html: verweist auf manifest.webmanifest", () => {
92+
const html = fs.readFileSync(indexPath, "utf8");
93+
assert.match(html, /manifest\.webmanifest/);
94+
});
95+
96+
test("app.js: registriert Service Worker", () => {
97+
const src = fs.readFileSync(appJsPath, "utf8");
98+
assert.match(src, /serviceWorker\.register/);
2499
});

0 commit comments

Comments
 (0)