Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { act } from "react";
|
||||
import CalendarSearch from "../../src/components/Calendar/CalendarSearch";
|
||||
import * as CalendarApi from "../../src/features/Calendars/CalendarApi";
|
||||
import * as CalendarSlice from "../../src/features/Calendars/CalendarSlice";
|
||||
import { searchUsers } from "../../src/features/User/userAPI";
|
||||
import { renderWithProviders } from "../utils/Renderwithproviders";
|
||||
|
||||
jest.mock("../../src/features/User/userAPI");
|
||||
jest.mock("../../src/features/Calendars/CalendarApi");
|
||||
|
||||
const mockedSearchUsers = searchUsers as jest.MockedFunction<
|
||||
typeof searchUsers
|
||||
>;
|
||||
const mockedGetCalendars = CalendarApi.getCalendars as jest.MockedFunction<
|
||||
typeof CalendarApi.getCalendars
|
||||
>;
|
||||
|
||||
describe("CalendarSearch", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
const mockUser = {
|
||||
email: "user@example.com",
|
||||
displayName: "Test User",
|
||||
avatarUrl: "https://example.com/avatar.jpg",
|
||||
openpaasId: "user123",
|
||||
};
|
||||
|
||||
const mockCalendar = {
|
||||
"dav:name": "Test Calendar",
|
||||
"apple:color": "#FF0000",
|
||||
_links: {
|
||||
self: {
|
||||
href: "/calendars/user123/cal1.json",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "mockSid",
|
||||
openpaasId: "user1",
|
||||
},
|
||||
tokens: { accessToken: "token" },
|
||||
},
|
||||
calendars: {
|
||||
list: {
|
||||
"user1/cal1": {
|
||||
name: "My Calendar",
|
||||
id: "user1/cal1",
|
||||
color: "#0000FF",
|
||||
ownerEmails: ["test@test.com"],
|
||||
events: {},
|
||||
},
|
||||
},
|
||||
pending: false,
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("searches for users and displays their calendars", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([mockUser]);
|
||||
mockedGetCalendars.mockResolvedValueOnce({
|
||||
_embedded: {
|
||||
"dav:calendar": [mockCalendar],
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await act(async () => {
|
||||
userEvent.type(input, "Test");
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Test User");
|
||||
await act(async () => {
|
||||
fireEvent.click(option);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockedGetCalendars).toHaveBeenCalledWith(
|
||||
"user123",
|
||||
"sharedPublic=true&withRights=true"
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test Calendar")).toBeInTheDocument();
|
||||
expect(screen.getByText("user@example.com")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("adds selected calendars on save", async () => {
|
||||
const addSharedCalendarSpy = jest
|
||||
.spyOn(CalendarSlice, "addSharedCalendarAsync")
|
||||
.mockImplementation((payload) => {
|
||||
return () => Promise.resolve(payload) as any;
|
||||
});
|
||||
|
||||
mockedSearchUsers.mockResolvedValueOnce([mockUser]);
|
||||
mockedGetCalendars.mockResolvedValueOnce({
|
||||
_embedded: {
|
||||
"dav:calendar": [mockCalendar],
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await act(async () => {
|
||||
userEvent.type(input, "Test");
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Test User");
|
||||
await act(async () => {
|
||||
fireEvent.click(option);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test Calendar")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const addButton = screen.getByRole("button", { name: /add/i });
|
||||
await act(async () => {
|
||||
fireEvent.click(addButton);
|
||||
});
|
||||
|
||||
expect(addSharedCalendarSpy).toHaveBeenCalled();
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not add calendars that already exist", async () => {
|
||||
const addSharedCalendarSpy = jest
|
||||
.spyOn(CalendarSlice, "addSharedCalendarAsync")
|
||||
.mockImplementation((payload) => {
|
||||
return () => Promise.resolve(payload) as any;
|
||||
});
|
||||
|
||||
const existingCalendar = {
|
||||
...mockCalendar,
|
||||
_links: {
|
||||
self: {
|
||||
href: "/calendars/user1/cal1.json",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockedSearchUsers.mockResolvedValueOnce([mockUser]);
|
||||
mockedGetCalendars.mockResolvedValueOnce({
|
||||
_embedded: {
|
||||
"dav:calendar": [existingCalendar],
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await act(async () => {
|
||||
userEvent.type(input, "Test");
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Test User");
|
||||
await act(async () => {
|
||||
fireEvent.click(option);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("No more Calendar for Test User")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const addButton = screen.getByRole("button", { name: /add/i });
|
||||
await act(async () => {
|
||||
fireEvent.click(addButton);
|
||||
});
|
||||
|
||||
expect(addSharedCalendarSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("displays message when user has no publicly available calendars", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([mockUser]);
|
||||
mockedGetCalendars.mockResolvedValueOnce({});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await act(async () => {
|
||||
userEvent.type(input, "Test");
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Test User");
|
||||
await act(async () => {
|
||||
fireEvent.click(option);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("No publicly available calendars for Test User")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("changes calendar color", async () => {
|
||||
mockedSearchUsers.mockResolvedValueOnce([mockUser]);
|
||||
mockedGetCalendars.mockResolvedValueOnce({
|
||||
_embedded: {
|
||||
"dav:calendar": [mockCalendar],
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await act(async () => {
|
||||
userEvent.type(input, "Test");
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Test User");
|
||||
await act(async () => {
|
||||
fireEvent.click(option);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test Calendar")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// ColorPicker would need to be interacted with based on its implementation
|
||||
// This is a placeholder for color change interaction
|
||||
const colorPicker = document.querySelector('[data-testid="color-picker"]');
|
||||
if (colorPicker) {
|
||||
await act(async () => {
|
||||
fireEvent.click(colorPicker);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("handles multiple calendars from the same user", async () => {
|
||||
const secondCalendar = {
|
||||
"dav:name": "Second Calendar",
|
||||
"apple:color": "#00FF00",
|
||||
_links: {
|
||||
self: {
|
||||
href: "/calendars/user123/cal2.json",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockedSearchUsers.mockResolvedValueOnce([mockUser]);
|
||||
mockedGetCalendars.mockResolvedValueOnce({
|
||||
_embedded: {
|
||||
"dav:calendar": [mockCalendar, secondCalendar],
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await act(async () => {
|
||||
userEvent.type(input, "Test");
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Test User");
|
||||
await act(async () => {
|
||||
fireEvent.click(option);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test Calendar")).toBeInTheDocument();
|
||||
expect(screen.getByText("Second Calendar")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("does not call addSharedCalendarAsync when no calendars are selected", async () => {
|
||||
const addSharedCalendarSpy = jest
|
||||
.spyOn(CalendarSlice, "addSharedCalendarAsync")
|
||||
.mockImplementation((payload) => {
|
||||
return () => Promise.resolve(payload) as any;
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const addButton = screen.getByRole("button", { name: /add/i });
|
||||
await act(async () => {
|
||||
fireEvent.click(addButton);
|
||||
});
|
||||
|
||||
expect(addSharedCalendarSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("BUGFIX : handles calendar with no apple:color", async () => {
|
||||
const mockCalendarNoColor = {
|
||||
"dav:name": "Test Calendar",
|
||||
_links: {
|
||||
self: {
|
||||
href: "/calendars/user123/cal1.json",
|
||||
},
|
||||
},
|
||||
};
|
||||
mockedSearchUsers.mockResolvedValueOnce([mockUser]);
|
||||
mockedGetCalendars.mockResolvedValueOnce({
|
||||
_embedded: {
|
||||
"dav:calendar": [mockCalendarNoColor],
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
renderWithProviders(
|
||||
<CalendarSearch anchorEl={null} open={true} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
});
|
||||
|
||||
const input = screen.getByRole("combobox");
|
||||
await act(async () => {
|
||||
userEvent.type(input, "Test");
|
||||
});
|
||||
|
||||
const option = await screen.findByText("Test User");
|
||||
await act(async () => {
|
||||
fireEvent.click(option);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockedGetCalendars).toHaveBeenCalledWith(
|
||||
"user123",
|
||||
"sharedPublic=true&withRights=true"
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test Calendar")).toBeInTheDocument();
|
||||
expect(screen.getByText("user@example.com")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,7 @@ import { addSharedCalendarAsync } from "../../features/Calendars/CalendarSlice";
|
||||
import { ColorPicker } from "./CalendarColorPicker";
|
||||
import { Calendars } from "../../features/Calendars/CalendarTypes";
|
||||
import { User, PeopleSearch } from "../Attendees/PeopleSearch";
|
||||
import { getAccessiblePair } from "./utils/calendarColorsUtils";
|
||||
import { defaultColors, getAccessiblePair } from "./utils/calendarColorsUtils";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { ResponsiveDialog } from "../Dialog";
|
||||
|
||||
@@ -54,10 +54,10 @@ function CalendarItem({
|
||||
src={cal.owner.avatarUrl}
|
||||
alt={cal.owner.email}
|
||||
style={{
|
||||
border: `2px solid ${cal.cal["apple:color"] ?? "transparent"}`,
|
||||
border: `2px solid ${cal.cal["apple:color"] || defaultColors[0]}`,
|
||||
boxShadow: cal.cal["apple:color"]
|
||||
? `0 0 0 2px ${cal.cal["apple:color"]}`
|
||||
: "none",
|
||||
: `0 0 0 2px ${defaultColors[0]}`,
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
@@ -75,8 +75,10 @@ function CalendarItem({
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<ColorPicker
|
||||
selectedColor={{
|
||||
light: cal.cal["apple:color"],
|
||||
dark: getAccessiblePair(cal.cal["apple:color"], theme),
|
||||
light: cal.cal["apple:color"] ?? defaultColors[0].light,
|
||||
dark: cal.cal["apple:color"]
|
||||
? getAccessiblePair(cal.cal["apple:color"], theme)
|
||||
: defaultColors[0].dark,
|
||||
}}
|
||||
onChange={onColorChange}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user