[#7] added MOVE support for changing calendar for event + missing tests for this PR
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import {
|
||||
putEvent,
|
||||
moveEvent,
|
||||
deleteEvent,
|
||||
} from "../../../src/features/Events/EventApi";
|
||||
import { calendarEventToJCal } from "../../../src/features/Events/eventUtils";
|
||||
import { clientConfig } from "../../../src/features/User/oidcAuth";
|
||||
import { api } from "../../../src/utils/apiUtils";
|
||||
clientConfig.url = "https://example.com";
|
||||
|
||||
jest.mock("../../../src/utils/apiUtils");
|
||||
|
||||
const day = new Date();
|
||||
|
||||
const mockEvent = {
|
||||
uid: "event1",
|
||||
title: "Test Event",
|
||||
timezone: "UTC",
|
||||
calId: "667037022b752d0026472254/cal1",
|
||||
URL: "/calendars/667037022b752d0026472254/667037022b752d0026472254/cal1.ics",
|
||||
start: day,
|
||||
end: day,
|
||||
status: "PUBLIC",
|
||||
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",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe("eventApi", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test("putEvent sends PUT request with JCal body", async () => {
|
||||
const mockResponse = { status: 201, url: "/dav/cals/test.ics" };
|
||||
(api as unknown as jest.Mock).mockReturnValue(mockResponse);
|
||||
const result = await putEvent(mockEvent);
|
||||
const expectedResult = calendarEventToJCal(mockEvent);
|
||||
expect(api).toHaveBeenCalledWith(
|
||||
"dav/calendars/667037022b752d0026472254/667037022b752d0026472254/cal1.ics",
|
||||
expect.objectContaining({
|
||||
method: "PUT",
|
||||
headers: { "content-type": "text/calendar; charset=utf-8" },
|
||||
body: JSON.stringify(expectedResult),
|
||||
})
|
||||
);
|
||||
expect(result).toBe(mockResponse);
|
||||
});
|
||||
|
||||
test("putEvent logs when status is 201", async () => {
|
||||
const mockResponse = { status: 201, url: "/dav/cals/test.ics" };
|
||||
(api as unknown as jest.Mock).mockReturnValue(mockResponse);
|
||||
const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await putEvent(mockEvent);
|
||||
expect(logSpy).toHaveBeenCalledWith("PUT (201) :", "/dav/cals/test.ics");
|
||||
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
test("moveEvent sends MOVE request with destination header", async () => {
|
||||
const mockResponse = { status: 204 };
|
||||
(api as unknown as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue(mockResponse),
|
||||
});
|
||||
const result = await moveEvent(mockEvent);
|
||||
|
||||
expect(api).toHaveBeenCalledWith(
|
||||
"dav/calendars/667037022b752d0026472254/667037022b752d0026472254/cal1.ics",
|
||||
expect.objectContaining({
|
||||
method: "MOVE",
|
||||
headers: {
|
||||
destination:
|
||||
"/calendars/667037022b752d0026472254/667037022b752d0026472254/cal1.ics",
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("deleteEvent sends DELETE request and returns json response", async () => {
|
||||
const mockResponse = { ok: true };
|
||||
(api as unknown as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue(mockResponse),
|
||||
});
|
||||
|
||||
const result = await deleteEvent("/calendars/test.ics");
|
||||
|
||||
expect(api).toHaveBeenCalledWith("dav/calendars/test.ics", {
|
||||
method: "DELETE",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,14 @@
|
||||
import { screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import * as eventThunks from "../../../src/features/Calendars/CalendarSlice";
|
||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||
import EventPopover from "../../../src/features/Events/EventModal";
|
||||
import { DateSelectArg } from "@fullcalendar/core";
|
||||
import preview from "jest-preview";
|
||||
import { formatDateToYYYYMMDDTHHMMSS } from "../../../src/utils/dateUtils";
|
||||
import EventDisplayModal, {
|
||||
InfoRow,
|
||||
stringAvatar,
|
||||
stringToColor,
|
||||
} from "../../../src/features/Events/EventDisplay";
|
||||
import EventPreviewModal from "../../../src/features/Events/EventDisplayPreview";
|
||||
|
||||
describe("Event Display", () => {
|
||||
describe("Event Preview Display", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
const day = new Date();
|
||||
const RealDateToLocaleString = Date.prototype.toLocaleString;
|
||||
@@ -109,7 +110,6 @@ describe("Event Display", () => {
|
||||
const dayOfMonth = day.getDate().toString();
|
||||
|
||||
expect(screen.getByText("Test Event")).toBeInTheDocument();
|
||||
preview.debug();
|
||||
expect(screen.getByText(new RegExp(weekday, "i"))).toBeInTheDocument();
|
||||
expect(screen.getByText(new RegExp(month, "i"))).toBeInTheDocument();
|
||||
expect(
|
||||
@@ -475,3 +475,577 @@ describe("Event Display", () => {
|
||||
expect(updatedEvent.attendee[0].partstat).toBe("DECLINED");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Event Full Display", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
const day = new Date();
|
||||
const RealDateToLocaleString = Date.prototype.toLocaleString;
|
||||
|
||||
beforeEach(() => {
|
||||
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: "First Calendar",
|
||||
color: "#FF0000",
|
||||
events: {
|
||||
event1: {
|
||||
id: "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: {
|
||||
id: "event1",
|
||||
calId: "otherCal/cal",
|
||||
title: "Test Event Other cal",
|
||||
start: day.toISOString(),
|
||||
end: day.toISOString(),
|
||||
organizer: { cn: "john", cal_address: "john@test.com" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pending: false,
|
||||
},
|
||||
};
|
||||
|
||||
it("renders correctly event data", () => {
|
||||
jest
|
||||
.spyOn(Date.prototype, "toLocaleString")
|
||||
.mockImplementation(function (
|
||||
this: Date,
|
||||
locales?: Intl.LocalesArgument,
|
||||
options?: Intl.DateTimeFormatOptions | undefined
|
||||
): string {
|
||||
return RealDateToLocaleString.call(this, "en-UK", options);
|
||||
});
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
day.setHours(day.getHours() + 2);
|
||||
const date = day.toISOString().slice(0, 16);
|
||||
|
||||
expect(screen.getByDisplayValue("Test Event")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getAllByDisplayValue(new RegExp(date, "i"))[0]
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getAllByDisplayValue(new RegExp(date, "i")).length
|
||||
).toBeLessThanOrEqual(2);
|
||||
|
||||
expect(screen.getByText("First Calendar")).toBeInTheDocument();
|
||||
});
|
||||
it("calls onClose when Cancel clicked", () => {
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
fireEvent.click(screen.getAllByTestId("CloseIcon")[0]);
|
||||
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
|
||||
});
|
||||
it("Shows delete button only when calendar is own", () => {
|
||||
// Renders the other cal event
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"otherCal/cal"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
expect(screen.queryByTestId("DeleteIcon")).not.toBeInTheDocument();
|
||||
// Renders the personnal cal event
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
expect(screen.queryByTestId("DeleteIcon")).toBeInTheDocument();
|
||||
});
|
||||
it("calls delete when Delete clicked", async () => {
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
const spy = jest
|
||||
.spyOn(eventThunks, "deleteEventAsync")
|
||||
.mockImplementation((payload) => {
|
||||
return () => Promise.resolve(payload) as any;
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId("DeleteIcon"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const receivedPayload = spy.mock.calls[0][0];
|
||||
|
||||
expect(receivedPayload).toEqual({
|
||||
calId: "667037022b752d0026472254/cal1",
|
||||
eventId: "event1",
|
||||
});
|
||||
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
|
||||
});
|
||||
it("renders RSVP buttons when user is an attendee", () => {
|
||||
const rsvpStateIsOrga = {
|
||||
...preloadedState,
|
||||
calendars: {
|
||||
...preloadedState.calendars,
|
||||
list: {
|
||||
...preloadedState.calendars.list,
|
||||
"667037022b752d0026472254/cal1": {
|
||||
...preloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
||||
events: {
|
||||
event1: {
|
||||
...preloadedState.calendars.list[
|
||||
"667037022b752d0026472254/cal1"
|
||||
].events.event1,
|
||||
attendee: [
|
||||
{
|
||||
cal_address: "test@test.com",
|
||||
cn: "Test User",
|
||||
partstat: "NEEDS-ACTION",
|
||||
},
|
||||
{
|
||||
cal_address: "organizer@test.com",
|
||||
cn: "Test Organizer",
|
||||
partstat: "NEEDS-ACTION",
|
||||
},
|
||||
],
|
||||
organizer: {
|
||||
cal_address: "organizer@test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
rsvpStateIsOrga
|
||||
);
|
||||
|
||||
expect(screen.getByRole("button", { name: "Accept" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Maybe" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Decline" })).toBeInTheDocument();
|
||||
});
|
||||
it("doesnt renders RSVP buttons when user isnt an attendee", () => {
|
||||
const rsvpStateIsOrga = {
|
||||
...preloadedState,
|
||||
calendars: {
|
||||
...preloadedState.calendars,
|
||||
list: {
|
||||
...preloadedState.calendars.list,
|
||||
"667037022b752d0026472254/cal1": {
|
||||
...preloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
||||
events: {
|
||||
event1: {
|
||||
...preloadedState.calendars.list[
|
||||
"667037022b752d0026472254/cal1"
|
||||
].events.event1,
|
||||
attendee: [
|
||||
{
|
||||
cal_address: "organizer@test.com",
|
||||
cn: "Test Organizer",
|
||||
partstat: "NEEDS-ACTION",
|
||||
},
|
||||
],
|
||||
organizer: {
|
||||
cal_address: "organizer@test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
rsvpStateIsOrga
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByRole("button", { name: "Accept" })
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole("button", { name: "Maybe" })
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole("button", { name: "Decline" })
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles RSVP Accept click", async () => {
|
||||
const spy = jest
|
||||
.spyOn(eventThunks, "putEventAsync")
|
||||
.mockImplementation((payload) => {
|
||||
return () => Promise.resolve(payload) as any;
|
||||
});
|
||||
|
||||
const rsvpState = {
|
||||
...preloadedState,
|
||||
calendars: {
|
||||
...preloadedState.calendars,
|
||||
list: {
|
||||
...preloadedState.calendars.list,
|
||||
"667037022b752d0026472254/cal1": {
|
||||
...preloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
||||
events: {
|
||||
event1: {
|
||||
...preloadedState.calendars.list[
|
||||
"667037022b752d0026472254/cal1"
|
||||
].events.event1,
|
||||
attendee: [
|
||||
{
|
||||
cal_address: "test@test.com",
|
||||
cn: "Test User",
|
||||
partstat: "NEEDS-ACTION",
|
||||
},
|
||||
],
|
||||
organizer: {
|
||||
cal_address: "organizer@test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
rsvpState
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Accept" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
|
||||
});
|
||||
|
||||
const updatedEvent = spy.mock.calls[0][0].newEvent;
|
||||
expect(updatedEvent.attendee[0].partstat).toBe("ACCEPTED");
|
||||
});
|
||||
|
||||
it("handles RSVP Maybe click", async () => {
|
||||
const spy = jest
|
||||
.spyOn(eventThunks, "putEventAsync")
|
||||
.mockImplementation((payload) => {
|
||||
return () => Promise.resolve(payload) as any;
|
||||
});
|
||||
|
||||
const rsvpState = {
|
||||
...preloadedState,
|
||||
calendars: {
|
||||
...preloadedState.calendars,
|
||||
list: {
|
||||
...preloadedState.calendars.list,
|
||||
"667037022b752d0026472254/cal1": {
|
||||
...preloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
||||
events: {
|
||||
event1: {
|
||||
...preloadedState.calendars.list[
|
||||
"667037022b752d0026472254/cal1"
|
||||
].events.event1,
|
||||
attendee: [
|
||||
{
|
||||
cal_address: "test@test.com",
|
||||
cn: "Test User",
|
||||
partstat: "NEEDS-ACTION",
|
||||
},
|
||||
],
|
||||
organizer: {
|
||||
cal_address: "organizer@test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
rsvpState
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Maybe" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
|
||||
});
|
||||
|
||||
const updatedEvent = spy.mock.calls[0][0].newEvent;
|
||||
expect(updatedEvent.attendee[0].partstat).toBe("TENTATIVE");
|
||||
});
|
||||
|
||||
it("handles RSVP Decline click", async () => {
|
||||
const spy = jest
|
||||
.spyOn(eventThunks, "putEventAsync")
|
||||
.mockImplementation((payload) => {
|
||||
return () => Promise.resolve(payload) as any;
|
||||
});
|
||||
|
||||
const rsvpState = {
|
||||
...preloadedState,
|
||||
calendars: {
|
||||
...preloadedState.calendars,
|
||||
list: {
|
||||
...preloadedState.calendars.list,
|
||||
"667037022b752d0026472254/cal1": {
|
||||
...preloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
||||
events: {
|
||||
event1: {
|
||||
...preloadedState.calendars.list[
|
||||
"667037022b752d0026472254/cal1"
|
||||
].events.event1,
|
||||
attendee: [
|
||||
{
|
||||
cal_address: "test@test.com",
|
||||
cn: "Test User",
|
||||
partstat: "NEEDS-ACTION",
|
||||
},
|
||||
],
|
||||
organizer: {
|
||||
cal_address: "organizer@test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
rsvpState
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Decline" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
|
||||
});
|
||||
|
||||
const updatedEvent = spy.mock.calls[0][0].newEvent;
|
||||
expect(updatedEvent.attendee[0].partstat).toBe("DECLINED");
|
||||
});
|
||||
test("toggle Show More reveals extra fields", () => {
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
fireEvent.click(screen.getByText("Show More"));
|
||||
expect(screen.getByLabelText("Alarm")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Repetition")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Visibility")).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByText("Show Less"));
|
||||
});
|
||||
|
||||
test("can edit title when user is organizer", () => {
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
const titleField = screen.getByLabelText("Title");
|
||||
fireEvent.change(titleField, { target: { value: "New Title" } });
|
||||
expect(screen.getByDisplayValue("New Title")).toBeInTheDocument();
|
||||
});
|
||||
test("calendar select is disabled when not organizer", () => {
|
||||
const rsvpState = {
|
||||
...preloadedState,
|
||||
calendars: {
|
||||
...preloadedState.calendars,
|
||||
list: {
|
||||
...preloadedState.calendars.list,
|
||||
"667037022b752d0026472254/cal1": {
|
||||
...preloadedState.calendars.list["667037022b752d0026472254/cal1"],
|
||||
events: {
|
||||
event1: {
|
||||
...preloadedState.calendars.list[
|
||||
"667037022b752d0026472254/cal1"
|
||||
].events.event1,
|
||||
attendee: [
|
||||
{
|
||||
cal_address: "test@test.com",
|
||||
cn: "Test User",
|
||||
partstat: "NEEDS-ACTION",
|
||||
},
|
||||
],
|
||||
organizer: {
|
||||
cal_address: "organizer@test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
rsvpState
|
||||
);
|
||||
expect(screen.getByLabelText("Calendar")).toHaveClass("Mui-disabled");
|
||||
});
|
||||
test("toggle all-day updates end date correctly", () => {
|
||||
renderWithProviders(
|
||||
<EventDisplayModal
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calId={"667037022b752d0026472254/cal1"}
|
||||
eventId={"event1"}
|
||||
/>,
|
||||
preloadedState
|
||||
);
|
||||
const allDayCheckbox = screen.getByLabelText("All day");
|
||||
fireEvent.click(allDayCheckbox);
|
||||
expect(allDayCheckbox).toBeChecked();
|
||||
const date = day.toISOString().split("T")[0];
|
||||
|
||||
expect(
|
||||
screen.getAllByDisplayValue(new RegExp(date, "i"))[0]
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Helper functions", () => {
|
||||
test("stringToColor generates consistent color", () => {
|
||||
expect(stringToColor("Alice")).toMatch(/^#[0-9a-f]{6}$/);
|
||||
expect(stringToColor("Alice")).toBe(stringToColor("Alice"));
|
||||
});
|
||||
|
||||
test("stringAvatar returns correct props", () => {
|
||||
const result = stringAvatar("Alice");
|
||||
expect(result.children).toBe("A");
|
||||
expect(result.sx.bgcolor).toMatch(/^#/);
|
||||
});
|
||||
|
||||
test("InfoRow renders text and link if url is valid", () => {
|
||||
renderWithProviders(
|
||||
<InfoRow
|
||||
icon={<span>ico</span>}
|
||||
text="Meeting"
|
||||
data="https://example.com"
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Meeting").closest("a")).toHaveAttribute(
|
||||
"href",
|
||||
"https://example.com"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CalendarEvent } from "../Events/EventsTypes";
|
||||
import { getCalendar, getCalendars } from "./CalendarApi";
|
||||
import { getOpenPaasUser, getUserDetails } from "../User/userAPI";
|
||||
import { parseCalendarEvent } from "../Events/eventUtils";
|
||||
import { deleteEvent, putEvent } from "../Events/EventApi";
|
||||
import { deleteEvent, moveEvent, putEvent } from "../Events/EventApi";
|
||||
import { formatDateToYYYYMMDDTHHMMSS } from "../../utils/dateUtils";
|
||||
|
||||
export const getCalendarsListAsync = createAsyncThunk<
|
||||
@@ -92,6 +92,32 @@ export const putEventAsync = createAsyncThunk<
|
||||
events,
|
||||
};
|
||||
});
|
||||
export const moveEventAsync = createAsyncThunk<
|
||||
{ calId: string; events: CalendarEvent[] }, // Return type
|
||||
{ cal: Calendars; newEvent: CalendarEvent } // Arg type
|
||||
>("calendars/moveEvent", async ({ cal, newEvent }) => {
|
||||
const response = await moveEvent(newEvent);
|
||||
const calEvents = (await getCalendar(cal.id, {
|
||||
start: formatDateToYYYYMMDDTHHMMSS(new Date(newEvent.start)),
|
||||
end: formatDateToYYYYMMDDTHHMMSS(
|
||||
new Date(new Date(newEvent.start).getTime() + 86400000)
|
||||
),
|
||||
})) as Record<string, any>;
|
||||
const events: CalendarEvent[] = calEvents._embedded["dav:item"].flatMap(
|
||||
(eventdata: any) => {
|
||||
const vevents = eventdata.data[2] as any[][];
|
||||
const eventURL = eventdata._links.self.href;
|
||||
return vevents.map((vevent: any[]) => {
|
||||
return parseCalendarEvent(vevent[1], cal.color ?? "", cal.id, eventURL);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
calId: cal.id,
|
||||
events,
|
||||
};
|
||||
});
|
||||
|
||||
export const deleteEventAsync = createAsyncThunk<
|
||||
{ calId: string; eventId: string }, // Return type
|
||||
@@ -207,6 +233,32 @@ const CalendarSlice = createSlice({
|
||||
});
|
||||
}
|
||||
)
|
||||
.addCase(
|
||||
moveEventAsync.fulfilled,
|
||||
(
|
||||
state,
|
||||
action: PayloadAction<{ calId: string; events: CalendarEvent[] }>
|
||||
) => {
|
||||
state.pending = false;
|
||||
if (!state.list[action.payload.calId]) {
|
||||
state.list[action.payload.calId] = {
|
||||
id: action.payload.calId,
|
||||
events: {},
|
||||
} as Calendars;
|
||||
}
|
||||
action.payload.events.forEach((event) => {
|
||||
state.list[action.payload.calId].events[event.uid] = event;
|
||||
});
|
||||
Object.keys(state.list[action.payload.calId].events).forEach((id) => {
|
||||
state.list[action.payload.calId].events[id].color =
|
||||
state.list[action.payload.calId].color;
|
||||
state.list[action.payload.calId].events[id].calId =
|
||||
action.payload.calId;
|
||||
state.list[action.payload.calId].events[id].timezone =
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
});
|
||||
}
|
||||
)
|
||||
.addCase(deleteEventAsync.fulfilled, (state, action) => {
|
||||
state.pending = false;
|
||||
delete state.list[action.payload.calId].events[action.payload.eventId];
|
||||
@@ -220,6 +272,9 @@ const CalendarSlice = createSlice({
|
||||
.addCase(putEventAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
})
|
||||
.addCase(moveEventAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
})
|
||||
.addCase(deleteEventAsync.pending, (state) => {
|
||||
state.pending = true;
|
||||
});
|
||||
|
||||
@@ -16,9 +16,19 @@ export async function putEvent(event: CalendarEvent) {
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function moveEvent(event: CalendarEvent) {
|
||||
const response = await api(`dav${event.URL}`, {
|
||||
method: "MOVE",
|
||||
headers: {
|
||||
destination: event.URL,
|
||||
},
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function deleteEvent(eventURL: string) {
|
||||
const response = await api(`dav${eventURL}`, {
|
||||
method: "DELETE",
|
||||
}).json();
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { deleteEventAsync, putEventAsync } from "../Calendars/CalendarSlice";
|
||||
import {
|
||||
deleteEventAsync,
|
||||
moveEventAsync,
|
||||
putEventAsync,
|
||||
} from "../Calendars/CalendarSlice";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import AttendeeSelector from "../../components/Attendees/AttendeeSearch";
|
||||
import {
|
||||
@@ -86,7 +90,7 @@ export default function EventDisplayModal({
|
||||
const [alarm, setAlarm] = useState("");
|
||||
const [eventClass, setEventClass] = useState(event?.class ?? "PUBLIC");
|
||||
const [timezone, setTimezone] = useState(event?.timezone ?? "UTC");
|
||||
|
||||
const [newCalId, setNewCalId] = useState(event.calId);
|
||||
const [calendarid, setCalendarid] = useState(
|
||||
event?.calId.split("/")[0] === user.userData.openpaasId
|
||||
? userPersonnalCalendars.findIndex((cal) => cal.id === calId)
|
||||
@@ -169,6 +173,12 @@ export default function EventDisplayModal({
|
||||
newEvent,
|
||||
})
|
||||
);
|
||||
|
||||
if (newCalId !== calId) {
|
||||
dispatch(
|
||||
moveEventAsync({ cal: userPersonnalCalendars[calendarid], newEvent })
|
||||
);
|
||||
}
|
||||
onClose({}, "backdropClick");
|
||||
};
|
||||
|
||||
@@ -279,9 +289,11 @@ export default function EventDisplayModal({
|
||||
labelId="calendar-select-label"
|
||||
value={calendarid.toString()}
|
||||
label="Calendar"
|
||||
onChange={(e: SelectChangeEvent) =>
|
||||
setCalendarid(Number(e.target.value))
|
||||
}
|
||||
onChange={(e: SelectChangeEvent) => {
|
||||
const newId = Number(e.target.value);
|
||||
setCalendarid(newId);
|
||||
setNewCalId(userPersonnalCalendars[newId].id);
|
||||
}}
|
||||
>
|
||||
{calList}
|
||||
</Select>
|
||||
@@ -325,11 +337,6 @@ export default function EventDisplayModal({
|
||||
const endDate = new Date(end);
|
||||
const startDate = new Date(start);
|
||||
setAllDay(!allday);
|
||||
console.log(
|
||||
endDate.getDate() === startDate.getDate(),
|
||||
endDate.getDate(),
|
||||
startDate.getDate()
|
||||
);
|
||||
if (endDate.getDate() === startDate.getDate()) {
|
||||
endDate.setDate(startDate.getDate() + 1);
|
||||
setEnd(formatLocalDateTime(endDate));
|
||||
@@ -390,7 +397,6 @@ export default function EventDisplayModal({
|
||||
onClick={() => {
|
||||
const newAttendeesList = [...attendees];
|
||||
setAttendees(newAttendeesList.splice(idx, 1));
|
||||
console.log(attendees, newAttendeesList);
|
||||
}}
|
||||
>
|
||||
<CloseIcon fontSize="small" />
|
||||
@@ -496,18 +502,19 @@ export default function EventDisplayModal({
|
||||
|
||||
<CardActions>
|
||||
<ButtonGroup>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
onClose({}, "backdropClick");
|
||||
dispatch(
|
||||
deleteEventAsync({ calId, eventId, eventURL: event.URL })
|
||||
);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
|
||||
{isOwn && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
onClose({}, "backdropClick");
|
||||
dispatch(
|
||||
deleteEventAsync({ calId, eventId, eventURL: event.URL })
|
||||
);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
<Button size="small" onClick={() => setShowMore(!showMore)}>
|
||||
{showMore ? "Show Less" : "Show More"}
|
||||
</Button>
|
||||
|
||||
@@ -324,13 +324,6 @@ function formatEnd(start: Date, end: Date, allday?: boolean) {
|
||||
startDate.getDate() === endDate.getDate();
|
||||
|
||||
if (allday) {
|
||||
console.log(
|
||||
endDate.toLocaleDateString(undefined, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
})
|
||||
);
|
||||
return sameDay
|
||||
? null
|
||||
: endDate.toLocaleDateString(undefined, {
|
||||
|
||||
@@ -233,14 +233,8 @@ function EventPopover({
|
||||
const endDate = new Date(end);
|
||||
const startDate = new Date(start);
|
||||
setAllDay(!allday);
|
||||
console.log(
|
||||
endDate.getDate() === startDate.getDate(),
|
||||
endDate.getDate(),
|
||||
startDate.getDate()
|
||||
);
|
||||
if (endDate.getDate() === startDate.getDate()) {
|
||||
endDate.setDate(startDate.getDate() + 1);
|
||||
console.log("formatedd", formatLocalDateTime(endDate));
|
||||
setEnd(formatLocalDateTime(endDate));
|
||||
}
|
||||
|
||||
@@ -258,7 +252,6 @@ function EventPopover({
|
||||
),
|
||||
allDay: allday,
|
||||
};
|
||||
console.log(newRange, selectedRange);
|
||||
setSelectedRange(newRange);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -126,11 +126,6 @@ export function calendarEventToJCal(event: CalendarEvent): any[] {
|
||||
];
|
||||
|
||||
if (event.end) {
|
||||
console.log(
|
||||
event.end,
|
||||
event.start,
|
||||
event.end.getTime() === event.start.getTime()
|
||||
);
|
||||
if (event.allday && event.end.getTime() === event.start.getTime()) {
|
||||
event.end.setDate(event.start.getDate() + 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user