Search.test.tsx (3319B)
1 import { describe, it, expect, vi } from "vitest"; 2 import { render, screen, fireEvent } from "@testing-library/react"; 3 import { SearchBar } from "./SearchBar"; 4 import { SearchResults } from "./SearchResults"; 5 6 describe("SearchBar", () => { 7 it("renders with placeholder", () => { 8 render(<SearchBar onSearch={vi.fn()} isSearching={false} />); 9 expect(screen.getByPlaceholderText("Search messages... (Ctrl+K)")).toBeInTheDocument(); 10 }); 11 12 it("calls onSearch after debounce", () => { 13 vi.useFakeTimers(); 14 const onSearch = vi.fn(); 15 render(<SearchBar onSearch={onSearch} isSearching={false} />); 16 const input = screen.getByPlaceholderText("Search messages... (Ctrl+K)"); 17 fireEvent.change(input, { target: { value: "hello" } }); 18 expect(onSearch).not.toHaveBeenCalled(); 19 vi.advanceTimersByTime(300); 20 expect(onSearch).toHaveBeenCalledWith("hello"); 21 vi.useRealTimers(); 22 }); 23 24 it("clears query and calls onSearch with empty string", () => { 25 const onSearch = vi.fn(); 26 render(<SearchBar onSearch={onSearch} isSearching={false} />); 27 const input = screen.getByPlaceholderText("Search messages... (Ctrl+K)") as HTMLInputElement; 28 fireEvent.change(input, { target: { value: "test" } }); 29 fireEvent.click(screen.getByLabelText("Clear search")); 30 expect(input.value).toBe(""); 31 expect(onSearch).toHaveBeenCalledWith(""); 32 }); 33 34 it("shows searching indicator", () => { 35 render(<SearchBar onSearch={vi.fn()} isSearching={true} />); 36 expect(screen.getByText("Searching...")).toBeInTheDocument(); 37 }); 38 }); 39 40 describe("SearchResults", () => { 41 it("shows empty message when no results", () => { 42 render(<SearchResults results={[]} onSelectMessage={vi.fn()} onClose={vi.fn()} />); 43 expect(screen.getByText("No results found")).toBeInTheDocument(); 44 }); 45 46 it("renders grouped results by session", () => { 47 const results = [ 48 { 49 session_id: "s1", 50 session_name: "Test Session", 51 messages: [ 52 { id: "m1", content: "Hello world", role: "user" }, 53 { id: "m2", content: "Hi there!", role: "assistant" }, 54 ], 55 }, 56 ]; 57 render(<SearchResults results={results} onSelectMessage={vi.fn()} onClose={vi.fn()} />); 58 expect(screen.getByText("Test Session")).toBeInTheDocument(); 59 expect(screen.getByText("Hello world")).toBeInTheDocument(); 60 }); 61 62 it("calls onSelectMessage with correct ids on click", () => { 63 const onSelectMessage = vi.fn(); 64 const onClose = vi.fn(); 65 const results = [ 66 { 67 session_id: "s1", 68 session_name: "Session", 69 messages: [{ id: "m1", content: "test", role: "user" }], 70 }, 71 ]; 72 render( 73 <SearchResults results={results} onSelectMessage={onSelectMessage} onClose={onClose} />, 74 ); 75 fireEvent.click(screen.getByText("test")); 76 expect(onSelectMessage).toHaveBeenCalledWith("s1", "m1"); 77 expect(onClose).toHaveBeenCalled(); 78 }); 79 80 it("truncates long messages", () => { 81 const results = [ 82 { 83 session_id: "s1", 84 session_name: "S", 85 messages: [{ id: "m1", content: "a".repeat(200), role: "user" }], 86 }, 87 ]; 88 render(<SearchResults results={results} onSelectMessage={vi.fn()} onClose={vi.fn()} />); 89 expect(screen.getByText(/a{120}\.\.\./)).toBeInTheDocument(); 90 }); 91 });