Files
Alfred/tests/web/test-utils.tsx
lanyuanxiaoyao 15ba33db27 refactor: 前端 UI 框架从 TDesign 迁移到 antd 6.x
- 移除 tdesign-react + tdesign-icons-react,新增 antd@6.4.3 + @ant-design/icons@6.2.3
- Layout/Header/Sider/Content 替换 TDesign Layout,Sider 内置折叠管理
- Segmented 替换 RadioGroup 主题切换,ConfigProvider 主题算法切换
- Menu items prop 模式,Sidebar 简化为无 props 纯组件
- Table/Modal/Form/Input.TextArea/Tabs/Tag/Popconfirm 全量迁移
- App.useApp().message 替换 MessagePlugin(hooks 模式)
- --td-* CSS 变量替换为 --ant-* antd CSS 变量
- 测试适配:ConfigProvider+App wrapper,.ant-menu-item-selected,antd CSS-in-JS jsdom 兼容
- 文档更新:frontend.md, development/README.md, config.yaml, deploy.md
- vendor-antd chunk 755KB gzipped 240KB
2026-05-28 13:20:12 +08:00

96 lines
3.0 KiB
TypeScript

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render } from "@testing-library/react";
import { App, ConfigProvider } from "antd";
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] },
createElement(ConfigProvider, null, createElement(App, null, 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,
};
},
};