theo-agent-dashboard

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

ModelPicker.test.tsx (7302B)


      1 import { describe, it, expect, vi } from "vitest";
      2 import { render, screen, fireEvent } from "@testing-library/react";
      3 import { ModelPicker } from "./ModelPicker";
      4 
      5 const mockModels = [
      6   { id: "gpt-4", name: "GPT-4" },
      7   { id: "claude-3", name: "Claude 3" },
      8   { id: "llama-3", name: "Llama 3" },
      9 ];
     10 
     11 describe("ModelPicker", () => {
     12   it("renders model selector button", () => {
     13     render(<ModelPicker models={mockModels} selectedModel={null} />);
     14     expect(screen.getByText(/model/i)).toBeInTheDocument();
     15   });
     16 
     17   it("shows selected model name", () => {
     18     render(<ModelPicker models={mockModels} selectedModel="gpt-4" />);
     19     expect(screen.getByText("GPT-4")).toBeInTheDocument();
     20   });
     21 
     22   it("shows 'Model' when no model is selected", () => {
     23     render(<ModelPicker models={mockModels} selectedModel={null} />);
     24     expect(screen.getByText("Model")).toBeInTheDocument();
     25   });
     26 
     27   it("shows model id as fallback when model not found in list", () => {
     28     render(<ModelPicker models={mockModels} selectedModel="unknown-model" />);
     29     expect(screen.getByText("unknown-model")).toBeInTheDocument();
     30   });
     31 
     32   it("opens dropdown on click", () => {
     33     render(<ModelPicker models={mockModels} selectedModel={null} />);
     34     fireEvent.click(screen.getByRole("button"));
     35     expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
     36     expect(screen.getByText("Claude 3")).toBeInTheDocument();
     37     expect(screen.getByText("Llama 3")).toBeInTheDocument();
     38   });
     39 
     40   it("calls onSelect when a model is chosen", () => {
     41     const onSelect = vi.fn();
     42     render(<ModelPicker models={mockModels} selectedModel={null} onSelect={onSelect} />);
     43     fireEvent.click(screen.getByRole("button"));
     44     fireEvent.click(screen.getByText("Claude 3"));
     45     expect(onSelect).toHaveBeenCalledWith("claude-3");
     46   });
     47 
     48   it("closes dropdown after selecting a model", () => {
     49     render(<ModelPicker models={mockModels} selectedModel={null} onSelect={vi.fn()} />);
     50     fireEvent.click(screen.getByRole("button"));
     51     fireEvent.click(screen.getByText("Claude 3"));
     52     expect(screen.queryByPlaceholderText(/search/i)).not.toBeInTheDocument();
     53   });
     54 
     55   it("clears search after selecting a model", () => {
     56     render(<ModelPicker models={mockModels} selectedModel={null} onSelect={vi.fn()} />);
     57     fireEvent.click(screen.getByRole("button"));
     58     const input = screen.getByPlaceholderText(/search/i);
     59     fireEvent.change(input, { target: { value: "claude" } });
     60     fireEvent.click(screen.getByText("Claude 3"));
     61     // Open again - search should be cleared
     62     fireEvent.click(screen.getByRole("button"));
     63     expect(screen.getByText("Claude 3")).toBeInTheDocument();
     64     expect(screen.getByText("Llama 3")).toBeInTheDocument();
     65   });
     66 
     67   it("filters models by search input (name)", () => {
     68     render(<ModelPicker models={mockModels} selectedModel={null} />);
     69     fireEvent.click(screen.getByRole("button"));
     70     const input = screen.getByPlaceholderText(/search/i);
     71     fireEvent.change(input, { target: { value: "claude" } });
     72     expect(screen.getByText("Claude 3")).toBeInTheDocument();
     73     expect(screen.queryByText("Llama 3")).not.toBeInTheDocument();
     74   });
     75 
     76   it("filters models by id", () => {
     77     render(<ModelPicker models={mockModels} selectedModel={null} />);
     78     fireEvent.click(screen.getByRole("button"));
     79     const input = screen.getByPlaceholderText(/search/i);
     80     fireEvent.change(input, { target: { value: "gpt" } });
     81     // When searching "gpt", GPT-4 button shows in dropdown (not the main button)
     82     expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
     83   });
     84 
     85   it("shows 'No models found' when search has no matches", () => {
     86     render(<ModelPicker models={mockModels} selectedModel={null} />);
     87     fireEvent.click(screen.getByRole("button"));
     88     const input = screen.getByPlaceholderText(/search/i);
     89     fireEvent.change(input, { target: { value: "nonexistent" } });
     90     expect(screen.getByText("No models found")).toBeInTheDocument();
     91   });
     92 
     93   it("shows all models when search is cleared", () => {
     94     render(<ModelPicker models={mockModels} selectedModel={null} />);
     95     fireEvent.click(screen.getByRole("button"));
     96     const input = screen.getByPlaceholderText(/search/i);
     97     fireEvent.change(input, { target: { value: "claude" } });
     98     expect(screen.queryByText("Llama 3")).not.toBeInTheDocument();
     99     fireEvent.change(input, { target: { value: "" } });
    100     expect(screen.getByText("Claude 3")).toBeInTheDocument();
    101     expect(screen.getByText("Llama 3")).toBeInTheDocument();
    102   });
    103 
    104   it("closes dropdown on outside click", () => {
    105     render(
    106       <div data-testid="outside">
    107         <ModelPicker models={mockModels} selectedModel={null} />
    108       </div>,
    109     );
    110     fireEvent.click(screen.getByRole("button"));
    111     expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
    112     fireEvent.mouseDown(screen.getByTestId("outside"));
    113     expect(screen.queryByPlaceholderText(/search/i)).not.toBeInTheDocument();
    114   });
    115 
    116   it("highlights selected model in dropdown", () => {
    117     render(<ModelPicker models={mockModels} selectedModel="gpt-4" />);
    118     fireEvent.click(screen.getByRole("button"));
    119     // Find the dropdown item (not the button) - the dropdown items are in the list
    120     const dropdownItems = screen.getAllByText("GPT-4");
    121     // One in button, one in dropdown list
    122     const dropdownItem = dropdownItems.find(
    123       (el) => el.tagName === "BUTTON" && el.className.includes("block"),
    124     );
    125     expect(dropdownItem).toBeDefined();
    126     expect(dropdownItem?.className).toContain("bg-blue");
    127   });
    128 
    129   it("does not highlight unselected models", () => {
    130     render(<ModelPicker models={mockModels} selectedModel="gpt-4" />);
    131     fireEvent.click(screen.getByRole("button"));
    132     const claudeItems = screen.getAllByText("Claude 3");
    133     const dropdownItem = claudeItems.find(
    134       (el) => el.tagName === "BUTTON" && el.className.includes("block"),
    135     );
    136     expect(dropdownItem?.className).not.toContain("bg-blue");
    137   });
    138 
    139   it("search is case-insensitive", () => {
    140     render(<ModelPicker models={mockModels} selectedModel={null} />);
    141     fireEvent.click(screen.getByRole("button"));
    142     const input = screen.getByPlaceholderText(/search/i);
    143     fireEvent.change(input, { target: { value: "CLAUDE" } });
    144     expect(screen.getByText("Claude 3")).toBeInTheDocument();
    145   });
    146 
    147   it("handles empty models list", () => {
    148     render(<ModelPicker models={[]} selectedModel={null} />);
    149     fireEvent.click(screen.getByRole("button"));
    150     expect(screen.getByText("No models found")).toBeInTheDocument();
    151   });
    152 
    153   it("handles onSelect being undefined", () => {
    154     render(<ModelPicker models={mockModels} selectedModel={null} />);
    155     fireEvent.click(screen.getByRole("button"));
    156     // Should not throw
    157     fireEvent.click(screen.getByText("Claude 3"));
    158   });
    159 
    160   it("search by whitespace-only shows all models", () => {
    161     render(<ModelPicker models={mockModels} selectedModel={null} />);
    162     fireEvent.click(screen.getByRole("button"));
    163     const input = screen.getByPlaceholderText(/search/i);
    164     fireEvent.change(input, { target: { value: "   " } });
    165     // Whitespace-only should show all models (trim returns empty)
    166     expect(screen.getByText("Claude 3")).toBeInTheDocument();
    167     expect(screen.getByText("Llama 3")).toBeInTheDocument();
    168   });
    169 });