From e31d2c1c08ee3eaca1f9b998b966ec436cb494e8 Mon Sep 17 00:00:00 2001 From: Travis Bonnet Date: Wed, 8 Apr 2026 19:48:19 -0500 Subject: [PATCH] fix(tests): mock globalThis.localStorage before zustand persist init Node.js 22+ exposes a built-in globalThis.localStorage that requires the --localstorage-file flag. Without it, the object exists but its methods (getItem, setItem, removeItem) are undefined. Zustand's persist middleware calls createJSONStorage(() => localStorage) at module init time, capturing this broken reference before jsdom or test-level mocks can replace it, causing "storage.setItem is not a function" in all 8 onboardingStore tests. Move the localStorage mock into vi.hoisted() so it installs on globalThis before module imports are evaluated. This ensures zustand's persist middleware captures a functional storage object. Co-Authored-By: Tadao --- .../organizations/lib/onboardingStore.test.ts | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/apps/web/modules/ee/organizations/lib/onboardingStore.test.ts b/apps/web/modules/ee/organizations/lib/onboardingStore.test.ts index c850aafb20f813..d52fabce620d19 100644 --- a/apps/web/modules/ee/organizations/lib/onboardingStore.test.ts +++ b/apps/web/modules/ee/organizations/lib/onboardingStore.test.ts @@ -2,16 +2,38 @@ * @vitest-environment jsdom */ // @ts-nocheck - Test file with mock type compatibility issues that don't affect test functionality -import { renderHook, act, waitFor } from "@testing-library/react"; -import { useSession } from "next-auth/react"; -import { useSearchParams, useRouter, usePathname } from "next/navigation"; -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; + +// Node.js 22+ exposes a built-in globalThis.localStorage that requires --localstorage-file. +// Without it, the object exists but its methods (getItem, setItem, etc.) are undefined. +// Zustand's persist middleware calls createJSONStorage(() => localStorage) at module init, +// which captures the broken Node.js localStorage before jsdom or test-level mocks can +// override it. vi.hoisted runs before imports, ensuring the mock is in place when the +// store module initializes. +const { localStorageMock } = vi.hoisted(() => { + const localStorageMock = { + getItem: vi.fn(), + setItem: vi.fn(), + removeItem: vi.fn(), + clear: vi.fn(), + key: vi.fn(), + length: 0, + }; + Object.defineProperty(globalThis, "localStorage", { + value: localStorageMock, + writable: true, + configurable: true, + }); + return { localStorageMock }; +}); import { WEBAPP_URL } from "@calcom/lib/constants"; import { UserPermissionRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; - -import { useOnboardingStore, useOnboarding } from "./onboardingStore"; +import { act, renderHook, waitFor } from "@testing-library/react"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { useSession } from "next-auth/react"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { useOnboarding, useOnboardingStore } from "./onboardingStore"; // Mock all dependencies vi.mock("next-auth/react"); @@ -117,14 +139,7 @@ const createTestSearchParams = (params?: Record) => { return new URLSearchParams(params); }; -// Mock localStorage -const localStorageMock = { - getItem: vi.fn(), - setItem: vi.fn(), - removeItem: vi.fn(), - clear: vi.fn(), -}; - +// Also install on window for jsdom compatibility (localStorageMock defined via vi.hoisted above) Object.defineProperty(window, "localStorage", { value: localStorageMock, writable: true,