import { DateSelectArg } from "@fullcalendar/core"; import { act, fireEvent, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import preview from "jest-preview"; import * as eventThunks from "../../../src/features/Calendars/CalendarSlice"; import EventPopover from "../../../src/features/Events/EventModal"; import { api } from "../../../src/utils/apiUtils"; import { formatDateToYYYYMMDDTHHMMSS } from "../../../src/utils/dateUtils"; import { renderWithProviders } from "../../utils/Renderwithproviders"; jest.mock("../../../src/utils/apiUtils"); describe("EventPopover", () => { const mockOnClose = jest.fn(); const mockSetSelectedRange = jest.fn(); const mockCalendarRef = { current: { select: jest.fn() } } as any; const preloadedState = { user: { userData: { sub: "test", email: "test@test.com", sid: "aiYbWZSk2g0F+LrQeD7Dg4QcUMR8R/zTZdZBiA7N6Ro", openpaasId: "667037022b752d0026472254", }, organiserData: { cn: "test", cal_address: "test@test.com", }, }, calendars: { list: { "667037022b752d0026472254/cal1": { id: "667037022b752d0026472254/cal1", name: "Calendar 1", color: "#FF0000", }, "667037022b752d0026472254/cal2": { id: "667037022b752d0026472254/cal2", name: "Calendar 2", color: "#00FF00", }, }, pending: false, }, }; const mockUsers = [ { id: "john@example.com", objectType: "user", emailAddresses: [ { value: "john@example.com", type: "default", }, ], names: [ { displayName: "John Doe", type: "default", }, ], }, { id: "jane@example.com", objectType: "user", emailAddresses: [ { value: "jane@example.com", type: "default", }, ], names: [ { displayName: "Jane Smith", type: "default", }, ], }, ]; (api.post as jest.Mock).mockReturnValue({ json: jest.fn().mockResolvedValue(mockUsers), }); const defaultSelectedRange = { startStr: "2025-07-18T09:00", endStr: "2025-07-18T10:00", start: new Date("2025-07-18T09:00"), end: new Date("2025-07-18T10:00"), allDay: false, resource: undefined, } as unknown as DateSelectArg; const renderPopover = (selectedRange = defaultSelectedRange) => { renderWithProviders( , preloadedState ); }; it("renders correctly with inputs and calendar options", () => { renderPopover(); // Check dialog title expect(screen.getByText("Create Event")).toBeInTheDocument(); // Check inputs exist by their roles const titleInput = screen.getByRole("textbox", { name: /title/i }); expect(titleInput).toBeInTheDocument(); // Description input is only visible after clicking "Add description" button const addDescriptionButton = screen.getByRole("button", { name: /Add description/i, }); expect(addDescriptionButton).toBeInTheDocument(); const calendarSelect = screen.getByRole("combobox", { name: /calendar/i }); expect(calendarSelect).toBeInTheDocument(); // Check button expect( screen.getByRole("button", { name: /More options/i }) ).toBeInTheDocument(); // Extended mode fireEvent.click(screen.getByRole("button", { name: /More options/i })); // Back button appears expect(screen.getByLabelText("show less")).toBeInTheDocument(); // Extended labels appear expect(screen.getAllByText("Repeat")).toHaveLength(1); expect(screen.getAllByText("Notification")).toHaveLength(1); expect(screen.getAllByText("Visible to")).toHaveLength(1); expect(screen.getAllByText("Show me as")).toHaveLength(1); }); it("fills start from selectedRange", () => { renderPopover({ startStr: "2026-07-20T10:00", endStr: "2026-07-20T12:00", start: new Date("2026-07-20T10:00"), end: new Date("2026-07-20T12:00"), allDay: false, } as unknown as DateSelectArg); expect(screen.getByLabelText("Start Date")).toHaveValue("2026-07-20"); expect(screen.getByLabelText("Start Time")).toHaveValue("10:00"); }); it("updates inputs on change", () => { renderPopover(); fireEvent.change(screen.getByLabelText("Title"), { target: { value: "My Event" }, }); expect(screen.getByLabelText("Title")).toHaveValue("My Event"); // Click "Add description" button first fireEvent.click(screen.getByRole("button", { name: /Add description/i })); fireEvent.change(screen.getByLabelText("Description"), { target: { value: "Event Description" }, }); expect(screen.getByLabelText("Description")).toHaveValue( "Event Description" ); fireEvent.change(screen.getByLabelText("Location"), { target: { value: "Conference Room" }, }); expect(screen.getByLabelText("Location")).toHaveValue("Conference Room"); }); it("changes selected calendar", async () => { renderPopover(); const select = screen.getByLabelText("Calendar"); fireEvent.mouseDown(select); // Open menu const option = await screen.findByText("Calendar 2"); fireEvent.click(option); // Find the calendar combobox specifically by its aria-labelledby const calendarSelect = screen.getByRole("combobox", { name: /Calendar/i }); expect(calendarSelect).toHaveTextContent("Calendar 2"); }); it("adds a attendee", async () => { jest.useFakeTimers(); renderPopover(); fireEvent.change(screen.getByLabelText("Title"), { target: { value: "newEvent" }, }); const select = screen.getByLabelText("Start typing a name or email"); act(() => { select.focus(); fireEvent.mouseDown(select); userEvent.type(select, "john"); }); await act(async () => { jest.advanceTimersByTime(400); }); await waitFor(() => expect(api.post).toHaveBeenCalledTimes(1)); await waitFor(() => { expect(screen.getByText("John Doe")).toBeInTheDocument(); }); await act(async () => { userEvent.click(screen.getByText("John Doe")); }); const spy = jest .spyOn(eventThunks, "putEventAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); fireEvent.click(screen.getByRole("button", { name: /Save/i })); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const receivedPayload = spy.mock.calls[0][0]; expect(receivedPayload.cal).toEqual( preloadedState.calendars.list["667037022b752d0026472254/cal1"] ); expect(receivedPayload.newEvent.attendee).toHaveLength(2); expect(receivedPayload.newEvent.attendee).toStrictEqual([ { cn: "test", cal_address: "test@test.com", partstat: "ACCEPTED", rsvp: "FALSE", role: "CHAIR", cutype: "INDIVIDUAL", }, { cn: "John Doe", cal_address: "john@example.com", partstat: "NEED_ACTION", rsvp: "FALSE", role: "CHAIR", cutype: "INDIVIDUAL", }, ]); }); it("dispatches putEventAsync and calls onClose when Save is clicked", async () => { renderPopover(); const newEvent = { title: "Meeting", start: "2025-07-18T00:00:00.000Z", end: "2025-07-19T00:00:00.000Z", allday: false, uid: "6045c603-11ab-43c5-bc30-0641420bb3a8", description: "Discuss project", location: "Zoom", organizer: { cn: "test", cal_address: "test@test.com" }, timezone: "Europe/Paris", transp: "TRANSPARENT", }; // Fill inputs fireEvent.change(screen.getByLabelText("Title"), { target: { value: newEvent.title }, }); fireEvent.click(screen.getByLabelText("All day")); fireEvent.change(screen.getByLabelText("Start Date"), { target: { value: newEvent.start.split("T")[0] }, }); fireEvent.change(screen.getByLabelText("End Date"), { target: { value: newEvent.end.split("T")[0] }, }); // Click "Add description" button first fireEvent.click(screen.getByRole("button", { name: /Add description/i })); fireEvent.change(screen.getByLabelText("Description"), { target: { value: newEvent.description }, }); fireEvent.change(screen.getByLabelText("Location"), { target: { value: newEvent.location }, }); const spy = jest .spyOn(eventThunks, "putEventAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); fireEvent.click(screen.getByRole("button", { name: /Save/i })); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const receivedPayload = spy.mock.calls[0][0]; expect(receivedPayload.cal).toEqual( preloadedState.calendars.list["667037022b752d0026472254/cal1"] ); expect(receivedPayload.newEvent.title).toBe(newEvent.title); expect(receivedPayload.newEvent.description).toBe(newEvent.description); expect( formatDateToYYYYMMDDTHHMMSS( new Date(receivedPayload.newEvent.start) ).split("T")[0] ).toBe(formatDateToYYYYMMDDTHHMMSS(new Date(newEvent.start)).split("T")[0]); expect( formatDateToYYYYMMDDTHHMMSS( new Date(receivedPayload.newEvent.end || new Date()) ).split("T")[0] ).toBe(formatDateToYYYYMMDDTHHMMSS(new Date(newEvent.end)).split("T")[0]); expect(receivedPayload.newEvent.location).toBe(newEvent.location); expect(receivedPayload.newEvent.organizer).toEqual(newEvent.organizer); expect(receivedPayload.newEvent.color).toEqual( preloadedState.calendars.list["667037022b752d0026472254/cal1"].color ); // onClose should be called expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); }); it("calls onClose when Cancel clicked", () => { renderPopover(); // Cancel button only appears in expanded mode fireEvent.click(screen.getByRole("button", { name: /More options/i })); fireEvent.click(screen.getByRole("button", { name: /Cancel/i })); expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); }); it("BUGFIX: Prefill Calendar field", async () => { renderPopover(); await waitFor(() => expect(screen.getByLabelText("Calendar")).toHaveTextContent("Calendar 1") ); }); });