/** * Jest Test Setup * Global test configuration and helpers */ // Mock browser APIs that are not available in jsdom global.requestAnimationFrame = (callback) => { return setTimeout(callback, 16); }; global.cancelAnimationFrame = (id) => { clearTimeout(id); }; // Mock performance API global.performance = { now: () => Date.now(), mark: () => {}, measure: () => {}, getEntriesByType: () => [], getEntriesByName: () => [] }; // Mock IntersectionObserver global.IntersectionObserver = class IntersectionObserver { constructor(callback, options = {}) { this.callback = callback; this.options = options; this.elements = new Set(); } observe(element) { this.elements.add(element); } unobserve(element) { this.elements.delete(element); } disconnect() { this.elements.clear(); } // Helper for testing trigger(entries) { this.callback(entries); } }; // Mock ResizeObserver global.ResizeObserver = class ResizeObserver { constructor(callback) { this.callback = callback; this.elements = new Set(); } observe(element) { this.elements.add(element); } unobserve(element) { this.elements.delete(element); } disconnect() { this.elements.clear(); } }; // Mock navigator Object.defineProperty(global.navigator, 'connection', { value: { effectiveType: '4g' }, writable: true, configurable: true }); // Mock console methods for testing const originalConsole = { ...console }; global.console = { ...console, // Keep error/warn for debugging error: originalConsole.error, warn: originalConsole.warn, // Mock log/info to avoid spam log: () => {}, info: () => {} }; // Global test helpers global.createMockElement = (tagName = 'div', attributes = {}) => { const element = document.createElement(tagName); Object.entries(attributes).forEach(([key, value]) => { if (key.startsWith('data-')) { element.dataset[key.replace('data-', '')] = value; } else { element.setAttribute(key, value); } }); return element; }; global.waitForAnimationFrame = () => { return new Promise(resolve => requestAnimationFrame(resolve)); }; global.waitFor = (ms) => { return new Promise(resolve => setTimeout(resolve, ms)); }; // Module test helpers global.createMockModule = (name, overrides = {}) => { // Create mock functions manually (jest.fn() not available here) const createMockFn = () => { const fn = (...args) => {}; fn.mock = { calls: [], results: [] }; return fn; }; return { init: createMockFn(), destroy: createMockFn(), definition: { name, version: '1.0.0', dependencies: [], provides: [], priority: 0 }, ...overrides }; }; // State test helpers global.createMockStateManager = () => { const state = new Map(); const subscribers = new Map(); // Create mock functions manually (jest.fn() not available here) const createMockFn = (impl) => { const fn = (...args) => impl(...args); fn.mock = { calls: [], results: [] }; return fn; }; return { register: createMockFn((key, defaultValue) => { state.set(key, defaultValue); subscribers.set(key, []); }), get: createMockFn((key) => state.get(key)), set: createMockFn((key, value) => { const oldValue = state.get(key); state.set(key, value); // Trigger subscribers const subs = subscribers.get(key) || []; subs.forEach(callback => callback(value, oldValue, key)); return true; }), subscribe: createMockFn((key, callback) => { if (!subscribers.has(key)) { subscribers.set(key, []); } subscribers.get(key).push(callback); return `sub_${key}_${Date.now()}`; }), unsubscribe: createMockFn(() => {}), cleanup: createMockFn(() => {}), reset: createMockFn(() => { state.clear(); subscribers.clear(); }) }; }; // Clean up after each test if (typeof afterEach !== 'undefined') { afterEach(() => { // Reset DOM document.body.innerHTML = ''; document.head.innerHTML = ''; // Clear all timers if (typeof jest !== 'undefined' && jest.clearAllTimers) { jest.clearAllTimers(); } // Clear all mocks if (typeof jest !== 'undefined' && jest.clearAllMocks) { jest.clearAllMocks(); } }); }