- 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).
67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
/**
|
|
* 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')
|
|
}
|