// Vitest setup для Vuetify в JSDOM-среде. // Vuetify-компоненты используют ResizeObserver/IntersectionObserver/matchMedia, // которые отсутствуют в JSDOM. Добавляем минимальные stub'ы. class ResizeObserverStub { observe(): void {} unobserve(): void {} disconnect(): void {} } class IntersectionObserverStub { observe(): void {} unobserve(): void {} disconnect(): void {} takeRecords(): never[] { return []; } root = null; rootMargin = ''; thresholds: number[] = []; } // Глобальные браузерные API заглушки для JSDOM-среды. (globalThis as unknown as { ResizeObserver: typeof ResizeObserverStub }).ResizeObserver = ResizeObserverStub; (globalThis as unknown as { IntersectionObserver: typeof IntersectionObserverStub }).IntersectionObserver = IntersectionObserverStub; if (!window.matchMedia) { Object.defineProperty(window, 'matchMedia', { writable: true, value: (query: string) => ({ matches: false, media: query, onchange: null, addListener: () => {}, removeListener: () => {}, addEventListener: () => {}, removeEventListener: () => {}, dispatchEvent: () => false, }), }); } // CSS.supports — Vuetify проверяет наличие color-mix. if (!window.CSS || !window.CSS.supports) { Object.defineProperty(window, 'CSS', { writable: true, value: { supports: () => true }, }); } // visualViewport — VOverlay/v-menu/v-snackbar используют для location strategies. if (!window.visualViewport) { Object.defineProperty(window, 'visualViewport', { writable: true, value: { width: 1024, height: 768, offsetLeft: 0, offsetTop: 0, pageLeft: 0, pageTop: 0, scale: 1, addEventListener: () => {}, removeEventListener: () => {}, }, }); }