Files
frontend-svelte/vitest.setup.jsdom.ts
T

109 lines
3.5 KiB
TypeScript
Raw Normal View History

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<string, string> = {};
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();
2026-06-02 16:03:54 +03:00
// jsdom lacks the Popover API. Minimal shim: methods toggle an internal flag,
// dispatch a `toggle` event ({ oldState, newState }), and make
// matches(':popover-open') reflect the flag so components can sync state.
if (typeof HTMLElement.prototype.showPopover !== 'function') {
const openFlag = new WeakSet<HTMLElement>();
const fireToggle = (el: HTMLElement, oldState: string, newState: string) => {
const event = new Event('toggle') as Event & { oldState: string; newState: string };
event.oldState = oldState;
event.newState = newState;
el.dispatchEvent(event);
};
HTMLElement.prototype.showPopover = function showPopover(this: HTMLElement) {
if (openFlag.has(this)) {
return;
}
openFlag.add(this);
fireToggle(this, 'closed', 'open');
};
HTMLElement.prototype.hidePopover = function hidePopover(this: HTMLElement) {
if (!openFlag.has(this)) {
return;
}
openFlag.delete(this);
fireToggle(this, 'open', 'closed');
};
HTMLElement.prototype.togglePopover = function togglePopover(this: HTMLElement) {
if (openFlag.has(this)) {
this.hidePopover();
return !openFlag.has(this);
}
this.showPopover();
return openFlag.has(this);
};
const originalMatches = Element.prototype.matches;
Element.prototype.matches = function matches(this: Element, selector: string): boolean {
if (selector === ':popover-open') {
return this instanceof HTMLElement && openFlag.has(this);
}
return originalMatches.call(this, selector);
} as typeof Element.prototype.matches;
}