import '@testing-library/jest-dom/vitest'; // jsdom lacks IntersectionObserver; Framer Motion's `whileInView` needs it. if (typeof window !== 'undefined' && !('IntersectionObserver' in window)) { class IntersectionObserverStub { observe() {} unobserve() {} disconnect() {} takeRecords() { return []; } root = null; rootMargin = ''; thresholds = []; } // @ts-expect-error – installing stub on window window.IntersectionObserver = IntersectionObserverStub; global.IntersectionObserver = IntersectionObserverStub; } // jsdom lacks ResizeObserver; Embla Carousel (Carrusel primitive) needs it. if (typeof window !== 'undefined' && !('ResizeObserver' in window)) { class ResizeObserverStub { observe() {} unobserve() {} disconnect() {} } // @ts-expect-error – installing stub on window window.ResizeObserver = ResizeObserverStub; global.ResizeObserver = ResizeObserverStub; } // jsdom lacks matchMedia; usePrefersReducedMotion() calls it in useEffect. // Note: vitest's jsdom environment pre-defines `matchMedia` as an own property // set to `undefined`, so `'matchMedia' in window` is true even though it isn't // a function — guard on typeof instead of `in`. if (typeof window !== 'undefined' && typeof window.matchMedia !== 'function') { window.matchMedia = (query: string) => ({ matches: false, media: query, onchange: null, addEventListener: () => {}, removeEventListener: () => {}, addListener: () => {}, removeListener: () => {}, dispatchEvent: () => false, }) as unknown as MediaQueryList; }