import { fireEvent, render, screen } from "@testing-library/react"; import type { ComponentProps, PropsWithChildren } from "react"; import { useForm } from "react-hook-form"; import { describe, expect, it, vi } from "vitest"; import { OpenCodeFormFields } from "@/components/providers/forms/OpenCodeFormFields"; import { Form } from "@/components/ui/form"; type OpenCodeFormFieldsProps = ComponentProps; const FormShell = ({ children }: PropsWithChildren) => { const form = useForm(); return
{children}
; }; const renderOpenCodeForm = ( overrides: Partial = {}, ) => { const props: OpenCodeFormFieldsProps = { npm: "@ai-sdk/openai-compatible", onNpmChange: vi.fn(), apiKey: "sk-test", onApiKeyChange: vi.fn(), category: "custom", shouldShowApiKeyLink: false, websiteUrl: "", baseUrl: "https://api.example.com/v1", onBaseUrlChange: vi.fn(), headers: {}, onHeadersChange: vi.fn(), models: { "kimi-k2": { name: "Kimi K2", limit: { context: 1048576, output: 131072 }, }, }, onModelsChange: vi.fn(), extraOptions: {}, onExtraOptionsChange: vi.fn(), ...overrides, }; return { props, ...render( , ), }; }; const expandFirstModel = () => { fireEvent.click(screen.getByRole("button", { name: "Toggle model details" })); }; describe("OpenCodeFormFields", () => { it("surfaces existing provider headers", () => { renderOpenCodeForm({ headers: { "HTTP-Referer": "https://cc-switch.app", "X-Title": "CC Switch", }, }); expect(screen.getByDisplayValue("HTTP-Referer")).toBeInTheDocument(); expect( screen.getByDisplayValue("https://cc-switch.app"), ).toBeInTheDocument(); expect(screen.getByDisplayValue("X-Title")).toBeInTheDocument(); expect(screen.getByDisplayValue("CC Switch")).toBeInTheDocument(); }); it("updates provider headers", () => { const onHeadersChange = vi.fn(); renderOpenCodeForm({ headers: { "X-Title": "CC Switch" }, onHeadersChange, }); fireEvent.change(screen.getByDisplayValue("CC Switch"), { target: { value: "OpenCode" }, }); expect(onHeadersChange).toHaveBeenCalledWith({ "X-Title": "OpenCode", }); }); it("shows a blank header name for newly added headers", () => { const onHeadersChange = vi.fn(); const { rerender, props } = renderOpenCodeForm({ onHeadersChange }); fireEvent.click(screen.getByRole("button", { name: "Add header" })); const nextHeaders = onHeadersChange.mock.calls[0][0]; const headerKey = Object.keys(nextHeaders)[0]; expect(headerKey).toMatch(/^draft-header:/); rerender( , ); expect(screen.getByPlaceholderText("X-Title")).toHaveValue(""); }); it("removes provider headers", () => { const onHeadersChange = vi.fn(); renderOpenCodeForm({ headers: { "X-Title": "CC Switch" }, onHeadersChange, }); fireEvent.click(screen.getByRole("button", { name: "Remove header" })); expect(onHeadersChange).toHaveBeenCalledWith({}); }); it("rejects case-insensitive duplicate header names and restores the input", () => { const onHeadersChange = vi.fn(); renderOpenCodeForm({ headers: { "X-A": "A", "X-B": "B" }, onHeadersChange, }); const keyInput = screen.getByDisplayValue("X-B"); fireEvent.change(keyInput, { target: { value: "x-a" } }); fireEvent.blur(keyInput); expect(onHeadersChange).not.toHaveBeenCalled(); expect(keyInput).toHaveValue("X-B"); }); it("surfaces provider options whose names start with option-", () => { renderOpenCodeForm({ extraOptions: { "option-mode": "legacy" }, }); expect(screen.getByDisplayValue("option-mode")).toBeInTheDocument(); expect(screen.getByDisplayValue("legacy")).toBeInTheDocument(); }); it("surfaces existing model token limits", () => { renderOpenCodeForm(); expandFirstModel(); expect(screen.getByLabelText("Context")).toHaveValue(1048576); expect(screen.getByLabelText("Output")).toHaveValue(131072); }); it("updates model token limits as structured numbers", () => { const onModelsChange = vi.fn(); renderOpenCodeForm({ onModelsChange }); expandFirstModel(); fireEvent.change(screen.getByLabelText("Context"), { target: { value: "2000000" }, }); expect(onModelsChange).toHaveBeenCalledWith({ "kimi-k2": { name: "Kimi K2", limit: { context: 2000000, output: 131072 }, }, }); }); it("removes model limit when both fields are cleared", () => { const onModelsChange = vi.fn(); const { rerender, props } = renderOpenCodeForm({ onModelsChange }); expandFirstModel(); fireEvent.change(screen.getByLabelText("Context"), { target: { value: "" }, }); const withoutContext = { "kimi-k2": { name: "Kimi K2", limit: { output: 131072 }, }, }; expect(onModelsChange).toHaveBeenLastCalledWith(withoutContext); rerender( , ); fireEvent.change(screen.getByLabelText("Output"), { target: { value: "" }, }); expect(onModelsChange).toHaveBeenLastCalledWith({ "kimi-k2": { name: "Kimi K2", }, }); }); });