import { ResourceSearch, Resource, } from "@/components/Attendees/ResourceSearch"; import { searchUsers } from "@/features/User/userAPI"; import { act, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { renderWithProviders } from "../utils/Renderwithproviders"; jest.mock("@/features/User/userAPI"); const mockedSearchUsers = searchUsers as jest.MockedFunction< typeof searchUsers >; describe("ResourceSearch", () => { const baseResource: Resource = { displayName: "Projector Room", }; function setup( selectedResources: Resource[] = [], props?: Partial> ) { const onChange = jest.fn(); renderWithProviders( ); return { onChange }; } beforeEach(() => { jest.useFakeTimers(); mockedSearchUsers.mockReset(); }); afterEach(() => { jest.useRealTimers(); }); it("calls searchUsers after debounce when typing", async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource, ] as unknown as Awaited>); setup(); const input = screen.getByRole("combobox"); await userEvent.type(input, "Room"); await act(async () => { jest.advanceTimersByTime(300); }); await waitFor(() => { expect(mockedSearchUsers).toHaveBeenCalledWith("Room", ["resource"]); }); }); it("renders search results and allows selection", async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource, ] as unknown as Awaited>); const { onChange } = setup(); const input = screen.getByRole("combobox"); await userEvent.type(input, "Room"); await act(async () => { jest.advanceTimersByTime(300); }); const option = await screen.findByText("Projector Room"); await userEvent.click(option); await waitFor(() => { expect(onChange).toHaveBeenCalled(); }); }); it("does not show already selected resources in options", async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource, ] as unknown as Awaited>); setup([baseResource]); const input = screen.getByRole("combobox"); await userEvent.type(input, "Projector"); await act(async () => { jest.advanceTimersByTime(300); }); await waitFor(() => { // It shouldn't be in the dropdown options anymore const options = screen.queryAllByRole("option"); expect( options.find((opt) => opt.textContent === "Projector Room") ).toBeUndefined(); }); }); it("respects disabled state", () => { setup([], { disabled: true }); expect(screen.getByRole("combobox")).toBeDisabled(); }); it("no options doesn't show dropdown when input is empty", async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource, ] as unknown as Awaited>); setup(); const input = screen.getByRole("combobox"); await userEvent.type(input, "Room"); await act(async () => { jest.advanceTimersByTime(300); }); await waitFor(() => { expect(screen.getByRole("listbox")).toBeInTheDocument(); }); await userEvent.clear(input); await waitFor(() => { expect(screen.queryByRole("listbox")).not.toBeInTheDocument(); }); }); it("shows 'No results' when search succeeds but returns empty array", async () => { mockedSearchUsers.mockResolvedValueOnce([]); setup(); const input = screen.getByRole("combobox"); await userEvent.type(input, "Room"); await act(async () => { jest.advanceTimersByTime(300); await Promise.resolve(); await Promise.resolve(); }); const noResults = await screen.findByText( "resourceSearch.noResults", {}, { timeout: 5000 } ); expect(noResults).toBeInTheDocument(); }); it("clears options when search fails and shows error snackbar", async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource, ] as unknown as Awaited>); setup(); const input = screen.getByRole("combobox"); await userEvent.type(input, "Room"); await act(async () => { jest.advanceTimersByTime(300); await Promise.resolve(); }); await waitFor(() => { expect(screen.getByText("Projector Room")).toBeInTheDocument(); }); mockedSearchUsers.mockRejectedValueOnce(new Error("Network error")); await userEvent.clear(input); await userEvent.type(input, "Error"); await act(async () => { jest.advanceTimersByTime(300); await Promise.resolve(); }); const errorMessage = await screen.findByText("resourceSearch.searchError"); expect(errorMessage).toBeInTheDocument(); expect(screen.queryByText("Projector Room")).not.toBeInTheDocument(); mockedSearchUsers.mockResolvedValueOnce([ baseResource, ] as unknown as Awaited>); await userEvent.clear(input); await userEvent.type(input, "Room"); await act(async () => { jest.advanceTimersByTime(300); await Promise.resolve(); }); await waitFor(() => { expect(screen.getByText("Projector Room")).toBeInTheDocument(); }); }); it("shows loading text when searching", async () => { let resolveSearch: (value: Resource[]) => void; const searchPromise = new Promise((resolve) => { resolveSearch = resolve; }); mockedSearchUsers.mockReturnValueOnce( searchPromise as unknown as ReturnType ); setup(); const input = screen.getByRole("combobox"); await userEvent.type(input, "Room"); await act(async () => { jest.advanceTimersByTime(300); }); const loadingText = await screen.findByText( "resourceSearch.loading", {}, { timeout: 5000 } ); expect(loadingText).toBeInTheDocument(); await act(async () => { resolveSearch!([baseResource]); await searchPromise; }); }); });