mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 11:53:10 +08:00
test(desktop): restore web storage under Node 22+
Node 22+ defines a global `localStorage` accessor that stays inert unless the process was started with `--localstorage-file`. vitest's jsdom environment only copies a window key onto `globalThis` when nothing is there yet, so Node's inert stub wins over jsdom's real Storage and `window.localStorage` reads back undefined. Every suite whose `beforeEach` calls `localStorage.clear()` therefore died at setup, taking the whole file with it — 12 files and 396 tests on a clean tree, none of them a real failure. Install a Storage implementation from a setup file so browser semantics hold regardless of Node version. Local desktop suite: 396 failures across 12 files -> 2 across 1.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Node 22+ defines a global `localStorage` / `sessionStorage` accessor that
|
||||
* stays inert unless the process was started with `--localstorage-file`.
|
||||
* vitest's jsdom environment only copies a window key onto `globalThis` when
|
||||
* nothing is there yet, so Node's inert stub wins over jsdom's real Storage and
|
||||
* every `localStorage.clear()` in a `beforeEach` throws. Installing a real
|
||||
* Storage here restores browser semantics regardless of Node version.
|
||||
*/
|
||||
class MemoryStorage implements Storage {
|
||||
#entries = new Map<string, string>()
|
||||
|
||||
get length(): number {
|
||||
return this.#entries.size
|
||||
}
|
||||
|
||||
key(index: number): string | null {
|
||||
return [...this.#entries.keys()][index] ?? null
|
||||
}
|
||||
|
||||
getItem(key: string): string | null {
|
||||
return this.#entries.get(String(key)) ?? null
|
||||
}
|
||||
|
||||
setItem(key: string, value: string): void {
|
||||
this.#entries.set(String(key), String(value))
|
||||
}
|
||||
|
||||
removeItem(key: string): void {
|
||||
this.#entries.delete(String(key))
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.#entries.clear()
|
||||
}
|
||||
|
||||
[name: string]: unknown
|
||||
}
|
||||
|
||||
function installStorage(name: 'localStorage' | 'sessionStorage'): void {
|
||||
let usable = false
|
||||
try {
|
||||
const existing = (globalThis as Record<string, unknown>)[name] as Storage | undefined
|
||||
usable = typeof existing?.setItem === 'function' && typeof existing?.clear === 'function'
|
||||
} catch {
|
||||
// Node's accessor throws when web storage is unavailable — treat as missing.
|
||||
}
|
||||
if (usable) return
|
||||
|
||||
Object.defineProperty(globalThis, name, {
|
||||
value: new MemoryStorage(),
|
||||
configurable: true,
|
||||
writable: true,
|
||||
})
|
||||
}
|
||||
|
||||
installStorage('localStorage')
|
||||
installStorage('sessionStorage')
|
||||
@@ -13,7 +13,7 @@ export default defineConfig({
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
css: true,
|
||||
setupFiles: [],
|
||||
setupFiles: ['./src/test/webStorage.ts'],
|
||||
coverage: {
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
exclude: [
|
||||
@@ -21,6 +21,7 @@ export default defineConfig({
|
||||
'src/**/*.d.ts',
|
||||
'src/types/**',
|
||||
'src/mocks/**',
|
||||
'src/test/**',
|
||||
'src/vite-env.d.ts',
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user