Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
@@ -572,7 +572,7 @@ describe("calendar Availability search", () => {
|
||||
|
||||
const callCount = spy.mock.calls.length;
|
||||
expect(callCount).toBeGreaterThan(0);
|
||||
expect(callCount).toBeLessThanOrEqual(24);
|
||||
expect(callCount).toBeLessThanOrEqual(36);
|
||||
});
|
||||
|
||||
it("prefetches hidden calendars only after active load completes", async () => {
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
import CalendarResources from "@/components/Calendar/CalendarResources";
|
||||
import { getCalendars } from "@/features/Calendars/CalendarApi";
|
||||
import { addCalendarResourceAsync } from "@/features/Calendars/api/addCalendarResourceAsync";
|
||||
import { act, fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../utils/Renderwithproviders";
|
||||
|
||||
jest.mock("@/features/Calendars/CalendarApi");
|
||||
jest.mock("@/features/Calendars/api/addCalendarResourceAsync");
|
||||
jest.mock("@/components/Attendees/ResourceSearch", () => ({
|
||||
ResourceSearch: ({
|
||||
onChange,
|
||||
}: {
|
||||
onChange: (
|
||||
event: null,
|
||||
value: { displayName: string; openpaasId: string }[]
|
||||
) => void;
|
||||
}) => (
|
||||
<div data-testid="resource-search">
|
||||
<button
|
||||
data-testid="mock-resource-search-select"
|
||||
onClick={() =>
|
||||
onChange(null, [
|
||||
{ displayName: "Room A", openpaasId: "room-a-id" },
|
||||
{ displayName: "Room B", openpaasId: "room-b-id" },
|
||||
])
|
||||
}
|
||||
>
|
||||
Select Resources
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
const mockedGetCalendars = getCalendars as jest.Mock;
|
||||
const mockedAddCalendarResourceAsync =
|
||||
addCalendarResourceAsync as unknown as jest.Mock;
|
||||
|
||||
describe("CalendarResources", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const baseUser = {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "mockSid",
|
||||
openpaasId: "user1",
|
||||
},
|
||||
tokens: { accessToken: "token" },
|
||||
};
|
||||
|
||||
const setup = (isOpen = true) => {
|
||||
const onClose = jest.fn();
|
||||
renderWithProviders(<CalendarResources onClose={onClose} open={isOpen} />, {
|
||||
user: baseUser,
|
||||
});
|
||||
return { onClose };
|
||||
};
|
||||
|
||||
it("renders correctly and closes on cancel", () => {
|
||||
const { onClose } = setup();
|
||||
|
||||
expect(screen.getByText("calendar.browseResources")).toBeInTheDocument();
|
||||
|
||||
const cancelButton = screen.getByRole("button", {
|
||||
name: "common.cancel",
|
||||
});
|
||||
fireEvent.click(cancelButton);
|
||||
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not render when isOpen is false", () => {
|
||||
setup(false);
|
||||
expect(
|
||||
screen.queryByText("calendar.browseResources")
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("fetches resource calendars and adds them using Promise.allSettled logic", async () => {
|
||||
const mockCalendarDataA = {
|
||||
_embedded: {
|
||||
"dav:calendar": [
|
||||
{
|
||||
_links: { self: { href: "/calendars/room-a-id/cal-a.json" } },
|
||||
"dav:name": "Room A Calendar",
|
||||
"caldav:description": "Main room A calendar",
|
||||
color: "red",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
mockedGetCalendars.mockImplementation((userId) => {
|
||||
if (userId === "room-a-id") return Promise.resolve(mockCalendarDataA);
|
||||
// Simulate failure for room-b
|
||||
if (userId === "room-b-id")
|
||||
return Promise.reject(new Error("Failed fetching Room B"));
|
||||
return Promise.resolve([]);
|
||||
});
|
||||
|
||||
const mockDispatchResult = {
|
||||
unwrap: () => Promise.resolve({ type: "success" }),
|
||||
};
|
||||
mockedAddCalendarResourceAsync.mockReturnValue(() => mockDispatchResult);
|
||||
|
||||
const { onClose } = setup();
|
||||
|
||||
// Trigger resource selection
|
||||
const mockSelectBtn = screen.getByTestId("mock-resource-search-select");
|
||||
await act(async () => {
|
||||
fireEvent.click(mockSelectBtn);
|
||||
});
|
||||
|
||||
// After selection, the button should be enabled and say Add
|
||||
const addButton = await screen.findByRole("button", {
|
||||
name: "actions.add",
|
||||
});
|
||||
expect(addButton).not.toBeDisabled();
|
||||
|
||||
// Click add to trigger addCalendarResourceAsync
|
||||
await act(async () => {
|
||||
fireEvent.click(addButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// It should only have been called for Room A because Room B failed (Promise.allSettled)
|
||||
expect(mockedAddCalendarResourceAsync).toHaveBeenCalledTimes(1);
|
||||
|
||||
const payload = mockedAddCalendarResourceAsync.mock.calls[0][0];
|
||||
expect(payload).toEqual(
|
||||
expect.objectContaining({
|
||||
userId: "user1",
|
||||
calId: expect.any(String),
|
||||
cal: expect.objectContaining({
|
||||
cal: mockCalendarDataA._embedded["dav:calendar"][0],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("adds existing user details fallback logic within submit handler", async () => {
|
||||
const mockCalendarDataA = {
|
||||
_embedded: {
|
||||
"dav:calendar": [
|
||||
{
|
||||
_links: { self: { href: "/calendars/room-a-id/cal-a.json" } },
|
||||
"dav:name": "Room A Calendar",
|
||||
"caldav:description": "Main room A calendar",
|
||||
color: "red",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
mockedGetCalendars.mockResolvedValueOnce(mockCalendarDataA);
|
||||
mockedGetCalendars.mockResolvedValueOnce([]); // Mock array of size 2 (for both A and B but second resolves empty)
|
||||
|
||||
// Simulate successful API response
|
||||
const mockDispatchResult = {
|
||||
unwrap: () => Promise.resolve(),
|
||||
};
|
||||
mockedAddCalendarResourceAsync.mockReturnValue(() => mockDispatchResult);
|
||||
|
||||
const { onClose } = setup();
|
||||
|
||||
// Trigger resource selection
|
||||
const mockSelectBtn = screen.getByTestId("mock-resource-search-select");
|
||||
await act(async () => {
|
||||
fireEvent.click(mockSelectBtn);
|
||||
});
|
||||
|
||||
const addButton = await screen.findByRole("button", {
|
||||
name: "actions.add",
|
||||
});
|
||||
await act(async () => {
|
||||
fireEvent.click(addButton);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -98,5 +98,5 @@ describe("Event Error Handling", () => {
|
||||
}
|
||||
|
||||
consoleWarnSpy.mockRestore();
|
||||
});
|
||||
}, 15000);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
import {
|
||||
ResourceSearch,
|
||||
Resource,
|
||||
} from "@/components/Attendees/ResourceSearch";
|
||||
import { searchUsers } from "@/features/User/userAPI";
|
||||
import { act, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "../utils/Renderwithproviders";
|
||||
|
||||
jest.mock("@/features/User/userAPI");
|
||||
const mockedSearchUsers = searchUsers as jest.MockedFunction<
|
||||
typeof searchUsers
|
||||
>;
|
||||
|
||||
describe("ResourceSearch", () => {
|
||||
const baseResource: Resource = {
|
||||
displayName: "Projector Room",
|
||||
};
|
||||
|
||||
function setup(
|
||||
selectedResources: Resource[] = [],
|
||||
props?: Partial<React.ComponentProps<typeof ResourceSearch>>
|
||||
) {
|
||||
const onChange = jest.fn();
|
||||
renderWithProviders(
|
||||
<ResourceSearch
|
||||
objectTypes={["resource"]}
|
||||
selectedResources={selectedResources}
|
||||
onChange={onChange}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
return { onChange };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
mockedSearchUsers.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("calls searchUsers after debounce when typing", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([
|
||||
baseResource,
|
||||
] as unknown as Awaited<ReturnType<typeof searchUsers>>);
|
||||
setup();
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await userEvent.type(input, "Room");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockedSearchUsers).toHaveBeenCalledWith("Room", ["resource"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("renders search results and allows selection", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([
|
||||
baseResource,
|
||||
] as unknown as Awaited<ReturnType<typeof searchUsers>>);
|
||||
const { onChange } = setup();
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await userEvent.type(input, "Room");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Projector Room");
|
||||
await userEvent.click(option);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("does not show already selected resources in options", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([
|
||||
baseResource,
|
||||
] as unknown as Awaited<ReturnType<typeof searchUsers>>);
|
||||
setup([baseResource]);
|
||||
const input = screen.getByRole("combobox");
|
||||
await userEvent.type(input, "Projector");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// It shouldn't be in the dropdown options anymore
|
||||
const options = screen.queryAllByRole("option");
|
||||
expect(
|
||||
options.find((opt) => opt.textContent === "Projector Room")
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("respects disabled state", () => {
|
||||
setup([], { disabled: true });
|
||||
expect(screen.getByRole("combobox")).toBeDisabled();
|
||||
});
|
||||
|
||||
it("no options doesn't show dropdown when input is empty", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([
|
||||
baseResource,
|
||||
] as unknown as Awaited<ReturnType<typeof searchUsers>>);
|
||||
setup();
|
||||
const input = screen.getByRole("combobox");
|
||||
|
||||
await userEvent.type(input, "Room");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("listbox")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await userEvent.clear(input);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows 'No results' when search succeeds but returns empty array", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([]);
|
||||
setup();
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await userEvent.type(input, "Room");
|
||||
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const noResults = await screen.findByText(
|
||||
"resourceSearch.noResults",
|
||||
{},
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
expect(noResults).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("clears options when search fails and shows error snackbar", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([
|
||||
baseResource,
|
||||
] as unknown as Awaited<ReturnType<typeof searchUsers>>);
|
||||
setup();
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await userEvent.type(input, "Room");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Projector Room")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
mockedSearchUsers.mockRejectedValueOnce(new Error("Network error"));
|
||||
await userEvent.clear(input);
|
||||
await userEvent.type(input, "Error");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const errorMessage = await screen.findByText("resourceSearch.searchError");
|
||||
expect(errorMessage).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText("Projector Room")).not.toBeInTheDocument();
|
||||
|
||||
mockedSearchUsers.mockResolvedValueOnce([
|
||||
baseResource,
|
||||
] as unknown as Awaited<ReturnType<typeof searchUsers>>);
|
||||
await userEvent.clear(input);
|
||||
await userEvent.type(input, "Room");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Projector Room")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows loading text when searching", async () => {
|
||||
let resolveSearch: (value: Resource[]) => void;
|
||||
const searchPromise = new Promise<Resource[]>((resolve) => {
|
||||
resolveSearch = resolve;
|
||||
});
|
||||
mockedSearchUsers.mockReturnValueOnce(
|
||||
searchPromise as unknown as ReturnType<typeof searchUsers>
|
||||
);
|
||||
setup();
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await userEvent.type(input, "Room");
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
});
|
||||
|
||||
const loadingText = await screen.findByText(
|
||||
"resourceSearch.loading",
|
||||
{},
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
expect(loadingText).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
resolveSearch!([baseResource]);
|
||||
await searchPromise;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
import { renderHook, act } from "@testing-library/react";
|
||||
import { useUserSearch } from "@/components/Attendees/useUserSearch";
|
||||
import { searchUsers } from "@/features/User/userAPI";
|
||||
|
||||
jest.mock("@/features/User/userAPI", () => ({
|
||||
searchUsers: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("useUserSearch", () => {
|
||||
const mockSearchUsers = searchUsers as jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("should initialize with default values", () => {
|
||||
const { result } = renderHook(() =>
|
||||
useUserSearch({ objectTypes: ["user"], errorMessage: "Error" })
|
||||
);
|
||||
|
||||
expect(result.current.query).toBe("");
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.options).toEqual([]);
|
||||
expect(result.current.hasSearched).toBe(false);
|
||||
expect(result.current.isOpen).toBe(false);
|
||||
expect(result.current.inputError).toBeNull();
|
||||
expect(result.current.snackbarOpen).toBe(false);
|
||||
expect(result.current.snackbarMessage).toBe("");
|
||||
});
|
||||
|
||||
it("should debounce and fetch users when query changes", async () => {
|
||||
const mockUsers = [{ displayName: "John Doe", email: "john@example.com" }];
|
||||
mockSearchUsers.mockResolvedValueOnce(mockUsers);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useUserSearch({ objectTypes: ["user"], errorMessage: "Error" })
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setQuery("John");
|
||||
});
|
||||
|
||||
expect(result.current.loading).toBe(false); // Before debounce
|
||||
|
||||
// Wait for the mock promise to resolve within act
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
await Promise.resolve(); // allow microtasks to flush
|
||||
});
|
||||
|
||||
expect(mockSearchUsers).toHaveBeenCalledWith("John", ["user"]);
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.options).toEqual(mockUsers);
|
||||
expect(result.current.hasSearched).toBe(true);
|
||||
});
|
||||
|
||||
it("should clear options and handle empty query", () => {
|
||||
const { result } = renderHook(() =>
|
||||
useUserSearch({ objectTypes: ["user"], errorMessage: "Error" })
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setQuery(" "); // empty query with spaces
|
||||
});
|
||||
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(300);
|
||||
});
|
||||
|
||||
expect(mockSearchUsers).not.toHaveBeenCalled();
|
||||
expect(result.current.options).toEqual([]);
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.hasSearched).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle search errors and show the custom error message", async () => {
|
||||
mockSearchUsers.mockRejectedValueOnce(new Error("API Error"));
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useUserSearch({ objectTypes: ["user"], errorMessage: "Custom Error" })
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setQuery("FailedSearch");
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(300);
|
||||
await Promise.resolve(); // allow microtasks to flush
|
||||
});
|
||||
|
||||
expect(result.current.hasSearched).toBe(false);
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.snackbarOpen).toBe(true);
|
||||
expect(result.current.snackbarMessage).toBe("Custom Error");
|
||||
});
|
||||
});
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user