import "@testing-library/jest-dom/vitest"; import { cleanup } from "@testing-library/react"; import { afterEach } from "vitest"; // With `globals: false`, @testing-library/react does not auto-register a // cleanup hook, so unmount rendered trees between tests explicitly. This keeps // `screen.getByTestId` from seeing DOM from prior tests in the same file. afterEach(() => { cleanup(); }); // ProseMirror probes layout APIs jsdom doesn't implement. if (typeof document !== "undefined") { if (!document.elementFromPoint) { document.elementFromPoint = () => null; } if (typeof Range !== "undefined" && !Range.prototype.getClientRects) { Range.prototype.getClientRects = () => [] as unknown as DOMRectList; Range.prototype.getBoundingClientRect = () => ({ x: 0, y: 0, top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0, toJSON: () => ({}) }) as DOMRect; } // CodeMirror probes additional layout APIs jsdom doesn't implement while measuring. if (typeof Element !== "undefined" && !Element.prototype.getClientRects) { Element.prototype.getClientRects = () => [] as unknown as DOMRectList; } } // jsdom runs with an opaque origin (no URL set), so its built-in localStorage // implementation throws SecurityError on every access. Install a Map-backed shim // so any test that touches localStorage directly can call getItem/setItem/removeItem/clear // without additional per-file setup. Tests that need special behavior (hooks.test.tsx, // memo-editor-cache.test.ts) override this via Object.defineProperty / vi.stubGlobal. if (typeof globalThis.localStorage === "undefined" || typeof globalThis.localStorage.clear !== "function") { let _store = new Map(); const localStorageShim: Storage = { get length() { return _store.size; }, getItem: (key: string) => _store.get(key) ?? null, setItem: (key: string, value: string) => _store.set(key, value), removeItem: (key: string) => _store.delete(key), clear: () => { _store = new Map(); }, key: (index: number) => Array.from(_store.keys())[index] ?? null, }; Object.defineProperty(globalThis, "localStorage", { configurable: true, value: localStorageShim, }); } // Defensive shim: `@/auth-state` constructs a BroadcastChannel at module load // to coordinate token refreshes across tabs. jsdom historically has not shipped // BroadcastChannel, so any future test that transitively imports auth-state // would otherwise throw. Current tests avoid that import path on purpose, but // installing the shim keeps authoring new tests frictionless. No-op when jsdom // already provides an implementation. if (typeof globalThis.BroadcastChannel === "undefined") { class NoopBroadcastChannel { readonly name: string; onmessage: ((event: MessageEvent) => void) | null = null; constructor(name: string) { this.name = name; } postMessage(_data: unknown): void {} close(): void {} addEventListener(): void {} removeEventListener(): void {} dispatchEvent(): boolean { return true; } } // @ts-expect-error — attach the shim to the global scope for tests. globalThis.BroadcastChannel = NoopBroadcastChannel; }