From 9fbe07571ead7c9fff1321a551271e04ab84a5ea Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Thu, 23 Apr 2026 02:23:05 +0200 Subject: [PATCH 1/2] test(web): polyfill localStorage/sessionStorage in jsdom env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node 20+ ships a built-in localStorage/sessionStorage stub on globalThis that throws "storage.setItem is not a function" unless Node is launched with --localstorage-file=PATH. Vitest's jsdom env only overwrites a fixed allow-list of globals, and neither storage is in that list — so Node's broken stub shadows jsdom's working implementation and crashes any code using zustand's persist middleware. Override both globals in the test setup with an in-memory Storage implementation. Resolves 10 keybindStore failures plus 1 FriendsPage toast failure (all symptoms of the same root cause). The remaining FriendsPage DM assertion failure is unrelated and is left for follow-up triage as it touches federation DM-creation behavior. --- packages/web/src/test/setup.ts | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/web/src/test/setup.ts b/packages/web/src/test/setup.ts index 509fa785..ffce9c32 100644 --- a/packages/web/src/test/setup.ts +++ b/packages/web/src/test/setup.ts @@ -1,5 +1,40 @@ import '@testing-library/jest-dom/vitest'; +// Node 20+ ships a built-in `localStorage`/`sessionStorage` stub on globalThis +// that has no methods unless `--localstorage-file=PATH` is provided. Vitest's +// jsdom env only overwrites globals it knows about, and neither storage is in +// that list — so Node's broken stub shadows jsdom's working implementation, +// breaking anything that persists via zustand's `persist` middleware. +// Replace both with an in-memory Storage-compatible polyfill. +class InMemoryStorage implements Storage { + private store = new Map(); + 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)! : null; + } + key(index: number): string | null { + return Array.from(this.store.keys())[index] ?? null; + } + removeItem(key: string): void { + this.store.delete(key); + } + setItem(key: string, value: string): void { + this.store.set(key, String(value)); + } +} +for (const name of ['localStorage', 'sessionStorage'] as const) { + Object.defineProperty(globalThis, name, { + value: new InMemoryStorage(), + configurable: true, + writable: true, + }); +} + // Polyfill ClipboardItem for jsdom (not included in jsdom) if (typeof ClipboardItem === 'undefined') { // eslint-disable-next-line @typescript-eslint/no-explicit-any From ae5bdaa338c66159dd872b3a45044df06a5443a9 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Thu, 23 Apr 2026 02:28:57 +0200 Subject: [PATCH 2/2] test(friends): drop stale origin arg from addDmChannel assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DM "Message button" test asserted addDmChannel was called with two arguments — the channel and an empty-string origin — but the assertion has been stale since commit 7f3ca4e ("route DM creation to home instance with federated identity", 2026-04-01). That refactor made FriendsPage always route DM creation through the home api client and dropped the second argument from the addDmChannel call because the remote friend's instanceOrigin no longer applies — home-created DMs don't need a channelOriginMap entry (lookups default to '' for missing keys; remote- delivered DMs still get their origin tagged by useWebSocket). The two-arg assertion was introduced on 2026-03-25 (commit 277b69a) against an intermediate form of the code that was later rewritten. Drop the trailing '' so the assertion matches the current, intentional one-arg call. --- packages/web/src/components/chat/FriendsPage.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/components/chat/FriendsPage.test.tsx b/packages/web/src/components/chat/FriendsPage.test.tsx index 422129d6..662e6a76 100644 --- a/packages/web/src/components/chat/FriendsPage.test.tsx +++ b/packages/web/src/components/chat/FriendsPage.test.tsx @@ -322,7 +322,7 @@ describe('FriendsPage', () => { }); await waitFor(() => { - expect(mockAddDmChannel).toHaveBeenCalledWith(expect.objectContaining({ id: 'dm-channel-99' }), ''); + expect(mockAddDmChannel).toHaveBeenCalledWith(expect.objectContaining({ id: 'dm-channel-99' })); }); await waitFor(() => {