import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { render } from "@testing-library/react"; import { mock } from "bun:test"; import { createElement, StrictMode } from "react"; import { MemoryRouter } from "react-router"; import { ErrorBoundary } from "../../src/web/components/ErrorBoundary"; // Mock recharts BEFORE any component imports void mock.module("recharts", () => ({ Area: () => null, CartesianGrid: () => null, Line: () => null, LineChart: ({ children }: { children: unknown }) => children, ResponsiveContainer: ({ children }: { children: unknown }) => children, Tooltip: () => null, XAxis: () => null, YAxis: () => null, })); export interface RenderWithProvidersOptions { initialRoute?: string; } export function renderWithProviders(ui: React.ReactElement, options?: RenderWithProvidersOptions) { const queryClient = createTestQueryClient(); const initialRoute = options?.initialRoute ?? "/"; return render( createElement( StrictMode, null, createElement( ErrorBoundary, null, createElement( QueryClientProvider, { client: queryClient }, createElement(MemoryRouter, { initialEntries: [initialRoute] }, ui), ), ), ), ); } function createTestQueryClient() { return new QueryClient({ defaultOptions: { queries: { retry: false, staleTime: 0, }, }, }); } // Custom test helpers (替代 jest-dom matchers) export const testHelpers = { toBeInTheDocument: (element: Element | null) => { const pass = element !== null && document.contains(element); return { message: () => (pass ? "Expected element not to be in document" : "Expected element to be in document"), pass, }; }, toHaveAttribute: (element: Element | null, attr: string, value?: string) => { const pass = value === undefined ? (element?.hasAttribute(attr) ?? false) : element?.getAttribute(attr) === value; return { message: () => pass ? `Expected element not to have attribute "${attr}"` : `Expected element to have attribute "${attr}"`, pass, }; }, toHaveClass: (element: Element | null, className: string) => { const pass = element?.classList.contains(className) ?? false; return { message: () => pass ? `Expected element not to have class "${className}"` : `Expected element to have class "${className}"`, pass, }; }, toHaveTextContent: (element: Element | null, text: RegExp | string) => { const content = element?.textContent ?? ""; const pass = element !== null && (typeof text === "string" ? content.includes(text) : text.test(content)); return { message: () => (pass ? `Expected element not to have text "${text}"` : `Expected element to have text "${text}"`), pass, }; }, };