import React from "react"; import { render } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import Search from "../Search"; import { SearchProps } from "../Search.types"; function renderSearch(props?: Partial) { return render(); } describe("Search", () => { it("should render correctly", () => { const { getByRole } = renderSearch(); expect(getByRole("searchbox")).toBeInTheDocument(); }); it("should display the search icon by default", () => { const { getByTestId } = renderSearch(); expect(getByTestId("icon")).toBeInTheDocument(); }); it("should not display the clear icon when the input is empty", () => { const { queryByLabelText } = renderSearch(); expect(queryByLabelText("Clear")).toBeNull(); }); it("should display both the search icon and clear icon when input has value", () => { const { getByTestId, getAllByTestId } = renderSearch({ value: "Test" }); expect(getAllByTestId("icon")).toHaveLength(2); expect(getByTestId("clean-search-button")).toBeInTheDocument(); }); it("should clear the input value when the clear icon is clicked", () => { const { getByRole, getByLabelText } = renderSearch({ value: "Test" }); userEvent.click(getByLabelText("Clear")); expect(getByRole("searchbox")).toHaveValue(""); }); it("should display the clear icon once user inputs", () => { const { getByRole, getByTestId } = renderSearch(); userEvent.type(getByRole("searchbox"), "Test"); expect(getByTestId("clean-search-button")).toBeInTheDocument(); }); it("should display a loader when the loading prop is true", () => { const { getByTestId } = renderSearch({ loading: true }); expect(getByTestId("loader")).toBeInTheDocument(); }); it("should trigger onChange with the correct value when typing without debounce", () => { const onChange = jest.fn(); const { getByRole } = renderSearch({ onChange, debounceRate: 0 }); userEvent.type(getByRole("searchbox"), "Hello, World!"); expect(onChange).toHaveBeenCalledTimes("Hello, World!".length); expect(onChange).toHaveBeenLastCalledWith("Hello, World!"); }); it("should trigger onClear when the user press the clear button", () => { const onClear = jest.fn(); const { getByRole, getByLabelText } = renderSearch({ value: "Test" }); userEvent.type(getByRole("searchbox"), "Hello, World!"); userEvent.click(getByLabelText("Clear")); expect(onClear).toHaveBeenCalled; }); it("should debounce the onChange call", () => { jest.useFakeTimers(); const onChange = jest.fn(); const { getByRole } = renderSearch({ onChange, debounceRate: 100 }); userEvent.type(getByRole("searchbox"), "Hello"); expect(onChange).not.toHaveBeenCalled(); jest.advanceTimersByTime(100); expect(onChange).toHaveBeenCalledWith("Hello"); jest.useRealTimers(); }); it("should respect the autoFocus prop", () => { const { getByRole } = renderSearch({ autoFocus: true }); expect(getByRole("searchbox")).toHaveFocus(); }); it("should not allow input when disabled is true", () => { const { getByRole } = renderSearch({ value: "Test", disabled: true }); const input = getByRole("searchbox"); expect(input).toBeDisabled(); userEvent.type(input, "Hello, World!"); expect(input).toHaveValue("Test"); }); it("should not show clear when disabled is true", () => { const { queryByLabelText } = renderSearch({ value: "Test", disabled: true }); expect(queryByLabelText("Clear")).not.toBeInTheDocument(); }); it("should display additional action render if provided", () => { const AdditionalActionButton = ; const { getByText } = renderSearch({ renderAction: AdditionalActionButton }); expect(getByText("Extra Action")).toBeInTheDocument(); }); it("should display additional action render with hideRenderActionOnInput option true and no input value", () => { const AdditionalActionButton = ; const { getByText } = renderSearch({ renderAction: AdditionalActionButton, hideRenderActionOnInput: true }); expect(getByText("Extra Action")).toBeInTheDocument(); }); it("should not display additional action render with hideRenderActionOnInput option true and input value", () => { const AdditionalActionButton = ; const { queryByText } = renderSearch({ renderAction: AdditionalActionButton, hideRenderActionOnInput: true, value: "Test" }); expect(queryByText("Extra Action")).not.toBeInTheDocument(); }); describe("a11y", () => { it("should have default input role when searchResultsContainerId is not provided", () => { const { getByRole } = renderSearch(); expect(getByRole("searchbox")).toBeInTheDocument(); }); it("should have role 'combobox' when searchResultsContainerId is provided", () => { const { getByRole } = renderSearch({ searchResultsContainerId: "results" }); expect(getByRole("combobox")).toBeInTheDocument(); }); it("should set aria-controls when searchResultsContainerId is provided and ariaExpanded is true", () => { const { getByRole } = renderSearch({ searchResultsContainerId: "search-results", ariaExpanded: true }); expect(getByRole("combobox")).toHaveAttribute("aria-controls", "search-results"); }); it("should set aria-activedescendant when activeDescendant is provided", () => { const { getByRole } = renderSearch({ currentAriaResultId: "option-1" }); expect(getByRole("searchbox")).toHaveAttribute("aria-activedescendant", "option-1"); }); it("should set aria-busy when loading is true", () => { const { getByRole } = renderSearch({ loading: true }); expect(getByRole("searchbox")).toHaveAttribute("aria-busy", "true"); }); }); describe("interactions", () => { it("should handle focus and blur events", () => { const onFocus = jest.fn(); const onBlur = jest.fn(); const { getByRole } = renderSearch({ onFocus, onBlur }); const input = getByRole("searchbox"); userEvent.click(input); expect(onFocus).toHaveBeenCalled(); userEvent.tab(); expect(onBlur).toHaveBeenCalled(); }); it("should call onKeyDown when Enter key is pressed", () => { const onKeyDown = jest.fn(); const { getByRole } = renderSearch({ onKeyDown }); const input = getByRole("searchbox"); userEvent.click(input); userEvent.keyboard("{Enter}"); expect(onKeyDown).toHaveBeenCalledTimes(1); }); it("should not call onKeyDown when input is disabled", () => { const onKeyDown = jest.fn(); const { getByRole } = renderSearch({ onKeyDown, disabled: true }); const input = getByRole("searchbox"); userEvent.click(input); userEvent.keyboard("{Enter}"); expect(onKeyDown).not.toHaveBeenCalled(); }); it("should call onKeyDown for each character when input is typed with content", () => { const onKeyDown = jest.fn(); const string = "Hello, World!"; const { getByRole } = renderSearch({ onKeyDown }); const input = getByRole("searchbox"); userEvent.click(input); userEvent.type(input, string); expect(onKeyDown).toHaveBeenCalledTimes(string.length); }); }); });