[tests] updated tests

This commit is contained in:
Camille Moussu
2025-07-23 17:32:13 +02:00
parent a5e326b5ac
commit c38fa29244
3 changed files with 348 additions and 108 deletions
+108 -100
View File
@@ -1,26 +1,46 @@
import { screen, fireEvent } from "@testing-library/react"; import { screen, fireEvent } from "@testing-library/react";
import { import * as eventThunks from "../../../src/features/Calendars/CalendarSlice";
addEvent,
createCalendar,
} from "../../../src/features/Calendars/CalendarSlice";
import { renderWithProviders } from "../../utils/Renderwithproviders"; import { renderWithProviders } from "../../utils/Renderwithproviders";
import EventPopover from "../../../src/features/Events/EventModal"; import EventPopover from "../../../src/features/Events/EventModal";
import { DateSelectArg } from "@fullcalendar/core"; import { DateSelectArg } from "@fullcalendar/core";
import { randomUUID } from "crypto";
import { formatDateToYYYYMMDDTHHMMSS } from "../../../src/utils/dateUtils";
import preview from "jest-preview"; import preview from "jest-preview";
import * as appHooks from "../../../src/app/hooks"; // Import the module
describe("EventPopover", () => { describe("EventPopover", () => {
const mockOnClose = jest.fn(); const mockOnClose = jest.fn();
jest.mock("crypto"); jest.mock("crypto");
const dispatch = jest.fn();
jest.spyOn(appHooks, "useAppDispatch").mockReturnValue(dispatch);
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
const preloadedState = {
user: {
userData: {
sub: "test",
email: "test@test.com",
sid: "aiYbWZSk2g0F+LrQeD7Dg4QcUMR8R/zTZdZBiA7N6Ro",
openpaasId: "667037022b752d0026472254",
},
organiserData: {
cn: "test",
cal_address: "mailto: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 renderPopover = ( const renderPopover = (
selectedRange = { selectedRange = {
startStr: "2025-07-18T09:00", startStr: "2025-07-18T09:00",
@@ -31,33 +51,6 @@ describe("EventPopover", () => {
resource: undefined, resource: undefined,
} as unknown as DateSelectArg } as unknown as DateSelectArg
) => { ) => {
const preloadedState = {
user: {
userData: {
sub: "test",
email: "test@test.com",
sid: "aiYbWZSk2g0F+LrQeD7Dg4QcUMR8R/zTZdZBiA7N6Ro",
openpaasId: "667037022b752d0026472254",
},
organiserData: {
cn: "test",
cal_address: "mailto:test@test.com",
},
},
calendars: {
list: {
"667037022b752d0026472254/cal1": {
name: "Calendar 1",
color: "#FF0000",
},
"667037022b752d0026472254/cal2": {
name: "Calendar 2",
color: "#00FF00",
},
},
pending: false,
},
};
renderWithProviders( renderWithProviders(
<EventPopover <EventPopover
anchorEl={document.body} anchorEl={document.body}
@@ -72,21 +65,23 @@ describe("EventPopover", () => {
it("renders correctly with inputs and calendar options", () => { it("renders correctly with inputs and calendar options", () => {
renderPopover(); renderPopover();
expect(screen.getByText(/Create Event/i)).toBeInTheDocument(); expect(screen.getByText("Create Event")).toBeInTheDocument();
expect(screen.getByLabelText(/title/i)).toBeInTheDocument(); expect(screen.getByLabelText("Calendar")).toBeInTheDocument();
expect(screen.getByLabelText(/Start/i)).toBeInTheDocument(); expect(screen.getByLabelText("Title")).toBeInTheDocument();
expect(screen.getByLabelText(/End/i)).toBeInTheDocument(); expect(screen.getByLabelText("Start")).toBeInTheDocument();
expect(screen.getByLabelText(/Description/i)).toBeInTheDocument(); expect(screen.getByLabelText("End")).toBeInTheDocument();
expect(screen.getByLabelText(/Location/i)).toBeInTheDocument(); expect(screen.getByLabelText("All day")).toBeInTheDocument();
preview.debug(); expect(screen.getByLabelText("Description")).toBeInTheDocument();
expect(screen.getByLabelText("Location")).toBeInTheDocument();
expect(screen.getByLabelText("Repetition")).toBeInTheDocument();
expect(screen.getByLabelText("Time Zone")).toBeInTheDocument();
// Calendar options // Calendar options
const select = screen.getByRole("combobox"); const select = screen.getByLabelText("Calendar");
fireEvent.mouseDown(select); fireEvent.mouseDown(select);
expect(screen.getByText("Calendar 1")).toBeInTheDocument(); expect(screen.getAllByRole("option")).toHaveLength(2);
expect(screen.getByText("Calendar 2")).toBeInTheDocument();
}); });
it("fills start and end from selectedRange", () => { it("fills start from selectedRange", () => {
const selectedRange = { const selectedRange = {
startStr: "2026-07-20T10:00", startStr: "2026-07-20T10:00",
endStr: "2026-07-20T12:00", endStr: "2026-07-20T12:00",
@@ -97,93 +92,106 @@ describe("EventPopover", () => {
}; };
renderPopover(selectedRange as unknown as DateSelectArg); renderPopover(selectedRange as unknown as DateSelectArg);
expect(screen.getByLabelText("Start")).toHaveValue("2026-07-20T10:00");
expect(screen.getByLabelText(/Start/i)).toHaveValue("2026-07-20T10:00");
expect(screen.getByLabelText(/End/i)).toHaveValue("2026-07-20T12:00");
}); });
it("updates inputs on change", () => { it("updates inputs on change", () => {
renderWithProviders( renderPopover();
<EventPopover
anchorEl={document.body}
open={true}
onClose={mockOnClose}
selectedRange={null}
/>
);
fireEvent.change(screen.getByLabelText(/title/i), { fireEvent.change(screen.getByLabelText("Title"), {
target: { value: "My Event" }, target: { value: "My Event" },
}); });
expect(screen.getByLabelText(/title/i)).toHaveValue("My Event"); expect(screen.getByLabelText("Title")).toHaveValue("My Event");
fireEvent.change(screen.getByLabelText(/Description/i), { fireEvent.change(screen.getByLabelText("Description"), {
target: { value: "Event Description" }, target: { value: "Event Description" },
}); });
expect(screen.getByLabelText(/Description/i)).toHaveValue( expect(screen.getByLabelText("Description")).toHaveValue(
"Event Description" "Event Description"
); );
fireEvent.change(screen.getByLabelText(/Location/i), { fireEvent.change(screen.getByLabelText("Location"), {
target: { value: "Conference Room" }, target: { value: "Conference Room" },
}); });
expect(screen.getByLabelText(/Location/i)).toHaveValue("Conference Room"); expect(screen.getByLabelText("Location")).toHaveValue("Conference Room");
}); });
it("changes selected calendar", async () => { it("changes selected calendar", async () => {
renderPopover(); renderPopover();
const select = screen.getByRole("combobox"); const select = screen.getByLabelText("Calendar");
fireEvent.mouseDown(select); // Open menu fireEvent.mouseDown(select); // Open menu
const option = await screen.findByText("Calendar 2"); const option = await screen.findByText("Calendar 2");
fireEvent.click(option); fireEvent.click(option);
// The Select control value isn't easy to check directly because MUI Select wraps input, expect(screen.getAllByRole("combobox")[0]).toHaveTextContent("Calendar 2");
// but we can test by triggering save later
}); });
it("dispatches addEvent and calls onClose when Save is clicked", () => { it("dispatches putEventAsync and calls onClose when Save is clicked", async () => {
renderPopover(); renderPopover();
const newEvent = {
// Select calendar id title: "Meeting",
const select = screen.getByRole("combobox"); start: "2025-07-18T00:00:00.000Z",
fireEvent.mouseDown(select); end: "2025-07-19T00:00:00.000Z",
fireEvent.click(screen.getByText("Calendar 1")); allday: false,
uid: "6045c603-11ab-43c5-bc30-0641420bb3a8",
description: "Discuss project",
location: "Zoom",
repetition: "",
organizer: { cn: "test", cal_address: "mailto:test@test.com" },
timezone: "Europe/Paris",
transp: "OPAQUE",
};
// Fill inputs // Fill inputs
fireEvent.change(screen.getByLabelText(/title/i), { fireEvent.change(screen.getByLabelText("Title"), {
target: { value: "Meeting" }, target: { value: newEvent.title },
}); });
fireEvent.change(screen.getByLabelText(/Start/i), { fireEvent.click(screen.getByLabelText("All day"));
target: { value: "2025-07-18T09:00" }, fireEvent.change(screen.getByLabelText("Start"), {
target: { value: newEvent.start.split("T")[0] },
}); });
fireEvent.change(screen.getByLabelText(/End/i), { fireEvent.change(screen.getByLabelText("End"), {
target: { value: "2025-07-18T10:00" }, target: { value: newEvent.end.split("T")[0] },
});
fireEvent.change(screen.getByLabelText(/Description/i), {
target: { value: "Discuss project" },
});
fireEvent.change(screen.getByLabelText(/Location/i), {
target: { value: "Zoom" },
}); });
fireEvent.click(screen.getByText(/Save/i)); fireEvent.change(screen.getByLabelText("Description"), {
target: { value: newEvent.description },
});
fireEvent.change(screen.getByLabelText("Location"), {
target: { value: newEvent.location },
});
expect(dispatch).toHaveBeenCalled(); let receivedPayload: any = null;
jest.spyOn(eventThunks, "putEventAsync").mockImplementation((payload) => {
receivedPayload = payload;
return () => Promise.resolve() as any;
});
fireEvent.click(screen.getByText("Save"));
expect(eventThunks.putEventAsync).toHaveBeenCalled();
// Extract dispatched action argument // Extract dispatched action argument
const dispatchedArg = dispatch.mock.calls[0][0].payload; console.log(receivedPayload);
console.log(dispatchedArg); expect(receivedPayload.cal).toBe(
expect(dispatchedArg.calendarUid).toBe("667037022b752d0026472254/cal1"); preloadedState.calendars.list["667037022b752d0026472254/cal1"]
expect(dispatchedArg.event.title).toBe("Meeting"); );
expect(dispatchedArg.event.description).toBe("Discuss project"); expect(receivedPayload.newEvent.title).toBe(newEvent.title);
expect(dispatchedArg.event.location).toBe("Zoom"); expect(receivedPayload.newEvent.description).toBe(newEvent.description);
expect(dispatchedArg.event.color).toBe("#FF0000"); expect(receivedPayload.newEvent.start.toString()).toBe(
expect(dispatchedArg.event.organizer).toEqual({ new Date(newEvent.start).toString()
cn: "test", );
cal_address: "mailto:test@test.com", expect(receivedPayload.newEvent.end.toString()).toBe(
}); new Date(newEvent.end).toString()
);
expect(receivedPayload.newEvent.location).toBe(newEvent.location);
expect(receivedPayload.newEvent.organizer).toEqual(newEvent.organizer);
expect(receivedPayload.newEvent.repetition).toEqual(newEvent.repetition);
expect(receivedPayload.newEvent.color).toEqual(
preloadedState.calendars.list["667037022b752d0026472254/cal1"].color
);
// onClose should be called // onClose should be called
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
@@ -192,7 +200,7 @@ describe("EventPopover", () => {
it("calls onClose when Cancel clicked", () => { it("calls onClose when Cancel clicked", () => {
renderPopover(); renderPopover();
fireEvent.click(screen.getByText(/Cancel/i)); fireEvent.click(screen.getByText("Cancel"));
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick"); expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
}); });
+237 -5
View File
@@ -1,4 +1,8 @@
import { parseCalendarEvent } from "../../../src/features/Events/eventUtils"; import {
calendarEventToJCal,
parseCalendarEvent,
} from "../../../src/features/Events/eventUtils";
import { TIMEZONES } from "../../../src/utils/timezone-data";
describe("parseCalendarEvent", () => { describe("parseCalendarEvent", () => {
const baseColor = "#00FF00"; const baseColor = "#00FF00";
@@ -22,6 +26,8 @@ describe("parseCalendarEvent", () => {
["X-OPENPAAS-VIDEOCONFERENCE", {}, "text", "https://meet.link"], ["X-OPENPAAS-VIDEOCONFERENCE", {}, "text", "https://meet.link"],
["STATUS", {}, "text", "CONFIRMED"], ["STATUS", {}, "text", "CONFIRMED"],
["SEQUENCE", {}, "integer", "2"], ["SEQUENCE", {}, "integer", "2"],
["TRANSP", {}, "text", "OPAQUE"],
["CLASS", {}, "text", "PUBLIC"],
["DTSTAMP", {}, "date-time", "2025-07-18T08:00:00Z"], ["DTSTAMP", {}, "date-time", "2025-07-18T08:00:00Z"],
] as unknown as [string, Record<string, string>, string, any]; ] as unknown as [string, Record<string, string>, string, any];
@@ -37,6 +43,8 @@ describe("parseCalendarEvent", () => {
expect(result.sequence).toBe(2); expect(result.sequence).toBe(2);
expect(result.color).toBe(baseColor); expect(result.color).toBe(baseColor);
expect(result.status).toBe("CONFIRMED"); expect(result.status).toBe("CONFIRMED");
expect(result.transp).toBe("OPAQUE");
expect(result.class).toBe("PUBLIC");
expect(result.x_openpass_videoconference).toBe("https://meet.link"); expect(result.x_openpass_videoconference).toBe("https://meet.link");
expect(result.organizer).toEqual({ expect(result.organizer).toEqual({
@@ -88,10 +96,10 @@ describe("parseCalendarEvent", () => {
it("handles optional organizer and attendee fields gracefully", () => { it("handles optional organizer and attendee fields gracefully", () => {
const rawData = [ const rawData = [
["UID", {}, "text", "event-4"], ["UID", {}, "text", "event-4"],
["DTSTART", {}, "date-time", "2025-07-18T09:00:00Z"], ["DTSTART", {}, "date-time", "2025-07-18T09:00:00Z"],
["ATTENDEE", {}, "cal-address", "mailto:john@example.com"], ["ATTENDEE", {}, "cal-address", "mailto:john@example.com"],
["ORGANIZER", {}, "cal-address", "mailto:jane@example.com"], ["ORGANIZER", {}, "cal-address", "mailto:jane@example.com"],
] as unknown as [string, Record<string, string>, string, any]; ] as unknown as [string, Record<string, string>, string, any];
const result = parseCalendarEvent(rawData, baseColor, calendarId); const result = parseCalendarEvent(rawData, baseColor, calendarId);
@@ -113,3 +121,227 @@ describe("parseCalendarEvent", () => {
}); });
}); });
}); });
describe("calendarEventToJCal", () => {
beforeAll(() => {
jest.mock("ical.js", () => ({
Timezone: jest.fn().mockImplementation(({ component, tzid }) => ({
component: {
jCal: [`vtimezone`, [], [["tzid", {}, "text", tzid]]],
},
})),
}));
});
it("should convert a CalendarEvent to JCal format", () => {
const mockEvent = {
uid: "event-123",
title: "Team Meeting",
start: new Date("2025-07-23T10:00:00"),
end: new Date("2025-07-23T11:00:00"),
timezone: "Europe/Paris",
transp: "OPAQUE",
class: "PUBLIC",
allday: false,
location: "Room 101",
description: "Discuss project roadmap.",
repetition: "WEEKLY",
organizer: {
cn: "Alice",
cal_address: "mailto:alice@example.com",
},
attendee: [
{
cn: "Bob",
partstat: "ACCEPTED",
rsvp: "TRUE",
role: "REQ-PARTICIPANT",
cutype: "INDIVIDUAL",
cal_address: "mailto:bob@example.com",
},
],
};
const result = calendarEventToJCal(mockEvent);
expect(result[0]).toBe("vcalendar");
const [vevent, vtimezone] = result[2];
expect(vevent[0]).toBe("vevent");
const props = vevent[1];
expect(props).toEqual(
expect.arrayContaining([
["uid", {}, "text", "event-123"],
["summary", {}, "text", "Team Meeting"],
["transp", {}, "text", "OPAQUE"],
["dtstart", { tzid: "Europe/Paris" }, "date-time", "20250723T100000"],
["dtend", { tzid: "Europe/Paris" }, "date-time", "20250723T110000"],
["class", {}, "text", "PUBLIC"],
["location", {}, "text", "Room 101"],
["description", {}, "text", "Discuss project roadmap."],
["x-openpaas-videoconference", {}, "unknown", null],
["rrule", {}, "recur", { freq: "WEEKLY" }],
[
"organizer",
{ cn: "Alice" },
"cal-address",
"mailto:alice@example.com",
],
[
"attendee",
{
cn: "Bob",
partstat: "ACCEPTED",
rsvp: "TRUE",
role: "REQ-PARTICIPANT",
cutype: "INDIVIDUAL",
},
"cal-address",
"mailto:bob@example.com",
],
])
);
expect(vtimezone[0]).toBe("vtimezone");
expect(vtimezone[2]).toEqual(
expect.arrayContaining([
[
"daylight",
[
["tzoffsetfrom", {}, "utc-offset", "+01:00"],
["tzoffsetto", {}, "utc-offset", "+02:00"],
["tzname", {}, "text", "CEST"],
["dtstart", {}, "date-time", "1970-03-29T02:00:00"],
[
"rrule",
{},
"recur",
{ byday: "-1SU", bymonth: 3, freq: "YEARLY" },
],
],
[],
],
[
"standard",
[
["tzoffsetfrom", {}, "utc-offset", "+02:00"],
["tzoffsetto", {}, "utc-offset", "+01:00"],
["tzname", {}, "text", "CET"],
["dtstart", {}, "date-time", "1970-10-25T03:00:00"],
[
"rrule",
{},
"recur",
{ byday: "-1SU", bymonth: 10, freq: "YEARLY" },
],
],
[],
],
])
);
});
it("should convert a CalendarEvent to JCal format, with all day activated", () => {
const mockEvent = {
uid: "event-123",
title: "Team Meeting",
start: new Date("2025-07-23"),
end: new Date("2025-07-23"),
timezone: "Europe/Paris",
transp: "OPAQUE",
class: "PUBLIC",
allday: true,
location: "Room 101",
description: "Discuss project roadmap.",
repetition: "WEEKLY",
organizer: {
cn: "Alice",
cal_address: "mailto:alice@example.com",
},
attendee: [
{
cn: "Bob",
partstat: "ACCEPTED",
rsvp: "TRUE",
role: "REQ-PARTICIPANT",
cutype: "INDIVIDUAL",
cal_address: "mailto:bob@example.com",
},
],
};
const result = calendarEventToJCal(mockEvent);
expect(result[0]).toBe("vcalendar");
const [vevent, vtimezone] = result[2];
expect(vevent[0]).toBe("vevent");
const props = vevent[1];
expect(props).toEqual(
expect.arrayContaining([
["uid", {}, "text", "event-123"],
["summary", {}, "text", "Team Meeting"],
["transp", {}, "text", "OPAQUE"],
["dtstart", { tzid: "Europe/Paris" }, "date", "2025-07-23"],
["dtend", { tzid: "Europe/Paris" }, "date", "2025-07-23"],
["class", {}, "text", "PUBLIC"],
["location", {}, "text", "Room 101"],
["description", {}, "text", "Discuss project roadmap."],
["x-openpaas-videoconference", {}, "unknown", null],
["rrule", {}, "recur", { freq: "WEEKLY" }],
[
"organizer",
{ cn: "Alice" },
"cal-address",
"mailto:alice@example.com",
],
[
"attendee",
{
cn: "Bob",
partstat: "ACCEPTED",
rsvp: "TRUE",
role: "REQ-PARTICIPANT",
cutype: "INDIVIDUAL",
},
"cal-address",
"mailto:bob@example.com",
],
])
);
expect(vtimezone[0]).toBe("vtimezone");
expect(vtimezone[2]).toEqual(
expect.arrayContaining([
[
"daylight",
[
["tzoffsetfrom", {}, "utc-offset", "+01:00"],
["tzoffsetto", {}, "utc-offset", "+02:00"],
["tzname", {}, "text", "CEST"],
["dtstart", {}, "date-time", "1970-03-29T02:00:00"],
[
"rrule",
{},
"recur",
{ byday: "-1SU", bymonth: 3, freq: "YEARLY" },
],
],
[],
],
[
"standard",
[
["tzoffsetfrom", {}, "utc-offset", "+02:00"],
["tzoffsetto", {}, "utc-offset", "+01:00"],
["tzname", {}, "text", "CET"],
["dtstart", {}, "date-time", "1970-10-25T03:00:00"],
[
"rrule",
{},
"recur",
{ byday: "-1SU", bymonth: 10, freq: "YEARLY" },
],
],
[],
],
])
);
});
});
+3 -3
View File
@@ -136,9 +136,9 @@ function EventPopover({
setCalendarid(Number(e.target.value)) setCalendarid(Number(e.target.value))
} }
> >
{Object.keys(userPersonnalCalendars).map((value, calendar) => ( {Object.keys(userPersonnalCalendars).map((calendar, index) => (
<MenuItem key={calendar} value={calendar}> <MenuItem key={index} value={index}>
{userPersonnalCalendars[calendar].name} {userPersonnalCalendars[index].name}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>