import SearchBar from "@/components/Menubar/EventSearchBar"; import * as searchThunk from "@/features/Search/SearchSlice"; import { fireEvent, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { renderWithProviders } from "../../utils/Renderwithproviders"; describe("EventSearchBar", () => { const today = new Date(); const start = new Date(today); start.setHours(10, 0, 0, 0); const end = new Date(today); end.setHours(11, 0, 0, 0); const preloadedState = { user: { userData: { sub: "test", email: "test@test.com", sid: "mockSid", openpaasId: "user1", }, tokens: { accessToken: "token" }, }, calendars: { list: { "user1/cal1": { name: "Calendar personal", id: "user1/cal1", color: { light: "#FF0000", dark: "#000" }, ownerEmails: ["alice@example.com"], events: { event1: { id: "event1", calId: "user1/cal1", uid: "event1", title: "Test Event", start: start.toISOString(), end: end.toISOString(), partstat: "ACCEPTED", organizer: { cn: "Alice", cal_address: "alice@example.com", }, attendee: [ { cn: "Alice", partstat: "ACCEPTED", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", cal_address: "alice@example.com", }, ], }, }, }, "user2/cal1": { name: "Calendar delegated", delegated: true, id: "user2/cal1", color: { light: "#FF0000", dark: "#000" }, ownerEmails: ["alice@example.com"], events: { event1: { id: "event1", calId: "user2/cal1", uid: "event1", title: "Test Event", start: start.toISOString(), end: end.toISOString(), partstat: "ACCEPTED", organizer: { cn: "Alice", cal_address: "alice@example.com", }, attendee: [ { cn: "Alice", partstat: "ACCEPTED", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", cal_address: "alice@example.com", }, ], }, }, }, "user3/cal1": { name: "Calendar shared", id: "user3/cal1", color: { light: "#FF0000", dark: "#000" }, ownerEmails: ["alice@example.com"], events: { event1: { id: "event1", calId: "user3/cal1", uid: "event1", title: "Test Event", start: start.toISOString(), end: end.toISOString(), partstat: "ACCEPTED", organizer: { cn: "Alice", cal_address: "alice@example.com", }, attendee: [ { cn: "Alice", partstat: "ACCEPTED", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", cal_address: "alice@example.com", }, ], }, }, }, }, pending: false, }, }; it("should render search icon button initially", () => { renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); expect(searchButton).toBeInTheDocument(); }); it("should expand search bar when icon is clicked", () => { renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); const searchInput = screen.getByPlaceholderText("common.search"); expect(searchInput).toBeInTheDocument(); }); it("should unexpand search bar when field is unfocused and empty", () => { renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); const searchInput = screen.getByPlaceholderText("common.search"); fireEvent.mouseDown(document.body); expect(searchInput).not.toBeInTheDocument(); }); it("should not unexpand search bar when field is unfocused but not empty", () => { renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); const searchInput = screen.getByPlaceholderText("common.search"); userEvent.type(searchInput, "test"); fireEvent.blur(searchInput); expect(searchInput).toBeInTheDocument(); expect(searchInput).toHaveValue("test"); }); it("should update search value on input", () => { renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); const searchInput = screen.getByPlaceholderText("common.search"); fireEvent.change(searchInput, { target: { value: "meeting" } }); expect(searchInput).toHaveValue("meeting"); }); it("should open filter popover when tune icon is clicked", async () => { renderWithProviders(, preloadedState); // Expand search bar const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); // Click tune icon const tuneButton = screen.getByTestId("TuneIcon"); if (tuneButton) fireEvent.click(tuneButton); await waitFor(() => { expect(screen.getByText("search.searchIn")).toBeInTheDocument(); }); }); it("should clear search value when clear button is clicked", () => { renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); const searchInput = screen.getByPlaceholderText("common.search"); fireEvent.change(searchInput, { target: { value: "meeting" } }); const clearButton = screen.getByTestId("HighlightOffIcon"); fireEvent.click(clearButton); expect(searchInput).toHaveValue(""); }); it("should trigger search on Enter key", async () => { const searchSpy = jest.spyOn(searchThunk, "searchEventsAsync"); renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); const searchInput = screen.getByPlaceholderText("common.search"); fireEvent.change(searchInput, { target: { value: "test" } }); fireEvent.keyDown(searchInput, { key: "Enter" }); await waitFor(() => { expect(searchSpy).toHaveBeenCalledWith({ filters: { keywords: "", organizers: [], attendees: [], searchIn: ["user1/cal1"], }, search: "test", }); }); }); it("should not trigger search on Enter key when search is empty", async () => { const searchSpy = jest.spyOn(searchThunk, "searchEventsAsync"); renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); const searchInput = screen.getByPlaceholderText("common.search"); fireEvent.keyDown(searchInput, { key: "Enter" }); await waitFor(() => { expect(searchSpy).not.toHaveBeenCalled(); }); }); it("should not trigger search when filter keywords is empty", async () => { const searchSpy = jest.spyOn(searchThunk, "searchEventsAsync"); renderWithProviders(, preloadedState); const searchButton = screen.getByRole("button"); fireEvent.click(searchButton); // Click tune icon const tuneButton = screen.getByTestId("TuneIcon"); if (tuneButton) fireEvent.click(tuneButton); fireEvent.click(screen.getByText("common.search")); await waitFor(() => { expect(searchSpy).not.toHaveBeenCalled(); }); }); it("keeps search bar expanded when popover opens", () => { renderWithProviders(, preloadedState); fireEvent.click(screen.getByRole("button")); const tuneBtn = screen .getAllByRole("button") .find((b) => b.querySelector('[data-testid="TuneIcon"]')); fireEvent.click(tuneBtn!); expect(screen.getByPlaceholderText("common.search")).toBeInTheDocument(); }); it("does NOT collapse search when clicking inside popover", () => { renderWithProviders(, preloadedState); fireEvent.click(screen.getByRole("button")); const tuneBtn = screen .getAllByRole("button") .find((b) => b.querySelector('[data-testid="TuneIcon"]')); fireEvent.click(tuneBtn!); fireEvent.mouseDown(screen.getByText("search.searchIn")); expect(screen.getByPlaceholderText("common.search")).toBeInTheDocument(); }); it("clicking Cancel closes popover and search collapses if empty", async () => { renderWithProviders(, preloadedState); fireEvent.click(screen.getByRole("button")); const tuneBtn = screen .getAllByRole("button") .find((b) => b.querySelector('[data-testid="TuneIcon"]')); fireEvent.click(tuneBtn!); fireEvent.click(screen.getByText("common.cancel")); await waitFor(() => expect(screen.queryByText("search.searchIn")).not.toBeInTheDocument() ); expect(screen.queryByPlaceholderText("common.search")).toBeNull(); }); it("does not collapse when selecting a filter value", async () => { renderWithProviders(, preloadedState); fireEvent.click(screen.getByRole("button")); const tuneBtn = screen .getAllByRole("button") .find((b) => b.querySelector('[data-testid="TuneIcon"]')); fireEvent.click(tuneBtn!); fireEvent.mouseDown(screen.getByText("search.searchIn")); // simulate interaction inside menu expect(screen.getByPlaceholderText("common.search")).toBeInTheDocument(); }); it("keeps input value after popover closes if not empty", () => { renderWithProviders(, preloadedState); fireEvent.click(screen.getByRole("button")); userEvent.type(screen.getByPlaceholderText("common.search"), "hello"); const tuneBtn = screen .getAllByRole("button") .find((b) => b.querySelector('[data-testid="TuneIcon"]')); fireEvent.click(tuneBtn!); fireEvent.click(document.body); const inputAfter = screen.getByPlaceholderText("common.search"); expect(inputAfter).toHaveValue("hello"); }); });