119 lines
4.9 KiB
TypeScript
119 lines
4.9 KiB
TypeScript
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
import { screen, waitFor } from "@testing-library/react";
|
|
import { renderWithProviders } from "@/test/utils";
|
|
import userEvent from "@testing-library/user-event";
|
|
|
|
const listProviders = vi.fn();
|
|
const createProvider = vi.fn();
|
|
const deleteProvider = vi.fn();
|
|
const applyProvider = vi.fn();
|
|
const restore = vi.fn();
|
|
const getStatus = vi.fn();
|
|
const getSettings = vi.fn();
|
|
const listLogs = vi.fn();
|
|
const listBackups = vi.fn();
|
|
const updateProvider = vi.fn();
|
|
const updateSettings = vi.fn();
|
|
|
|
vi.mock("@/lib/api", () => ({
|
|
api: {
|
|
listProviders: () => listProviders(),
|
|
createProvider: (input: unknown) => createProvider(input),
|
|
updateProvider: (id: string, input: unknown) => updateProvider(id, input),
|
|
deleteProvider: (id: string) => deleteProvider(id),
|
|
applyProvider: (id: string, target: string) => applyProvider(id, target),
|
|
restore: (input: unknown) => restore(input),
|
|
getStatus: () => getStatus(),
|
|
getSettings: () => getSettings(),
|
|
updateSettings: (patch: unknown) => updateSettings(patch),
|
|
listLogs: () => listLogs(),
|
|
listBackups: () => listBackups(),
|
|
},
|
|
}));
|
|
|
|
import Providers from "./Providers";
|
|
|
|
const provider = {
|
|
id: "p_1",
|
|
name: "DeepSeek",
|
|
preset: "deepseek",
|
|
base_url: "https://api.deepseek.com/v1",
|
|
api_key: "sk-x",
|
|
models: { default: "deepseek-chat", sonnet: "deepseek-chat", haiku: "deepseek-chat", opus: "deepseek-chat" },
|
|
enabled_for_claude: false,
|
|
target: "claude",
|
|
created_at: "2026-10-18T00:00:00Z",
|
|
};
|
|
|
|
function renderPage() {
|
|
return renderWithProviders(<Providers />);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
listProviders.mockResolvedValue({ items: [provider] });
|
|
createProvider.mockResolvedValue({ ...provider, id: "p_2" });
|
|
deleteProvider.mockResolvedValue({ deleted: "p_1" });
|
|
applyProvider.mockResolvedValue({ provider, env: { ANTHROPIC_BASE_URL: "http://127.0.0.1:1234" } });
|
|
});
|
|
|
|
describe("Providers page", () => {
|
|
it("renders the provider list", async () => {
|
|
renderPage();
|
|
expect(await screen.findByText("DeepSeek")).toBeInTheDocument();
|
|
expect(screen.getByText("https://api.deepseek.com/v1")).toBeInTheDocument();
|
|
});
|
|
|
|
it("validates the create form before submitting", async () => {
|
|
const user = userEvent.setup();
|
|
renderPage();
|
|
await user.click(await screen.findByRole("button", { name: /新增供应商|Add provider/ }));
|
|
await user.click(screen.getByRole("button", { name: /保存|Save/ }));
|
|
expect(await screen.findByText(/请填写名称|Name is required/)).toBeInTheDocument();
|
|
expect(await screen.findByText(/请填写 http|http\(s\) base URL is required/)).toBeInTheDocument();
|
|
expect(createProvider).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("creates a provider with the preset defaults", async () => {
|
|
const user = userEvent.setup();
|
|
renderPage();
|
|
await user.click(await screen.findByRole("button", { name: /新增供应商|Add provider/ }));
|
|
await user.type(screen.getByLabelText(/名称|Name/), "Kimi");
|
|
await user.type(screen.getByLabelText(/Base URL/), "https://api.moonshot.cn/v1");
|
|
await user.type(screen.getByLabelText(/API Key/), "sk-kimi");
|
|
await user.click(screen.getByRole("button", { name: /保存|Save/ }));
|
|
await waitFor(() => expect(createProvider).toHaveBeenCalledTimes(1));
|
|
const input = createProvider.mock.calls[0][0];
|
|
expect(input).toMatchObject({
|
|
name: "Kimi",
|
|
base_url: "https://api.moonshot.cn/v1",
|
|
api_key: "sk-kimi",
|
|
target: "claude",
|
|
});
|
|
});
|
|
|
|
it("activates a provider and shows the applied env", async () => {
|
|
const user = userEvent.setup();
|
|
renderPage();
|
|
await user.click(await screen.findByRole("button", { name: /激活并写入|Activate & apply/ }));
|
|
await waitFor(() => expect(applyProvider).toHaveBeenCalledWith("p_1", "claude"));
|
|
expect(await screen.findByText(/ANTHROPIC_BASE_URL/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("submits exact model mappings from the mapping editor", async () => {
|
|
const user = userEvent.setup();
|
|
renderPage();
|
|
await user.click(await screen.findByRole("button", { name: /新增供应商|Add provider/ }));
|
|
await user.click(screen.getByRole("button", { name: /添加一行|Add row/ }));
|
|
await user.type(screen.getByPlaceholderText(/请求的模型名|Requested model/), "gpt-5-codex");
|
|
await user.type(screen.getByPlaceholderText(/上游模型名|Upstream model/), "deepseek-chat");
|
|
await user.type(screen.getByLabelText(/名称|Name/), "Kimi");
|
|
await user.type(screen.getByLabelText(/Base URL/), "https://api.moonshot.cn/v1");
|
|
await user.type(screen.getByLabelText(/API Key/), "sk-kimi");
|
|
await user.click(screen.getByRole("button", { name: /保存|Save/ }));
|
|
await waitFor(() => expect(createProvider).toHaveBeenCalledTimes(1));
|
|
const input = createProvider.mock.calls[0][0];
|
|
expect(input.models.extra).toEqual({ "gpt-5-codex": "deepseek-chat" });
|
|
});
|
|
});
|