#599 ability to register to a resource calendar (#631)

Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
lethemanh
2026-03-17 15:33:15 +07:00
committed by GitHub
parent a9e7b1ac82
commit 0dd7f1b2fd
20 changed files with 1647 additions and 75 deletions
@@ -0,0 +1,128 @@
import { addCalendarResourceAsync } from "@/features/Calendars/api/addCalendarResourceAsync";
import { addSharedCalendar } from "@/features/Calendars/CalendarApi";
import { getResourceDetails } from "@/features/User/userAPI";
import { toRejectedError } from "@/utils/errorUtils";
import { configureStore } from "@reduxjs/toolkit";
jest.mock("@/features/Calendars/CalendarApi");
jest.mock("@/features/User/userAPI");
jest.mock("@/utils/errorUtils");
const mockedAddSharedCalendar = addSharedCalendar as jest.Mock;
const mockedGetResourceDetails = getResourceDetails as jest.Mock;
const mockedToRejectedError = toRejectedError as jest.Mock;
describe("addCalendarResourceAsync thunk", () => {
let store: ReturnType<typeof configureStore>;
const dispatch = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
store = configureStore({
reducer: () => ({}),
});
});
const mockPayload = {
userId: "user-123",
calId: "cal-123",
cal: {
color: {
background: "#000000",
foreground: "#FFFFFF",
},
cal: {
"dav:name": "Resource Room A",
"caldav:description": "A meeting room",
_links: {
self: {
href: "/calendars/res-456/cal-123.json",
},
},
},
},
};
const mockResolvedResourceData = {
_id: "res-456",
id: "res-456",
name: "Resource Room A",
description: "A meeting room",
creator: "user-789",
deleted: false,
_rev: "1",
};
it("should add shared calendar, fetch resource details, map userData", async () => {
mockedGetResourceDetails.mockResolvedValueOnce(mockResolvedResourceData);
mockedAddSharedCalendar.mockResolvedValueOnce({});
const result = await addCalendarResourceAsync(
mockPayload as unknown as Parameters<typeof addCalendarResourceAsync>[0]
)(dispatch, store.getState, undefined);
expect(mockedAddSharedCalendar).toHaveBeenCalledWith(
mockPayload.userId,
mockPayload.calId,
mockPayload.cal
);
expect(mockedGetResourceDetails).toHaveBeenCalledWith("res-456");
expect(result.type).toBe("calendars/addCalendarResource/fulfilled");
expect(result.payload).toEqual({
calId: "res-456/cal-123",
color: { background: "#000000", foreground: "#FFFFFF" },
desc: "A meeting room",
link: "/calendars/user-123/cal-123.json",
name: "Resource Room A",
owner: mockResolvedResourceData,
});
});
it("should fallback to name if resource details fetch fails", async () => {
mockedAddSharedCalendar.mockResolvedValueOnce({});
const errorDetails = new Error("Fetch failed");
mockedGetResourceDetails.mockRejectedValueOnce(errorDetails);
const mockRejectedErrorResult = { message: "Fetch failed" };
mockedToRejectedError.mockReturnValueOnce(mockRejectedErrorResult);
const result = await addCalendarResourceAsync(
mockPayload as unknown as Parameters<typeof addCalendarResourceAsync>[0]
)(dispatch, store.getState, undefined);
expect(mockedAddSharedCalendar).toHaveBeenCalledWith(
mockPayload.userId,
mockPayload.calId,
mockPayload.cal
);
expect(mockedGetResourceDetails).toHaveBeenCalledWith("res-456");
expect(mockedToRejectedError).toHaveBeenCalledWith(errorDetails);
expect(result.type).toBe("calendars/addCalendarResource/rejected");
expect(result.payload).toEqual(mockRejectedErrorResult);
});
it("should handle error if addSharedCalendar fails", async () => {
const errorAdd = new Error("Add failed");
mockedAddSharedCalendar.mockRejectedValueOnce(errorAdd);
const mockRejectedErrorResult = { message: "Add failed" };
mockedToRejectedError.mockReturnValueOnce(mockRejectedErrorResult);
const result = await addCalendarResourceAsync(
mockPayload as unknown as Parameters<typeof addCalendarResourceAsync>[0]
)(dispatch, store.getState, undefined);
expect(mockedAddSharedCalendar).toHaveBeenCalledWith(
mockPayload.userId,
mockPayload.calId,
mockPayload.cal
);
expect(mockedGetResourceDetails).not.toHaveBeenCalled();
expect(mockedToRejectedError).toHaveBeenCalledWith(errorAdd);
expect(result.type).toBe("calendars/addCalendarResource/rejected");
expect(result.payload).toEqual(mockRejectedErrorResult);
});
});
@@ -15,10 +15,9 @@ jest.mock("@/features/Calendars/utils/normalizeCalendar");
const mockedGetOpenPaasUser = getOpenPaasUser as jest.Mock;
const mockedGetUserDetails = getUserDetails as jest.Mock;
const mockedgetResourceDetails = getResourceDetails as jest.Mock;
const mockedGetResourceDetails = getResourceDetails as jest.Mock;
const mockedGetCalendars = getCalendars as jest.Mock;
const mockedFormatReduxError = formatReduxError as jest.Mock;
const mockedToRejectedError = toRejectedError as jest.Mock;
const mockedNormalizeCalendar = normalizeCalendar as jest.Mock;
describe("getCalendarsListAsync", () => {
@@ -143,7 +142,12 @@ describe("getCalendarsListAsync", () => {
response: { status: 500 },
message: "Server Error",
});
mockedToRejectedError.mockReturnValueOnce({
// toRejectedError is imported from a mocked module; provide the expected return
const { toRejectedError } = jest.requireMock("@/utils/errorUtils") as {
toRejectedError: jest.Mock;
};
toRejectedError.mockReturnValueOnce({
status: 500,
message: "Server Error",
});
@@ -175,7 +179,7 @@ describe("getCalendarsListAsync", () => {
// getUserDetails fails with 404 for the resource ID
mockedGetUserDetails.mockRejectedValueOnce({ response: { status: 404 } });
// Then getResourceDetails is called and succeeds
mockedgetResourceDetails.mockResolvedValueOnce({ creator: "creator-456" });
mockedGetResourceDetails.mockResolvedValueOnce({ creator: "creator-456" });
// Then getUserDetails is called for the creator and succeeds
mockedGetUserDetails.mockResolvedValueOnce({
firstname: "Creator",
@@ -187,7 +191,7 @@ describe("getCalendarsListAsync", () => {
const result = await thunk(dispatch, getState, undefined);
const payload = result.payload as any;
expect(mockedgetResourceDetails).toHaveBeenCalledWith("resource-123");
expect(mockedGetResourceDetails).toHaveBeenCalledWith("resource-123");
expect(mockedGetUserDetails).toHaveBeenCalledWith("creator-456");
expect(payload.importedCalendars["cal-1"].owner).toEqual({
firstname: "Creator",