/** * @vitest-environment happy-dom */ import { act } from "react"; import { createRoot } from "react-dom/client"; import { describe, expect, it, vi } from "vitest"; import { AdaptiveThinkingEditor } from "@/app/[locale]/settings/providers/_components/adaptive-thinking-editor"; import type { AnthropicAdaptiveThinkingConfig } from "@/types/provider"; // Mock next-intl vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, })); // Mock UI components vi.mock("@/components/ui/select", () => ({ Select: ({ children, value, onValueChange, disabled, }: { children: React.ReactNode; value: string; onValueChange: (val: string) => void; disabled?: boolean; }) => (
), SelectTrigger: ({ children }: { children: React.ReactNode }) =>
{children}
, SelectValue: () => null, SelectContent: ({ children }: { children: React.ReactNode }) => <>{children}, SelectItem: ({ value, children }: { value: string; children: React.ReactNode }) => ( ), })); vi.mock("@/components/ui/switch", () => ({ Switch: ({ checked, onCheckedChange, disabled, }: { checked: boolean; onCheckedChange: (checked: boolean) => void; disabled?: boolean; }) => ( ), })); vi.mock("@/components/ui/tag-input", () => ({ TagInput: ({ value, onChange, disabled, placeholder, }: { value: string[]; onChange: (tags: string[]) => void; disabled?: boolean; placeholder?: string; }) => ( onChange(e.target.value.split(",").filter(Boolean))} disabled={disabled} placeholder={placeholder} /> ), })); vi.mock("@/components/ui/tooltip", () => ({ Tooltip: ({ children }: { children: React.ReactNode }) =>
{children}
, TooltipTrigger: ({ children }: { children: React.ReactNode }) =>
{children}
, TooltipContent: ({ children }: { children: React.ReactNode }) =>
{children}
, })); vi.mock("./forms/provider-form/components/section-card", () => ({ SmartInputWrapper: ({ label, children }: { label: string; children: React.ReactNode }) => (
{children}
), ToggleRow: ({ label, children }: { label: string; children: React.ReactNode }) => (
{children}
), })); // Mock lucide-react vi.mock("lucide-react", () => ({ Info: () =>
, })); function render(node: React.ReactNode) { const container = document.createElement("div"); document.body.appendChild(container); const root = createRoot(container); act(() => { root.render(node); }); return { container, unmount: () => { act(() => root.unmount()); container.remove(); }, }; } describe("AdaptiveThinkingEditor", () => { const defaultConfig: AnthropicAdaptiveThinkingConfig = { effort: "medium", modelMatchMode: "all", models: [], }; const mockOnEnabledChange = vi.fn(); const mockOnConfigChange = vi.fn(); it("renders correctly in disabled state (switch off)", () => { const { container, unmount } = render( ); const switchBtn = container.querySelector('[data-testid="switch"]'); expect(switchBtn).toBeTruthy(); expect(switchBtn?.textContent).toBe("Off"); expect(container.querySelector('[data-testid="select-trigger"]')).toBeNull(); unmount(); }); it("calls onEnabledChange when switch is clicked", () => { const { container, unmount } = render( ); const switchBtn = container.querySelector('[data-testid="switch"]') as HTMLButtonElement; act(() => { switchBtn.click(); }); expect(mockOnEnabledChange).toHaveBeenCalledWith(true); unmount(); }); it("renders configuration options when enabled", () => { const { container, unmount } = render( ); const switchBtn = container.querySelector('[data-testid="switch"]'); expect(switchBtn?.textContent).toBe("On"); // Should have 2 selects: effort and mode (since mode is 'all') const selects = container.querySelectorAll('[data-testid="select-trigger"]'); expect(selects.length).toBe(2); unmount(); }); it("calls onConfigChange when effort is changed", () => { const { container, unmount } = render( ); const selects = container.querySelectorAll("select"); // First select is effort const effortSelect = selects[0]; act(() => { effortSelect.value = "high"; effortSelect.dispatchEvent(new Event("change", { bubbles: true })); }); expect(mockOnConfigChange).toHaveBeenCalledWith({ ...defaultConfig, effort: "high", }); unmount(); }); it("calls onConfigChange when model match mode is changed", () => { const { container, unmount } = render( ); const selects = container.querySelectorAll("select"); // Second select is model match mode const modeSelect = selects[1]; act(() => { modeSelect.value = "specific"; modeSelect.dispatchEvent(new Event("change", { bubbles: true })); }); expect(mockOnConfigChange).toHaveBeenCalledWith({ ...defaultConfig, modelMatchMode: "specific", }); unmount(); }); it("renders model input when mode is specific", () => { const specificConfig: AnthropicAdaptiveThinkingConfig = { ...defaultConfig, modelMatchMode: "specific", }; const { container, unmount } = render( ); expect(container.querySelector('[data-testid="tag-input"]')).toBeTruthy(); unmount(); }); it("calls onConfigChange when models are changed", () => { const specificConfig: AnthropicAdaptiveThinkingConfig = { ...defaultConfig, modelMatchMode: "specific", }; const { container, unmount } = render( ); const input = container.querySelector('[data-testid="tag-input"]') as HTMLInputElement; act(() => { // Simulate typing a tag // For standard HTML inputs, simply setting value and dispatching event works // The Object.getOwnPropertyDescriptor trick is needed for React controlled inputs // but here we are using a mocked input which might just need the event const nativeInputValueSetter = Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, "value" )?.set; nativeInputValueSetter?.call(input, "claude-3-5-sonnet"); input.dispatchEvent(new Event("change", { bubbles: true })); }); expect(mockOnConfigChange).toHaveBeenCalledWith({ ...specificConfig, models: ["claude-3-5-sonnet"], }); unmount(); }); it("passes disabled prop to children", () => { const { container, unmount } = render( ); const switchBtn = container.querySelector('[data-testid="switch"]') as HTMLButtonElement; expect(switchBtn.disabled).toBe(true); const selects = container.querySelectorAll("select"); selects.forEach((select) => { expect(select.disabled).toBe(true); }); unmount(); }); });