From 4cebc4b7389cfa8d5043c835878c47d6fe915b7f Mon Sep 17 00:00:00 2001 From: Konstantinos Thermos Date: Thu, 16 Jul 2026 14:17:42 +0300 Subject: [PATCH] chore(test): make jsdom Storage work under Node's native Web Storage globals --- client/tests/environment/jsdom-native-abort.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/client/tests/environment/jsdom-native-abort.ts b/client/tests/environment/jsdom-native-abort.ts index 1413dd8ae..1a16aaa8b 100644 --- a/client/tests/environment/jsdom-native-abort.ts +++ b/client/tests/environment/jsdom-native-abort.ts @@ -2,15 +2,22 @@ * Custom Vitest environment that extends jsdom but preserves the native * Node.js AbortController and AbortSignal. * - * Problem: jsdom replaces globalThis.AbortController and AbortSignal with its + * Problem 1: jsdom replaces globalThis.AbortController and AbortSignal with its * own implementations. Node.js's undici-based fetch validates signals via * `signal instanceof AbortSignal` against its own native class reference. * jsdom's AbortSignal instances fail this check, causing fetch to throw: * TypeError: RequestInit: Expected signal ("AbortSignal {}") to be an * instance of AbortSignal. - * * Fix: after jsdom installs its globals, restore the native AbortController * and AbortSignal so fetch works correctly in tests. + * + * Problem 2 (Node 22+ experimental Web Storage): Node now exposes its own + * `localStorage`/`sessionStorage` globals, inert unless `--localstorage-file` + * is passed. Vitest's global-populate skips any key already present on the + * global that isn't in its own allow-list, and Storage isn't in it — so + * jsdom's working Storage never overwrites Node's inert one and every test + * throws on `localStorage.clear()`. Fix: delete Node's shadow globals before + * jsdom setup runs, so vitest copies jsdom's Storage across. */ import { builtinEnvironments } from 'vitest/environments'; @@ -26,6 +33,10 @@ export default { const NativeAbortController = global.AbortController; const NativeAbortSignal = global.AbortSignal; + // Clear Node's inert Web Storage globals so jsdom's win, not this shadow. + delete (global as { localStorage?: unknown }).localStorage; + delete (global as { sessionStorage?: unknown }).sessionStorage; + // Run standard jsdom setup (installs jsdom globals, including its own AbortController) const env = await jsdomEnv.setup(global, options as Parameters[1]);