import { vi } from 'vitest'; // jsdom lacks ResizeObserver global.ResizeObserver = class { observe = vi.fn(); unobserve = vi.fn(); disconnect = vi.fn(); } as unknown as typeof ResizeObserver; // jsdom lacks Web Animations API Element.prototype.animate = vi.fn().mockReturnValue({ onfinish: null, cancel: vi.fn(), finish: vi.fn(), pause: vi.fn(), play: vi.fn(), }); // jsdom lacks SVG geometry methods (SVGElement.prototype as any).getTotalLength = vi.fn(() => 0); // Robust localStorage mock for jsdom environment 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 = {}; }), key: vi.fn((index: number) => Object.keys(store)[index] || null), get length() { return Object.keys(store).length; }, }; })(); Object.defineProperty(window, 'localStorage', { value: localStorageMock, writable: true, }); // jsdom lacks PointerEvent; back it with MouseEvent so clientX/clientY survive. if (typeof PointerEvent === 'undefined') { class PointerEventPolyfill extends MouseEvent { pointerId: number; constructor(type: string, params: PointerEventInit = {}) { super(type, params); this.pointerId = params.pointerId ?? 1; } } // @ts-expect-error assigning polyfill to the global scope global.PointerEvent = PointerEventPolyfill; } // jsdom lacks pointer capture HTMLElement.prototype.setPointerCapture = vi.fn(); HTMLElement.prototype.releasePointerCapture = vi.fn();