import '@testing-library/jest-dom'; import { afterEach, beforeAll } from 'vitest'; import { cleanup } from '@testing-library/react'; import { vi } from 'vitest'; // Re-export test utilities for convenience export * from './test-utils'; // Override render globally to use our wrapper by default // This ensures all tests have Router and Toast context beforeAll(() => { // Import and setup test utilities // The wrapper will be used automatically via test-utils.tsx }); // Mock BroadcastChannel to avoid serialization issues in tests if (typeof globalThis.BroadcastChannel === 'undefined') { globalThis.BroadcastChannel = class MockBroadcastChannel { postMessage = vi.fn(); close = vi.fn(); addEventListener = vi.fn(); removeEventListener = vi.fn(); onmessage = null; constructor(public name: string) {} } as unknown as typeof BroadcastChannel; } // Cleanup après chaque test afterEach(() => { cleanup(); }); // Mock des APIs du navigateur Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockImplementation((query) => ({ matches: false, media: query, onchange: null, addListener: vi.fn(), removeListener: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), })), }); // Mock localStorage avec stockage réel en mémoire const localStorageMock = (() => { let store: Record = {}; return { getItem: (key: string) => { return store[key] || null; }, setItem: (key: string, value: string) => { store[key] = String(value); }, removeItem: (key: string) => { delete store[key]; }, clear: () => { store = {}; }, }; })(); Object.defineProperty(window, 'localStorage', { value: localStorageMock, }); // Mock FileReader pour les tests class MockFileReader { result: string | null = null; onload: ((event: { target: MockFileReader }) => void) | null = null; onerror: (() => void) | null = null; readAsDataURL(file: File) { // Simuler un résultat immédiat this.result = `data:${file.type};base64,test`; // Utiliser Promise.resolve().then() pour simuler l'asynchrone de manière fiable Promise.resolve().then(() => { if (this.onload) { this.onload({ target: this }); } }); } } Object.defineProperty(window, 'FileReader', { writable: true, value: MockFileReader, }); // Mock WebSocket class MockWebSocket { static CONNECTING = 0; static OPEN = 1; static CLOSING = 2; static CLOSED = 3; readyState = MockWebSocket.CONNECTING; url: string; onopen: ((event: Event) => void) | null = null; onclose: ((event: CloseEvent) => void) | null = null; onmessage: ((event: MessageEvent) => void) | null = null; onerror: ((event: Event) => void) | null = null; private listeners: Map> = new Map(); constructor(url: string) { this.url = url; // Simuler une connexion réussie après un court délai setTimeout(() => { this.readyState = MockWebSocket.OPEN; this.onopen?.(new Event('open')); }, 100); } send(_data: string) { // Mock de l'envoi de données } close() { this.readyState = MockWebSocket.CLOSED; this.onclose?.(new CloseEvent('close')); } addEventListener(type: string, listener: EventListener) { if (!this.listeners.has(type)) { this.listeners.set(type, new Set()); } this.listeners.get(type)?.add(listener); } removeEventListener(type: string, listener: EventListener) { this.listeners.get(type)?.delete(listener); } dispatchEvent(event: Event) { const listeners = this.listeners.get(event.type); if (listeners) { listeners.forEach((listener) => { if (typeof listener === 'function') { listener(event); } }); } return true; } } Object.defineProperty(window, 'WebSocket', { value: MockWebSocket, }); // Mock HTMLCanvasElement.getContext (JSDOM does not implement it) HTMLCanvasElement.prototype.getContext = function ( this: HTMLCanvasElement, contextId: string ): CanvasRenderingContext2D | null { if (contextId === '2d') { return { canvas: this, fillRect: vi.fn(), clearRect: vi.fn(), getImageData: vi.fn(() => ({ data: new Uint8ClampedArray(0), width: 0, height: 0 })), putImageData: vi.fn(), createImageData: vi.fn(() => ({ data: new Uint8ClampedArray(0), width: 0, height: 0 })), setTransform: vi.fn(), drawImage: vi.fn(), save: vi.fn(), restore: vi.fn(), beginPath: vi.fn(), moveTo: vi.fn(), lineTo: vi.fn(), closePath: vi.fn(), stroke: vi.fn(), translate: vi.fn(), scale: vi.fn(), rotate: vi.fn(), arc: vi.fn(), fill: vi.fn(), measureText: vi.fn(() => ({ width: 0 })), transform: vi.fn(), rect: vi.fn(), clip: vi.fn(), } as unknown as CanvasRenderingContext2D; } return null; }; // Mock HTMLMediaElement.pause (JSDOM does not implement it) HTMLMediaElement.prototype.pause = vi.fn(); Object.defineProperty(HTMLMediaElement.prototype, 'paused', { get: () => true, configurable: true, }); // Mock des variables d'environnement Object.defineProperty(import.meta, 'env', { value: { VITE_API_URL: 'http://localhost:8080/api/v1', VITE_WS_URL: 'ws://localhost:8081/ws', VITE_APP_NAME: 'Veza', VITE_DEBUG: 'true', VITE_STORYBOOK: undefined, // Ensure toasts render in tests (not storybook mode) }, });