import { fireEvent, screen, waitFor } from "@testing-library/react"; import { describe, expect, test } from "bun:test"; import { createElement } from "react"; import type { Provider } from "../../../src/shared/api"; import { App } from "../../../src/web/app"; import { ProviderFormModal } from "../../../src/web/features/models/components/ProviderFormModal"; import { installFetchMock, jsonResponse, mockMetaResponse, renderWithProviders } from "../test-utils"; const ENABLED_PROVIDER: Provider = { apiKey: "sk-test", baseUrl: "https://api.openai.com/v1", createdAt: "2024-01-01T00:00:00.000Z", id: "pv1", name: "OpenAI", type: "openai", updatedAt: "2024-01-01T00:00:00.000Z", }; function clickLatestConfirmButton() { const buttons = screen.getAllByRole("button", { name: /OK|确\s*定/ }); fireEvent.click(buttons[buttons.length - 1]!); } describe("ProviderFormModal", () => { test("编辑供应商表单只提交变更字段", async () => { const updateCalls: unknown[] = []; renderWithProviders( createElement(ProviderFormModal, { editingProvider: ENABLED_PROVIDER, onCancel: () => undefined, onCreate: () => Promise.resolve(), onOpenChange: () => undefined, onTest: () => Promise.resolve({ message: "连接成功", ok: true }), onUpdate: (args: unknown) => { updateCalls.push(args); return Promise.resolve(); }, open: true, submitting: false, }), ); await waitFor(() => expect(screen.getByPlaceholderText("请输入供应商名称")).not.toBeNull()); fireEvent.change(screen.getByPlaceholderText("请输入供应商名称"), { target: { value: "New OpenAI" } }); clickLatestConfirmButton(); await waitFor(() => expect(updateCalls.length).toBe(1)); expect(updateCalls[0]).toEqual({ data: { name: "New OpenAI" }, id: "pv1" }); }); test("新建供应商默认使用 openai-compatible 类型", async () => { const createCalls: unknown[] = []; renderWithProviders( createElement(ProviderFormModal, { editingProvider: null, onCancel: () => undefined, onCreate: (data: unknown) => { createCalls.push(data); return Promise.resolve(); }, onOpenChange: () => undefined, onTest: () => Promise.resolve({ message: "连接成功", ok: true }), onUpdate: () => Promise.resolve(), open: true, submitting: false, }), ); await waitFor(() => expect(screen.getByPlaceholderText("请输入供应商名称")).not.toBeNull()); fireEvent.change(screen.getByPlaceholderText("请输入供应商名称"), { target: { value: "兼容供应商" } }); fireEvent.change(screen.getByPlaceholderText("https://api.openai.com/v1"), { target: { value: "https://api.test.com/v1" }, }); fireEvent.change(screen.getByPlaceholderText("请输入 API Key"), { target: { value: "sk-test" } }); clickLatestConfirmButton(); await waitFor(() => expect(createCalls.length).toBe(1)); expect(createCalls[0]).toEqual({ apiKey: "sk-test", baseUrl: "https://api.test.com/v1", name: "兼容供应商", type: "openai-compatible", }); }); test("供应商表单可使用当前表单配置测试连接", async () => { const testCalls: unknown[] = []; renderWithProviders( createElement(ProviderFormModal, { editingProvider: null, onCancel: () => undefined, onCreate: () => Promise.resolve(), onOpenChange: () => undefined, onTest: (data: unknown) => { testCalls.push(data); return Promise.resolve({ message: "连接成功", ok: true }); }, onUpdate: () => Promise.resolve(), open: true, submitting: false, }), ); await waitFor(() => expect(screen.getByPlaceholderText("请输入供应商名称")).not.toBeNull()); fireEvent.change(screen.getByPlaceholderText("请输入供应商名称"), { target: { value: "兼容供应商" } }); fireEvent.change(screen.getByPlaceholderText("https://api.openai.com/v1"), { target: { value: "https://api.test.com/v1" }, }); fireEvent.change(screen.getByPlaceholderText("请输入 API Key"), { target: { value: "sk-test" } }); fireEvent.click(screen.getByRole("button", { name: "测试连接" })); await waitFor(() => expect(testCalls.length).toBe(1)); expect(testCalls[0]).toEqual({ apiKey: "sk-test", baseUrl: "https://api.test.com/v1", name: "兼容供应商", type: "openai-compatible", }); }); }); const TEST_PROVIDER: Provider = { apiKey: "sk-test", baseUrl: "https://api.openai.com/v1", createdAt: "2024-01-01T00:00:00.000Z", id: "pv1", name: "OpenAI", type: "openai", updatedAt: "2024-01-01T00:00:00.000Z", }; function createProviderFetchMock() { let providers = [TEST_PROVIDER]; return installFetchMock((call) => { if (call.url.includes("/api/meta")) return mockMetaResponse(); const url = new URL(call.url, "http://localhost"); if (url.pathname === "/api/providers" && call.method === "POST") { const data = JSON.parse(typeof call.body === "string" ? call.body : "{}") as Record; const created: Provider = { ...TEST_PROVIDER, ...data, createdAt: "2024-01-02T00:00:00.000Z", id: "pv-new", updatedAt: "2024-01-02T00:00:00.000Z", }; providers = [created, ...providers]; return jsonResponse({ provider: created }, { status: 201 }); } if (/^\/api\/providers\/[^/]+$/.exec(url.pathname) && call.method === "PATCH") { const id = url.pathname.split("/").pop()!; const data = JSON.parse(typeof call.body === "string" ? call.body : "{}") as Record; const existing = providers.find((p) => p.id === id) ?? TEST_PROVIDER; const updated = { ...existing, ...(data as Partial) }; providers = providers.map((p) => (p.id === id ? updated : p)); return jsonResponse({ provider: updated }); } if (/^\/api\/providers\/[^/]+$/.exec(url.pathname) && call.method === "DELETE") { const id = url.pathname.split("/").pop()!; providers = providers.filter((p) => p.id !== id); return new Response(null, { status: 204 }); } if (url.pathname === "/api/providers" && call.method === "GET") { const keyword = url.searchParams.get("keyword") ?? ""; const items = keyword ? providers.filter((p) => p.name.includes(keyword)) : providers; return jsonResponse({ items, page: 1, pageSize: 20, total: items.length }); } if (/\/api\/providers\/[^/]+\/test$/.exec(url.pathname) && call.method === "POST") { return jsonResponse({ message: "连接成功", ok: true }); } return jsonResponse({ error: "Not Found" }, { status: 404 }); }); } describe("ProviderListPage", () => { test("渲染供应商列表页并请求供应商数据", async () => { const calls = createProviderFetchMock(); renderWithProviders(createElement(App), { initialRoute: "/models/providers" }); await waitFor(() => { expect(screen.getAllByText("OpenAI").length).toBeGreaterThan(0); }); expect(screen.getByPlaceholderText("搜索供应商名称")).not.toBeNull(); expect(screen.getByRole("button", { name: /新建供应商/ })).not.toBeNull(); expect(calls.some((call) => call.url.includes("/api/providers"))).toBe(true); }, 15000); test("搜索供应商更新请求参数", async () => { const calls = createProviderFetchMock(); renderWithProviders(createElement(App), { initialRoute: "/models/providers" }); await waitFor(() => expect(screen.getAllByText("OpenAI").length).toBeGreaterThan(0)); fireEvent.change(screen.getByPlaceholderText("搜索供应商名称"), { target: { value: "Open" } }); fireEvent.click(screen.getByRole("button", { name: /搜\s*索/ })); await waitFor(() => expect(calls.some((call) => call.url.includes("keyword=Open"))).toBe(true)); }, 15000); test("新建供应商弹窗可以打开", async () => { createProviderFetchMock(); renderWithProviders(createElement(App), { initialRoute: "/models/providers" }); await waitFor(() => expect(screen.getByRole("button", { name: /新建供应商/ })).not.toBeNull()); fireEvent.click(screen.getByRole("button", { name: /新建供应商/ })); await waitFor(() => expect(screen.getByPlaceholderText("请输入供应商名称")).not.toBeNull()); }, 15000); });