product-context.test.tsx failed with "localStorage.clear is not a function". Root cause: Node 25 ships a global `localStorage` Web Storage stub that is non-functional without --localstorage-file, and it shadows the test DOM environment's storage. The two DOM tests also relied on `jsdom`, which was only present transitively (not a tracker-web dependency) while the rest of the monorepo standardizes on happy-dom. - Add happy-dom as a tracker-web devDependency; switch the two `@vitest-environment jsdom` tests (product-context, command-menu) to happy-dom. - Add vitest.setup.ts that installs a real in-memory Web Storage over Node 25's non-functional stub when the active localStorage/sessionStorage lacks the Storage API; wire it via test.setupFiles. Verified: full tracker-web suite 230/230 (was 228/2). Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
/**
|
|
* Test bootstrap for tracker-web.
|
|
*
|
|
* Node 25 ships a global `localStorage` (Web Storage) that is a non-functional
|
|
* stub unless started with `--localstorage-file=<path>` — accessing it yields an
|
|
* object with no `getItem`/`setItem`/`clear`. That global shadows the DOM test
|
|
* environment's storage, so tests (and the code under test) that use
|
|
* `localStorage` break with "localStorage.clear is not a function".
|
|
*
|
|
* Install a real in-memory Web Storage over it when the active one is missing the
|
|
* Storage API. Tests that stub localStorage themselves (vi.stubGlobal) still work
|
|
* — this only provides the baseline the rest of the suite assumes.
|
|
*/
|
|
function createMemoryStorage(): Storage {
|
|
let store: Record<string, string> = {};
|
|
return {
|
|
get length() {
|
|
return Object.keys(store).length;
|
|
},
|
|
clear() {
|
|
store = {};
|
|
},
|
|
getItem(key: string) {
|
|
return Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null;
|
|
},
|
|
setItem(key: string, value: string) {
|
|
store[String(key)] = String(value);
|
|
},
|
|
removeItem(key: string) {
|
|
delete store[key];
|
|
},
|
|
key(index: number) {
|
|
return Object.keys(store)[index] ?? null;
|
|
},
|
|
} as Storage;
|
|
}
|
|
|
|
for (const name of ['localStorage', 'sessionStorage'] as const) {
|
|
const current = (globalThis as Record<string, unknown>)[name] as Storage | undefined;
|
|
if (!current || typeof current.clear !== 'function') {
|
|
Object.defineProperty(globalThis, name, {
|
|
value: createMemoryStorage(),
|
|
configurable: true,
|
|
writable: true,
|
|
});
|
|
}
|
|
}
|