- 移除供应商/模型启用禁用能力,清理DB schema/migration/API/前端 - 供应商测试改为Base URL连通性+/models探测 - 新增POST /api/models/test模型连接测试 - 新增GET /api/providers/options专用供应商选项接口 - 统一工具栏为ModelsToolbar,参考项目管理布局 - 模型弹窗优化:默认能力、响应式3列标签、并排数值 - 前后端正整数校验、供应商下拉loading/error/empty状态 - 表格列宽统一,操作列/名称列固定宽度
102 lines
3.0 KiB
TypeScript
102 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";
|
|
|
|
const REAL_FETCH = globalThis.fetch.bind(globalThis);
|
|
|
|
// 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 FetchMockCall {
|
|
body?: BodyInit | null;
|
|
method: string;
|
|
url: string;
|
|
}
|
|
|
|
export interface RenderWithProvidersOptions {
|
|
initialRoute?: string;
|
|
}
|
|
|
|
export function installFetchMock(handler: (call: FetchMockCall) => Promise<Response> | Response): FetchMockCall[] {
|
|
const calls: FetchMockCall[] = [];
|
|
const mocked = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const request = input instanceof Request ? input : undefined;
|
|
const url = request?.url ?? (typeof input === "string" ? input : input instanceof URL ? input.href : input.url);
|
|
if (url.startsWith("http://") || url.startsWith("https://")) return REAL_FETCH(input, init);
|
|
const call: FetchMockCall = {
|
|
body: init?.body ?? null,
|
|
method: init?.method ?? request?.method ?? "GET",
|
|
url,
|
|
};
|
|
calls.push(call);
|
|
return handler(call);
|
|
}) as typeof fetch;
|
|
|
|
globalThis.fetch = mocked;
|
|
window.fetch = mocked;
|
|
return calls;
|
|
}
|
|
|
|
export function jsonResponse(body: unknown, init?: ResponseInit): Response {
|
|
const headers = new Headers(init?.headers);
|
|
if (!headers.has("Content-Type")) headers.set("Content-Type", "application/json");
|
|
return new Response(JSON.stringify(body), {
|
|
headers,
|
|
status: init?.status ?? 200,
|
|
});
|
|
}
|
|
|
|
export function mockMetaResponse(): Response {
|
|
return jsonResponse({ ok: true, service: "test-app", timestamp: "2024-01-01T00:00:00.000Z", version: "0.1.0" });
|
|
}
|
|
|
|
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,
|
|
},
|
|
},
|
|
});
|
|
}
|