import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, fireEvent } from "@testing-library/react"; import { SearchInput } from "../SearchInput"; describe("SearchInput", () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.restoreAllMocks(); vi.useRealTimers(); }); it("should render search input field", () => { render(); const input = screen.getByPlaceholderText(/search/i); expect(input).toBeInTheDocument(); }); it("should call onSearch after debounce delay", () => { const onSearch = vi.fn(); render(); const input = screen.getByPlaceholderText(/search/i); fireEvent.change(input, { target: { value: "test query" } }); // Should not call immediately expect(onSearch).not.toHaveBeenCalled(); // Fast-forward time vi.advanceTimersByTime(300); expect(onSearch).toHaveBeenCalledWith("test query"); }); it("should debounce multiple keystrokes", () => { const onSearch = vi.fn(); render(); const input = screen.getByPlaceholderText(/search/i); fireEvent.change(input, { target: { value: "a" } }); vi.advanceTimersByTime(100); fireEvent.change(input, { target: { value: "ab" } }); vi.advanceTimersByTime(100); fireEvent.change(input, { target: { value: "abc" } }); vi.advanceTimersByTime(100); // Should only call once after final delay expect(onSearch).not.toHaveBeenCalled(); vi.advanceTimersByTime(300); expect(onSearch).toHaveBeenCalledTimes(1); expect(onSearch).toHaveBeenCalledWith("abc"); }); it("should focus input when Cmd+K is pressed", () => { render(); const input = screen.getByPlaceholderText(/search/i); // Simulate Cmd+K fireEvent.keyDown(document, { key: "k", metaKey: true }); expect(document.activeElement).toBe(input); }); it("should focus input when Ctrl+K is pressed on Windows/Linux", () => { render(); const input = screen.getByPlaceholderText(/search/i); // Simulate Ctrl+K fireEvent.keyDown(document, { key: "k", ctrlKey: true }); expect(document.activeElement).toBe(input); }); it("should clear input when Escape is pressed", () => { const onSearch = vi.fn(); render(); const input = screen.getByPlaceholderText(/search/i); fireEvent.change(input, { target: { value: "test query" } }); expect((input as HTMLInputElement).value).toBe("test query"); fireEvent.keyDown(input, { key: "Escape" }); expect((input as HTMLInputElement).value).toBe(""); }); it("should display initial value", () => { render(); const input = screen.getByPlaceholderText(/search/i); expect((input as HTMLInputElement).value).toBe("initial query"); }); it("should show loading indicator when isLoading is true", () => { render(); const loader = screen.getByTestId("search-loading"); expect(loader).toBeInTheDocument(); }); it("should not call onSearch for empty strings", () => { const onSearch = vi.fn(); render(); const input = screen.getByPlaceholderText(/search/i); fireEvent.change(input, { target: { value: " " } }); vi.advanceTimersByTime(300); expect(onSearch).not.toHaveBeenCalled(); }); it("should show keyboard shortcut hint", () => { render(); const hint = screen.getByText(/⌘K|Ctrl\+K/i); expect(hint).toBeInTheDocument(); }); });