test: fix pre-existing failures — localStorage polyfill + prefs shape drift

- 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).
This commit is contained in:
Yaojia Wang
2026-07-07 21:09:49 +02:00
parent fc3b849a08
commit 57725f7ef2
3 changed files with 74 additions and 0 deletions

View File

@@ -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<string, string>()
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<string, unknown>, 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<string, unknown>
ensureStorage(globalTarget, 'localStorage')
ensureStorage(globalTarget, 'sessionStorage')
// Mirror onto `window` (jsdom env) so `window.localStorage` resolves too.
const win = (globalThis as { window?: Record<string, unknown> }).window
if (win) {
ensureStorage(win, 'localStorage')
ensureStorage(win, 'sessionStorage')
}