theo-agent-dashboard

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

SearchModal.test.tsx (5980B)


      1 import { describe, it, expect, vi, beforeEach } from "vitest";
      2 import { render, screen, fireEvent, waitFor } from "@testing-library/react";
      3 import { SearchModal } from "./SearchModal";
      4 import { useSessionStore } from "../../store/session";
      5 
      6 vi.mock("../../lib/api", () => ({
      7   api: {
      8     get: vi.fn(),
      9   },
     10 }));
     11 
     12 import { api } from "../../lib/api";
     13 
     14 beforeEach(() => {
     15   vi.clearAllMocks();
     16   useSessionStore.setState({ activeSessionId: null });
     17 });
     18 
     19 describe("SearchModal", () => {
     20   it("renders nothing when not open", () => {
     21     const { container } = render(
     22       <SearchModal isOpen={false} onClose={vi.fn()} onSelectSession={vi.fn()} />,
     23     );
     24     expect(container.innerHTML).toBe("");
     25   });
     26 
     27   it("renders search bar when open", () => {
     28     render(
     29       <SearchModal isOpen={true} onClose={vi.fn()} onSelectSession={vi.fn()} />,
     30     );
     31     expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
     32   });
     33 
     34   it("closes on Escape key", () => {
     35     const onClose = vi.fn();
     36     render(
     37       <SearchModal isOpen={true} onClose={onClose} onSelectSession={vi.fn()} />,
     38     );
     39     fireEvent.keyDown(document, { key: "Escape" });
     40     expect(onClose).toHaveBeenCalled();
     41   });
     42 
     43   it("searches via API and displays results", async () => {
     44     vi.mocked(api.get).mockResolvedValue({
     45       results: [
     46         {
     47           session_id: "s1",
     48           session_name: "Test Session",
     49           messages: [{ id: "m1", content: "Hello world", role: "user" }],
     50         },
     51       ],
     52     });
     53 
     54     render(
     55       <SearchModal isOpen={true} onClose={vi.fn()} onSelectSession={vi.fn()} />,
     56     );
     57 
     58     const input = screen.getByPlaceholderText(/search/i);
     59     fireEvent.change(input, { target: { value: "hello" } });
     60 
     61     await waitFor(() => {
     62       expect(screen.getByText("Hello world")).toBeInTheDocument();
     63     });
     64   });
     65 
     66   it("calls onSelectSession when a result is clicked", async () => {
     67     const onSelectSession = vi.fn();
     68     vi.mocked(api.get).mockResolvedValue({
     69       results: [
     70         {
     71           session_id: "s1",
     72           session_name: "Session",
     73           messages: [{ id: "m1", content: "test message", role: "user" }],
     74         },
     75       ],
     76     });
     77 
     78     render(
     79       <SearchModal
     80         isOpen={true}
     81         onClose={vi.fn()}
     82         onSelectSession={onSelectSession}
     83       />,
     84     );
     85 
     86     fireEvent.change(screen.getByPlaceholderText(/search/i), {
     87       target: { value: "test" },
     88     });
     89 
     90     await waitFor(() => {
     91       fireEvent.click(screen.getByText("test message"));
     92     });
     93 
     94     expect(onSelectSession).toHaveBeenCalledWith("s1");
     95   });
     96 
     97   it("clears results when search is empty", async () => {
     98     vi.mocked(api.get).mockResolvedValue({
     99       results: [
    100         {
    101           session_id: "s1",
    102           session_name: "Session",
    103           messages: [{ id: "m1", content: "found", role: "user" }],
    104         },
    105       ],
    106     });
    107 
    108     render(
    109       <SearchModal isOpen={true} onClose={vi.fn()} onSelectSession={vi.fn()} />,
    110     );
    111 
    112     const input = screen.getByPlaceholderText(/search/i);
    113     fireEvent.change(input, { target: { value: "find" } });
    114 
    115     await waitFor(() => {
    116       expect(screen.getByText("found")).toBeInTheDocument();
    117     });
    118 
    119     // Clear search
    120     fireEvent.change(input, { target: { value: "" } });
    121 
    122     await waitFor(() => {
    123       expect(screen.queryByText("found")).not.toBeInTheDocument();
    124     });
    125   });
    126 
    127   it("handles API error gracefully", async () => {
    128     vi.mocked(api.get).mockRejectedValue(new Error("network error"));
    129 
    130     render(
    131       <SearchModal isOpen={true} onClose={vi.fn()} onSelectSession={vi.fn()} />,
    132     );
    133 
    134     fireEvent.change(screen.getByPlaceholderText(/search/i), {
    135       target: { value: "test" },
    136     });
    137 
    138     await waitFor(() => {
    139       // Should show "No results found" on error
    140       expect(screen.getByText("No results found")).toBeInTheDocument();
    141     });
    142   });
    143 
    144   it("sets active session in store when selecting result", async () => {
    145     vi.mocked(api.get).mockResolvedValue({
    146       results: [
    147         {
    148           session_id: "s1",
    149           session_name: "Session",
    150           messages: [{ id: "m1", content: "click me", role: "user" }],
    151         },
    152       ],
    153     });
    154 
    155     render(
    156       <SearchModal
    157         isOpen={true}
    158         onClose={vi.fn()}
    159         onSelectSession={vi.fn()}
    160       />,
    161     );
    162 
    163     fireEvent.change(screen.getByPlaceholderText(/search/i), {
    164       target: { value: "click" },
    165     });
    166 
    167     await waitFor(() => {
    168       fireEvent.click(screen.getByText("click me"));
    169     });
    170 
    171     expect(useSessionStore.getState().activeSessionId).toBe("s1");
    172   });
    173 
    174   it("does not render search bar content when not open", () => {
    175     const { container } = render(
    176       <SearchModal isOpen={false} onClose={vi.fn()} onSelectSession={vi.fn()} />,
    177     );
    178     expect(container.querySelector("input")).toBeNull();
    179   });
    180 
    181   it("shows results with session name", async () => {
    182     vi.mocked(api.get).mockResolvedValue({
    183       results: [
    184         {
    185           session_id: "s1",
    186           session_name: "My Session",
    187           messages: [{ id: "m1", content: "msg", role: "assistant" }],
    188         },
    189       ],
    190     });
    191 
    192     render(
    193       <SearchModal isOpen={true} onClose={vi.fn()} onSelectSession={vi.fn()} />,
    194     );
    195 
    196     fireEvent.change(screen.getByPlaceholderText(/search/i), {
    197       target: { value: "msg" },
    198     });
    199 
    200     await waitFor(() => {
    201       expect(screen.getByText("My Session")).toBeInTheDocument();
    202     });
    203   });
    204 
    205   it("shows role for each message", async () => {
    206     vi.mocked(api.get).mockResolvedValue({
    207       results: [
    208         {
    209           session_id: "s1",
    210           session_name: "S",
    211           messages: [{ id: "m1", content: "hi", role: "user" }],
    212         },
    213       ],
    214     });
    215 
    216     render(
    217       <SearchModal isOpen={true} onClose={vi.fn()} onSelectSession={vi.fn()} />,
    218     );
    219 
    220     fireEvent.change(screen.getByPlaceholderText(/search/i), {
    221       target: { value: "hi" },
    222     });
    223 
    224     await waitFor(() => {
    225       expect(screen.getByText("user")).toBeInTheDocument();
    226     });
    227   });
    228 });