import { afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, render, screen, fireEvent, waitFor } from "@testing-library/react"; import { StateProvider } from "@/components/state-provider"; import { CliProfileEditor } from "./cli-profile-editor"; import { agentProfileId as toAgentProfileId } from "@/lib/types/ids"; vi.mock("sonner", () => ({ toast: { success: vi.fn(), error: vi.fn() } })); const updateActionMock = vi.fn(); const createProfileActionMock = vi.fn(); const createAgentActionMock = vi.fn(); const MODEL_ID = "claude-sonnet-4-5"; const CREATED_AT = "2026-01-01T00:00:00Z"; const UPDATED_AT = "2026-05-04T00:00:00Z"; vi.mock("@/app/actions/agents", () => ({ updateAgentProfileAction: (...args: unknown[]) => updateActionMock(...args), createAgentProfileAction: (...args: unknown[]) => createProfileActionMock(...args), createAgentAction: (...args: unknown[]) => createAgentActionMock(...args), })); afterEach(() => { cleanup(); vi.clearAllMocks(); }); const baseAvailableAgent = { name: "claude", display_name: "Claude", supports_mcp: true, installation_paths: [], available: true, capabilities: { supports_session_resume: true, supports_shell: true, supports_workspace_only: false, }, model_config: { default_model: MODEL_ID, available_models: [ { id: MODEL_ID, name: "Claude Sonnet 4.5" }, { id: "claude-opus-4", name: "Claude Opus 4" }, ], supports_dynamic_models: false, }, permission_settings: {}, updated_at: UPDATED_AT, }; const agentWithRecommendedFlag = { ...baseAvailableAgent, name: "copilot", display_name: "Copilot", permission_settings: { allow_all_tools: { key: "allow_all_tools", label: "Allow all tools", description: "Allow Copilot to use tools without prompting.", supported: true, default: true, apply_method: "cli_flag", cli_flag: "--allow-all-tools", }, }, }; describe("CliProfileEditor", () => { it("renders the create form with the CLI client picker, profile name, and model selector", () => { render( , ); expect(screen.getByLabelText("Profile name")).toBeTruthy(); expect(screen.getByText("Create profile")).toBeTruthy(); }); it("shows existing profile values in edit mode", () => { const profile = { id: toAgentProfileId("p1"), name: "default", agentId: "claude", agentDisplayName: "Claude", model: MODEL_ID, mode: "", allowIndexing: false, autoApprove: false, cliFlags: [], cliPassthrough: false, createdAt: CREATED_AT, updatedAt: CREATED_AT, }; render( , ); const nameInput = screen.getByLabelText("Profile name") as HTMLInputElement; expect(nameInput.value).toBe("default"); expect(screen.getByText("Save profile")).toBeTruthy(); }); it("invokes onCancel when the Cancel button is clicked", () => { const onCancel = vi.fn(); render( , ); fireEvent.click(screen.getByText("Cancel")); expect(onCancel).toHaveBeenCalled(); }); }); describe("CliProfileEditor fallback settings", () => { it("keeps inline fallback settings collapsed and disables the explicit choice in automatic mode", () => { const profile = { id: toAgentProfileId("p-fallback"), name: "fallback", agentId: "claude", agentDisplayName: "Claude", model: MODEL_ID, fallbackModel: "claude-opus-4", autoFallback: true, mode: "", allowIndexing: false, autoApprove: false, cliFlags: [], cliPassthrough: false, createdAt: CREATED_AT, updatedAt: CREATED_AT, }; render( , ); const trigger = screen.getByTestId("profile-fallback-settings-trigger"); expect(trigger.getAttribute("aria-expanded")).toBe("false"); expect(screen.getByTestId("profile-fallback-settings-summary").textContent).toContain( "Automatic fallback enabled", ); fireEvent.click(trigger); const fallbackSwitch = screen.getByRole("switch", { name: "Agent fallback" }); expect((fallbackSwitch as HTMLButtonElement).disabled).toBe(true); const modelComboboxes = screen.getAllByTestId("profile-model-combobox-trigger"); expect((modelComboboxes.at(-1) as HTMLButtonElement).disabled).toBe(true); }); it("keeps dormant fallback values while exact model mode is enabled", () => { const profile = { id: toAgentProfileId("p-strict"), name: "strict", agentId: "claude", agentDisplayName: "Claude", model: MODEL_ID, fallbackModel: "claude-opus-4", autoFallback: true, requireExactModel: true, mode: "", allowIndexing: false, autoApprove: false, cliFlags: [], cliPassthrough: false, createdAt: CREATED_AT, updatedAt: CREATED_AT, }; render( , ); expect(screen.getByTestId("profile-fallback-settings-summary").textContent).toContain( "Exact model required", ); fireEvent.click(screen.getByTestId("profile-fallback-settings-trigger")); expect( screen.getByRole("switch", { name: "Fallback automatically to next model" }), ).toHaveProperty("disabled", true); expect(screen.getByRole("switch", { name: "Agent fallback" })).toHaveProperty("disabled", true); }); }); describe("CliProfileEditor recommended flags", () => { it("can hide passthrough while showing recommended CLI flags", () => { render( , ); expect(screen.queryByText("CLI passthrough")).toBeNull(); expect(screen.getByText("Allow all tools")).toBeTruthy(); }); it("saves seeded recommended CLI flags for new profiles", async () => { createAgentActionMock.mockResolvedValue({ profiles: [ { id: "profile-copilot", name: "default", agentId: "copilot", agentDisplayName: "Copilot", model: MODEL_ID, cliFlags: [], cliPassthrough: false, allowIndexing: false, autoApprove: false, createdAt: CREATED_AT, updatedAt: CREATED_AT, }, ], }); render( , ); fireEvent.click(screen.getByText("Create profile")); await waitFor(() => expect(createAgentActionMock).toHaveBeenCalled()); expect(createAgentActionMock.mock.calls[0][0].profiles[0].cli_flags).toEqual([ { description: "Allow Copilot to use tools without prompting.", enabled: true, flag: "--allow-all-tools", }, ]); }); });