import '@testing-library/jest-dom/vitest'; import { afterEach, vi } from 'vitest'; import { cleanup } from '@testing-library/react'; // jsdom is missing a handful of browser APIs that antd / pro-components touch on // mount. Provide minimal stand-ins so component pages can render in tests. if (!window.matchMedia) { window.matchMedia = vi.fn().mockImplementation((query: string) => ({ matches: false, media: query, onchange: null, addListener: vi.fn(), removeListener: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), })); } class ResizeObserverStub { observe() {} unobserve() {} disconnect() {} } // @ts-expect-error -- assigning test stub window.ResizeObserver = window.ResizeObserver || ResizeObserverStub; class IntersectionObserverStub { root = null; rootMargin = ''; thresholds = []; observe() {} unobserve() {} disconnect() {} takeRecords() { return []; } } // @ts-expect-error -- assigning test stub window.IntersectionObserver = window.IntersectionObserver || IntersectionObserverStub; if (!window.scrollTo) { // @ts-expect-error -- jsdom does not implement scrollTo window.scrollTo = vi.fn(); } // Some pages read auth tokens from localStorage during render; ensure a working // Storage implementation is present regardless of the jsdom build. if (typeof window.localStorage?.getItem !== 'function') { const store = new Map(); const storage: Storage = { get length() { return store.size; }, clear: () => store.clear(), getItem: (k: string) => (store.has(k) ? store.get(k)! : null), key: (i: number) => Array.from(store.keys())[i] ?? null, removeItem: (k: string) => void store.delete(k), setItem: (k: string, v: string) => void store.set(k, String(v)), }; Object.defineProperty(window, 'localStorage', { value: storage, configurable: true }); } // jsdom's getContext is unimplemented; the CRM graph page guards against a null // context, so returning null keeps it on its safe path. if (!HTMLCanvasElement.prototype.getContext) { HTMLCanvasElement.prototype.getContext = () => null; } // jsdom throws "Not implemented" when getComputedStyle is called with a // pseudo-element (rc-table does this while measuring scrollbars). Drop the // unsupported second argument so the call succeeds quietly. const originalGetComputedStyle = window.getComputedStyle.bind(window); window.getComputedStyle = ((elt: Element) => originalGetComputedStyle(elt)) as typeof window.getComputedStyle; afterEach(() => { cleanup(); });