From 3cd9a2377aee6047a8980d9b708e928269b0ad4a Mon Sep 17 00:00:00 2001 From: Lumen Stage1 Date: Wed, 30 Sep 2026 13:44:31 -0500 Subject: [PATCH] =?UTF-8?q?Fix=20checkpoint=20regressions:=20jsdom=20local?= =?UTF-8?q?Storage=20shim=20(Node=2022=20shadowing)=20+=20restore=20persis?= =?UTF-8?q?tent=20nav=20links=20required=20by=20=C2=A77=20one-tap=20emerge?= =?UTF-8?q?ncy=20reachability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/layout.ts | 13 ++++++++++++- tests/setup.ts | 35 +++++++++++++++++++++++++++++++++++ vitest.config.ts | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/setup.ts diff --git a/src/app/layout.ts b/src/app/layout.ts index 21cb5a7..4c92d9d 100644 --- a/src/app/layout.ts +++ b/src/app/layout.ts @@ -73,7 +73,18 @@ export function createLayout(): { nav.className = "side-nav"; nav.setAttribute("aria-label", "Primary navigation"); nav.setAttribute("role", "navigation"); - // Bottom navigation removed โ€” redundant with main-screen buttons. + // Persistent navigation: home + the four destinations (contract ยง7 โ€” + // emergency must stay one tap from every screen). + const navTargets = [HOME_ROUTE, ...ROUTES]; + for (const r of navTargets) { + const a = document.createElement("a"); + a.href = r.path; + a.className = "side-nav__link"; + a.setAttribute("data-route", r.path); + a.textContent = r.id === "home" ? "Home" : r.label; + if (r.id === "emergency") a.classList.add("side-nav__link--emergency"); + nav.append(a); + } root.append(nav); return { root, main, nav, brand, statusChip, skipLink }; diff --git a/tests/setup.ts b/tests/setup.ts new file mode 100644 index 0000000..1b6a4ae --- /dev/null +++ b/tests/setup.ts @@ -0,0 +1,35 @@ +/** + * Vitest setup โ€” runs before every test file. + * + * Why: on Node >=22.4 the experimental native `localStorage` global shadows + * jsdom's implementation and is `undefined` unless `--localstorage-file` is + * passed, so jsdom-environment tests see `localStorage === undefined` + * (ExperimentalWarning at runtime). Install a minimal in-memory stand-in + * whenever the global is missing. + */ +class MemoryStorage implements Storage { + private map = new Map(); + get length(): number { + return this.map.size; + } + clear(): void { + this.map.clear(); + } + getItem(key: string): string | null { + return this.map.has(key) ? (this.map.get(key) as string) : null; + } + key(index: number): string | null { + return [...this.map.keys()][index] ?? null; + } + removeItem(key: string): void { + this.map.delete(key); + } + setItem(key: string, value: string): void { + this.map.set(key, String(value)); + } +} + +const g = globalThis as unknown as { localStorage?: Storage }; +if (typeof g.localStorage === "undefined") { + g.localStorage = new MemoryStorage(); +} diff --git a/vitest.config.ts b/vitest.config.ts index 8cdd72f..5765f26 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,6 +5,7 @@ export default defineConfig({ globals: false, include: ["tests/**/*.test.ts"], environment: "node", + setupFiles: ["./tests/setup.ts"], reporters: ["verbose"], coverage: { enabled: false,