import '@testing-library/jest-dom/vitest' import { createElement } from 'react' import { afterAll, afterEach, beforeAll, beforeEach, vi } from 'vitest' import { queryClient } from '@/lib/queryClient' import { server } from './msw/server' // Boot a Node-side MSW server for every test. Each test may use // `server.use(...)` to layer extra handlers on top of the defaults. beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) afterEach(() => server.resetHandlers()) afterAll(() => server.close()) // The shared React Query cache leaks across tests unless reset — component // tests render into it so they can observe `lib/io/scene.ts` invalidations. beforeEach(() => queryClient.clear()) // --------------------------------------------------------------------------- // Global mocks: keep components focused on behaviour, not framework wiring. // --------------------------------------------------------------------------- // `useTranslation` → return the key verbatim so tests can match on stable, // unique identifiers regardless of locale. Keys look like `welcome.new`. vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key, i18n: { language: 'en-US', changeLanguage: async () => {} }, }), Trans: ({ i18nKey, children }: { i18nKey?: string; children?: unknown }) => (i18nKey ?? (children as never)) as never, I18nextProvider: ({ children }: { children: unknown }) => children as never, // Consumed by `lib/i18n.ts` at import time. initReactI18next: { type: '3rdParty', init: () => {} }, })) // next/image — render as a plain . vi.mock('next/image', () => ({ __esModule: true, default: (props: Record) => { const { priority: _priority, ...rest } = props return createElement('img', rest) }, })) // ResizeObserver/IntersectionObserver stubs for jsdom. class StubObserver { observe() {} unobserve() {} disconnect() {} takeRecords() { return [] } } Object.defineProperty(globalThis, 'ResizeObserver', { value: StubObserver, writable: true, }) Object.defineProperty(globalThis, 'IntersectionObserver', { value: StubObserver, writable: true, }) // Hotkeys + gesture libs call scroll/focus methods jsdom doesn't implement. Element.prototype.scrollIntoView = vi.fn() Element.prototype.releasePointerCapture = vi.fn() Element.prototype.hasPointerCapture = vi.fn(() => false) // jsdom doesn't implement `window.matchMedia`; next-themes + some radix bits // expect it during SSR-safe code paths. Object.defineProperty(window, 'matchMedia', { writable: true, value: 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(), })), }) // Mock localStorage for zustand persist const localStorageMock = (() => { let store: Record = {} return { getItem: vi.fn((key: string) => store[key] || null), setItem: vi.fn((key: string, value: string) => { store[key] = value.toString() }), removeItem: vi.fn((key: string) => { delete store[key] }), clear: vi.fn(() => { store = {} }), length: 0, key: vi.fn((index: number) => Object.keys(store)[index] || null), } })() Object.defineProperty(window, 'localStorage', { value: localStorageMock, }) // FontFace API stubs for jsdom class StubFontFace { constructor(family: string, source: string | ArrayBuffer | ArrayBufferView, descriptors?: any) {} load() { return Promise.resolve(this) } } Object.defineProperty(globalThis, 'FontFace', { value: StubFontFace, writable: true, }) Object.defineProperty(document, 'fonts', { value: { add: vi.fn(), delete: vi.fn(), clear: vi.fn(), check: vi.fn(() => true), load: vi.fn(() => Promise.resolve([])), ready: Promise.resolve([]), }, writable: true, })