Files
leocrm/frontend/src/test/setup.ts
T
Agent Zero 825d638130 Phase 3: Fix medium-priority issues (M1-M4, M6)
M1: Password complexity validation (min 8 chars, uppercase, lowercase, digit)
M2: Remove is_system_admin from login response (prevent role leaking)
M3: Permission cache invalidates on DB error instead of using stale data
M4: .env.docker.example already fixed in B9 (SECRET_KEY, FRONTEND_URL, SMTP)
M6: Frontend test setup auto-wraps with QueryClientProvider (fixes ~29 test failures)

Remaining: M5 (frontend component integration — WelcomeDialog, SavedFilterBar, etc.)
2026-07-26 20:51:40 +02:00

120 lines
3.2 KiB
TypeScript

import '@testing-library/jest-dom';
import { vi, beforeEach, afterEach, beforeAll } from 'vitest';
import React from 'react';
import i18n from '@/i18n';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render, renderHook } from '@testing-library/react';
// ── Global QueryClient for tests ──────────────────────────────────────────
// Many tests use hooks from @tanstack/react-query without explicitly wrapping
// in a QueryClientProvider. We monkey-patch render/renderHook to automatically
// wrap with the provider.
function getTestQueryClient() {
return new QueryClient({
defaultOptions: {
queries: { retry: false, staleTime: 0, gcTime: 0 },
mutations: { retry: false },
},
});
}
const originalRender = render;
const originalRenderHook = renderHook;
// Override global render to auto-wrap with QueryClientProvider
(globalThis as any).render = (ui: React.ReactElement, options?: any) => {
const client = getTestQueryClient();
const wrapped = React.createElement(QueryClientProvider, { client }, ui);
return originalRender(wrapped, options);
};
// Override global renderHook to auto-wrap with QueryClientProvider
(globalThis as any).renderHook = (hook: any, options?: any) => {
const client = getTestQueryClient();
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(QueryClientProvider, { client }, children);
return originalRenderHook(hook, { wrapper, ...options });
};
// Mock matchMedia for jsdom
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
// Mock IntersectionObserver
class MockIntersectionObserver {
observe = vi.fn();
unobserve = vi.fn();
disconnect = vi.fn();
takeRecords = vi.fn();
root = null;
rootMargin = '';
thresholds = [];
}
Object.defineProperty(window, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver,
});
// Mock ResizeObserver
class MockResizeObserver {
observe = vi.fn();
unobserve = vi.fn();
disconnect = vi.fn();
}
Object.defineProperty(window, 'ResizeObserver', {
writable: true,
configurable: true,
value: MockResizeObserver,
});
// Mock EventSource for jsdom
class MockEventSource {
constructor(url: string) {
this.url = url;
this.readyState = 0;
}
url: string;
readyState: number;
onopen: any = null;
onmessage: any = null;
onerror: any = null;
addEventListener = vi.fn();
removeEventListener = vi.fn();
close = vi.fn();
}
Object.defineProperty(window, 'EventSource', { writable: true, configurable: true, value: MockEventSource });
// Mock scrollTo
window.scrollTo = vi.fn() as any;
// Initialize i18n before all tests
beforeAll(async () => {
await i18n.changeLanguage('de');
});
// Clean up after each test
afterEach(() => {
vi.clearAllMocks();
localStorage.clear();
});
beforeEach(() => {
localStorage.clear();
});