import { RootState } from "@/app/store"; import { createEventHandlers, EventHandlersProps, } from "@/components/Calendar/handlers/eventHandlers"; import { EditModeDialog } from "@/components/Event/EditModeDialog"; import * as eventThunks from "@/features/Calendars/services"; import * as EventApi from "@/features/Events/EventApi"; import EventPreviewModal from "@/features/Events/EventDisplayPreview"; import EventUpdateModal from "@/features/Events/EventUpdateModal"; import { act, fireEvent, screen, waitFor } from "@testing-library/react"; import preview from "jest-preview"; import { renderWithProviders } from "../../utils/Renderwithproviders"; jest.mock("@/components/Event/utils/eventUtils", () => { const actual = jest.requireActual("@/components/Event/utils/eventUtils"); return { ...actual, refreshCalendars: jest.fn(() => Promise.resolve()), refreshSingularCalendar: jest.fn(() => Promise.resolve()), }; }); const mockOnClose = jest.fn(); const day = new Date("2025-03-15T10:00:00Z"); const basePreloadedState = { 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", color: "#FF0000", events: { "recurring-base/20250315T100000": { uid: "recurring-base/20250315T100000", title: "Recurring Event Instance", calId: "667037022b752d0026472254/cal1", start: day.toISOString(), end: new Date("2025-03-15T11:00:00Z").toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, recurrenceId: "20250315T100000", repetition: { freq: "weekly", occurrences: 4, interval: 1 }, timezone: "UTC", URL: "/calendars/667037022b752d0026472254/cal1/recurring-base.ics", attendee: [ { cn: "test", cal_address: "test@test.com", partstat: "NEEDS-ACTION", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", }, { cn: "John", cal_address: "john@test.com", partstat: "NEEDS-ACTION", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", }, ], }, "recurring-base/20250322T100000": { uid: "recurring-base/20250322T100000", title: "Recurring Event Instance", calId: "667037022b752d0026472254/cal1", start: new Date("2025-03-22T10:00:00Z").toISOString(), end: new Date("2025-03-22T11:00:00Z").toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, recurrenceId: "20250322T100000", timezone: "UTC", URL: "/calendars/667037022b752d0026472254/cal1/recurring-base.ics", attendee: [ { cn: "test", cal_address: "test@test.com", partstat: "NEEDS-ACTION", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", }, ], }, }, ownerEmails: ["test@test.com"], }, }, pending: false, templist: {}, }, } as unknown as RootState; describe("EditModeDialog Component", () => { beforeEach(() => { jest.clearAllMocks(); }); it("renders dialog when type is 'edit'", async () => { const mockSetOpen = jest.fn(); const mockEventAction = jest.fn(); await act(async () => renderWithProviders( , basePreloadedState ) ); expect( screen.getByText("editModeDialog.updateRecurrentEvent") ).toBeInTheDocument(); expect(screen.getByText("editModeDialog.thisEvent")).toBeInTheDocument(); expect(screen.getByText("editModeDialog.allEvents")).toBeInTheDocument(); }); it("renders dialog when type is 'attendance'", async () => { const mockSetOpen = jest.fn(); const mockEventAction = jest.fn(); await act(async () => renderWithProviders( , basePreloadedState ) ); expect( screen.getByText("editModeDialog.updateParticipationStatus") ).toBeInTheDocument(); }); it("calls eventAction with 'solo' when solo option is selected and Ok clicked", async () => { const mockSetOpen = jest.fn(); const mockEventAction = jest.fn(); await act(async () => renderWithProviders( , basePreloadedState ) ); await act(async () => { fireEvent.click(screen.getByLabelText("editModeDialog.thisEvent")); fireEvent.click(screen.getByRole("button", { name: "common.ok" })); }); await waitFor(() => { expect(mockEventAction).toHaveBeenCalledWith("solo"); expect(mockSetOpen).toHaveBeenCalledWith(null); }); }); it("calls eventAction with 'all' when all option is selected and Ok clicked", async () => { const mockSetOpen = jest.fn(); const mockEventAction = jest.fn(); await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click(screen.getByLabelText("editModeDialog.allEvents")); fireEvent.click(screen.getByRole("button", { name: /Ok/i })); await waitFor(() => { expect(mockEventAction).toHaveBeenCalledWith("all"); expect(mockSetOpen).toHaveBeenCalledWith(null); }); }); it("calls setOpen with null when Cancel is clicked", async () => { const mockSetOpen = jest.fn(); const mockEventAction = jest.fn(); await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click(screen.getByText("common.cancel")); await waitFor(() => { expect(mockSetOpen).toHaveBeenCalledWith(null); expect(mockEventAction).not.toHaveBeenCalled(); }); }); }); describe("EventPreviewModal - Recurring Event Interactions", () => { beforeEach(() => { jest.clearAllMocks(); }); it("shows EditModeDialog when editing a recurring event", async () => { jest.spyOn(eventThunks, "getEventAsync").mockImplementation((payload) => { return () => Promise.resolve({ calId: payload.calId, event: basePreloadedState.calendars.list["667037022b752d0026472254/cal1"] .events["recurring-base/20250315T100000"], }) as any; }); await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click(screen.getByTestId("EditIcon")); await waitFor(() => { expect( screen.getByText("editModeDialog.updateRecurrentEvent") ).toBeInTheDocument(); }); }); it("shows EditModeDialog when deleting a recurring event", async () => { await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click(screen.getByTestId("MoreVertIcon")); fireEvent.click( screen.getByRole("menuitem", { name: "eventPreview.deleteEvent" }) ); await waitFor(() => { expect( screen.getByText("editModeDialog.deleteRecurrentEvent") ).toBeInTheDocument(); }); }); it("shows EditModeDialog when RSVP to a recurring event", async () => { await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click( screen.getByRole("button", { name: "eventPreview.ACCEPTED" }) ); await waitFor(() => { expect( screen.getByText("editModeDialog.updateParticipationStatus") ).toBeInTheDocument(); }); }); it("does not show EditModeDialog for non-recurring events", async () => { const nonRecurringState = { ...basePreloadedState, calendars: { ...basePreloadedState.calendars, list: { "667037022b752d0026472254/cal1": { ...basePreloadedState.calendars.list[ "667037022b752d0026472254/cal1" ], events: { "single-event": { uid: "single-event", title: "Single Event", calId: "667037022b752d0026472254/cal1", start: day.toISOString(), end: new Date("2025-03-15T11:00:00Z").toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, timezone: "UTC", URL: "/calendars/667037022b752d0026472254/cal1/single-event.ics", attendee: [], }, }, }, }, }, }; const spy = jest .spyOn(eventThunks, "deleteEventAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); await act(async () => renderWithProviders( , nonRecurringState ) ); fireEvent.click(screen.getByTestId("MoreVertIcon")); fireEvent.click( screen.getByRole("menuitem", { name: "eventPreview.deleteEvent" }) ); await waitFor(() => { expect(spy).toHaveBeenCalled(); expect(mockOnClose).toHaveBeenCalled(); }); expect( screen.queryByText("editModeDialog.deleteRecurrentEvent") ).not.toBeInTheDocument(); }); }); describe("Delete Recurring Event Instance", () => { beforeEach(() => { jest.clearAllMocks(); }); it("calls deleteEventInstanceAsync when deleting single instance", async () => { const spy = jest .spyOn(eventThunks, "deleteEventInstanceAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click(screen.getByTestId("MoreVertIcon")); fireEvent.click( screen.getByRole("menuitem", { name: "eventPreview.deleteEvent" }) ); await waitFor(() => { expect( screen.getByText("editModeDialog.deleteRecurrentEvent") ).toBeInTheDocument(); }); fireEvent.click(screen.getByLabelText("editModeDialog.thisEvent")); fireEvent.click(screen.getByRole("button", { name: /Ok/i })); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const receivedPayload = spy.mock.calls[0][0]; expect(receivedPayload.event.uid).toBe("recurring-base/20250315T100000"); }); it("calls deleteEventAsync when deleting all instances", async () => { jest.spyOn(EventApi, "getEvent").mockResolvedValue({ ...basePreloadedState.calendars.list["667037022b752d0026472254/cal1"] .events["recurring-base/20250315T100000"], uid: "recurring-base", } as any); const spy = jest .spyOn(eventThunks, "deleteEventAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click(screen.getByTestId("MoreVertIcon")); fireEvent.click( screen.getByRole("menuitem", { name: "eventPreview.deleteEvent" }) ); await waitFor(() => { expect( screen.getByText("editModeDialog.deleteRecurrentEvent") ).toBeInTheDocument(); }); fireEvent.click(screen.getByLabelText("editModeDialog.allEvents")); fireEvent.click(screen.getByRole("button", { name: /Ok/i })); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const receivedPayload = spy.mock.calls[0][0]; expect(receivedPayload.eventId).toBe("recurring-base/20250315T100000"); }); }); describe("RSVP to Recurring Event", () => { beforeEach(() => { jest.clearAllMocks(); }); it("calls updateEventInstanceAsync when accepting single instance", async () => { const spy = jest .spyOn(eventThunks, "updateEventInstanceAsync") .mockImplementation((payload) => { const promise = Promise.resolve(payload); (promise as any).unwrap = () => promise; return () => promise as any; }); await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click( screen.getByRole("button", { name: "eventPreview.ACCEPTED" }) ); await waitFor(() => { expect( screen.getByText("editModeDialog.updateParticipationStatus") ).toBeInTheDocument(); }); fireEvent.click(screen.getByLabelText("editModeDialog.thisEvent")); fireEvent.click(screen.getByRole("button", { name: /Ok/i })); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const updatedEvent = spy.mock.calls[0][0].event; expect(updatedEvent.attendee[0].partstat).toBe("ACCEPTED"); }); it("calls updateSeriesPartstat when accepting all instances", async () => { const spy = jest .spyOn(EventApi, "updateSeriesPartstat") .mockResolvedValue({} as any); await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click( screen.getByRole("button", { name: "eventPreview.ACCEPTED" }) ); await waitFor(() => { expect( screen.getByText("editModeDialog.updateParticipationStatus") ).toBeInTheDocument(); }); fireEvent.click(screen.getByLabelText("editModeDialog.allEvents")); fireEvent.click(screen.getByRole("button", { name: "common.ok" })); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const callArgs = spy.mock.calls[0]; expect(callArgs[0].uid).toBe("recurring-base/20250315T100000"); expect(callArgs[1]).toBe("test@test.com"); expect(callArgs[2]).toBe("ACCEPTED"); }); }); describe("Edit Recurring Event in Full Display", () => { beforeEach(() => { jest.clearAllMocks(); }); it("renders event with recurrenceId in URL", async () => { await act(async () => renderWithProviders( , basePreloadedState ) ); expect( screen.getByDisplayValue("Recurring Event Instance") ).toBeInTheDocument(); }); it("calls updateEventInstanceAsync when saving single instance with typeOfAction='solo'", async () => { jest.spyOn(EventApi, "getEvent").mockResolvedValue({ ...basePreloadedState.calendars.list["667037022b752d0026472254/cal1"] .events["recurring-base/20250315T100000"], } as any); const spy = jest .spyOn(eventThunks, "updateEventInstanceAsync") .mockImplementation((payload) => { return () => { const promise = Promise.resolve(); (promise as any).unwrap = () => promise; return promise as any; }; }); await act(async () => renderWithProviders( , basePreloadedState ) ); const titleField = screen.getByLabelText("event.form.title"); fireEvent.change(titleField, { target: { value: "Updated Single Instance" }, }); // Click Save fireEvent.click(screen.getByText("actions.save")); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const updatedEvent = spy.mock.calls[0][0].event; expect(updatedEvent.title).toBe("Updated Single Instance"); expect(updatedEvent.recurrenceId).toBe("20250315T100000"); }); it("calls updateSeriesAsync when saving all instances with typeOfAction='all'", async () => { const getEventSpy = jest.spyOn(EventApi, "getEvent").mockResolvedValue({ ...basePreloadedState.calendars.list["667037022b752d0026472254/cal1"] .events["recurring-base/20250315T100000"], } as any); const spy = jest .spyOn(eventThunks, "updateSeriesAsync") .mockImplementation((payload) => { return () => { const promise = Promise.resolve(); (promise as any).unwrap = () => promise; return promise as any; }; }); await act(async () => renderWithProviders( , basePreloadedState ) ); await waitFor(() => { expect(getEventSpy).toHaveBeenCalled(); }); const titleField = screen.getByDisplayValue("Recurring Event Instance"); fireEvent.change(titleField, { target: { value: "Updated All Instances" }, }); fireEvent.click(screen.getByRole("button", { name: "actions.save" })); await waitFor(() => { expect(spy).toHaveBeenCalled(); }); const updatedEvent = spy.mock.calls[0][0].event; expect(updatedEvent.title).toBe("Updated All Instances"); }); it("disables repetition editing when typeOfAction='solo'", async () => { await act(async () => renderWithProviders( , basePreloadedState ) ); fireEvent.click(screen.getByRole("button", { name: "common.moreOptions" })); expect(screen.getByText("event.repeat.repeatEvery")).toBeInTheDocument(); expect(screen.getByText("event.repeat.end.label")).toBeInTheDocument(); const frequencySelect = screen.getByRole("radio", { name: /event.repeat.end.after \d event.repeat.end.occurrences/i, }); expect(frequencySelect).toBeDisabled(); }); it("fetches master event data when typeOfAction='all'", async () => { const getEventSpy = jest.spyOn(EventApi, "getEvent").mockResolvedValue({ ...basePreloadedState.calendars.list["667037022b752d0026472254/cal1"] .events["recurring-base/20250315T100000"], uid: "recurring-base", title: "Master Event Title", description: "Master Description", } as any); await act(async () => renderWithProviders( , basePreloadedState ) ); await waitFor(() => { expect(getEventSpy).toHaveBeenCalled(); expect( screen.getByDisplayValue("Master Event Title") ).toBeInTheDocument(); }); }); }); describe("Event Drag and Drop - Recurring Events", () => { beforeEach(() => { jest.clearAllMocks(); }); it("shows EditModeDialog when dragging recurring event", () => { const mockDispatch = jest.fn(); const mockSetSelectedEvent = jest.fn(); const mockSetOpenEditModePopup = jest.fn(); const mockSetAfterChoiceFunc = jest.fn(); const eventHandlers = createEventHandlers({ setSelectedRange: jest.fn(), setOpenEventModal: jest.fn(), setTempEvent: jest.fn(), setOpenEventDisplay: jest.fn(), dispatch: mockDispatch, calendarRange: { start: new Date(), end: new Date() }, setEventDisplayedId: jest.fn(), setEventDisplayedCalId: jest.fn(), setEventDisplayedTemp: jest.fn(), calendars: basePreloadedState.calendars.list, setSelectedEvent: mockSetSelectedEvent, setAfterChoiceFunc: mockSetAfterChoiceFunc, setOpenEditModePopup: mockSetOpenEditModePopup, } as unknown as EventHandlersProps); const mockArg = { event: { _def: { extendedProps: { uid: "recurring-base/20250315T100000", calId: "667037022b752d0026472254/cal1", }, }, }, delta: { years: 0, months: 0, days: 1, milliseconds: 0 }, }; eventHandlers.handleEventDrop(mockArg); expect(mockSetSelectedEvent).toHaveBeenCalled(); expect(mockSetOpenEditModePopup).toHaveBeenCalledWith("edit"); expect(mockSetAfterChoiceFunc).toHaveBeenCalled(); }); }); describe("Event Resize - Recurring Events", () => { beforeEach(() => { jest.clearAllMocks(); }); it("shows EditModeDialog when resizing recurring event", () => { const mockDispatch = jest.fn(); const mockSetSelectedEvent = jest.fn(); const mockSetOpenEditModePopup = jest.fn(); const mockSetAfterChoiceFunc = jest.fn(); const eventHandlers = createEventHandlers({ setSelectedRange: jest.fn(), setOpenEventModal: jest.fn(), setTempEvent: jest.fn(), setOpenEventDisplay: jest.fn(), dispatch: mockDispatch, calendarRange: { start: new Date(), end: new Date() }, setEventDisplayedId: jest.fn(), setEventDisplayedCalId: jest.fn(), setEventDisplayedTemp: jest.fn(), calendars: basePreloadedState.calendars.list, setSelectedEvent: mockSetSelectedEvent, setAfterChoiceFunc: mockSetAfterChoiceFunc, setOpenEditModePopup: mockSetOpenEditModePopup, } as unknown as EventHandlersProps); const mockArg = { event: { _def: { extendedProps: { uid: "recurring-base/20250315T100000", calId: "667037022b752d0026472254/cal1", }, }, }, startDelta: { years: 0, months: 0, days: 0, milliseconds: 0 }, endDelta: { years: 0, months: 0, days: 0, milliseconds: 3600000 }, // 1 hour }; eventHandlers.handleEventResize(mockArg); expect(mockSetSelectedEvent).toHaveBeenCalled(); expect(mockSetOpenEditModePopup).toHaveBeenCalledWith("edit"); expect(mockSetAfterChoiceFunc).toHaveBeenCalled(); }); }); describe("RepeatEvent Component - Recurrence Editing", () => { beforeEach(() => { jest.clearAllMocks(); }); it("disables repetition fields when isOwn is false", async () => { await act(async () => renderWithProviders( , { ...basePreloadedState, calendars: { ...basePreloadedState.calendars, list: { "otherCal/cal": { id: "otherCal/cal", name: "Other Calendar", color: "#00FF00", events: { "recurring-base/20250315T100000": { ...basePreloadedState.calendars.list[ "667037022b752d0026472254/cal1" ].events["recurring-base/20250315T100000"], calId: "otherCal/cal", organizer: { cn: "other", cal_address: "other@test.com" }, }, }, }, }, }, } ) ); fireEvent.click(screen.getByRole("button", { name: "common.moreOptions" })); // Repetition section should not be visible for non-owner expect( screen.queryByLabelText("event.form.repetition") ).not.toBeInTheDocument(); }); }); describe("handleRSVP function", () => { beforeEach(() => { jest.clearAllMocks(); }); it("calls putEventAsync for non-recurring events", async () => { const mockDispatch = jest.fn(); const mockOnClose = jest.fn(); const { handleRSVP, } = require("@/components/Event/eventHandlers/eventHandlers"); jest.spyOn(eventThunks, "putEventAsync").mockImplementation((payload) => { const promise = Promise.resolve(payload); (promise as any).unwrap = () => promise; return () => promise as any; }); const nonRecurringEvent = { uid: "single-event", title: "Single Event", calId: "667037022b752d0026472254/cal1", start: day.toISOString(), end: new Date("2025-03-15T11:00:00Z").toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, attendee: [ { cn: "test", cal_address: "test@test.com", partstat: "NEEDS-ACTION", }, ], }; await handleRSVP( mockDispatch, basePreloadedState.calendars.list["667037022b752d0026472254/cal1"], basePreloadedState.user, nonRecurringEvent, "ACCEPTED" ); expect(mockDispatch).toHaveBeenCalled(); }); }); describe("handleDelete function", () => { beforeEach(() => { jest.clearAllMocks(); }); it("calls deleteEventAsync for non-recurring events", () => { const mockDispatch = jest.fn(); const mockOnClose = jest.fn(); const { handleDelete, } = require("@/components/Event/eventHandlers/eventHandlers"); jest .spyOn(eventThunks, "deleteEventAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); const nonRecurringEvent = { uid: "single-event", title: "Single Event", calId: "667037022b752d0026472254/cal1", URL: "/calendars/667037022b752d0026472254/cal1/single-event.ics", }; handleDelete( false, // isRecurring undefined, mockOnClose, mockDispatch, basePreloadedState.calendars.list["667037022b752d0026472254/cal1"], nonRecurringEvent, "667037022b752d0026472254/cal1", "single-event" ); expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); expect(mockDispatch).toHaveBeenCalled(); }); it("calls deleteEventInstanceAsync when deleting solo recurring event", () => { const mockDispatch = jest.fn(); const mockOnClose = jest.fn(); const { handleDelete, } = require("@/components/Event/eventHandlers/eventHandlers"); jest .spyOn(eventThunks, "deleteEventInstanceAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); handleDelete( true, // isRecurring "solo", mockOnClose, mockDispatch, basePreloadedState.calendars.list["667037022b752d0026472254/cal1"], basePreloadedState.calendars.list["667037022b752d0026472254/cal1"].events[ "recurring-base/20250315T100000" ], "667037022b752d0026472254/cal1", "recurring-base/20250315T100000" ); expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); expect(mockDispatch).toHaveBeenCalled(); }); it("calls deleteEventAsync when deleting all recurring events", () => { const mockDispatch = jest.fn(); const mockOnClose = jest.fn(); const { handleDelete, } = require("@/components/Event/eventHandlers/eventHandlers"); jest .spyOn(eventThunks, "deleteEventAsync") .mockImplementation((payload) => { return () => Promise.resolve(payload) as any; }); handleDelete( true, // isRecurring "all", mockOnClose, mockDispatch, basePreloadedState.calendars.list["667037022b752d0026472254/cal1"], basePreloadedState.calendars.list["667037022b752d0026472254/cal1"].events[ "recurring-base/20250315T100000" ], "667037022b752d0026472254/cal1", "recurring-base/20250315T100000" ); expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); expect(mockDispatch).toHaveBeenCalled(); }); }); describe("Calendar Integration - EditModeDialog Flow", () => { beforeEach(() => { jest.clearAllMocks(); }); it("passes correct eventId when editing all instances from preview", async () => { await act(async () => renderWithProviders( , basePreloadedState ) ); // Click edit fireEvent.click(screen.getByTestId("EditIcon")); await waitFor(() => { expect( screen.getByText("editModeDialog.updateRecurrentEvent") ).toBeInTheDocument(); }); // Select "All the events" fireEvent.click(screen.getByLabelText("editModeDialog.allEvents")); fireEvent.click(screen.getByRole("button", { name: /Ok/i })); await waitFor(() => { // Update modal should open expect(screen.getByText("event.updateEvent")).toBeInTheDocument(); }); }); }); describe("Event URL handling for recurring events", () => { beforeEach(() => { jest.clearAllMocks(); }); it("uses base ID for event URL when moving recurring event", async () => { const moveEventSpy = jest .spyOn(eventThunks, "moveEventAsync") .mockImplementation((payload) => { const result = { calId: payload.cal.id, events: [] }; const promise = Promise.resolve(result); (promise as any).unwrap = () => promise; return () => promise as any; }); const twoCalState = { 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", color: "#FF0000", events: { "recurring-base/20250315T100000": { uid: "recurring-base/20250315T100000", title: "Recurring Event Instance", calId: "667037022b752d0026472254/cal1", start: day.toISOString(), end: new Date("2025-03-15T11:00:00Z").toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, recurrenceId: "20250315T100000", repetition: { freq: "weekly", occurrences: 4, interval: 1 }, timezone: "UTC", URL: "/calendars/667037022b752d0026472254/cal1/recurring-base.ics", attendee: [ { cn: "test", cal_address: "test@test.com", partstat: "NEEDS-ACTION", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", }, { cn: "John", cal_address: "john@test.com", partstat: "NEEDS-ACTION", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", }, ], }, "recurring-base/20250322T100000": { uid: "recurring-base/20250322T100000", title: "Recurring Event Instance", calId: "667037022b752d0026472254/cal1", start: new Date("2025-03-22T10:00:00Z").toISOString(), end: new Date("2025-03-22T11:00:00Z").toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, recurrenceId: "20250322T100000", timezone: "UTC", URL: "/calendars/667037022b752d0026472254/cal1/recurring-base.ics", attendee: [ { cn: "test", cal_address: "test@test.com", partstat: "NEEDS-ACTION", rsvp: "TRUE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", }, ], }, }, ownerEmails: ["test@test.com"], }, "667037022b752d0026472254/cal2": { id: "667037022b752d0026472254/cal2", name: "Calendar 2", color: "#00FF00", events: {}, }, }, pending: false, templist: {}, }, }; await act(async () => renderWithProviders( , twoCalState ) ); // Expand to show calendar combobox (normal mode shows SectionPreviewRow) fireEvent.click(screen.getByRole("button", { name: "common.moreOptions" })); fireEvent.mouseDown(screen.getByLabelText("event.form.calendar")); const option = await screen.findByText("Calendar 2"); fireEvent.click(option); // Click Save button const saveButton = screen.getByRole("button", { name: "actions.save" }); await act(async () => { fireEvent.click(saveButton); // Wait for the 500ms delay in the code await new Promise((resolve) => setTimeout(resolve, 600)); }); await waitFor(() => { expect(moveEventSpy).toHaveBeenCalled(); }); const movePayload = moveEventSpy.mock.calls[0][0]; // Should use base ID, not full uid with recurrence ID expect(movePayload.newURL).toContain("recurring-base.ics"); expect(movePayload.newURL).not.toContain("/20250315T100000"); }); });