From 57725f7ef2753d5e028b756bb5a527d624ffb283 Mon Sep 17 00:00:00 2001 From: Yaojia Wang Date: Tue, 7 Jul 2026 21:09:49 +0200 Subject: [PATCH] =?UTF-8?q?test:=20fix=20pre-existing=20failures=20?= =?UTF-8?q?=E2=80=94=20localStorage=20polyfill=20+=20prefs=20shape=20drift?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - jsdom 29's localStorage methods are undefined under this vitest config, so every frontend test calling localStorage.clear()/setItem() threw. Add a shared in-memory Web Storage polyfill via setupFiles (no-op when a working Storage exists / in node env). Fixes quick-reply, push, projects-panel (135 tests). - Update desktop prefs test: defaultPrefs gained remoteHosts/selectedHostId (remote-host mTLS work); sync EXPECTED_DEFAULTS and the round-trip fixture. Full suite now green: 1473 passed (was 142 failing). --- test/desktop/prefs.test.ts | 4 +++ test/setup/local-storage.ts | 66 +++++++++++++++++++++++++++++++++++++ vitest.config.ts | 4 +++ 3 files changed, 74 insertions(+) create mode 100644 test/setup/local-storage.ts diff --git a/test/desktop/prefs.test.ts b/test/desktop/prefs.test.ts index ec173bb..55d7ad6 100644 --- a/test/desktop/prefs.test.ts +++ b/test/desktop/prefs.test.ts @@ -18,6 +18,8 @@ const EXPECTED_DEFAULTS: DesktopPrefs = { openAtLogin: false, notifyOnApproval: true, notifyOnStatusChange: true, + remoteHosts: [], + selectedHostId: null, } describe('defaultPrefs', () => { @@ -60,6 +62,8 @@ describe('validatePrefs', () => { openAtLogin: true, notifyOnApproval: false, notifyOnStatusChange: false, + remoteHosts: [], + selectedHostId: null, } // Act diff --git a/test/setup/local-storage.ts b/test/setup/local-storage.ts new file mode 100644 index 0000000..4f2f11a --- /dev/null +++ b/test/setup/local-storage.ts @@ -0,0 +1,66 @@ +/** + * test/setup/local-storage.ts — in-memory Web Storage polyfill for the test env. + * + * jsdom 29 (the `@vitest-environment jsdom` files) exposes a `localStorage` + * object whose Storage methods (getItem/setItem/clear/…) are `undefined` under + * this vitest config — so any test that calls `localStorage.clear()` throws + * "localStorage.clear is not a function". This registered setupFile installs a + * spec-compliant in-memory Storage whenever the ambient one is missing or + * non-functional, so frontend unit tests (quick-reply, push, projects-panel) + * get real persistence semantics. It is a no-op if a working Storage already + * exists, and harmless in the node environment (just adds an unused global). + */ + +class MemoryStorage implements Storage { + private store = new Map() + + get length(): number { + return this.store.size + } + + clear(): void { + this.store.clear() + } + + getItem(key: string): string | null { + return this.store.has(key) ? (this.store.get(key) as string) : null + } + + setItem(key: string, value: string): void { + this.store.set(String(key), String(value)) + } + + removeItem(key: string): void { + this.store.delete(key) + } + + key(index: number): string | null { + return Array.from(this.store.keys())[index] ?? null + } +} + +/** True when `s` is a usable Storage (has a callable `clear`). */ +function isUsableStorage(s: unknown): s is Storage { + return s != null && typeof (s as Storage).clear === 'function' +} + +/** Install an in-memory Storage on `target[name]` unless a working one exists. */ +function ensureStorage(target: Record, name: string): void { + if (isUsableStorage(target[name])) return + Object.defineProperty(target, name, { + value: new MemoryStorage(), + configurable: true, + writable: true, + }) +} + +const globalTarget = globalThis as unknown as Record +ensureStorage(globalTarget, 'localStorage') +ensureStorage(globalTarget, 'sessionStorage') + +// Mirror onto `window` (jsdom env) so `window.localStorage` resolves too. +const win = (globalThis as { window?: Record }).window +if (win) { + ensureStorage(win, 'localStorage') + ensureStorage(win, 'sessionStorage') +} diff --git a/vitest.config.ts b/vitest.config.ts index 029ab37..6a1ee82 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,6 +4,10 @@ export default defineConfig({ test: { include: ['test/**/*.test.ts'], environment: 'node', + // jsdom 29's localStorage methods are undefined under this config; a shared + // in-memory Web Storage polyfill fixes the frontend (@vitest-environment + // jsdom) tests that call localStorage.clear()/setItem(). No-op in node env. + setupFiles: ['test/setup/local-storage.ts'], // Scaffold has no tests yet; don't fail the script until modules add theirs. passWithNoTests: true, // Coverage target is the global 80% rule; enforced with `--coverage`.