import { InfoRow } from "@/components/Event/InfoRow"; import { LONG_DATE_FORMAT } from "@/components/Event/utils/dateTimeFormatters"; import * as eventThunks from "@/features/Calendars/services"; import EventUpdateModal from "@/features/Events/EventUpdateModal"; import { act, fireEvent, screen, waitFor } from "@testing-library/react"; import dayjs from "dayjs"; import { renderWithProviders } from "../../utils/Renderwithproviders"; describe("Event Full Display", () => { const mockOnClose = jest.fn(); const day = new Date("2025-01-15T10:00:00.000Z"); // Fixed date in UTC: Jan 15, 2025 10:00 AM UTC beforeEach(() => { jest.clearAllMocks(); }); 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: "First Calendar", color: "#FF0000", events: { event1: { uid: "event1", title: "Test Event", calId: "667037022b752d0026472254/cal1", start: day.toISOString(), end: day.toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, 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", }, ], }, }, }, "otherCal/cal": { id: "otherCal/cal", name: "Calendar 1", color: "#FF0000", events: { event1: { uid: "event1", calId: "otherCal/cal", title: "Test Event Other cal", start: day.toISOString(), end: day.toISOString(), organizer: { cn: "john", cal_address: "john@test.com" }, }, }, }, }, }, }; it("renders correctly event data with fixed timezone", () => { // Use fixed timezone UTC for consistent test results across all environments const fixedDate = new Date("2025-01-15T10:00:00.000Z"); // 10AM UTC const endDate = new Date(fixedDate.getTime() + 3600000); // 11AM UTC // With UTC timezone set, formatLocalDateTime produces predictable values const expectedStart = "2025-01-15T10:00"; // 10:00 AM UTC const expectedEnd = "2025-01-15T11:00"; // 11:00 AM UTC const stateWithFixedDate = { ...preloadedState, calendars: { pending: false, list: { "667037022b752d0026472254/cal1": { ...preloadedState.calendars.list["667037022b752d0026472254/cal1"], events: { event1: { ...preloadedState.calendars.list[ "667037022b752d0026472254/cal1" ].events.event1, start: fixedDate.toISOString(), end: endDate.toISOString(), timezone: "UTC", }, }, }, }, }, }; renderWithProviders( , stateWithFixedDate ); expect(screen.getByDisplayValue("Test Event")).toBeInTheDocument(); // Expand to show date/time inputs (normal mode shows DateTimeSummary) fireEvent.click(screen.getByRole("button", { name: "common.moreOptions" })); const startDateInput = screen.getByTestId("start-date-input"); const startTimeInput = screen.getByTestId("start-time-input"); const endTimeInput = screen.getByTestId("end-time-input"); expect(startDateInput).toBeInTheDocument(); expect(startTimeInput).toBeInTheDocument(); expect(endTimeInput).toBeInTheDocument(); expect(startDateInput).toHaveValue( dayjs(expectedStart).format(LONG_DATE_FORMAT) ); expect(startTimeInput).toHaveValue(dayjs(expectedStart).format("HH:mm")); expect(endTimeInput).toHaveValue(dayjs(expectedEnd).format("HH:mm")); expect(screen.getByText("First Calendar")).toBeInTheDocument(); }); it("calls onClose when Cancel clicked", () => { renderWithProviders( , preloadedState ); fireEvent.click(screen.getAllByTestId("CloseIcon")[0]); expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); }); it("toggle Show More reveals extra fields", async () => { renderWithProviders( , preloadedState ); act(() => { fireEvent.click( screen.getByRole("button", { name: "common.moreOptions" }) ); }); await waitFor(() => { expect(screen.getByText("event.form.notification")).toBeInTheDocument(); }); // EventDisplay modal doesn't have Repeat checkbox, only RepeatEvent component // which shows repetition settings when repetition data exists // Since test event has no repetition data, RepeatEvent component won't show Repeat checkbox fireEvent.click(screen.getByRole("button", { name: /Show Less/i })); }); it("can edit title when user is organizer", () => { renderWithProviders( , preloadedState ); const titleField = screen.getByLabelText("event.form.title"); fireEvent.change(titleField, { target: { value: "New Title" } }); expect(screen.getByDisplayValue("New Title")).toBeInTheDocument(); }); it("toggle all-day updates end date correctly", () => { renderWithProviders( , preloadedState ); fireEvent.click(screen.getByRole("button", { name: "common.moreOptions" })); const allDayCheckbox = screen.getByLabelText("event.form.allDay"); fireEvent.click(allDayCheckbox); expect(allDayCheckbox).toBeChecked(); const expectedDate = dayjs(day).format(LONG_DATE_FORMAT); const startDateInput = screen.getByTestId("start-date-input"); const endDateInput = screen.getByTestId("end-date-input"); expect(startDateInput).toHaveValue(expectedDate); expect(endDateInput).toHaveValue(expectedDate); }); it("saves event and moves it when calendar is changed", async () => { const spyPut = jest .spyOn(eventThunks, "putEventAsync") .mockImplementation((payload) => { const promise = Promise.resolve(payload); (promise as any).unwrap = () => promise; return () => promise as any; }); const spyMove = jest .spyOn(eventThunks, "moveEventAsync") .mockImplementation((payload) => { const promise = Promise.resolve(payload); (promise as any).unwrap = () => promise; return () => promise as any; }); const testDate = new Date("2025-01-15T10:00:00.000Z"); const testEndDate = new Date("2025-01-15T11:00:00.000Z"); const preloadedTwoCals = { ...preloadedState, calendars: { pending: false, list: { "667037022b752d0026472254/cal1": { id: "667037022b752d0026472254/cal1", name: "Calendar One", color: "#FF0000", events: { event1: { uid: "event1", title: "Test Event", calId: "667037022b752d0026472254/cal1", start: testDate.toISOString(), end: testEndDate.toISOString(), organizer: { cn: "test", cal_address: "test@test.com" }, attendee: [{ cn: "test", cal_address: "test@test.com" }], }, }, }, "667037022b752d0026472254/cal2": { id: "667037022b752d0026472254/cal2", name: "Calendar Two", color: "#00FF00", events: {}, }, }, pending: false, }, }; await act(async () => renderWithProviders( , preloadedTwoCals ) ); fireEvent.click(screen.getByRole("button", { name: "common.moreOptions" })); const calendarSelect = screen.getByLabelText("event.form.calendar"); await act(async () => fireEvent.mouseDown(calendarSelect)); const option = await screen.findByText("Calendar Two"); fireEvent.click(option); await act(async () => { fireEvent.click(screen.getByRole("button", { name: "actions.save" })); }); await waitFor( () => { expect(spyPut).toHaveBeenCalled(); }, { timeout: 3000 } ); await waitFor( () => { expect(spyMove).toHaveBeenCalled(); }, { timeout: 3000 } ); }); it("edit modal displays event time in original event timezone", () => { // GIVEN user timezone is UTC+2 // WHEN the user edits an event at 2PM UTC+7 (Asia/Bangkok) // THEN the update modal displays the time as 2PM in Asia/Bangkok timezone const eventDateUTC7 = new Date("2025-01-15T07:00:00.000Z"); // 7AM UTC = 2PM UTC+7 const stateWithTimezone = { ...preloadedState, calendars: { pending: false, list: { "667037022b752d0026472254/cal1": { id: "667037022b752d0026472254/cal1", name: "Test Calendar", color: "#FF0000", events: { event1: { uid: "event1", title: "Timezone Edit Test", calId: "667037022b752d0026472254/cal1", start: eventDateUTC7.toISOString(), end: new Date(eventDateUTC7.getTime() + 3600000).toISOString(), timezone: "Asia/Bangkok", allday: false, organizer: { cn: "test", cal_address: "test@test.com" }, attendee: [{ cn: "test", cal_address: "test@test.com" }], }, }, }, }, pending: false, }, }; renderWithProviders( , stateWithTimezone ); // Expand to show timezone selector (normal mode hides it) fireEvent.click(screen.getByRole("button", { name: "common.moreOptions" })); // The timezone select should have Asia/Bangkok selected const timeZone = screen.getByDisplayValue(/Asia\/Bangkok/i); expect(timeZone).toBeInTheDocument(); }); it("InfoRow renders error style when error prop is true", () => { renderWithProviders(i} text="Bad" error />); expect(screen.getByText("Bad")).toBeInTheDocument(); }); it("can remove an attendee with the close button", () => { renderWithProviders( , preloadedState ); const removeBtn = screen.getAllByTestId("CloseIcon").pop()!; fireEvent.click(removeBtn); expect(screen.queryByText(/John/)).not.toBeInTheDocument(); }); it("renders video conference info when x_openpass_videoconference exists", () => { const videoState = { ...preloadedState, calendars: { pending: false, list: { "667037022b752d0026472254/cal1": { ...preloadedState.calendars.list["667037022b752d0026472254/cal1"], events: { event1: { ...preloadedState.calendars.list[ "667037022b752d0026472254/cal1" ].events.event1, x_openpass_videoconference: "https://meet.test/video", }, }, }, }, }, }; renderWithProviders( , videoState ); expect( screen.getByText("event.form.joinVisioConference") ).toBeInTheDocument(); }); });