diff --git a/Client/tauri-client/tests/setup.ts b/Client/tauri-client/tests/setup.ts new file mode 100644 index 00000000..f3ca64db --- /dev/null +++ b/Client/tauri-client/tests/setup.ts @@ -0,0 +1,54 @@ +// Vitest global setup for the jsdom suite. +// +// Why this exists: Node 26 defines `localStorage` as a native global accessor +// that returns `undefined` unless the process was started with +// `--localstorage-file`. Vitest's jsdom environment makes `window === globalThis`, +// so that native accessor shadows the one jsdom would otherwise install, and +// every test touching `localStorage` throws. `sessionStorage` is unaffected — +// jsdom's implementation still wins there. +// +// We deliberately do NOT pass `--localstorage-file`: it is file-backed and would +// be shared by vitest's parallel workers, leaking state between test files. + +/** + * Minimal in-memory Web Storage implementation used to replace the shadowed + * `localStorage` global. + * + * Matches the spec on the three points app code depends on: `getItem` returns + * `null` (not `undefined`) for absent keys, `setItem` coerces values to strings, + * and `key(i)` enumerates in insertion order — src/lib/themes.ts:30 and + * src/components/settings/AdvancedTab.ts:351 both sweep storage via `key(i)` + * over `length`, so ordering is load-bearing. + */ +function createMemoryStorage(): Storage { + const store = new Map(); + + return { + get length(): number { + return store.size; + }, + key(index: number): string | null { + return [...store.keys()][index] ?? null; + }, + getItem(key: string): string | null { + return store.get(String(key)) ?? null; + }, + setItem(key: string, value: string): void { + store.set(String(key), String(value)); + }, + removeItem(key: string): void { + store.delete(String(key)); + }, + clear(): void { + store.clear(); + }, + } satisfies Storage; +} + +if (typeof globalThis.localStorage === "undefined") { + Object.defineProperty(globalThis, "localStorage", { + value: createMemoryStorage(), + configurable: true, + writable: true, + }); +} diff --git a/Client/tauri-client/vitest.config.ts b/Client/tauri-client/vitest.config.ts index 02016d66..33cc4a22 100644 --- a/Client/tauri-client/vitest.config.ts +++ b/Client/tauri-client/vitest.config.ts @@ -13,6 +13,8 @@ export default defineConfig({ }, test: { environment: "jsdom", + // Restores `localStorage`, which Node 26 shadows out of the jsdom global. + setupFiles: ["./tests/setup.ts"], // Both the `tests/**/*.test.ts` suite and component-local // `src/**/*.test.ts` files are picked up. include: ["tests/**/*.test.ts", "src/**/*.test.ts"],