import { fireEvent, render, screen } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import ParamsPanel from "@/components/ParamsPanel"; import type { ParamSpec } from "@/lib/api"; const specs: ParamSpec[] = [ { name: "exaggeration", label: "Exaggeration", type: "float", default: 0.5, min: 0, max: 2, step: 0.05 }, { name: "is_fast", label: "Fast mode", type: "bool", default: false }, { name: "lang", label: "Lang", type: "enum", default: "en", choices: ["en", "fr"] }, ]; describe("ParamsPanel", () => { it("renders one control per spec", () => { render( {}} />); expect(screen.getByLabelText(/exaggeration/i)).toBeInTheDocument(); expect(screen.getByLabelText(/fast mode/i)).toBeInTheDocument(); expect(screen.getByLabelText(/^lang$/i)).toBeInTheDocument(); }); it("emits onChange with merged values", () => { const onChange = vi.fn(); render(); fireEvent.change(screen.getByLabelText(/exaggeration/i), { target: { value: "1.2" } }); expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ exaggeration: 1.2 })); }); }); const specsMixed: ParamSpec[] = [ { name: "temperature", label: "Temperature", type: "float", default: 0.8, min: 0.1, max: 1.5, step: 0.05, group: "basic" }, { name: "seed", label: "Seed", type: "int", default: -1, min: -1, step: 1, group: "advanced" }, { name: "top_p", label: "Top p", type: "float", default: 1.0, min: 0, max: 1, step: 0.01, group: "advanced" }, ]; describe("ParamsPanel groups", () => { it("renders basic params and a closed advanced disclosure by default", () => { render( {}} />); expect(screen.getByLabelText(/temperature/i)).toBeInTheDocument(); // advanced is in the DOM but not visible until
opens const seed = screen.getByLabelText(/^seed$/i) as HTMLInputElement; const detailsAncestor = seed.closest("details"); expect(detailsAncestor).not.toBeNull(); expect(detailsAncestor!.open).toBe(false); }); it("opens disclosure on summary click and shows advanced params", () => { render( {}} />); const summary = screen.getByText(/advanced/i); fireEvent.click(summary); const seed = screen.getByLabelText(/^seed$/i) as HTMLInputElement; expect(seed.closest("details")!.open).toBe(true); }); it("propagates onChange from advanced params", () => { const onChange = vi.fn(); render(); fireEvent.click(screen.getByText(/advanced/i)); fireEvent.change(screen.getByLabelText(/^top p$/i), { target: { value: "0.6" } }); expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ top_p: 0.6 })); }); }); describe("ParamsPanel seed control", () => { it("renders an int input plus a randomize button for seed", () => { const specs: ParamSpec[] = [ { name: "seed", label: "Seed", type: "int", default: -1, min: -1, step: 1, group: "advanced" }, ]; render( {}} />); fireEvent.click(screen.getByText(/advanced/i)); expect(screen.getByLabelText(/^seed$/i)).toHaveAttribute("type", "number"); expect(screen.getByRole("button", { name: /random/i })).toBeInTheDocument(); }); it("clicking randomize sets seed to -1 via onChange", () => { const specs: ParamSpec[] = [ { name: "seed", label: "Seed", type: "int", default: -1, min: -1, step: 1, group: "advanced" }, ]; const onChange = vi.fn(); render(); fireEvent.click(screen.getByText(/advanced/i)); fireEvent.click(screen.getByRole("button", { name: /random/i })); expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ seed: -1 })); }); });