0dd7f1b2fd
Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
225 lines
6.1 KiB
TypeScript
225 lines
6.1 KiB
TypeScript
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<React.ComponentProps<typeof ResourceSearch>>
|
|
) {
|
|
const onChange = jest.fn();
|
|
renderWithProviders(
|
|
<ResourceSearch
|
|
objectTypes={["resource"]}
|
|
selectedResources={selectedResources}
|
|
onChange={onChange}
|
|
{...props}
|
|
/>
|
|
);
|
|
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<ReturnType<typeof searchUsers>>);
|
|
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<ReturnType<typeof searchUsers>>);
|
|
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<ReturnType<typeof searchUsers>>);
|
|
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<ReturnType<typeof searchUsers>>);
|
|
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<ReturnType<typeof searchUsers>>);
|
|
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<ReturnType<typeof searchUsers>>);
|
|
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<Resource[]>((resolve) => {
|
|
resolveSearch = resolve;
|
|
});
|
|
mockedSearchUsers.mockReturnValueOnce(
|
|
searchPromise as unknown as ReturnType<typeof searchUsers>
|
|
);
|
|
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;
|
|
});
|
|
});
|
|
});
|