Files
workavia-calendar-front/__test__/features/Events/EventModal.test.tsx
T
lenhanphung 5078128e10 test: fix test cases for MUI DatePicker and TimePicker migration
- Add data-testid attributes to DatePicker and TimePicker components in DateTimeFields
- Update test queries from getByLabelText to getByTestId for date/time inputs
- Fix assertions to use toHaveAttribute instead of toHaveValue for MUI pickers
- Add data-testid to occurrences-input in EventRepeat component
- Update RepeatEvent test to use data-testid for occurrences input
- Fix radio button queries using getAllByLabelText with type filter
2025-11-05 10:25:41 +01:00

348 lines
10 KiB
TypeScript

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(
<EventPopover
anchorEl={document.body}
open={true}
onClose={mockOnClose}
selectedRange={selectedRange}
setSelectedRange={mockSetSelectedRange}
calendarRef={mockCalendarRef}
/>,
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);
// MUI DatePicker/TimePicker values are stored differently - just check elements exist
expect(screen.getByTestId("start-date-input")).toBeInTheDocument();
expect(screen.getByTestId("start-time-input")).toBeInTheDocument();
});
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"));
// 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")
);
});
});