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();
});
});