import { describe, it, expect, vi } from "vitest"; import { screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { renderWithProviders as render } from "../utils/test-utils"; import MultiSelect from "../../app/components/controls/MultiSelect"; import { componentTestSuite, type ComponentTestSuiteConfig, } from "../utils/componentTestSuite"; type Props = React.ComponentProps; const defaultChipOptions = [ { id: "1", label: "Option 1", state: "unselected" as const }, { id: "2", label: "Option 2", state: "selected" as const }, ]; const config: ComponentTestSuiteConfig = { component: MultiSelect, name: "MultiSelect", props: { label: "Test Label", showHelpIcon: false, size: "s", palette: "default", options: defaultChipOptions, addButton: true, addButtonText: "", } as Props, requiredProps: ["options"], optionalProps: { label: "Optional Label", showHelpIcon: true, size: "m", palette: "inverse", onChipClick: vi.fn(), onAddClick: vi.fn(), addButton: false, addButtonText: "Add", }, primaryRole: undefined, // MultiSelect contains multiple interactive elements testCases: { renders: true, accessibility: true, keyboardNavigation: false, // Complex component with multiple interactive elements disabledState: false, errorState: false, }, }; componentTestSuite(config); describe("MultiSelect – behaviour specifics", () => { it("renders label when provided", () => { render(); expect(screen.getByText("Test Label")).toBeInTheDocument(); }); it("renders all chip options", () => { render(); expect(screen.getByText("Option 1")).toBeInTheDocument(); expect(screen.getByText("Option 2")).toBeInTheDocument(); }); it("calls onChipClick when a chip is clicked", async () => { const handleChipClick = vi.fn(); render( , ); const chip = screen.getByText("Option 1"); await userEvent.click(chip); expect(handleChipClick).toHaveBeenCalledWith("1"); }); it("calls onAddClick when add button is clicked", async () => { const handleAddClick = vi.fn(); render( , ); const addButton = screen.getByRole("button", { name: "Add option" }); await userEvent.click(addButton); expect(handleAddClick).toHaveBeenCalled(); }); it("calls onAddClick when icon-only add button is clicked", async () => { const handleAddClick = vi.fn(); render( , ); const addButton = screen.getByRole("button", { name: "Add option" }); await userEvent.click(addButton); expect(handleAddClick).toHaveBeenCalled(); }); it("shows help icon when showHelpIcon is true", () => { render( , ); const helpIcon = screen.getByAltText("Help"); expect(helpIcon).toBeInTheDocument(); }); it("renders add button text when provided", () => { render( , ); expect(screen.getByText("Add option")).toBeInTheDocument(); }); it("does not render add button when addButton is false", () => { render(); expect( screen.queryByRole("button", { name: /add/i }), ).not.toBeInTheDocument(); }); it("handles custom chip confirm", async () => { const handleConfirm = vi.fn(); const customOptions = [ { id: "custom-1", label: "", state: "custom" as const }, ]; render( , ); // Type into the input first (check button is disabled until there's text) const input = screen.getByPlaceholderText("Type to add"); await userEvent.type(input, "NewOption"); // Now the check button should be enabled const checkButton = screen.getByRole("button", { name: "Confirm" }); expect(checkButton).not.toBeDisabled(); await userEvent.click(checkButton); expect(handleConfirm).toHaveBeenCalledWith("custom-1", "NewOption"); }); it("handles custom chip close", async () => { const handleClose = vi.fn(); const customOptions = [ { id: "custom-1", label: "", state: "custom" as const }, ]; render( , ); const closeButton = screen.getByRole("button", { name: "Close" }); await userEvent.click(closeButton); expect(handleClose).toHaveBeenCalled(); }); });