* #708 apply strictier linting rules and fix simple eslint bugs * #708 fix eslint errors relate to promise * #708 fix eslint import/no-extraneous-dependencies * #708 fix eslint errors of react-hook * #708 enable eslint check for typescript --------- Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
@@ -6,197 +6,197 @@ import {
|
||||
getSecretLink,
|
||||
postCalendar,
|
||||
proppatchCalendar,
|
||||
removeCalendar,
|
||||
} from "@/features/Calendars/CalendarApi";
|
||||
import { clientConfig } from "@/features/User/oidcAuth";
|
||||
import { api } from "@/utils/apiUtils";
|
||||
clientConfig.url = "https://example.com";
|
||||
removeCalendar
|
||||
} from '@/features/Calendars/CalendarApi'
|
||||
import { clientConfig } from '@/features/User/oidcAuth'
|
||||
import { api } from '@/utils/apiUtils'
|
||||
clientConfig.url = 'https://example.com'
|
||||
|
||||
jest.mock("@/utils/apiUtils");
|
||||
jest.mock('@/utils/apiUtils')
|
||||
|
||||
describe("Calendar API", () => {
|
||||
describe('Calendar API', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("fetches calendar list for a user", async () => {
|
||||
const mockUserId = "user123";
|
||||
const mockResponse = [{ id: "calendar1" }, { id: "calendar2" }];
|
||||
it('fetches calendar list for a user', async () => {
|
||||
const mockUserId = 'user123'
|
||||
const mockResponse = [{ id: 'calendar1' }, { id: 'calendar2' }]
|
||||
|
||||
(api.get as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue(mockResponse),
|
||||
});
|
||||
;(api.get as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue(mockResponse)
|
||||
})
|
||||
|
||||
const calendars = await getCalendars(mockUserId);
|
||||
const calendars = await getCalendars(mockUserId)
|
||||
|
||||
expect(api.get).toHaveBeenCalledWith(
|
||||
`dav/calendars/${mockUserId}.json?personal=true&sharedDelegationStatus=accepted&sharedPublicSubscription=true&withRights=true`,
|
||||
{
|
||||
headers: { Accept: "application/calendar+json" },
|
||||
headers: { Accept: 'application/calendar+json' }
|
||||
}
|
||||
);
|
||||
expect(calendars).toEqual(mockResponse);
|
||||
});
|
||||
)
|
||||
expect(calendars).toEqual(mockResponse)
|
||||
})
|
||||
|
||||
it("fetches calendar events for a given ID and match window", async () => {
|
||||
const calendarId = "calendar1";
|
||||
const match = { start: "2025-07-01", end: "2025-07-31" };
|
||||
const mockCalendarData = { events: ["event1", "event2"] };
|
||||
it('fetches calendar events for a given ID and match window', async () => {
|
||||
const calendarId = 'calendar1'
|
||||
const match = { start: '2025-07-01', end: '2025-07-31' }
|
||||
const mockCalendarData = { events: ['event1', 'event2'] }
|
||||
|
||||
(api as unknown as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue(mockCalendarData),
|
||||
});
|
||||
;(api as unknown as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue(mockCalendarData)
|
||||
})
|
||||
|
||||
const result = await getCalendar(calendarId, match);
|
||||
const result = await getCalendar(calendarId, match)
|
||||
|
||||
expect(api).toHaveBeenCalledWith(`dav/calendars/${calendarId}.json`, {
|
||||
method: "REPORT",
|
||||
method: 'REPORT',
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
},
|
||||
body: JSON.stringify({ match }),
|
||||
});
|
||||
body: JSON.stringify({ match })
|
||||
})
|
||||
|
||||
expect(result).toEqual(mockCalendarData);
|
||||
});
|
||||
it("postCalendar", async () => {
|
||||
const calId = "calId";
|
||||
const userId = "userId";
|
||||
const color = { light: "calId" };
|
||||
const name = "new cal";
|
||||
const desc = "desc";
|
||||
expect(result).toEqual(mockCalendarData)
|
||||
})
|
||||
it('postCalendar', async () => {
|
||||
const calId = 'calId'
|
||||
const userId = 'userId'
|
||||
const color = { light: 'calId' }
|
||||
const name = 'new cal'
|
||||
const desc = 'desc'
|
||||
|
||||
const result = await postCalendar(userId, calId, color, name, desc);
|
||||
const result = await postCalendar(userId, calId, color, name, desc)
|
||||
|
||||
expect(api.post).toHaveBeenCalledWith(`dav/calendars/${userId}.json`, {
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: "calId",
|
||||
"dav:name": "new cal",
|
||||
"apple:color": "calId",
|
||||
"caldav:description": "desc",
|
||||
}),
|
||||
});
|
||||
});
|
||||
it("patch Calendar", async () => {
|
||||
const calId = "calId";
|
||||
const calLink = "/calendars/calId.json";
|
||||
const color = { light: "calIdLight", dark: "calIdDark" };
|
||||
const name = "new cal";
|
||||
const desc = "desc";
|
||||
id: 'calId',
|
||||
'dav:name': 'new cal',
|
||||
'apple:color': 'calId',
|
||||
'caldav:description': 'desc'
|
||||
})
|
||||
})
|
||||
})
|
||||
it('patch Calendar', async () => {
|
||||
const calId = 'calId'
|
||||
const calLink = '/calendars/calId.json'
|
||||
const color = { light: 'calIdLight', dark: 'calIdDark' }
|
||||
const name = 'new cal'
|
||||
const desc = 'desc'
|
||||
|
||||
const result = await proppatchCalendar(calLink, { color, name, desc });
|
||||
const result = await proppatchCalendar(calLink, { color, name, desc })
|
||||
|
||||
expect(api).toHaveBeenCalledWith(`dav${calLink}`, {
|
||||
method: "PROPPATCH",
|
||||
method: 'PROPPATCH',
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"dav:name": "new cal",
|
||||
"caldav:description": "desc",
|
||||
"apple:color": "calIdLight",
|
||||
}),
|
||||
});
|
||||
});
|
||||
'dav:name': 'new cal',
|
||||
'caldav:description': 'desc',
|
||||
'apple:color': 'calIdLight'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("remove Calendar", async () => {
|
||||
const calLink = "/calendars/calId.json";
|
||||
const result = await removeCalendar(calLink);
|
||||
it('remove Calendar', async () => {
|
||||
const calLink = '/calendars/calId.json'
|
||||
const result = await removeCalendar(calLink)
|
||||
|
||||
expect(api).toHaveBeenCalledWith(`dav${calLink}`, {
|
||||
method: "DELETE",
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
},
|
||||
});
|
||||
});
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("get secret link without reset", async () => {
|
||||
const calLink = "/calendars/calId.json";
|
||||
(api.get as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue("link"),
|
||||
});
|
||||
it('get secret link without reset', async () => {
|
||||
const calLink = '/calendars/calId.json'
|
||||
;(api.get as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue('link')
|
||||
})
|
||||
|
||||
const noreset = await getSecretLink(calLink, false);
|
||||
const noreset = await getSecretLink(calLink, false)
|
||||
|
||||
expect(api.get).toHaveBeenCalledWith(
|
||||
`calendar/api${calLink}/secret-link?shouldResetLink=false`,
|
||||
{
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
},
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
it("get secret link with reset", async () => {
|
||||
const calLink = "/calendars/calId.json";
|
||||
(api.get as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue("link"),
|
||||
});
|
||||
const reset = await getSecretLink(calLink, true);
|
||||
)
|
||||
})
|
||||
it('get secret link with reset', async () => {
|
||||
const calLink = '/calendars/calId.json'
|
||||
;(api.get as jest.Mock).mockReturnValue({
|
||||
json: jest.fn().mockResolvedValue('link')
|
||||
})
|
||||
const reset = await getSecretLink(calLink, true)
|
||||
|
||||
expect(api.get).toHaveBeenCalledWith(
|
||||
`calendar/api${calLink}/secret-link?shouldResetLink=true`,
|
||||
{
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
},
|
||||
Accept: 'application/json, text/plain, */*'
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
it("get export data ", async () => {
|
||||
const calLink = "/calendars/calId.json";
|
||||
(api.get as jest.Mock).mockReturnValue({
|
||||
text: jest.fn().mockResolvedValue("data"),
|
||||
});
|
||||
const data = await exportCalendar(calLink);
|
||||
it('get export data ', async () => {
|
||||
const calLink = '/calendars/calId.json'
|
||||
;(api.get as jest.Mock).mockReturnValue({
|
||||
text: jest.fn().mockResolvedValue('data')
|
||||
})
|
||||
const data = await exportCalendar(calLink)
|
||||
|
||||
expect(api.get).toHaveBeenCalledWith(`dav${calLink}?export`, {
|
||||
headers: {
|
||||
Accept: "application/calendar",
|
||||
},
|
||||
});
|
||||
});
|
||||
Accept: 'application/calendar'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("When adding a sharedCal with #default #default is preserved", async () => {
|
||||
const mockApiPost = jest.spyOn(api, "post");
|
||||
it('When adding a sharedCal with #default #default is preserved', async () => {
|
||||
const mockApiPost = jest.spyOn(api, 'post')
|
||||
|
||||
const calData = {
|
||||
cal: {
|
||||
id: "cal123",
|
||||
"dav:name": "#default",
|
||||
"apple:color": "#FF5733",
|
||||
"caldav:description": "Default calendar",
|
||||
id: 'cal123',
|
||||
'dav:name': '#default',
|
||||
'apple:color': '#FF5733',
|
||||
'caldav:description': 'Default calendar',
|
||||
acl: [],
|
||||
invite: [],
|
||||
_links: {
|
||||
self: {
|
||||
href: "/calendars/owner123/cal123.json",
|
||||
},
|
||||
},
|
||||
href: '/calendars/owner123/cal123.json'
|
||||
}
|
||||
}
|
||||
},
|
||||
owner: {
|
||||
displayName: "John Doe",
|
||||
email: "john.doe@example.com",
|
||||
openpaasId: "owner123",
|
||||
displayName: 'John Doe',
|
||||
email: 'john.doe@example.com',
|
||||
openpaasId: 'owner123'
|
||||
},
|
||||
color: "#FF5733",
|
||||
};
|
||||
color: '#FF5733'
|
||||
}
|
||||
|
||||
await addSharedCalendar("currentUserId", "newCalId123", calData);
|
||||
await addSharedCalendar('currentUserId', 'newCalId123', calData)
|
||||
|
||||
expect(mockApiPost).toHaveBeenCalledWith(
|
||||
"dav/calendars/currentUserId.json",
|
||||
'dav/calendars/currentUserId.json',
|
||||
expect.objectContaining({
|
||||
body: expect.stringContaining('"dav:name":"#default"'),
|
||||
body: expect.stringContaining('"dav:name":"#default"')
|
||||
})
|
||||
);
|
||||
)
|
||||
|
||||
const callBody = JSON.parse(String(mockApiPost.mock.calls[0][1]?.body));
|
||||
expect(callBody["dav:name"]).toBe("#default");
|
||||
});
|
||||
});
|
||||
const callBody = JSON.parse(String(mockApiPost.mock.calls[0][1]?.body))
|
||||
expect(callBody['dav:name']).toBe('#default')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,103 +1,103 @@
|
||||
import { AccessTab } from "@/components/Calendar/AccessTab";
|
||||
import { AccessTab } from '@/components/Calendar/AccessTab'
|
||||
import {
|
||||
CalendarAccessRights,
|
||||
UserWithAccess,
|
||||
} from "@/components/Calendar/CalendarAccessRights";
|
||||
import CalendarPopover from "@/components/Calendar/CalendarModal";
|
||||
import { updateDelegationCalendar } from "@/features/Calendars/api/updateDelegationCalendar";
|
||||
import { AccessRight, Calendar } from "@/features/Calendars/CalendarTypes";
|
||||
import * as eventThunks from "@/features/Calendars/services";
|
||||
import * as delegationThunks from "@/features/Calendars/services/updateDelegationCalendarAsync";
|
||||
import { getUserDetails } from "@/features/User/userAPI";
|
||||
import { accessRightToDavProp } from "@/utils/accessRightToDavProp";
|
||||
import { api } from "@/utils/apiUtils";
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||
UserWithAccess
|
||||
} from '@/components/Calendar/CalendarAccessRights'
|
||||
import CalendarPopover from '@/components/Calendar/CalendarModal'
|
||||
import { updateDelegationCalendar } from '@/features/Calendars/api/updateDelegationCalendar'
|
||||
import { AccessRight, Calendar } from '@/features/Calendars/CalendarTypes'
|
||||
import * as eventThunks from '@/features/Calendars/services'
|
||||
import * as delegationThunks from '@/features/Calendars/services/updateDelegationCalendarAsync'
|
||||
import { getUserDetails } from '@/features/User/userAPI'
|
||||
import { accessRightToDavProp } from '@/utils/accessRightToDavProp'
|
||||
import { api } from '@/utils/apiUtils'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { renderWithProviders } from '../../utils/Renderwithproviders'
|
||||
|
||||
jest.mock("@/utils/apiUtils", () => ({
|
||||
api: { post: jest.fn() },
|
||||
}));
|
||||
jest.mock('@/utils/apiUtils', () => ({
|
||||
api: { post: jest.fn() }
|
||||
}))
|
||||
|
||||
jest.mock("@/features/User/userAPI", () => ({
|
||||
getUserDetails: jest.fn(),
|
||||
}));
|
||||
jest.mock('@/features/User/userAPI', () => ({
|
||||
getUserDetails: jest.fn()
|
||||
}))
|
||||
|
||||
jest.mock("@/features/Calendars/CalendarApi", () => ({
|
||||
getSecretLink: jest.fn().mockReturnValue(""),
|
||||
exportCalendar: jest.fn(),
|
||||
}));
|
||||
jest.mock('@/features/Calendars/CalendarApi', () => ({
|
||||
getSecretLink: jest.fn().mockReturnValue(''),
|
||||
exportCalendar: jest.fn()
|
||||
}))
|
||||
|
||||
const mockThunkWithUnwrap = (resolvedValue: unknown = {}) =>
|
||||
jest.fn().mockImplementation(() => {
|
||||
const result = Object.assign(Promise.resolve(resolvedValue), {
|
||||
unwrap: () => Promise.resolve(resolvedValue),
|
||||
});
|
||||
return jest.fn().mockReturnValue(result);
|
||||
});
|
||||
unwrap: () => Promise.resolve(resolvedValue)
|
||||
})
|
||||
return jest.fn().mockReturnValue(result)
|
||||
})
|
||||
|
||||
describe("accessRightToDavProp", () => {
|
||||
it("maps 5 (ADMIN) → dav:administration", () => {
|
||||
expect(accessRightToDavProp(5)).toBe("dav:administration");
|
||||
});
|
||||
describe('accessRightToDavProp', () => {
|
||||
it('maps 5 (ADMIN) → dav:administration', () => {
|
||||
expect(accessRightToDavProp(5)).toBe('dav:administration')
|
||||
})
|
||||
|
||||
it("maps 3 (EDITOR) → dav:read-write", () => {
|
||||
expect(accessRightToDavProp(3)).toBe("dav:read-write");
|
||||
});
|
||||
it('maps 3 (EDITOR) → dav:read-write', () => {
|
||||
expect(accessRightToDavProp(3)).toBe('dav:read-write')
|
||||
})
|
||||
|
||||
it("maps 2 (VIEW) → dav:read", () => {
|
||||
expect(accessRightToDavProp(2)).toBe("dav:read");
|
||||
});
|
||||
it('maps 2 (VIEW) → dav:read', () => {
|
||||
expect(accessRightToDavProp(2)).toBe('dav:read')
|
||||
})
|
||||
|
||||
it("defaults to dav:read for unknown values (covered by default case)", () => {
|
||||
expect(accessRightToDavProp(999 as AccessRight)).toBe("dav:read");
|
||||
});
|
||||
});
|
||||
it('defaults to dav:read for unknown values (covered by default case)', () => {
|
||||
expect(accessRightToDavProp(999 as AccessRight)).toBe('dav:read')
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateDelegationCalendar", () => {
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
describe('updateDelegationCalendar', () => {
|
||||
beforeEach(() => jest.clearAllMocks())
|
||||
|
||||
it("posts to the correct DAV endpoint with the share body", async () => {
|
||||
(api.post as jest.Mock).mockResolvedValue({ ok: true });
|
||||
it('posts to the correct DAV endpoint with the share body', async () => {
|
||||
;(api.post as jest.Mock).mockResolvedValue({ ok: true })
|
||||
|
||||
const share = {
|
||||
set: [{ "dav:href": "mailto:alice@example.com", "dav:read": true }],
|
||||
remove: [],
|
||||
};
|
||||
set: [{ 'dav:href': 'mailto:alice@example.com', 'dav:read': true }],
|
||||
remove: []
|
||||
}
|
||||
|
||||
await updateDelegationCalendar("/calendars/user/cal1.json", share);
|
||||
await updateDelegationCalendar('/calendars/user/cal1.json', share)
|
||||
|
||||
await waitFor(() =>
|
||||
expect(api.post).toHaveBeenCalledWith(
|
||||
"dav/calendars/user/cal1.json",
|
||||
'dav/calendars/user/cal1.json',
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({ share }),
|
||||
body: JSON.stringify({ share })
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const baseCalendar: Calendar = {
|
||||
id: "user1/cal1",
|
||||
name: "My Calendar",
|
||||
description: "",
|
||||
color: { color: "#0062FF", dark: "#FFF" },
|
||||
link: "/calendars/user1/cal1.json",
|
||||
visibility: "public",
|
||||
id: 'user1/cal1',
|
||||
name: 'My Calendar',
|
||||
description: '',
|
||||
color: { color: '#0062FF', dark: '#FFF' },
|
||||
link: '/calendars/user1/cal1.json',
|
||||
visibility: 'public',
|
||||
events: {},
|
||||
invite: [],
|
||||
owner: { emails: ["user1@example.com"] },
|
||||
owner: { emails: ['user1@example.com'] },
|
||||
access: { write: true } as any,
|
||||
delegated: true,
|
||||
};
|
||||
delegated: true
|
||||
}
|
||||
|
||||
describe("CalendarAccessRights", () => {
|
||||
const mockOnChange = jest.fn();
|
||||
const mockOnInvitesLoaded = jest.fn();
|
||||
describe('CalendarAccessRights', () => {
|
||||
const mockOnChange = jest.fn()
|
||||
const mockOnInvitesLoaded = jest.fn()
|
||||
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
beforeEach(() => jest.clearAllMocks())
|
||||
|
||||
it("renders the grant access rights section", () => {
|
||||
it('renders the grant access rights section', () => {
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
calendar={baseCalendar}
|
||||
@@ -106,22 +106,22 @@ describe("CalendarAccessRights", () => {
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
expect(
|
||||
screen.getByText("calendarPopover.access.grantAccessRights")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
screen.getByText('calendarPopover.access.grantAccessRights')
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("shows a list of users already passed in via value prop", () => {
|
||||
it('shows a list of users already passed in via value prop', () => {
|
||||
const users: UserWithAccess[] = [
|
||||
{
|
||||
openpaasId: "user1",
|
||||
displayName: "Alice",
|
||||
email: "alice@example.com",
|
||||
accessRight: 2,
|
||||
},
|
||||
];
|
||||
openpaasId: 'user1',
|
||||
displayName: 'Alice',
|
||||
email: 'alice@example.com',
|
||||
accessRight: 2
|
||||
}
|
||||
]
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
@@ -130,21 +130,21 @@ describe("CalendarAccessRights", () => {
|
||||
onChange={mockOnChange}
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>
|
||||
);
|
||||
)
|
||||
|
||||
expect(screen.getByText("Alice")).toBeInTheDocument();
|
||||
expect(screen.getByText("alice@example.com")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('Alice')).toBeInTheDocument()
|
||||
expect(screen.getByText('alice@example.com')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("calls onChange with user removed when remove button is clicked", () => {
|
||||
it('calls onChange with user removed when remove button is clicked', () => {
|
||||
const users: UserWithAccess[] = [
|
||||
{
|
||||
openpaasId: "user1",
|
||||
displayName: "Alice",
|
||||
email: "alice@example.com",
|
||||
accessRight: 5,
|
||||
},
|
||||
];
|
||||
openpaasId: 'user1',
|
||||
displayName: 'Alice',
|
||||
email: 'alice@example.com',
|
||||
accessRight: 5
|
||||
}
|
||||
]
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
@@ -154,31 +154,31 @@ describe("CalendarAccessRights", () => {
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByLabelText(/remove/i));
|
||||
expect(mockOnChange).toHaveBeenCalledWith([]);
|
||||
});
|
||||
fireEvent.click(screen.getByLabelText(/remove/i))
|
||||
expect(mockOnChange).toHaveBeenCalledWith([])
|
||||
})
|
||||
|
||||
it("loads invited users from calendar.invite on mount", async () => {
|
||||
it('loads invited users from calendar.invite on mount', async () => {
|
||||
const calendarWithInvite: Calendar = {
|
||||
...baseCalendar,
|
||||
invite: [
|
||||
{
|
||||
href: "mailto:bob@example.com",
|
||||
principal: "/principals/users/bob123",
|
||||
href: 'mailto:bob@example.com',
|
||||
principal: '/principals/users/bob123',
|
||||
access: 3,
|
||||
inviteStatus: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
inviteStatus: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
(getUserDetails as jest.Mock).mockResolvedValue({
|
||||
preferredEmail: "bob@example.com",
|
||||
firstname: "Bob",
|
||||
lastname: "Smith",
|
||||
emails: ["bob@example.com"],
|
||||
});
|
||||
;(getUserDetails as jest.Mock).mockResolvedValue({
|
||||
preferredEmail: 'bob@example.com',
|
||||
firstname: 'Bob',
|
||||
lastname: 'Smith',
|
||||
emails: ['bob@example.com']
|
||||
})
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
@@ -188,35 +188,35 @@ describe("CalendarAccessRights", () => {
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
await waitFor(() => expect(getUserDetails).toHaveBeenCalledWith("bob123"));
|
||||
await waitFor(() => expect(getUserDetails).toHaveBeenCalledWith('bob123'))
|
||||
await waitFor(() =>
|
||||
expect(mockOnInvitesLoaded).toHaveBeenCalledWith(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
email: "bob@example.com",
|
||||
accessRight: 3,
|
||||
}),
|
||||
email: 'bob@example.com',
|
||||
accessRight: 3
|
||||
})
|
||||
])
|
||||
)
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
it("skips invite entries where getUserDetails throws", async () => {
|
||||
it('skips invite entries where getUserDetails throws', async () => {
|
||||
const calendarWithInvite: Calendar = {
|
||||
...baseCalendar,
|
||||
invite: [
|
||||
{
|
||||
href: "mailto:ghost@example.com",
|
||||
principal: "/principals/users/ghost",
|
||||
href: 'mailto:ghost@example.com',
|
||||
principal: '/principals/users/ghost',
|
||||
access: 2,
|
||||
inviteStatus: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
inviteStatus: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
(getUserDetails as jest.Mock).mockRejectedValue(new Error("Not found"));
|
||||
;(getUserDetails as jest.Mock).mockRejectedValue(new Error('Not found'))
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
@@ -226,28 +226,28 @@ describe("CalendarAccessRights", () => {
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
await waitFor(() => expect(mockOnInvitesLoaded).toHaveBeenCalledWith([]));
|
||||
});
|
||||
await waitFor(() => expect(mockOnInvitesLoaded).toHaveBeenCalledWith([]))
|
||||
})
|
||||
|
||||
it("shows a loading spinner while invite users are being fetched", async () => {
|
||||
it('shows a loading spinner while invite users are being fetched', async () => {
|
||||
const calendarWithInvite: Calendar = {
|
||||
...baseCalendar,
|
||||
invite: [
|
||||
{
|
||||
href: "mailto:carol@example.com",
|
||||
principal: "/principals/users/carol",
|
||||
href: 'mailto:carol@example.com',
|
||||
principal: '/principals/users/carol',
|
||||
access: 2,
|
||||
inviteStatus: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
inviteStatus: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Never resolves during the assertion window
|
||||
(getUserDetails as jest.Mock).mockImplementation(
|
||||
;(getUserDetails as jest.Mock).mockImplementation(
|
||||
() => new Promise(() => {})
|
||||
);
|
||||
)
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
@@ -257,12 +257,12 @@ describe("CalendarAccessRights", () => {
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
expect(document.querySelector('[role="progressbar"]')).toBeInTheDocument();
|
||||
});
|
||||
expect(document.querySelector('[role="progressbar"]')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("displays the calendar owner information", () => {
|
||||
it('displays the calendar owner information', () => {
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
calendar={baseCalendar}
|
||||
@@ -271,46 +271,44 @@ describe("CalendarAccessRights", () => {
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
expect(screen.getAllByText("user1@example.com").length).toBeGreaterThan(0);
|
||||
expect(
|
||||
screen.getByText("calendarPopover.access.owner")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getAllByText('user1@example.com').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('calendarPopover.access.owner')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("fetches and displays resource administrators for resource calendars", async () => {
|
||||
it('fetches and displays resource administrators for resource calendars', async () => {
|
||||
const resourceCalendar: any = {
|
||||
...baseCalendar,
|
||||
owner: {
|
||||
_id: "resource1",
|
||||
_id: 'resource1',
|
||||
resource: true,
|
||||
emails: ["resource1@example.com"],
|
||||
emails: ['resource1@example.com'],
|
||||
administrators: [
|
||||
{ id: "admin1" },
|
||||
{ id: "admin2" },
|
||||
{ id: "resource1" }, // Owner shouldn't be loaded as an admin
|
||||
],
|
||||
},
|
||||
};
|
||||
{ id: 'admin1' },
|
||||
{ id: 'admin2' },
|
||||
{ id: 'resource1' } // Owner shouldn't be loaded as an admin
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
(getUserDetails as jest.Mock).mockImplementation((id: string) => {
|
||||
if (id === "admin1") {
|
||||
;(getUserDetails as jest.Mock).mockImplementation((id: string) => {
|
||||
if (id === 'admin1') {
|
||||
return Promise.resolve({
|
||||
preferredEmail: "admin1@example.com",
|
||||
firstname: "Admin",
|
||||
lastname: "One",
|
||||
});
|
||||
preferredEmail: 'admin1@example.com',
|
||||
firstname: 'Admin',
|
||||
lastname: 'One'
|
||||
})
|
||||
}
|
||||
if (id === "admin2") {
|
||||
if (id === 'admin2') {
|
||||
return Promise.resolve({
|
||||
preferredEmail: "admin2@example.com",
|
||||
firstname: "Admin",
|
||||
lastname: "Two",
|
||||
});
|
||||
preferredEmail: 'admin2@example.com',
|
||||
firstname: 'Admin',
|
||||
lastname: 'Two'
|
||||
})
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
return Promise.resolve(null)
|
||||
})
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarAccessRights
|
||||
@@ -320,37 +318,37 @@ describe("CalendarAccessRights", () => {
|
||||
onInvitesLoaded={mockOnInvitesLoaded}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getUserDetails).toHaveBeenCalledWith("admin1");
|
||||
expect(getUserDetails).toHaveBeenCalledWith("admin2");
|
||||
});
|
||||
expect(getUserDetails).toHaveBeenCalledWith('admin1')
|
||||
expect(getUserDetails).toHaveBeenCalledWith('admin2')
|
||||
})
|
||||
|
||||
expect(getUserDetails).not.toHaveBeenCalledWith("resource1");
|
||||
expect(getUserDetails).not.toHaveBeenCalledWith('resource1')
|
||||
|
||||
expect(await screen.findByText("Admin One")).toBeInTheDocument();
|
||||
expect(screen.getByText("admin1@example.com")).toBeInTheDocument();
|
||||
expect(screen.getByText("Admin Two")).toBeInTheDocument();
|
||||
expect(screen.getByText("admin2@example.com")).toBeInTheDocument();
|
||||
expect(await screen.findByText('Admin One')).toBeInTheDocument()
|
||||
expect(screen.getByText('admin1@example.com')).toBeInTheDocument()
|
||||
expect(screen.getByText('Admin Two')).toBeInTheDocument()
|
||||
expect(screen.getByText('admin2@example.com')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getAllByText("calendarPopover.access.administrator")
|
||||
).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
screen.getAllByText('calendarPopover.access.administrator')
|
||||
).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
const userState = {
|
||||
user: {
|
||||
userData: { openpaasId: "user1", email: "user1@example.com" },
|
||||
},
|
||||
};
|
||||
userData: { openpaasId: 'user1', email: 'user1@example.com' }
|
||||
}
|
||||
}
|
||||
|
||||
describe("AccessTab – conditional rendering of CalendarAccessRights", () => {
|
||||
const noop = jest.fn();
|
||||
describe('AccessTab – conditional rendering of CalendarAccessRights', () => {
|
||||
const noop = jest.fn()
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
afterEach(() => jest.clearAllMocks())
|
||||
|
||||
it("shows CalendarAccessRights when the current user owns the calendar", () => {
|
||||
it('shows CalendarAccessRights when the current user owns the calendar', () => {
|
||||
renderWithProviders(
|
||||
<AccessTab
|
||||
calendar={baseCalendar}
|
||||
@@ -360,21 +358,21 @@ describe("AccessTab – conditional rendering of CalendarAccessRights", () => {
|
||||
/>,
|
||||
{
|
||||
...userState,
|
||||
calendars: { list: { "user1/cal1": baseCalendar } },
|
||||
calendars: { list: { 'user1/cal1': baseCalendar } }
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
expect(
|
||||
screen.getByText("calendarPopover.access.grantAccessRights")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
screen.getByText('calendarPopover.access.grantAccessRights')
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("hides CalendarAccessRights textfield for a non-owner without admin delegation", () => {
|
||||
it('hides CalendarAccessRights textfield for a non-owner without admin delegation', () => {
|
||||
const foreignCalendar: Calendar = {
|
||||
...baseCalendar,
|
||||
id: "otherUser/cal1",
|
||||
invite: [],
|
||||
};
|
||||
id: 'otherUser/cal1',
|
||||
invite: []
|
||||
}
|
||||
|
||||
renderWithProviders(
|
||||
<AccessTab
|
||||
@@ -385,29 +383,29 @@ describe("AccessTab – conditional rendering of CalendarAccessRights", () => {
|
||||
/>,
|
||||
{
|
||||
...userState,
|
||||
calendars: { list: { "otherUser/cal1": foreignCalendar } },
|
||||
calendars: { list: { 'otherUser/cal1': foreignCalendar } }
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
expect(screen.queryByText("peopleSearch.label")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('peopleSearch.label')).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByText("calendarPopover.access.accessRights")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
screen.getByText('calendarPopover.access.accessRights')
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("shows CalendarAccessRights when the user has access=5 (admin) delegation", () => {
|
||||
it('shows CalendarAccessRights when the user has access=5 (admin) delegation', () => {
|
||||
const delegatedCalendar: Calendar = {
|
||||
...baseCalendar,
|
||||
id: "otherUser/cal1",
|
||||
id: 'otherUser/cal1',
|
||||
invite: [
|
||||
{
|
||||
href: "mailto:user1@example.com",
|
||||
principal: "/principals/users/user1",
|
||||
href: 'mailto:user1@example.com',
|
||||
principal: '/principals/users/user1',
|
||||
access: 5,
|
||||
inviteStatus: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
inviteStatus: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
renderWithProviders(
|
||||
<AccessTab
|
||||
@@ -418,78 +416,78 @@ describe("AccessTab – conditional rendering of CalendarAccessRights", () => {
|
||||
/>,
|
||||
{
|
||||
...userState,
|
||||
calendars: { list: { "otherUser/cal1": delegatedCalendar } },
|
||||
calendars: { list: { 'otherUser/cal1': delegatedCalendar } }
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
expect(
|
||||
screen.getByText("calendarPopover.access.grantAccessRights")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
screen.getByText('calendarPopover.access.grantAccessRights')
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
const existingCalendar: Calendar = {
|
||||
id: "user1/cal1",
|
||||
name: "Existing Cal",
|
||||
description: "Desc",
|
||||
color: { color: "#0062FF", dark: "#FFF" },
|
||||
link: "/calendars/user/cal1",
|
||||
visibility: "public",
|
||||
id: 'user1/cal1',
|
||||
name: 'Existing Cal',
|
||||
description: 'Desc',
|
||||
color: { color: '#0062FF', dark: '#FFF' },
|
||||
link: '/calendars/user/cal1',
|
||||
visibility: 'public',
|
||||
events: {},
|
||||
invite: [],
|
||||
owner: { emails: ["user1@example.com"] },
|
||||
};
|
||||
owner: { emails: ['user1@example.com'] }
|
||||
}
|
||||
|
||||
describe("CalendarModal – updateDelegationCalendarAsync integration", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
describe('CalendarModal – updateDelegationCalendarAsync integration', () => {
|
||||
const mockOnClose = jest.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.clearAllMocks()
|
||||
jest
|
||||
.spyOn(eventThunks, "patchCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'patchCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
jest
|
||||
.spyOn(eventThunks, "patchACLCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'patchACLCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
jest
|
||||
.spyOn(delegationThunks, "updateDelegationCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
});
|
||||
.spyOn(delegationThunks, 'updateDelegationCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
})
|
||||
|
||||
it("does NOT call updateDelegationCalendarAsync when no users are added or removed", async () => {
|
||||
it('does NOT call updateDelegationCalendarAsync when no users are added or removed', async () => {
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
open={true}
|
||||
onClose={mockOnClose}
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ ...userState, calendars: { list: { "user1/cal1": existingCalendar } } }
|
||||
);
|
||||
{ ...userState, calendars: { list: { 'user1/cal1': existingCalendar } } }
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /save/i }));
|
||||
fireEvent.click(screen.getByRole('button', { name: /save/i }))
|
||||
|
||||
await waitFor(() => expect(mockOnClose).toHaveBeenCalled());
|
||||
await waitFor(() => expect(mockOnClose).toHaveBeenCalled())
|
||||
|
||||
expect(
|
||||
delegationThunks.updateDelegationCalendarAsync
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("CalendarModal – cancel button", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
describe('CalendarModal – cancel button', () => {
|
||||
const mockOnClose = jest.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.clearAllMocks()
|
||||
jest
|
||||
.spyOn(delegationThunks, "updateDelegationCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(delegationThunks, 'updateDelegationCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
jest
|
||||
.spyOn(eventThunks, "patchCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
});
|
||||
.spyOn(eventThunks, 'patchCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
})
|
||||
|
||||
it("calls onClose without saving when Cancel is clicked", async () => {
|
||||
it('calls onClose without saving when Cancel is clicked', async () => {
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
open={true}
|
||||
@@ -497,14 +495,14 @@ describe("CalendarModal – cancel button", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ ...userState }
|
||||
);
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /cancel/i }));
|
||||
fireEvent.click(screen.getByRole('button', { name: /cancel/i }))
|
||||
|
||||
await waitFor(() => expect(mockOnClose).toHaveBeenCalled());
|
||||
await waitFor(() => expect(mockOnClose).toHaveBeenCalled())
|
||||
expect(
|
||||
delegationThunks.updateDelegationCalendarAsync
|
||||
).not.toHaveBeenCalled();
|
||||
expect(eventThunks.patchCalendarAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
).not.toHaveBeenCalled()
|
||||
expect(eventThunks.patchCalendarAsync).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,141 +1,141 @@
|
||||
import CalendarPopover from "@/components/Calendar/CalendarModal";
|
||||
import { getSecretLink } from "@/features/Calendars/CalendarApi";
|
||||
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
||||
import * as eventThunks from "@/features/Calendars/services";
|
||||
import * as delegationThunks from "@/features/Calendars/services/updateDelegationCalendarAsync";
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||
import CalendarPopover from '@/components/Calendar/CalendarModal'
|
||||
import { getSecretLink } from '@/features/Calendars/CalendarApi'
|
||||
import { Calendar } from '@/features/Calendars/CalendarTypes'
|
||||
import * as eventThunks from '@/features/Calendars/services'
|
||||
import * as delegationThunks from '@/features/Calendars/services/updateDelegationCalendarAsync'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { renderWithProviders } from '../../utils/Renderwithproviders'
|
||||
|
||||
jest.mock("@/features/Calendars/CalendarApi", () => ({
|
||||
getSecretLink: jest.fn(),
|
||||
}));
|
||||
jest.mock('@/features/Calendars/CalendarApi', () => ({
|
||||
getSecretLink: jest.fn()
|
||||
}))
|
||||
|
||||
const mockThunkWithUnwrap = (resolvedValue: unknown = {}) =>
|
||||
jest.fn().mockImplementation(() => {
|
||||
const dispatchResult = Object.assign(Promise.resolve(resolvedValue), {
|
||||
unwrap: () => Promise.resolve(resolvedValue),
|
||||
});
|
||||
return jest.fn().mockReturnValue(dispatchResult);
|
||||
});
|
||||
unwrap: () => Promise.resolve(resolvedValue)
|
||||
})
|
||||
return jest.fn().mockReturnValue(dispatchResult)
|
||||
})
|
||||
|
||||
describe("CalendarPopover", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
describe('CalendarPopover', () => {
|
||||
const mockOnClose = jest.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.clearAllMocks()
|
||||
jest
|
||||
.spyOn(delegationThunks, "updateDelegationCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
});
|
||||
.spyOn(delegationThunks, 'updateDelegationCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
})
|
||||
|
||||
const renderPopover = (open = true) => {
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "aiYbWZSk2g0F+LrQeD7Dg4QcUMR8R/zTZdZBiA7N6Ro",
|
||||
openpaasId: "667037022b752d0026472254",
|
||||
},
|
||||
sub: 'test',
|
||||
email: 'test@test.com',
|
||||
sid: 'aiYbWZSk2g0F+LrQeD7Dg4QcUMR8R/zTZdZBiA7N6Ro',
|
||||
openpaasId: '667037022b752d0026472254'
|
||||
}
|
||||
},
|
||||
calendars: { list: {}, pending: true },
|
||||
};
|
||||
calendars: { list: {}, pending: true }
|
||||
}
|
||||
renderWithProviders(
|
||||
<CalendarPopover open={open} onClose={mockOnClose} />,
|
||||
preloadedState
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
it("renders popover and inputs", () => {
|
||||
renderPopover();
|
||||
it('renders popover and inputs', () => {
|
||||
renderPopover()
|
||||
|
||||
expect(screen.getByLabelText(/Name/i)).toBeInTheDocument();
|
||||
expect(screen.getByText("event.form.addDescription")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByLabelText(/Name/i)).toBeInTheDocument()
|
||||
expect(screen.getByText('event.form.addDescription')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("updates name and description fields", () => {
|
||||
renderPopover();
|
||||
it('updates name and description fields', () => {
|
||||
renderPopover()
|
||||
|
||||
const nameInput = screen.getByLabelText(/Name/i);
|
||||
fireEvent.change(nameInput, { target: { value: "My Calendar" } });
|
||||
expect(nameInput).toHaveValue("My Calendar");
|
||||
const nameInput = screen.getByLabelText(/Name/i)
|
||||
fireEvent.change(nameInput, { target: { value: 'My Calendar' } })
|
||||
expect(nameInput).toHaveValue('My Calendar')
|
||||
|
||||
fireEvent.click(screen.getByText("event.form.addDescription"));
|
||||
const descInput = screen.getByLabelText(/Description/i);
|
||||
fireEvent.change(descInput, { target: { value: "Test description" } });
|
||||
expect(descInput).toHaveValue("Test description");
|
||||
});
|
||||
fireEvent.click(screen.getByText('event.form.addDescription'))
|
||||
const descInput = screen.getByLabelText(/Description/i)
|
||||
fireEvent.change(descInput, { target: { value: 'Test description' } })
|
||||
expect(descInput).toHaveValue('Test description')
|
||||
})
|
||||
|
||||
it("dispatches createCalendar and calls onClose when Save clicked", async () => {
|
||||
it('dispatches createCalendar and calls onClose when Save clicked', async () => {
|
||||
jest
|
||||
.spyOn(eventThunks, "createCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'createCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
|
||||
renderPopover();
|
||||
renderPopover()
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/Name/i), {
|
||||
target: { value: "Test Calendar" },
|
||||
});
|
||||
fireEvent.click(screen.getByText("event.form.addDescription"));
|
||||
target: { value: 'Test Calendar' }
|
||||
})
|
||||
fireEvent.click(screen.getByText('event.form.addDescription'))
|
||||
fireEvent.change(screen.getByLabelText(/Description/i), {
|
||||
target: { value: "Test Description" },
|
||||
});
|
||||
target: { value: 'Test Description' }
|
||||
})
|
||||
|
||||
const colorButtons = screen.getAllByRole("button", {
|
||||
name: /select color/i,
|
||||
});
|
||||
fireEvent.click(colorButtons[0]);
|
||||
const colorButtons = screen.getAllByRole('button', {
|
||||
name: /select color/i
|
||||
})
|
||||
fireEvent.click(colorButtons[0])
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /Create/i }));
|
||||
fireEvent.click(screen.getByRole('button', { name: /Create/i }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(eventThunks.createCalendarAsync).toHaveBeenCalled()
|
||||
);
|
||||
)
|
||||
await waitFor(() =>
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick")
|
||||
);
|
||||
});
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, 'backdropClick')
|
||||
)
|
||||
})
|
||||
|
||||
it("calls onClose when Cancel clicked", () => {
|
||||
renderPopover();
|
||||
it('calls onClose when Cancel clicked', () => {
|
||||
renderPopover()
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /Cancel/i }));
|
||||
fireEvent.click(screen.getByRole('button', { name: /Cancel/i }))
|
||||
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, "backdropClick");
|
||||
});
|
||||
});
|
||||
expect(mockOnClose).toHaveBeenCalledWith({}, 'backdropClick')
|
||||
})
|
||||
})
|
||||
|
||||
describe("CalendarPopover (editing mode)", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
describe('CalendarPopover (editing mode)', () => {
|
||||
const mockOnClose = jest.fn()
|
||||
|
||||
const baseUser = {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "mockSid",
|
||||
openpaasId: "user1",
|
||||
},
|
||||
};
|
||||
sub: 'test',
|
||||
email: 'test@test.com',
|
||||
sid: 'mockSid',
|
||||
openpaasId: 'user1'
|
||||
}
|
||||
}
|
||||
|
||||
const existingCalendar: Calendar = {
|
||||
id: "user1/cal1",
|
||||
link: "/calendars/user/cal1",
|
||||
name: "Work Calendar",
|
||||
description: "Team meetings",
|
||||
color: { light: "#33B679" },
|
||||
owner: { firstname: "alice", emails: ["alice@example.com"] },
|
||||
visibility: "public",
|
||||
events: {},
|
||||
};
|
||||
id: 'user1/cal1',
|
||||
link: '/calendars/user/cal1',
|
||||
name: 'Work Calendar',
|
||||
description: 'Team meetings',
|
||||
color: { light: '#33B679' },
|
||||
owner: { firstname: 'alice', emails: ['alice@example.com'] },
|
||||
visibility: 'public',
|
||||
events: {}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.clearAllMocks()
|
||||
jest
|
||||
.spyOn(delegationThunks, "updateDelegationCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
});
|
||||
.spyOn(delegationThunks, 'updateDelegationCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
})
|
||||
|
||||
it("prefills fields when calendar prop is given", () => {
|
||||
it('prefills fields when calendar prop is given', () => {
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
open={true}
|
||||
@@ -143,34 +143,34 @@ describe("CalendarPopover (editing mode)", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ user: baseUser }
|
||||
);
|
||||
)
|
||||
|
||||
expect(screen.getByLabelText(/Name/i)).toHaveValue("Work Calendar");
|
||||
expect(screen.getByLabelText(/Description/i)).toHaveValue("Team meetings");
|
||||
});
|
||||
expect(screen.getByLabelText(/Name/i)).toHaveValue('Work Calendar')
|
||||
expect(screen.getByLabelText(/Description/i)).toHaveValue('Team meetings')
|
||||
})
|
||||
|
||||
test("Save button is disabled when name is empty or whitespace only", () => {
|
||||
test('Save button is disabled when name is empty or whitespace only', () => {
|
||||
renderWithProviders(<CalendarPopover open={true} onClose={jest.fn()} />, {
|
||||
user: baseUser,
|
||||
});
|
||||
user: baseUser
|
||||
})
|
||||
|
||||
const saveButton = screen.getByRole("button", { name: /create/i });
|
||||
expect(saveButton).toBeDisabled();
|
||||
const saveButton = screen.getByRole('button', { name: /create/i })
|
||||
expect(saveButton).toBeDisabled()
|
||||
// only spaces
|
||||
const nameInput = screen.getByLabelText(/name/i);
|
||||
fireEvent.change(nameInput, { target: { value: " " } });
|
||||
const nameInput = screen.getByLabelText(/name/i)
|
||||
fireEvent.change(nameInput, { target: { value: ' ' } })
|
||||
|
||||
expect(saveButton).toBeDisabled();
|
||||
expect(saveButton).toBeDisabled()
|
||||
|
||||
// valid name
|
||||
fireEvent.change(nameInput, { target: { value: "Work Calendar" } });
|
||||
expect(saveButton).toBeEnabled();
|
||||
});
|
||||
fireEvent.change(nameInput, { target: { value: 'Work Calendar' } })
|
||||
expect(saveButton).toBeEnabled()
|
||||
})
|
||||
|
||||
it("allows modifying and saving existing calendar", async () => {
|
||||
it('allows modifying and saving existing calendar', async () => {
|
||||
jest
|
||||
.spyOn(eventThunks, "patchCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'patchCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
@@ -179,87 +179,87 @@ describe("CalendarPopover (editing mode)", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ user: baseUser }
|
||||
);
|
||||
)
|
||||
|
||||
// Change name
|
||||
fireEvent.change(screen.getByLabelText(/Name/i), {
|
||||
target: { value: "Updated Calendar" },
|
||||
});
|
||||
target: { value: 'Updated Calendar' }
|
||||
})
|
||||
|
||||
// Save
|
||||
fireEvent.click(screen.getByRole("button", { name: "actions.save" }));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'actions.save' }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(eventThunks.patchCalendarAsync).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
calId: "user1/cal1",
|
||||
calLink: "/calendars/user/cal1",
|
||||
calId: 'user1/cal1',
|
||||
calLink: '/calendars/user/cal1',
|
||||
patch: {
|
||||
color: { light: "#33B679" },
|
||||
desc: "Team meetings",
|
||||
name: "Updated Calendar",
|
||||
},
|
||||
color: { light: '#33B679' },
|
||||
desc: 'Team meetings',
|
||||
name: 'Updated Calendar'
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => expect(mockOnClose).toHaveBeenCalled());
|
||||
});
|
||||
});
|
||||
)
|
||||
await waitFor(() => expect(mockOnClose).toHaveBeenCalled())
|
||||
})
|
||||
})
|
||||
|
||||
describe("CalendarPopover - Tabs Scenarios", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
describe('CalendarPopover - Tabs Scenarios', () => {
|
||||
const mockOnClose = jest.fn()
|
||||
const baseUser = {
|
||||
userData: {
|
||||
openpaasId: "user1",
|
||||
},
|
||||
};
|
||||
openpaasId: 'user1'
|
||||
}
|
||||
}
|
||||
|
||||
const writeText = jest.fn();
|
||||
const writeText = jest.fn()
|
||||
|
||||
Object.assign(navigator, {
|
||||
clipboard: {
|
||||
writeText,
|
||||
},
|
||||
});
|
||||
writeText
|
||||
}
|
||||
})
|
||||
|
||||
const existingCalendar: Calendar = {
|
||||
id: "user1/cal1",
|
||||
link: "/calendars/user1/cal1.json",
|
||||
name: "Work Calendar",
|
||||
description: "Team meetings",
|
||||
color: { light: "#33B679" },
|
||||
owner: { firstname: "alice", emails: ["alice@example.com"] },
|
||||
visibility: "public",
|
||||
events: {},
|
||||
};
|
||||
id: 'user1/cal1',
|
||||
link: '/calendars/user1/cal1.json',
|
||||
name: 'Work Calendar',
|
||||
description: 'Team meetings',
|
||||
color: { light: '#33B679' },
|
||||
owner: { firstname: 'alice', emails: ['alice@example.com'] },
|
||||
visibility: 'public',
|
||||
events: {}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.clearAllMocks()
|
||||
jest
|
||||
.spyOn(delegationThunks, "updateDelegationCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
});
|
||||
.spyOn(delegationThunks, 'updateDelegationCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
})
|
||||
|
||||
it("resets state after closing and reopening", () => {
|
||||
it('resets state after closing and reopening', () => {
|
||||
const { rerender } = renderWithProviders(
|
||||
<CalendarPopover open={true} onClose={mockOnClose} />,
|
||||
{ user: baseUser }
|
||||
);
|
||||
)
|
||||
|
||||
// Enter some data
|
||||
fireEvent.change(screen.getByLabelText(/Name/i), {
|
||||
target: { value: "Temp Calendar" },
|
||||
});
|
||||
fireEvent.click(screen.getByRole("button", { name: /Cancel/i }));
|
||||
target: { value: 'Temp Calendar' }
|
||||
})
|
||||
fireEvent.click(screen.getByRole('button', { name: /Cancel/i }))
|
||||
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
expect(mockOnClose).toHaveBeenCalled()
|
||||
|
||||
// Reopen: state should be reset
|
||||
rerender(<CalendarPopover open={true} onClose={mockOnClose} />);
|
||||
expect(screen.getByLabelText(/Name/i)).toHaveValue("");
|
||||
});
|
||||
rerender(<CalendarPopover open={true} onClose={mockOnClose} />)
|
||||
expect(screen.getByLabelText(/Name/i)).toHaveValue('')
|
||||
})
|
||||
|
||||
it("shows Access tab only when editing an existing calendar", () => {
|
||||
it('shows Access tab only when editing an existing calendar', () => {
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
open={true}
|
||||
@@ -267,29 +267,29 @@ describe("CalendarPopover - Tabs Scenarios", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ user: baseUser }
|
||||
);
|
||||
)
|
||||
|
||||
expect(screen.getByRole("tab", { name: /Access/i })).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByRole('tab', { name: /Access/i })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("does not show Access tab when creating new calendar", () => {
|
||||
it('does not show Access tab when creating new calendar', () => {
|
||||
renderWithProviders(<CalendarPopover open={true} onClose={mockOnClose} />, {
|
||||
user: baseUser,
|
||||
});
|
||||
user: baseUser
|
||||
})
|
||||
|
||||
expect(
|
||||
screen.queryByRole("tab", { name: /Access/i })
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
screen.queryByRole('tab', { name: /Access/i })
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("patches ACL when visibility changes", async () => {
|
||||
it('patches ACL when visibility changes', async () => {
|
||||
jest
|
||||
.spyOn(eventThunks, "patchCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'patchCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
|
||||
jest
|
||||
.spyOn(eventThunks, "patchACLCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'patchACLCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
@@ -298,42 +298,42 @@ describe("CalendarPopover - Tabs Scenarios", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ user: baseUser }
|
||||
);
|
||||
)
|
||||
|
||||
// By default: "All" (public) is selected
|
||||
const publicButton = screen.getByRole("button", { name: /All/i });
|
||||
const privateButton = screen.getByRole("button", { name: /You/i });
|
||||
const publicButton = screen.getByRole('button', { name: /All/i })
|
||||
const privateButton = screen.getByRole('button', { name: /You/i })
|
||||
|
||||
expect(publicButton).toHaveAttribute("aria-pressed", "true");
|
||||
expect(privateButton).toHaveAttribute("aria-pressed", "false");
|
||||
expect(publicButton).toHaveAttribute('aria-pressed', 'true')
|
||||
expect(privateButton).toHaveAttribute('aria-pressed', 'false')
|
||||
|
||||
// Change to private
|
||||
fireEvent.click(privateButton);
|
||||
fireEvent.click(privateButton)
|
||||
|
||||
expect(privateButton).toHaveAttribute("aria-pressed", "true");
|
||||
expect(publicButton).toHaveAttribute("aria-pressed", "false");
|
||||
expect(privateButton).toHaveAttribute('aria-pressed', 'true')
|
||||
expect(publicButton).toHaveAttribute('aria-pressed', 'false')
|
||||
|
||||
// Save
|
||||
fireEvent.click(screen.getByRole("button", { name: "actions.save" }));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'actions.save' }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(eventThunks.patchACLCalendarAsync).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
calId: "user1/cal1",
|
||||
request: "",
|
||||
calId: 'user1/cal1',
|
||||
request: ''
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
it("copies CalDAV link from Access tab", async () => {
|
||||
window.DAV_BASE_URL = "https://cal.example.org";
|
||||
it('copies CalDAV link from Access tab', async () => {
|
||||
window.DAV_BASE_URL = 'https://cal.example.org'
|
||||
Object.assign(navigator, {
|
||||
clipboard: { writeText: jest.fn() },
|
||||
});
|
||||
(getSecretLink as jest.Mock).mockResolvedValue({
|
||||
secretLink: "https://example.org/secret/initial",
|
||||
});
|
||||
clipboard: { writeText: jest.fn() }
|
||||
})
|
||||
;(getSecretLink as jest.Mock).mockResolvedValue({
|
||||
secretLink: 'https://example.org/secret/initial'
|
||||
})
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
@@ -342,79 +342,79 @@ describe("CalendarPopover - Tabs Scenarios", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ user: baseUser }
|
||||
);
|
||||
)
|
||||
|
||||
// Switch to Access tab
|
||||
fireEvent.click(screen.getByRole("tab", { name: /Access/i }));
|
||||
fireEvent.click(screen.getByRole('tab', { name: /Access/i }))
|
||||
|
||||
// Expect text field with caldav link
|
||||
const input = screen.getByLabelText("calendar.caldav_access");
|
||||
expect(input).toHaveValue("https://cal.example.org/calendars/user1/cal1");
|
||||
const input = screen.getByLabelText('calendar.caldav_access')
|
||||
expect(input).toHaveValue('https://cal.example.org/calendars/user1/cal1')
|
||||
|
||||
// Click copy button (find button containing ContentCopyIcon)
|
||||
const copyIcon = screen.getAllByTestId("ContentCopyIcon")[0];
|
||||
const copyButton = copyIcon.closest("button");
|
||||
const copyIcon = screen.getAllByTestId('ContentCopyIcon')[0]
|
||||
const copyButton = copyIcon.closest('button')
|
||||
if (copyButton) {
|
||||
fireEvent.click(copyButton);
|
||||
fireEvent.click(copyButton)
|
||||
}
|
||||
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
|
||||
"https://cal.example.org/calendars/user1/cal1"
|
||||
);
|
||||
'https://cal.example.org/calendars/user1/cal1'
|
||||
)
|
||||
|
||||
// Snackbar should appear
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText("common.link_copied")).toBeInTheDocument()
|
||||
);
|
||||
});
|
||||
expect(screen.getByText('common.link_copied')).toBeInTheDocument()
|
||||
)
|
||||
})
|
||||
|
||||
describe("Import flow", () => {
|
||||
const file = new File(["test"], "events.ics", { type: "text/calendar" });
|
||||
describe('Import flow', () => {
|
||||
const file = new File(['test'], 'events.ics', { type: 'text/calendar' })
|
||||
|
||||
it("creates a new calendar and imports events when Import with 'new' target", async () => {
|
||||
jest
|
||||
.spyOn(eventThunks, "createCalendarAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'createCalendarAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
jest
|
||||
.spyOn(eventThunks, "importEventFromFileAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'importEventFromFileAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarPopover open={true} onClose={mockOnClose} />,
|
||||
{
|
||||
user: baseUser,
|
||||
user: baseUser
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
// Switch to Import tab
|
||||
fireEvent.click(screen.getByRole("tab", { name: /Import/i }));
|
||||
fireEvent.click(screen.getByRole('tab', { name: /Import/i }))
|
||||
|
||||
// Provide new calendar params
|
||||
fireEvent.change(screen.getByLabelText(/Name/i), {
|
||||
target: { value: "Imported Calendar" },
|
||||
});
|
||||
const fileInput = screen.getByLabelText("common.select_file");
|
||||
fireEvent.change(fileInput, { target: { files: [file] } });
|
||||
target: { value: 'Imported Calendar' }
|
||||
})
|
||||
const fileInput = screen.getByLabelText('common.select_file')
|
||||
fireEvent.change(fileInput, { target: { files: [file] } })
|
||||
|
||||
// Click Import
|
||||
fireEvent.click(screen.getByRole("button", { name: "actions.import" }));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'actions.import' }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(eventThunks.createCalendarAsync).toHaveBeenCalled()
|
||||
);
|
||||
)
|
||||
await waitFor(() =>
|
||||
expect(eventThunks.importEventFromFileAsync).toHaveBeenCalled()
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
it("imports into an existing calendar when target is set", async () => {
|
||||
it('imports into an existing calendar when target is set', async () => {
|
||||
jest
|
||||
.spyOn(eventThunks, "importEventFromFileAsync")
|
||||
.mockImplementation(mockThunkWithUnwrap());
|
||||
.spyOn(eventThunks, 'importEventFromFileAsync')
|
||||
.mockImplementation(mockThunkWithUnwrap())
|
||||
|
||||
const calendars = {
|
||||
"user1/cal1": existingCalendar,
|
||||
};
|
||||
'user1/cal1': existingCalendar
|
||||
}
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
@@ -423,51 +423,50 @@ describe("CalendarPopover - Tabs Scenarios", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ user: baseUser, calendars: { list: calendars } }
|
||||
);
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole("tab", { name: /Import/i }));
|
||||
const fileInput = screen.getByLabelText("common.select_file");
|
||||
fireEvent.change(fileInput, { target: { files: [file] } });
|
||||
fireEvent.click(screen.getByRole('tab', { name: /Import/i }))
|
||||
const fileInput = screen.getByLabelText('common.select_file')
|
||||
fireEvent.change(fileInput, { target: { files: [file] } })
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "actions.import" }));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'actions.import' }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(eventThunks.importEventFromFileAsync).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
calLink: "/calendars/user1/cal1.json",
|
||||
file,
|
||||
calLink: '/calendars/user1/cal1.json',
|
||||
file
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
it("disables Import button until a file is uploaded", () => {
|
||||
it('disables Import button until a file is uploaded', () => {
|
||||
renderWithProviders(
|
||||
<CalendarPopover open={true} onClose={mockOnClose} />,
|
||||
{
|
||||
user: baseUser,
|
||||
user: baseUser
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole("tab", { name: /Import/i }));
|
||||
fireEvent.click(screen.getByRole('tab', { name: /Import/i }))
|
||||
|
||||
const importButton = screen.getByRole("button", {
|
||||
name: "actions.import",
|
||||
});
|
||||
expect(importButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
const importButton = screen.getByRole('button', {
|
||||
name: 'actions.import'
|
||||
})
|
||||
expect(importButton).toBeDisabled()
|
||||
})
|
||||
})
|
||||
|
||||
it("fetches and resets the secret link", async () => {
|
||||
window.DAV_BASE_URL = "https://cal.example.org";
|
||||
|
||||
(getSecretLink as jest.Mock)
|
||||
it('fetches and resets the secret link', async () => {
|
||||
window.DAV_BASE_URL = 'https://cal.example.org'
|
||||
;(getSecretLink as jest.Mock)
|
||||
.mockResolvedValueOnce({
|
||||
secretLink: "https://example.org/secret/initial",
|
||||
secretLink: 'https://example.org/secret/initial'
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
secretLink: "https://example.org/secret/new",
|
||||
});
|
||||
secretLink: 'https://example.org/secret/new'
|
||||
})
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarPopover
|
||||
@@ -476,27 +475,27 @@ describe("CalendarPopover - Tabs Scenarios", () => {
|
||||
calendar={existingCalendar}
|
||||
/>,
|
||||
{ user: baseUser }
|
||||
);
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole("tab", { name: /Access/i }));
|
||||
fireEvent.click(screen.getByRole('tab', { name: /Access/i }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.getByDisplayValue("https://example.org/secret/initial")
|
||||
screen.getByDisplayValue('https://example.org/secret/initial')
|
||||
).toBeInTheDocument()
|
||||
);
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /reset/i }));
|
||||
fireEvent.click(screen.getByRole('button', { name: /reset/i }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.getByDisplayValue("https://example.org/secret/new")
|
||||
screen.getByDisplayValue('https://example.org/secret/new')
|
||||
).toBeInTheDocument()
|
||||
);
|
||||
)
|
||||
|
||||
expect(getSecretLink).toHaveBeenCalledWith(
|
||||
existingCalendar.link.replace(".json", ""),
|
||||
existingCalendar.link.replace('.json', ''),
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as calAPI from "@/features/Calendars/CalendarApi";
|
||||
import * as calAPI from '@/features/Calendars/CalendarApi'
|
||||
import reducer, {
|
||||
addEvent,
|
||||
createCalendar,
|
||||
removeEvent,
|
||||
removeTempCal,
|
||||
updateEventLocal,
|
||||
} from "@/features/Calendars/CalendarSlice";
|
||||
import { Calendar } from "@/features/Calendars/CalendarTypes";
|
||||
updateEventLocal
|
||||
} from '@/features/Calendars/CalendarSlice'
|
||||
import { Calendar } from '@/features/Calendars/CalendarTypes'
|
||||
import {
|
||||
addSharedCalendarAsync,
|
||||
createCalendarAsync,
|
||||
@@ -14,523 +14,523 @@ import {
|
||||
getCalendarsListAsync,
|
||||
getEventAsync,
|
||||
getTempCalendarsListAsync,
|
||||
patchACLCalendarAsync,
|
||||
} from "@/features/Calendars/services";
|
||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||
import * as userAPI from "@/features/User/userAPI";
|
||||
import userReducer, { setUserData } from "@/features/User/userSlice";
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
patchACLCalendarAsync
|
||||
} from '@/features/Calendars/services'
|
||||
import { CalendarEvent } from '@/features/Events/EventsTypes'
|
||||
import * as userAPI from '@/features/User/userAPI'
|
||||
import userReducer, { setUserData } from '@/features/User/userSlice'
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
|
||||
jest.mock("@/features/Calendars/CalendarApi");
|
||||
jest.mock("@/features/User/userAPI");
|
||||
jest.mock("@/features/Events/EventApi");
|
||||
jest.mock("@/features/Events/utils");
|
||||
jest.mock("@/utils/apiUtils");
|
||||
jest.mock('@/features/Calendars/CalendarApi')
|
||||
jest.mock('@/features/User/userAPI')
|
||||
jest.mock('@/features/Events/EventApi')
|
||||
jest.mock('@/features/Events/utils')
|
||||
jest.mock('@/utils/apiUtils')
|
||||
|
||||
describe("CalendarSlice", () => {
|
||||
describe('CalendarSlice', () => {
|
||||
const initialState = {
|
||||
list: {},
|
||||
templist: {},
|
||||
pending: false,
|
||||
error: null,
|
||||
};
|
||||
error: null
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
|
||||
describe("reducers", () => {
|
||||
it("createCalendar adds new calendar", () => {
|
||||
describe('reducers', () => {
|
||||
it('createCalendar adds new calendar', () => {
|
||||
const action = createCalendar({
|
||||
name: "Test Cal",
|
||||
color: "#ff0000",
|
||||
description: "desc",
|
||||
});
|
||||
const state = reducer(initialState, action);
|
||||
const values = Object.values(state.list);
|
||||
expect(values[0].name).toBe("Test Cal");
|
||||
expect(values[0].color).toBe("#ff0000");
|
||||
});
|
||||
name: 'Test Cal',
|
||||
color: '#ff0000',
|
||||
description: 'desc'
|
||||
})
|
||||
const state = reducer(initialState, action)
|
||||
const values = Object.values(state.list)
|
||||
expect(values[0].name).toBe('Test Cal')
|
||||
expect(values[0].color).toBe('#ff0000')
|
||||
})
|
||||
|
||||
it("addEvent adds event to calendar", () => {
|
||||
const calId = "user/cal";
|
||||
const event = { uid: "event1", title: "My Event" } as any;
|
||||
it('addEvent adds event to calendar', () => {
|
||||
const calId = 'user/cal'
|
||||
const event = { uid: 'event1', title: 'My Event' } as any
|
||||
const stateWithCal = {
|
||||
...initialState,
|
||||
list: { [calId]: { id: calId, events: {} } as any },
|
||||
};
|
||||
list: { [calId]: { id: calId, events: {} } as any }
|
||||
}
|
||||
const state = reducer(
|
||||
stateWithCal,
|
||||
addEvent({ calendarUid: calId, event })
|
||||
);
|
||||
expect(state.list[calId].events["event1"]).toEqual(
|
||||
expect.objectContaining({ uid: "event1" })
|
||||
);
|
||||
});
|
||||
)
|
||||
expect(state.list[calId].events['event1']).toEqual(
|
||||
expect.objectContaining({ uid: 'event1' })
|
||||
)
|
||||
})
|
||||
|
||||
it("removeEvent deletes event", () => {
|
||||
const calId = "user/cal";
|
||||
it('removeEvent deletes event', () => {
|
||||
const calId = 'user/cal'
|
||||
const stateWithEvent = {
|
||||
...initialState,
|
||||
list: {
|
||||
[calId]: {
|
||||
id: calId,
|
||||
events: { e1: { uid: "e1" } },
|
||||
} as unknown as Calendar,
|
||||
},
|
||||
};
|
||||
events: { e1: { uid: 'e1' } }
|
||||
} as unknown as Calendar
|
||||
}
|
||||
}
|
||||
const state = reducer(
|
||||
stateWithEvent,
|
||||
removeEvent({ calendarUid: calId, eventUid: "e1" })
|
||||
);
|
||||
expect(state.list[calId].events).toEqual({});
|
||||
});
|
||||
removeEvent({ calendarUid: calId, eventUid: 'e1' })
|
||||
)
|
||||
expect(state.list[calId].events).toEqual({})
|
||||
})
|
||||
|
||||
it("updateEventLocal updates an event", () => {
|
||||
const calId = "user/cal";
|
||||
it('updateEventLocal updates an event', () => {
|
||||
const calId = 'user/cal'
|
||||
const stateWithEvent = {
|
||||
...initialState,
|
||||
list: {
|
||||
[calId]: {
|
||||
id: calId,
|
||||
events: { e1: { uid: "e1", title: "Old" } },
|
||||
} as unknown as Calendar,
|
||||
},
|
||||
};
|
||||
events: { e1: { uid: 'e1', title: 'Old' } }
|
||||
} as unknown as Calendar
|
||||
}
|
||||
}
|
||||
const state = reducer(
|
||||
stateWithEvent,
|
||||
updateEventLocal({ calId, event: { uid: "e1", title: "New" } as any })
|
||||
);
|
||||
expect(state.list[calId].events.e1.title).toBe("New");
|
||||
});
|
||||
updateEventLocal({ calId, event: { uid: 'e1', title: 'New' } as any })
|
||||
)
|
||||
expect(state.list[calId].events.e1.title).toBe('New')
|
||||
})
|
||||
|
||||
it("removeTempCal deletes temp calendar", () => {
|
||||
it('removeTempCal deletes temp calendar', () => {
|
||||
const stateWithTemp = {
|
||||
...initialState,
|
||||
templist: { temp1: { id: "temp1" } as any },
|
||||
};
|
||||
const state = reducer(stateWithTemp, removeTempCal("temp1"));
|
||||
expect(state.templist).toEqual({});
|
||||
});
|
||||
});
|
||||
templist: { temp1: { id: 'temp1' } as any }
|
||||
}
|
||||
const state = reducer(stateWithTemp, removeTempCal('temp1'))
|
||||
expect(state.templist).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
describe("extraReducers (thunks)", () => {
|
||||
describe('extraReducers (thunks)', () => {
|
||||
const storeFactory = () =>
|
||||
configureStore({
|
||||
reducer: { calendars: reducer, user: userReducer },
|
||||
});
|
||||
reducer: { calendars: reducer, user: userReducer }
|
||||
})
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
it("getCalendarsListAsync.fulfilled replaces list", async () => {
|
||||
(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: "u1" });
|
||||
(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { "dav:calendar": [] },
|
||||
});
|
||||
(userAPI.getUserDetails as jest.Mock).mockResolvedValue({
|
||||
firstname: "Alice",
|
||||
lastname: "Smith",
|
||||
emails: ["a@b.com"],
|
||||
});
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
it('getCalendarsListAsync.fulfilled replaces list', async () => {
|
||||
;(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: 'u1' })
|
||||
;(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { 'dav:calendar': [] }
|
||||
})
|
||||
;(userAPI.getUserDetails as jest.Mock).mockResolvedValue({
|
||||
firstname: 'Alice',
|
||||
lastname: 'Smith',
|
||||
emails: ['a@b.com']
|
||||
})
|
||||
|
||||
const store = storeFactory();
|
||||
await store.dispatch(getCalendarsListAsync() as any);
|
||||
const state = store.getState().calendars;
|
||||
expect(state.list).toEqual({});
|
||||
});
|
||||
const store = storeFactory()
|
||||
await store.dispatch(getCalendarsListAsync() as any)
|
||||
const state = store.getState().calendars
|
||||
expect(state.list).toEqual({})
|
||||
})
|
||||
|
||||
it("getCalendarsListAsync loads user details in parallel for multiple owners", async () => {
|
||||
it('getCalendarsListAsync loads user details in parallel for multiple owners', async () => {
|
||||
const mockCalendars = [
|
||||
{
|
||||
_links: { self: { href: "/calendars/u1/cal1.json" } },
|
||||
"dav:name": "Calendar 1",
|
||||
"apple:color": "#FF0000",
|
||||
acl: [],
|
||||
_links: { self: { href: '/calendars/u1/cal1.json' } },
|
||||
'dav:name': 'Calendar 1',
|
||||
'apple:color': '#FF0000',
|
||||
acl: []
|
||||
},
|
||||
{
|
||||
_links: { self: { href: "/calendars/u2/cal2.json" } },
|
||||
"dav:name": "Calendar 2",
|
||||
"apple:color": "#00FF00",
|
||||
acl: [],
|
||||
_links: { self: { href: '/calendars/u2/cal2.json' } },
|
||||
'dav:name': 'Calendar 2',
|
||||
'apple:color': '#00FF00',
|
||||
acl: []
|
||||
},
|
||||
{
|
||||
_links: { self: { href: "/calendars/u3/cal3.json" } },
|
||||
"dav:name": "Calendar 3",
|
||||
"apple:color": "#0000FF",
|
||||
acl: [],
|
||||
},
|
||||
];
|
||||
_links: { self: { href: '/calendars/u3/cal3.json' } },
|
||||
'dav:name': 'Calendar 3',
|
||||
'apple:color': '#0000FF',
|
||||
acl: []
|
||||
}
|
||||
]
|
||||
|
||||
(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: "u1" });
|
||||
(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { "dav:calendar": mockCalendars },
|
||||
});
|
||||
;(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: 'u1' })
|
||||
;(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { 'dav:calendar': mockCalendars }
|
||||
})
|
||||
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock;
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock
|
||||
getUserDetailsMock
|
||||
.mockResolvedValueOnce({
|
||||
firstname: "Alice",
|
||||
lastname: "Smith",
|
||||
emails: ["alice@example.com"],
|
||||
firstname: 'Alice',
|
||||
lastname: 'Smith',
|
||||
emails: ['alice@example.com']
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
firstname: "Bob",
|
||||
lastname: "Jones",
|
||||
emails: ["bob@example.com"],
|
||||
firstname: 'Bob',
|
||||
lastname: 'Jones',
|
||||
emails: ['bob@example.com']
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
firstname: "Charlie",
|
||||
lastname: "Brown",
|
||||
emails: ["charlie@example.com"],
|
||||
});
|
||||
firstname: 'Charlie',
|
||||
lastname: 'Brown',
|
||||
emails: ['charlie@example.com']
|
||||
})
|
||||
|
||||
const store = storeFactory();
|
||||
await store.dispatch(getCalendarsListAsync() as any);
|
||||
const store = storeFactory()
|
||||
await store.dispatch(getCalendarsListAsync() as any)
|
||||
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(3);
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith("u1");
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith("u2");
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith("u3");
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(3)
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith('u1')
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith('u2')
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith('u3')
|
||||
|
||||
const state = store.getState().calendars;
|
||||
expect(state.list["u1/cal1"].owner.firstname).toContain("Alice");
|
||||
expect(state.list["u2/cal2"].owner.firstname).toContain("Bob");
|
||||
expect(state.list["u3/cal3"].owner.firstname).toContain("Charlie");
|
||||
});
|
||||
const state = store.getState().calendars
|
||||
expect(state.list['u1/cal1'].owner.firstname).toContain('Alice')
|
||||
expect(state.list['u2/cal2'].owner.firstname).toContain('Bob')
|
||||
expect(state.list['u3/cal3'].owner.firstname).toContain('Charlie')
|
||||
})
|
||||
|
||||
it("getCalendarsListAsync deduplicates getUserDetails calls for same ownerId", async () => {
|
||||
it('getCalendarsListAsync deduplicates getUserDetails calls for same ownerId', async () => {
|
||||
const mockCalendars = [
|
||||
{
|
||||
_links: { self: { href: "/calendars/u1/cal1.json" } },
|
||||
"dav:name": "Calendar 1",
|
||||
"apple:color": "#FF0000",
|
||||
acl: [],
|
||||
_links: { self: { href: '/calendars/u1/cal1.json' } },
|
||||
'dav:name': 'Calendar 1',
|
||||
'apple:color': '#FF0000',
|
||||
acl: []
|
||||
},
|
||||
{
|
||||
_links: { self: { href: "/calendars/u1/cal2.json" } },
|
||||
"dav:name": "Calendar 2",
|
||||
"apple:color": "#00FF00",
|
||||
acl: [],
|
||||
_links: { self: { href: '/calendars/u1/cal2.json' } },
|
||||
'dav:name': 'Calendar 2',
|
||||
'apple:color': '#00FF00',
|
||||
acl: []
|
||||
},
|
||||
{
|
||||
_links: { self: { href: "/calendars/u1/cal3.json" } },
|
||||
"dav:name": "Calendar 3",
|
||||
"apple:color": "#0000FF",
|
||||
acl: [],
|
||||
},
|
||||
];
|
||||
_links: { self: { href: '/calendars/u1/cal3.json' } },
|
||||
'dav:name': 'Calendar 3',
|
||||
'apple:color': '#0000FF',
|
||||
acl: []
|
||||
}
|
||||
]
|
||||
|
||||
(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: "u1" });
|
||||
(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { "dav:calendar": mockCalendars },
|
||||
});
|
||||
;(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: 'u1' })
|
||||
;(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { 'dav:calendar': mockCalendars }
|
||||
})
|
||||
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock;
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock
|
||||
getUserDetailsMock.mockResolvedValue({
|
||||
firstname: "Alice",
|
||||
lastname: "Smith",
|
||||
emails: ["alice@example.com"],
|
||||
});
|
||||
firstname: 'Alice',
|
||||
lastname: 'Smith',
|
||||
emails: ['alice@example.com']
|
||||
})
|
||||
|
||||
const store = storeFactory();
|
||||
await store.dispatch(getCalendarsListAsync() as any);
|
||||
const store = storeFactory()
|
||||
await store.dispatch(getCalendarsListAsync() as any)
|
||||
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(1);
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith("u1");
|
||||
});
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(1)
|
||||
expect(getUserDetailsMock).toHaveBeenCalledWith('u1')
|
||||
})
|
||||
|
||||
it("getCalendarsListAsync processes owners in batches of 20", async () => {
|
||||
it('getCalendarsListAsync processes owners in batches of 20', async () => {
|
||||
const mockCalendars = Array.from({ length: 45 }, (_, i) => ({
|
||||
_links: { self: { href: `/calendars/u${i + 1}/cal${i + 1}.json` } },
|
||||
"dav:name": `Calendar ${i + 1}`,
|
||||
"apple:color": "#FF0000",
|
||||
acl: [],
|
||||
}));
|
||||
'dav:name': `Calendar ${i + 1}`,
|
||||
'apple:color': '#FF0000',
|
||||
acl: []
|
||||
}))
|
||||
|
||||
(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: "u1" });
|
||||
(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { "dav:calendar": mockCalendars },
|
||||
});
|
||||
;(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: 'u1' })
|
||||
;(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { 'dav:calendar': mockCalendars }
|
||||
})
|
||||
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock;
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock
|
||||
getUserDetailsMock.mockImplementation((ownerId: string) =>
|
||||
Promise.resolve({
|
||||
firstname: "User",
|
||||
firstname: 'User',
|
||||
lastname: ownerId,
|
||||
emails: [`${ownerId}@example.com`],
|
||||
emails: [`${ownerId}@example.com`]
|
||||
})
|
||||
);
|
||||
)
|
||||
|
||||
const store = storeFactory();
|
||||
await store.dispatch(getCalendarsListAsync() as any);
|
||||
const store = storeFactory()
|
||||
await store.dispatch(getCalendarsListAsync() as any)
|
||||
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(45);
|
||||
const state = store.getState().calendars;
|
||||
expect(Object.keys(state.list)).toHaveLength(45);
|
||||
});
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(45)
|
||||
const state = store.getState().calendars
|
||||
expect(Object.keys(state.list)).toHaveLength(45)
|
||||
})
|
||||
|
||||
it("getCalendarsListAsync doesnt call getUserDetails if userdata exist in store", async () => {
|
||||
it('getCalendarsListAsync doesnt call getUserDetails if userdata exist in store', async () => {
|
||||
const existingCalendars = {
|
||||
"u1/cal1": {
|
||||
id: "u1/cal1",
|
||||
name: "Existing Calendar",
|
||||
events: {},
|
||||
} as Calendar,
|
||||
};
|
||||
'u1/cal1': {
|
||||
id: 'u1/cal1',
|
||||
name: 'Existing Calendar',
|
||||
events: {}
|
||||
} as Calendar
|
||||
}
|
||||
|
||||
const store = storeFactory();
|
||||
const store = storeFactory()
|
||||
store.dispatch({
|
||||
type: "calendars/getCalendars/fulfilled",
|
||||
payload: { importedCalendars: existingCalendars, errors: "" },
|
||||
});
|
||||
store.dispatch(setUserData({ openpaasId: "bla" }));
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock;
|
||||
type: 'calendars/getCalendars/fulfilled',
|
||||
payload: { importedCalendars: existingCalendars, errors: '' }
|
||||
})
|
||||
store.dispatch(setUserData({ openpaasId: 'bla' }))
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock
|
||||
|
||||
await store.dispatch(getCalendarsListAsync() as any);
|
||||
await store.dispatch(getCalendarsListAsync() as any)
|
||||
|
||||
expect(getUserDetailsMock).not.toHaveBeenCalled();
|
||||
expect(getUserDetailsMock).not.toHaveBeenCalled()
|
||||
|
||||
const state = store.getState().calendars;
|
||||
expect(state.list).toEqual(existingCalendars);
|
||||
});
|
||||
const state = store.getState().calendars
|
||||
expect(state.list).toEqual(existingCalendars)
|
||||
})
|
||||
|
||||
it("getCalendarsListAsync handles errors in getUserDetails gracefully", async () => {
|
||||
it('getCalendarsListAsync handles errors in getUserDetails gracefully', async () => {
|
||||
const mockCalendars = [
|
||||
{
|
||||
_links: { self: { href: "/calendars/u1/cal1.json" } },
|
||||
"dav:name": "Calendar 1",
|
||||
"apple:color": "#FF0000",
|
||||
acl: [],
|
||||
_links: { self: { href: '/calendars/u1/cal1.json' } },
|
||||
'dav:name': 'Calendar 1',
|
||||
'apple:color': '#FF0000',
|
||||
acl: []
|
||||
},
|
||||
{
|
||||
_links: { self: { href: "/calendars/u2/cal2.json" } },
|
||||
"dav:name": "Calendar 2",
|
||||
"apple:color": "#00FF00",
|
||||
acl: [],
|
||||
},
|
||||
];
|
||||
_links: { self: { href: '/calendars/u2/cal2.json' } },
|
||||
'dav:name': 'Calendar 2',
|
||||
'apple:color': '#00FF00',
|
||||
acl: []
|
||||
}
|
||||
]
|
||||
|
||||
(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: "u1" });
|
||||
(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { "dav:calendar": mockCalendars },
|
||||
});
|
||||
;(userAPI.getOpenPaasUser as jest.Mock).mockResolvedValue({ id: 'u1' })
|
||||
;(calAPI.getCalendars as jest.Mock).mockResolvedValue({
|
||||
_embedded: { 'dav:calendar': mockCalendars }
|
||||
})
|
||||
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock;
|
||||
const getUserDetailsMock = userAPI.getUserDetails as jest.Mock
|
||||
getUserDetailsMock
|
||||
.mockResolvedValueOnce({
|
||||
firstname: "Alice",
|
||||
lastname: "Smith",
|
||||
emails: ["alice@example.com"],
|
||||
firstname: 'Alice',
|
||||
lastname: 'Smith',
|
||||
emails: ['alice@example.com']
|
||||
})
|
||||
.mockRejectedValueOnce(new Error("Failed to fetch user"));
|
||||
.mockRejectedValueOnce(new Error('Failed to fetch user'))
|
||||
|
||||
const store = storeFactory();
|
||||
const result = await store.dispatch(getCalendarsListAsync() as any);
|
||||
const store = storeFactory()
|
||||
const result = await store.dispatch(getCalendarsListAsync() as any)
|
||||
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(2);
|
||||
const state = store.getState().calendars;
|
||||
expect(state.list["u1/cal1"].owner.firstname).toContain("Alice");
|
||||
expect(state.list["u2/cal2"].owner.lastname).toContain("Unknown User");
|
||||
expect(result.payload.errors).toBeTruthy();
|
||||
});
|
||||
expect(getUserDetailsMock).toHaveBeenCalledTimes(2)
|
||||
const state = store.getState().calendars
|
||||
expect(state.list['u1/cal1'].owner.firstname).toContain('Alice')
|
||||
expect(state.list['u2/cal2'].owner.lastname).toContain('Unknown User')
|
||||
expect(result.payload.errors).toBeTruthy()
|
||||
})
|
||||
|
||||
it("patchACLCalendarAsync.fulfilled sets visibility", () => {
|
||||
it('patchACLCalendarAsync.fulfilled sets visibility', () => {
|
||||
const prev = {
|
||||
...initialState,
|
||||
list: { c1: { id: "c1", visibility: "public" } as any },
|
||||
};
|
||||
list: { c1: { id: 'c1', visibility: 'public' } as any }
|
||||
}
|
||||
const state = reducer(
|
||||
prev,
|
||||
patchACLCalendarAsync.fulfilled(
|
||||
{ calId: "c1", calLink: "l", request: "" },
|
||||
"req3",
|
||||
{ calId: "c1", calLink: "l", request: "" }
|
||||
{ calId: 'c1', calLink: 'l', request: '' },
|
||||
'req3',
|
||||
{ calId: 'c1', calLink: 'l', request: '' }
|
||||
)
|
||||
);
|
||||
expect(state.list.c1.visibility).toBe("private");
|
||||
});
|
||||
)
|
||||
expect(state.list.c1.visibility).toBe('private')
|
||||
})
|
||||
|
||||
it("createCalendarAsync.fulfilled adds a new calendar", () => {
|
||||
it('createCalendarAsync.fulfilled adds a new calendar', () => {
|
||||
const payload = {
|
||||
userData: {
|
||||
openpaasId: "u1",
|
||||
family_name: "Owner",
|
||||
email: "o@example.com",
|
||||
sid: "",
|
||||
sub: "",
|
||||
given_name: "Test",
|
||||
name: "Test Owner",
|
||||
openpaasId: 'u1',
|
||||
family_name: 'Owner',
|
||||
email: 'o@example.com',
|
||||
sid: '',
|
||||
sub: '',
|
||||
given_name: 'Test',
|
||||
name: 'Test Owner'
|
||||
},
|
||||
calId: "cal1",
|
||||
color: { "apple:color": "#f00" },
|
||||
name: "Test",
|
||||
desc: "Desc",
|
||||
};
|
||||
calId: 'cal1',
|
||||
color: { 'apple:color': '#f00' },
|
||||
name: 'Test',
|
||||
desc: 'Desc'
|
||||
}
|
||||
|
||||
const payloadResponse = {
|
||||
userId: "u1",
|
||||
calId: "cal1",
|
||||
color: { "apple:color": "#f00" },
|
||||
name: "Test",
|
||||
desc: "Desc",
|
||||
owner: { firstname: "Owner", emails: ["o@example.com"] },
|
||||
};
|
||||
userId: 'u1',
|
||||
calId: 'cal1',
|
||||
color: { 'apple:color': '#f00' },
|
||||
name: 'Test',
|
||||
desc: 'Desc',
|
||||
owner: { firstname: 'Owner', emails: ['o@example.com'] }
|
||||
}
|
||||
const state = reducer(
|
||||
initialState,
|
||||
createCalendarAsync.fulfilled(payloadResponse, "req4", payload)
|
||||
);
|
||||
expect(state.list["u1/cal1"].name).toBe("Test");
|
||||
expect(state.list["u1/cal1"].color?.["apple:color"]).toBe("#f00");
|
||||
});
|
||||
createCalendarAsync.fulfilled(payloadResponse, 'req4', payload)
|
||||
)
|
||||
expect(state.list['u1/cal1'].name).toBe('Test')
|
||||
expect(state.list['u1/cal1'].color?.['apple:color']).toBe('#f00')
|
||||
})
|
||||
|
||||
it("addSharedCalendarAsync.fulfilled adds shared calendar", () => {
|
||||
it('addSharedCalendarAsync.fulfilled adds shared calendar', () => {
|
||||
const payload = {
|
||||
calId: "c1",
|
||||
color: { "apple:color": "#0f0" },
|
||||
link: "/calendars/u1/c1.json",
|
||||
name: "Shared",
|
||||
desc: "Shared Desc",
|
||||
owner: { firstname: "O", emails: ["o@example.com"] },
|
||||
};
|
||||
calId: 'c1',
|
||||
color: { 'apple:color': '#0f0' },
|
||||
link: '/calendars/u1/c1.json',
|
||||
name: 'Shared',
|
||||
desc: 'Shared Desc',
|
||||
owner: { firstname: 'O', emails: ['o@example.com'] }
|
||||
}
|
||||
const mockCal = {
|
||||
cal: {
|
||||
_links: { self: { href: "/calendars/u1/c1.json" } },
|
||||
"apple:color": "#0f0",
|
||||
"caldav:description": "Shared Desc",
|
||||
"dav:name": "Shared",
|
||||
},
|
||||
};
|
||||
_links: { self: { href: '/calendars/u1/c1.json' } },
|
||||
'apple:color': '#0f0',
|
||||
'caldav:description': 'Shared Desc',
|
||||
'dav:name': 'Shared'
|
||||
}
|
||||
}
|
||||
const state = reducer(
|
||||
initialState,
|
||||
addSharedCalendarAsync.fulfilled(payload, "req5", {
|
||||
userId: "u1",
|
||||
calId: "c1",
|
||||
cal: mockCal,
|
||||
addSharedCalendarAsync.fulfilled(payload, 'req5', {
|
||||
userId: 'u1',
|
||||
calId: 'c1',
|
||||
cal: mockCal
|
||||
})
|
||||
);
|
||||
expect(state.list["c1"].name).toBe("Shared");
|
||||
});
|
||||
)
|
||||
expect(state.list['c1'].name).toBe('Shared')
|
||||
})
|
||||
|
||||
it("getTempCalendarsListAsync.fulfilled updates templist", () => {
|
||||
it('getTempCalendarsListAsync.fulfilled updates templist', () => {
|
||||
const payload = {
|
||||
t1: {
|
||||
id: "t1",
|
||||
name: "Temp",
|
||||
color: { "apple:color": "#aaa" },
|
||||
id: 't1',
|
||||
name: 'Temp',
|
||||
color: { 'apple:color': '#aaa' },
|
||||
events: {},
|
||||
visibility: "public",
|
||||
owner: { firstname: "O", emails: ["o@o.com"] },
|
||||
link: "/calendars/t1.json",
|
||||
description: "desc",
|
||||
} as Calendar,
|
||||
};
|
||||
visibility: 'public',
|
||||
owner: { firstname: 'O', emails: ['o@o.com'] },
|
||||
link: '/calendars/t1.json',
|
||||
description: 'desc'
|
||||
} as Calendar
|
||||
}
|
||||
const state = reducer(
|
||||
initialState,
|
||||
getTempCalendarsListAsync.fulfilled(payload, "req7", {
|
||||
openpaasId: "u1",
|
||||
color: { "apple:color": "#aaa" },
|
||||
displayName: "test",
|
||||
avatarUrl: "",
|
||||
email: "test@test.com",
|
||||
getTempCalendarsListAsync.fulfilled(payload, 'req7', {
|
||||
openpaasId: 'u1',
|
||||
color: { 'apple:color': '#aaa' },
|
||||
displayName: 'test',
|
||||
avatarUrl: '',
|
||||
email: 'test@test.com'
|
||||
})
|
||||
);
|
||||
expect(state.templist.t1.name).toBe("Temp");
|
||||
});
|
||||
)
|
||||
expect(state.templist.t1.name).toBe('Temp')
|
||||
})
|
||||
|
||||
it("getEventAsync.fulfilled adds single event", () => {
|
||||
const payload = { calId: "c1", event: { uid: "e1" } as any };
|
||||
it('getEventAsync.fulfilled adds single event', () => {
|
||||
const payload = { calId: 'c1', event: { uid: 'e1' } as any }
|
||||
const state = reducer(
|
||||
initialState,
|
||||
getEventAsync.fulfilled(payload, "req9", { uid: "e1" } as any)
|
||||
);
|
||||
expect(state.list.c1.events.e1.uid).toBe("e1");
|
||||
});
|
||||
getEventAsync.fulfilled(payload, 'req9', { uid: 'e1' } as any)
|
||||
)
|
||||
expect(state.list.c1.events.e1.uid).toBe('e1')
|
||||
})
|
||||
|
||||
it("getCalendarDetailAsync.fulfilled adds calendar events", () => {
|
||||
const payload = { calId: "c1", events: [{ uid: "e1" }] as any[] };
|
||||
it('getCalendarDetailAsync.fulfilled adds calendar events', () => {
|
||||
const payload = { calId: 'c1', events: [{ uid: 'e1' }] as any[] }
|
||||
const state = reducer(
|
||||
{
|
||||
...initialState,
|
||||
list: {
|
||||
["c1"]: {
|
||||
id: "c1",
|
||||
events: {},
|
||||
} as unknown as Calendar,
|
||||
},
|
||||
['c1']: {
|
||||
id: 'c1',
|
||||
events: {}
|
||||
} as unknown as Calendar
|
||||
}
|
||||
},
|
||||
getCalendarDetailAsync.fulfilled(payload, "req11", {
|
||||
calId: "c1",
|
||||
match: { start: "", end: "" },
|
||||
getCalendarDetailAsync.fulfilled(payload, 'req11', {
|
||||
calId: 'c1',
|
||||
match: { start: '', end: '' }
|
||||
})
|
||||
);
|
||||
expect(state.list.c1.events.e1.uid).toBe("e1");
|
||||
});
|
||||
)
|
||||
expect(state.list.c1.events.e1.uid).toBe('e1')
|
||||
})
|
||||
|
||||
it("getEventAsync.fulfilled doesnt create new events when there are already event with base UID", () => {
|
||||
const baseUid = "recurring-event-base";
|
||||
it('getEventAsync.fulfilled doesnt create new events when there are already event with base UID', () => {
|
||||
const baseUid = 'recurring-event-base'
|
||||
const existingEvent = {
|
||||
uid: `${baseUid}/20240115`,
|
||||
title: "Existing Recurring Event",
|
||||
recurrenceId: null,
|
||||
} as unknown as CalendarEvent;
|
||||
title: 'Existing Recurring Event',
|
||||
recurrenceId: null
|
||||
} as unknown as CalendarEvent
|
||||
|
||||
const newEventInstance = {
|
||||
uid: baseUid,
|
||||
title: "Fetched Master Event",
|
||||
recurrenceId: "20240115",
|
||||
} as CalendarEvent;
|
||||
title: 'Fetched Master Event',
|
||||
recurrenceId: '20240115'
|
||||
} as CalendarEvent
|
||||
|
||||
const stateWithEvent = {
|
||||
...initialState,
|
||||
list: {
|
||||
c1: {
|
||||
id: "c1",
|
||||
events: { [`${baseUid}/20240115`]: existingEvent },
|
||||
} as unknown as Calendar,
|
||||
},
|
||||
};
|
||||
id: 'c1',
|
||||
events: { [`${baseUid}/20240115`]: existingEvent }
|
||||
} as unknown as Calendar
|
||||
}
|
||||
}
|
||||
|
||||
const payload = { calId: "c1", event: newEventInstance };
|
||||
const payload = { calId: 'c1', event: newEventInstance }
|
||||
const state = reducer(
|
||||
stateWithEvent,
|
||||
getEventAsync.fulfilled(payload, "req", newEventInstance)
|
||||
);
|
||||
getEventAsync.fulfilled(payload, 'req', newEventInstance)
|
||||
)
|
||||
|
||||
// Should still only have the base event, not the instance
|
||||
expect(Object.keys(state.list.c1.events)).toHaveLength(1);
|
||||
expect(state.list.c1.events[`${baseUid}/20240115`]).toBeDefined();
|
||||
expect(state.list.c1.events[baseUid]).toBeUndefined();
|
||||
});
|
||||
expect(Object.keys(state.list.c1.events)).toHaveLength(1)
|
||||
expect(state.list.c1.events[`${baseUid}/20240115`]).toBeDefined()
|
||||
expect(state.list.c1.events[baseUid]).toBeUndefined()
|
||||
})
|
||||
|
||||
it("getEventAsync.fulfilled create new event when there isn't any event with base UID", () => {
|
||||
const eventUid = "new-event-uid";
|
||||
const eventUid = 'new-event-uid'
|
||||
const newEvent = {
|
||||
uid: eventUid,
|
||||
title: "New Event",
|
||||
recurrenceId: null,
|
||||
} as unknown as CalendarEvent;
|
||||
title: 'New Event',
|
||||
recurrenceId: null
|
||||
} as unknown as CalendarEvent
|
||||
|
||||
const stateWithoutEvent = {
|
||||
...initialState,
|
||||
list: {
|
||||
c1: {
|
||||
id: "c1",
|
||||
events: {},
|
||||
} as unknown as Calendar,
|
||||
},
|
||||
};
|
||||
id: 'c1',
|
||||
events: {}
|
||||
} as unknown as Calendar
|
||||
}
|
||||
}
|
||||
|
||||
const payload = { calId: "c1", event: newEvent };
|
||||
const payload = { calId: 'c1', event: newEvent }
|
||||
const state = reducer(
|
||||
stateWithoutEvent,
|
||||
getEventAsync.fulfilled(payload, "req", newEvent)
|
||||
);
|
||||
getEventAsync.fulfilled(payload, 'req', newEvent)
|
||||
)
|
||||
|
||||
// Should create the new event
|
||||
expect(Object.keys(state.list.c1.events)).toHaveLength(1);
|
||||
expect(state.list.c1.events[eventUid]).toBeDefined();
|
||||
expect(state.list.c1.events[eventUid].uid).toBe(eventUid);
|
||||
expect(state.list.c1.events[eventUid].title).toBe("New Event");
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(Object.keys(state.list.c1.events)).toHaveLength(1)
|
||||
expect(state.list.c1.events[eventUid]).toBeDefined()
|
||||
expect(state.list.c1.events[eventUid].uid).toBe(eventUid)
|
||||
expect(state.list.c1.events[eventUid].title).toBe('New Event')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,196 +1,196 @@
|
||||
import CalendarApp from "@/components/Calendar/Calendar";
|
||||
import * as calendarUtils from "@/components/Calendar/utils/calendarUtils";
|
||||
import { updateSlotLabelVisibility } from "@/components/Calendar/utils/calendarUtils";
|
||||
import EventPreviewModal from "@/features/Events/EventPreview";
|
||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||
import * as SettingsSlice from "@/features/Settings/SettingsSlice";
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||
import CalendarApp from '@/components/Calendar/Calendar'
|
||||
import * as calendarUtils from '@/components/Calendar/utils/calendarUtils'
|
||||
import { updateSlotLabelVisibility } from '@/components/Calendar/utils/calendarUtils'
|
||||
import EventPreviewModal from '@/features/Events/EventPreview'
|
||||
import { CalendarEvent } from '@/features/Events/EventsTypes'
|
||||
import * as SettingsSlice from '@/features/Settings/SettingsSlice'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { renderWithProviders } from '../../utils/Renderwithproviders'
|
||||
|
||||
describe("Calendar - Timezone Integration", () => {
|
||||
const mockCalendarRef = { current: null };
|
||||
describe('Calendar - Timezone Integration', () => {
|
||||
const mockCalendarRef = { current: null }
|
||||
|
||||
const baseState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "testSid",
|
||||
openpaasId: "user1",
|
||||
},
|
||||
sub: 'test',
|
||||
email: 'test@test.com',
|
||||
sid: 'testSid',
|
||||
openpaasId: 'user1'
|
||||
}
|
||||
},
|
||||
calendars: {
|
||||
list: {
|
||||
"user1/cal1": {
|
||||
id: "user1/cal1",
|
||||
link: "/calendars/user1/cal1.json",
|
||||
name: "Test Calendar",
|
||||
description: "",
|
||||
color: "#33B679",
|
||||
owner: { firstname: "user1", emails: ["test@test.com"] },
|
||||
visibility: "public",
|
||||
events: {},
|
||||
},
|
||||
'user1/cal1': {
|
||||
id: 'user1/cal1',
|
||||
link: '/calendars/user1/cal1.json',
|
||||
name: 'Test Calendar',
|
||||
description: '',
|
||||
color: '#33B679',
|
||||
owner: { firstname: 'user1', emails: ['test@test.com'] },
|
||||
visibility: 'public',
|
||||
events: {}
|
||||
}
|
||||
},
|
||||
timeZone: "America/New_York",
|
||||
pending: false,
|
||||
},
|
||||
};
|
||||
timeZone: 'America/New_York',
|
||||
pending: false
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("renders TimezoneSelector in week view", async () => {
|
||||
it('renders TimezoneSelector in week view', async () => {
|
||||
renderWithProviders(
|
||||
<CalendarApp calendarRef={mockCalendarRef} />,
|
||||
baseState
|
||||
);
|
||||
)
|
||||
|
||||
// Look for timezone selector button (should show offset)
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/UTC/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
expect(screen.getByText(/UTC/)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it("dispatches setTimeZone action when timezone is changed", async () => {
|
||||
const setTimeZoneSpy = jest.spyOn(SettingsSlice, "setTimeZone");
|
||||
it('dispatches setTimeZone action when timezone is changed', async () => {
|
||||
const setTimeZoneSpy = jest.spyOn(SettingsSlice, 'setTimeZone')
|
||||
|
||||
renderWithProviders(
|
||||
<CalendarApp calendarRef={mockCalendarRef} />,
|
||||
baseState
|
||||
);
|
||||
)
|
||||
|
||||
// Find and click timezone selector
|
||||
await waitFor(() => {
|
||||
const timezoneButton = screen.getByText(/UTC/i);
|
||||
fireEvent.click(timezoneButton);
|
||||
const timezoneButton = screen.getByText(/UTC/i)
|
||||
fireEvent.click(timezoneButton)
|
||||
|
||||
// Select a different timezone
|
||||
const autocomplete = screen.getByRole("combobox");
|
||||
fireEvent.change(autocomplete, { target: { value: "Tokyo" } });
|
||||
});
|
||||
const option = await screen.findByText(/Tokyo/i);
|
||||
fireEvent.click(option);
|
||||
const autocomplete = screen.getByRole('combobox')
|
||||
fireEvent.change(autocomplete, { target: { value: 'Tokyo' } })
|
||||
})
|
||||
const option = await screen.findByText(/Tokyo/i)
|
||||
fireEvent.click(option)
|
||||
|
||||
expect(setTimeZoneSpy).toHaveBeenCalledWith("Asia/Tokyo");
|
||||
});
|
||||
});
|
||||
expect(setTimeZoneSpy).toHaveBeenCalledWith('Asia/Tokyo')
|
||||
})
|
||||
})
|
||||
|
||||
describe("Calendar - Timezone Slot Label Visibility", () => {
|
||||
it("hides slot labels within 15 minutes of current time", () => {
|
||||
const currentTime = new Date("2025-01-15T14:30:00Z");
|
||||
const timezone = "UTC";
|
||||
describe('Calendar - Timezone Slot Label Visibility', () => {
|
||||
it('hides slot labels within 15 minutes of current time', () => {
|
||||
const currentTime = new Date('2025-01-15T14:30:00Z')
|
||||
const timezone = 'UTC'
|
||||
jest
|
||||
.spyOn(calendarUtils, "checkIfCurrentWeekOrDay")
|
||||
.mockImplementation(() => true);
|
||||
.spyOn(calendarUtils, 'checkIfCurrentWeekOrDay')
|
||||
.mockImplementation(() => true)
|
||||
|
||||
// 14:25 - within 15 minutes
|
||||
const slot1425 = { text: "14:25" };
|
||||
const slot1425 = { text: '14:25' }
|
||||
expect(updateSlotLabelVisibility(currentTime, slot1425, timezone)).toBe(
|
||||
"timegrid-slot-label-hidden"
|
||||
);
|
||||
'timegrid-slot-label-hidden'
|
||||
)
|
||||
|
||||
// 14:30 - exact match
|
||||
const slot1430 = { text: "14:30" };
|
||||
const slot1430 = { text: '14:30' }
|
||||
expect(updateSlotLabelVisibility(currentTime, slot1430, timezone)).toBe(
|
||||
"timegrid-slot-label-hidden"
|
||||
);
|
||||
'timegrid-slot-label-hidden'
|
||||
)
|
||||
|
||||
// 14:35 - within 15 minutes
|
||||
const slot1435 = { text: "14:35" };
|
||||
const slot1435 = { text: '14:35' }
|
||||
expect(updateSlotLabelVisibility(currentTime, slot1435, timezone)).toBe(
|
||||
"timegrid-slot-label-hidden"
|
||||
);
|
||||
});
|
||||
'timegrid-slot-label-hidden'
|
||||
)
|
||||
})
|
||||
|
||||
it("shows slot labels more than 15 minutes from current time", () => {
|
||||
it('shows slot labels more than 15 minutes from current time', () => {
|
||||
jest
|
||||
.spyOn(calendarUtils, "checkIfCurrentWeekOrDay")
|
||||
.mockImplementation(() => true);
|
||||
.spyOn(calendarUtils, 'checkIfCurrentWeekOrDay')
|
||||
.mockImplementation(() => true)
|
||||
|
||||
const currentTime = new Date("2025-01-15T14:30:00");
|
||||
const timezone = "UTC";
|
||||
const currentTime = new Date('2025-01-15T14:30:00')
|
||||
const timezone = 'UTC'
|
||||
|
||||
// 14:00 - 30 minutes before
|
||||
const slot1400 = { text: "14:00" };
|
||||
const slot1400 = { text: '14:00' }
|
||||
expect(updateSlotLabelVisibility(currentTime, slot1400, timezone)).toBe(
|
||||
"fc-timegrid-slot-label"
|
||||
);
|
||||
'fc-timegrid-slot-label'
|
||||
)
|
||||
|
||||
// 15:00 - 30 minutes after
|
||||
const slot1500 = { text: "15:00" };
|
||||
const slot1500 = { text: '15:00' }
|
||||
expect(updateSlotLabelVisibility(currentTime, slot1500, timezone)).toBe(
|
||||
"fc-timegrid-slot-label"
|
||||
);
|
||||
});
|
||||
'fc-timegrid-slot-label'
|
||||
)
|
||||
})
|
||||
|
||||
it("returns visible class when not in current week/day", () => {
|
||||
it('returns visible class when not in current week/day', () => {
|
||||
jest
|
||||
.spyOn(calendarUtils, "checkIfCurrentWeekOrDay")
|
||||
.mockImplementation(() => false);
|
||||
const currentTime = new Date("2025-01-15T14:30:00");
|
||||
const timezone = "UTC";
|
||||
const slot = { text: "14:30" };
|
||||
.spyOn(calendarUtils, 'checkIfCurrentWeekOrDay')
|
||||
.mockImplementation(() => false)
|
||||
const currentTime = new Date('2025-01-15T14:30:00')
|
||||
const timezone = 'UTC'
|
||||
const slot = { text: '14:30' }
|
||||
|
||||
// Should return visible class when not in current view
|
||||
const result = updateSlotLabelVisibility(currentTime, slot, timezone);
|
||||
expect(result).toBe("fc-timegrid-slot-label");
|
||||
});
|
||||
});
|
||||
const result = updateSlotLabelVisibility(currentTime, slot, timezone)
|
||||
expect(result).toBe('fc-timegrid-slot-label')
|
||||
})
|
||||
})
|
||||
|
||||
describe("EventDisplayPreview - Timezone Display", () => {
|
||||
const mockOnClose = jest.fn();
|
||||
describe('EventDisplayPreview - Timezone Display', () => {
|
||||
const mockOnClose = jest.fn()
|
||||
|
||||
const baseEvent: CalendarEvent = {
|
||||
uid: "event1",
|
||||
title: "Team Meeting",
|
||||
start: new Date("2025-01-15T14:00:00Z"),
|
||||
end: new Date("2025-01-15T15:00:00Z"),
|
||||
calendarId: "user1/cal1",
|
||||
allday: false,
|
||||
};
|
||||
uid: 'event1',
|
||||
title: 'Team Meeting',
|
||||
start: new Date('2025-01-15T14:00:00Z'),
|
||||
end: new Date('2025-01-15T15:00:00Z'),
|
||||
calendarId: 'user1/cal1',
|
||||
allday: false
|
||||
}
|
||||
|
||||
const allDayEvent = {
|
||||
...baseEvent,
|
||||
allday: true,
|
||||
start: new Date("2025-01-15"),
|
||||
end: new Date("2025-01-16"),
|
||||
};
|
||||
start: new Date('2025-01-15'),
|
||||
end: new Date('2025-01-16')
|
||||
}
|
||||
|
||||
const baseState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "testSid",
|
||||
openpaasId: "user1",
|
||||
},
|
||||
sub: 'test',
|
||||
email: 'test@test.com',
|
||||
sid: 'testSid',
|
||||
openpaasId: 'user1'
|
||||
}
|
||||
},
|
||||
calendars: {
|
||||
list: {
|
||||
"user1/cal1": {
|
||||
id: "user1/cal1",
|
||||
name: "Test Calendar",
|
||||
color: "#33B679",
|
||||
owner: { firstname: "user1", emails: ["test@test.com"] },
|
||||
visibility: "public",
|
||||
'user1/cal1': {
|
||||
id: 'user1/cal1',
|
||||
name: 'Test Calendar',
|
||||
color: '#33B679',
|
||||
owner: { firstname: 'user1', emails: ['test@test.com'] },
|
||||
visibility: 'public',
|
||||
events: {
|
||||
event1: baseEvent,
|
||||
allDayEvent,
|
||||
},
|
||||
},
|
||||
allDayEvent
|
||||
}
|
||||
}
|
||||
},
|
||||
timeZone: "America/New_York",
|
||||
pending: false,
|
||||
},
|
||||
};
|
||||
timeZone: 'America/New_York',
|
||||
pending: false
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("does not show timezone offset for all-day events", async () => {
|
||||
it('does not show timezone offset for all-day events', async () => {
|
||||
renderWithProviders(
|
||||
<EventPreviewModal
|
||||
open={true}
|
||||
@@ -199,33 +199,33 @@ describe("EventDisplayPreview - Timezone Display", () => {
|
||||
calId="user1/cal1"
|
||||
/>,
|
||||
baseState
|
||||
);
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
const title = screen.getByText(/Team Meeting/i);
|
||||
expect(title).toBeInTheDocument();
|
||||
});
|
||||
const title = screen.getByText(/Team Meeting/i)
|
||||
expect(title).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Should not show UTC offset
|
||||
expect(screen.queryByText(/UTC[+-]\d+/i)).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.queryByText(/UTC[+-]\d+/i)).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("displays correct timezone offset for different timezones", async () => {
|
||||
it('displays correct timezone offset for different timezones', async () => {
|
||||
const timezones = [
|
||||
{ tz: "America/New_York", expectedOffset: /UTC[-−][45]/ },
|
||||
{ tz: "Europe/Paris", expectedOffset: /UTC\+[12]/ },
|
||||
{ tz: "Asia/Tokyo", expectedOffset: /UTC\+9/ },
|
||||
{ tz: "Australia/Sydney", expectedOffset: /UTC\+1[01]/ },
|
||||
{ tz: "Asia/Kolkata", expectedOffset: /UTC\+5:30/ },
|
||||
];
|
||||
{ tz: 'America/New_York', expectedOffset: /UTC[-−][45]/ },
|
||||
{ tz: 'Europe/Paris', expectedOffset: /UTC\+[12]/ },
|
||||
{ tz: 'Asia/Tokyo', expectedOffset: /UTC\+9/ },
|
||||
{ tz: 'Australia/Sydney', expectedOffset: /UTC\+1[01]/ },
|
||||
{ tz: 'Asia/Kolkata', expectedOffset: /UTC\+5:30/ }
|
||||
]
|
||||
|
||||
for (const { tz, expectedOffset } of timezones) {
|
||||
const state = {
|
||||
...baseState,
|
||||
settings: {
|
||||
timeZone: tz,
|
||||
},
|
||||
};
|
||||
timeZone: tz
|
||||
}
|
||||
}
|
||||
|
||||
renderWithProviders(
|
||||
<EventPreviewModal
|
||||
@@ -235,12 +235,12 @@ describe("EventDisplayPreview - Timezone Display", () => {
|
||||
onClose={mockOnClose}
|
||||
/>,
|
||||
state
|
||||
);
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
const content = document.body.textContent || "";
|
||||
expect(content).toMatch(expectedOffset);
|
||||
});
|
||||
const content = document.body.textContent || ''
|
||||
expect(content).toMatch(expectedOffset)
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,134 +1,134 @@
|
||||
import { TimezoneSelector } from "@/components/Calendar/TimezoneSelector";
|
||||
import { cleanup, fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||
import { TimezoneSelector } from '@/components/Calendar/TimezoneSelector'
|
||||
import { cleanup, fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { renderWithProviders } from '../../utils/Renderwithproviders'
|
||||
|
||||
describe("TimezoneSelector", () => {
|
||||
const mockOnChange = jest.fn();
|
||||
describe('TimezoneSelector', () => {
|
||||
const mockOnChange = jest.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("renders with initial timezone value", () => {
|
||||
it('renders with initial timezone value', () => {
|
||||
renderWithProviders(
|
||||
<TimezoneSelector
|
||||
referenceDate={new Date()}
|
||||
value="America/New_York"
|
||||
onChange={mockOnChange}
|
||||
/>
|
||||
);
|
||||
)
|
||||
|
||||
const button = screen.getByRole("button");
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveTextContent(/UTC[-−][45]/i); // New York offset
|
||||
});
|
||||
const button = screen.getByRole('button')
|
||||
expect(button).toBeInTheDocument()
|
||||
expect(button).toHaveTextContent(/UTC[-−][45]/i) // New York offset
|
||||
})
|
||||
|
||||
it("opens popover when button is clicked", async () => {
|
||||
it('opens popover when button is clicked', async () => {
|
||||
renderWithProviders(
|
||||
<TimezoneSelector
|
||||
referenceDate={new Date()}
|
||||
value="Europe/Paris"
|
||||
onChange={mockOnChange}
|
||||
/>
|
||||
);
|
||||
)
|
||||
|
||||
const button = screen.getByRole("button");
|
||||
fireEvent.click(button);
|
||||
const button = screen.getByRole('button')
|
||||
fireEvent.click(button)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("combobox")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
expect(screen.getByRole('combobox')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it("calls onChange when a new timezone is selected", async () => {
|
||||
it('calls onChange when a new timezone is selected', async () => {
|
||||
renderWithProviders(
|
||||
<TimezoneSelector
|
||||
referenceDate={new Date()}
|
||||
value="Europe/Paris"
|
||||
onChange={mockOnChange}
|
||||
/>
|
||||
);
|
||||
)
|
||||
|
||||
const button = screen.getByRole("button");
|
||||
fireEvent.click(button);
|
||||
const button = screen.getByRole('button')
|
||||
fireEvent.click(button)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("combobox")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByRole('combobox')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const autocomplete = screen.getByRole("combobox");
|
||||
fireEvent.change(autocomplete, { target: { value: "Los Angeles" } });
|
||||
const autocomplete = screen.getByRole('combobox')
|
||||
fireEvent.change(autocomplete, { target: { value: 'Los Angeles' } })
|
||||
|
||||
// Find and click the Los Angeles option
|
||||
const option = await screen.findByText(/Los Angeles/i);
|
||||
fireEvent.click(option);
|
||||
const option = await screen.findByText(/Los Angeles/i)
|
||||
fireEvent.click(option)
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith("America/Los_Angeles");
|
||||
});
|
||||
expect(mockOnChange).toHaveBeenCalledWith('America/Los_Angeles')
|
||||
})
|
||||
|
||||
it("closes popover after timezone selection", async () => {
|
||||
it('closes popover after timezone selection', async () => {
|
||||
renderWithProviders(
|
||||
<TimezoneSelector
|
||||
referenceDate={new Date()}
|
||||
value="Europe/Paris"
|
||||
onChange={mockOnChange}
|
||||
/>
|
||||
);
|
||||
)
|
||||
|
||||
const button = screen.getByRole("button");
|
||||
fireEvent.click(button);
|
||||
const button = screen.getByRole('button')
|
||||
fireEvent.click(button)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("combobox")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByRole('combobox')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const autocomplete = screen.getByRole("combobox");
|
||||
fireEvent.change(autocomplete, { target: { value: "Tokyo" } });
|
||||
const autocomplete = screen.getByRole('combobox')
|
||||
fireEvent.change(autocomplete, { target: { value: 'Tokyo' } })
|
||||
|
||||
const option = await screen.findByText(/Tokyo/i);
|
||||
fireEvent.click(option);
|
||||
const option = await screen.findByText(/Tokyo/i)
|
||||
fireEvent.click(option)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
expect(screen.queryByRole('combobox')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it("displays timezones with half-hour offsets correctly", () => {
|
||||
it('displays timezones with half-hour offsets correctly', () => {
|
||||
renderWithProviders(
|
||||
<TimezoneSelector
|
||||
referenceDate={new Date()}
|
||||
value="Asia/Kolkata"
|
||||
onChange={mockOnChange}
|
||||
/>
|
||||
);
|
||||
)
|
||||
|
||||
const button = screen.getByRole("button");
|
||||
expect(button).toHaveTextContent("UTC+5:30"); // India offset
|
||||
});
|
||||
const button = screen.getByRole('button')
|
||||
expect(button).toHaveTextContent('UTC+5:30') // India offset
|
||||
})
|
||||
|
||||
it("shows correct offset for Europe/Paris depending on daylight saving time", () => {
|
||||
it('shows correct offset for Europe/Paris depending on daylight saving time', () => {
|
||||
// Summer date (DST on)
|
||||
const summerDate = new Date("2025-07-15T12:00:00Z");
|
||||
const summerDate = new Date('2025-07-15T12:00:00Z')
|
||||
renderWithProviders(
|
||||
<TimezoneSelector
|
||||
value="Europe/Paris"
|
||||
onChange={mockOnChange}
|
||||
referenceDate={summerDate}
|
||||
/>
|
||||
);
|
||||
let button = screen.getByRole("button");
|
||||
expect(button).toHaveTextContent(/UTC\+2\b/);
|
||||
cleanup();
|
||||
)
|
||||
let button = screen.getByRole('button')
|
||||
expect(button).toHaveTextContent(/UTC\+2\b/)
|
||||
cleanup()
|
||||
// Rerender with a winter date (DST off)
|
||||
const winterDate = new Date("2025-01-15T12:00:00Z");
|
||||
const winterDate = new Date('2025-01-15T12:00:00Z')
|
||||
renderWithProviders(
|
||||
<TimezoneSelector
|
||||
value="Europe/Paris"
|
||||
onChange={mockOnChange}
|
||||
referenceDate={winterDate}
|
||||
/>
|
||||
);
|
||||
button = screen.getByRole("button");
|
||||
expect(button).toHaveTextContent(/UTC\+1\b/);
|
||||
});
|
||||
});
|
||||
)
|
||||
button = screen.getByRole('button')
|
||||
expect(button).toHaveTextContent(/UTC\+1\b/)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,152 +1,150 @@
|
||||
import { addCalendarResourceAsync } from "@/features/Calendars/api/addCalendarResourceAsync";
|
||||
import { addSharedCalendar } from "@/features/Calendars/CalendarApi";
|
||||
import { fetchOwnerOfResource } from "@/features/Calendars/services/helpers";
|
||||
import { toRejectedError } from "@/utils/errorUtils";
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import { addCalendarResourceAsync } from '@/features/Calendars/api/addCalendarResourceAsync'
|
||||
import { addSharedCalendar } from '@/features/Calendars/CalendarApi'
|
||||
import { fetchOwnerOfResource } from '@/features/Calendars/services/helpers'
|
||||
import { toRejectedError } from '@/utils/errorUtils'
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
|
||||
jest.mock("@/features/Calendars/CalendarApi");
|
||||
jest.mock("@/features/Calendars/services/helpers");
|
||||
jest.mock("@/utils/errorUtils");
|
||||
jest.mock('@/features/Calendars/CalendarApi')
|
||||
jest.mock('@/features/Calendars/services/helpers')
|
||||
jest.mock('@/utils/errorUtils')
|
||||
|
||||
const mockedAddSharedCalendar = addSharedCalendar as jest.Mock;
|
||||
const mockedFetchOwnerOfResource = fetchOwnerOfResource as jest.Mock;
|
||||
const mockedToRejectedError = toRejectedError as jest.Mock;
|
||||
const mockedAddSharedCalendar = addSharedCalendar as jest.Mock
|
||||
const mockedFetchOwnerOfResource = fetchOwnerOfResource as jest.Mock
|
||||
const mockedToRejectedError = toRejectedError as jest.Mock
|
||||
|
||||
describe("addCalendarResourceAsync thunk", () => {
|
||||
let store: ReturnType<typeof configureStore>;
|
||||
const dispatch = jest.fn();
|
||||
describe('addCalendarResourceAsync thunk', () => {
|
||||
let store: ReturnType<typeof configureStore>
|
||||
const dispatch = jest.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.clearAllMocks()
|
||||
store = configureStore({
|
||||
reducer: () => ({}),
|
||||
});
|
||||
});
|
||||
reducer: () => ({})
|
||||
})
|
||||
})
|
||||
|
||||
const mockPayload = {
|
||||
userId: "user-123",
|
||||
calId: "cal-123",
|
||||
userId: 'user-123',
|
||||
calId: 'cal-123',
|
||||
cal: {
|
||||
color: {
|
||||
background: "#000000",
|
||||
foreground: "#FFFFFF",
|
||||
background: '#000000',
|
||||
foreground: '#FFFFFF'
|
||||
},
|
||||
cal: {
|
||||
"dav:name": "Resource Room A",
|
||||
"caldav:description": "A meeting room",
|
||||
'dav:name': 'Resource Room A',
|
||||
'caldav:description': 'A meeting room',
|
||||
_links: {
|
||||
self: {
|
||||
href: "/calendars/res-456/cal-123.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
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",
|
||||
_id: 'res-456',
|
||||
id: 'res-456',
|
||||
name: 'Resource Room A',
|
||||
description: 'A meeting room',
|
||||
creator: 'user-789',
|
||||
deleted: false,
|
||||
_rev: "1",
|
||||
};
|
||||
_rev: '1'
|
||||
}
|
||||
|
||||
it("should add shared calendar, fetch resource details, map userData", async () => {
|
||||
it('should add shared calendar, fetch resource details, map userData', async () => {
|
||||
mockedFetchOwnerOfResource.mockResolvedValueOnce({
|
||||
firstname: "Creator",
|
||||
lastname: "User",
|
||||
emails: ["creator@example.com"],
|
||||
});
|
||||
mockedAddSharedCalendar.mockResolvedValueOnce({});
|
||||
firstname: 'Creator',
|
||||
lastname: 'User',
|
||||
emails: ['creator@example.com']
|
||||
})
|
||||
mockedAddSharedCalendar.mockResolvedValueOnce({})
|
||||
|
||||
const result = await addCalendarResourceAsync(
|
||||
mockPayload as unknown as Parameters<typeof addCalendarResourceAsync>[0]
|
||||
)(dispatch, store.getState, undefined);
|
||||
)(dispatch, store.getState, undefined)
|
||||
|
||||
expect(mockedAddSharedCalendar).toHaveBeenCalledWith(
|
||||
mockPayload.userId,
|
||||
mockPayload.calId,
|
||||
mockPayload.cal
|
||||
);
|
||||
expect(mockedFetchOwnerOfResource).toHaveBeenCalledWith("res-456");
|
||||
)
|
||||
expect(mockedFetchOwnerOfResource).toHaveBeenCalledWith('res-456')
|
||||
|
||||
expect(result.type).toBe("calendars/addCalendarResource/fulfilled");
|
||||
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",
|
||||
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: {
|
||||
firstname: "Creator",
|
||||
lastname: "User",
|
||||
emails: ["creator@example.com"],
|
||||
resource: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
firstname: 'Creator',
|
||||
lastname: 'User',
|
||||
emails: ['creator@example.com'],
|
||||
resource: true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("should fallback to name if resource details fetch fails", async () => {
|
||||
mockedAddSharedCalendar.mockResolvedValueOnce({});
|
||||
const errorDetails = new Error("Fetch failed");
|
||||
mockedFetchOwnerOfResource.mockRejectedValueOnce(errorDetails);
|
||||
it('should fallback to name if resource details fetch fails', async () => {
|
||||
mockedAddSharedCalendar.mockResolvedValueOnce({})
|
||||
const errorDetails = new Error('Fetch failed')
|
||||
mockedFetchOwnerOfResource.mockRejectedValueOnce(errorDetails)
|
||||
|
||||
// Silence expected console error in tests
|
||||
const consoleSpy = jest
|
||||
.spyOn(console, "error")
|
||||
.mockImplementation(() => {});
|
||||
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
||||
|
||||
const result = await addCalendarResourceAsync(
|
||||
mockPayload as unknown as Parameters<typeof addCalendarResourceAsync>[0]
|
||||
)(dispatch, store.getState, undefined);
|
||||
)(dispatch, store.getState, undefined)
|
||||
|
||||
expect(mockedAddSharedCalendar).toHaveBeenCalledWith(
|
||||
mockPayload.userId,
|
||||
mockPayload.calId,
|
||||
mockPayload.cal
|
||||
);
|
||||
expect(mockedFetchOwnerOfResource).toHaveBeenCalledWith("res-456");
|
||||
)
|
||||
expect(mockedFetchOwnerOfResource).toHaveBeenCalledWith('res-456')
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
consoleSpy.mockRestore()
|
||||
|
||||
expect(result.type).toBe("calendars/addCalendarResource/fulfilled");
|
||||
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",
|
||||
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: {
|
||||
firstname: "",
|
||||
lastname: "Resource Room A",
|
||||
firstname: '',
|
||||
lastname: 'Resource Room A',
|
||||
emails: [],
|
||||
resource: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
resource: true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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);
|
||||
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);
|
||||
)(dispatch, store.getState, undefined)
|
||||
|
||||
expect(mockedAddSharedCalendar).toHaveBeenCalledWith(
|
||||
mockPayload.userId,
|
||||
mockPayload.calId,
|
||||
mockPayload.cal
|
||||
);
|
||||
expect(mockedFetchOwnerOfResource).not.toHaveBeenCalled();
|
||||
)
|
||||
expect(mockedFetchOwnerOfResource).not.toHaveBeenCalled()
|
||||
|
||||
expect(mockedToRejectedError).toHaveBeenCalledWith(errorAdd);
|
||||
expect(mockedToRejectedError).toHaveBeenCalledWith(errorAdd)
|
||||
|
||||
expect(result.type).toBe("calendars/addCalendarResource/rejected");
|
||||
expect(result.payload).toEqual(mockRejectedErrorResult);
|
||||
});
|
||||
});
|
||||
expect(result.type).toBe('calendars/addCalendarResource/rejected')
|
||||
expect(result.payload).toEqual(mockRejectedErrorResult)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,234 +1,234 @@
|
||||
import { getCalendarsListAsync } from "@/features/Calendars/services/getCalendarsListAsync";
|
||||
import { getOpenPaasUser } from "@/features/User/userAPI";
|
||||
import { fetchOwnerData } from "@/features/Calendars/services/helpers";
|
||||
import { getCalendars } from "@/features/Calendars/CalendarApi";
|
||||
import { formatReduxError } from "@/utils/errorUtils";
|
||||
import { normalizeCalendar } from "@/features/Calendars/utils/normalizeCalendar";
|
||||
import { getCalendarsListAsync } from '@/features/Calendars/services/getCalendarsListAsync'
|
||||
import { getOpenPaasUser } from '@/features/User/userAPI'
|
||||
import { fetchOwnerData } from '@/features/Calendars/services/helpers'
|
||||
import { getCalendars } from '@/features/Calendars/CalendarApi'
|
||||
import { formatReduxError } from '@/utils/errorUtils'
|
||||
import { normalizeCalendar } from '@/features/Calendars/utils/normalizeCalendar'
|
||||
|
||||
jest.mock("@/features/User/userAPI");
|
||||
jest.mock("@/features/Calendars/services/helpers");
|
||||
jest.mock("@/features/Calendars/CalendarApi");
|
||||
jest.mock("@/utils/errorUtils");
|
||||
jest.mock("@/features/Calendars/utils/normalizeCalendar");
|
||||
jest.mock("@/utils/getAccessiblePair", () => ({
|
||||
getAccessiblePair: jest.fn().mockReturnValue("#FFF"),
|
||||
}));
|
||||
jest.mock('@/features/User/userAPI')
|
||||
jest.mock('@/features/Calendars/services/helpers')
|
||||
jest.mock('@/features/Calendars/CalendarApi')
|
||||
jest.mock('@/utils/errorUtils')
|
||||
jest.mock('@/features/Calendars/utils/normalizeCalendar')
|
||||
jest.mock('@/utils/getAccessiblePair', () => ({
|
||||
getAccessiblePair: jest.fn().mockReturnValue('#FFF')
|
||||
}))
|
||||
|
||||
jest.mock("@mui/material/styles", () => ({
|
||||
createTheme: jest.fn().mockReturnValue({}),
|
||||
}));
|
||||
jest.mock('@mui/material/styles', () => ({
|
||||
createTheme: jest.fn().mockReturnValue({})
|
||||
}))
|
||||
|
||||
const mockedGetOpenPaasUser = getOpenPaasUser as jest.Mock;
|
||||
const mockedFetchOwnerData = fetchOwnerData as jest.Mock;
|
||||
const mockedGetCalendars = getCalendars as jest.Mock;
|
||||
const mockedFormatReduxError = formatReduxError as jest.Mock;
|
||||
const mockedNormalizeCalendar = normalizeCalendar as jest.Mock;
|
||||
const mockedGetOpenPaasUser = getOpenPaasUser as jest.Mock
|
||||
const mockedFetchOwnerData = fetchOwnerData as jest.Mock
|
||||
const mockedGetCalendars = getCalendars as jest.Mock
|
||||
const mockedFormatReduxError = formatReduxError as jest.Mock
|
||||
const mockedNormalizeCalendar = normalizeCalendar as jest.Mock
|
||||
|
||||
describe("getCalendarsListAsync", () => {
|
||||
let dispatch: jest.Mock;
|
||||
let getState: jest.Mock;
|
||||
describe('getCalendarsListAsync', () => {
|
||||
let dispatch: jest.Mock
|
||||
let getState: jest.Mock
|
||||
|
||||
beforeEach(() => {
|
||||
dispatch = jest.fn();
|
||||
getState = jest.fn();
|
||||
jest.clearAllMocks();
|
||||
dispatch = jest.fn()
|
||||
getState = jest.fn()
|
||||
jest.clearAllMocks()
|
||||
|
||||
mockedFormatReduxError.mockImplementation((err) => {
|
||||
if (err?.message) return err.message;
|
||||
if (typeof err === "string") return err;
|
||||
return JSON.stringify(err);
|
||||
});
|
||||
});
|
||||
mockedFormatReduxError.mockImplementation(err => {
|
||||
if (err?.message) return err.message
|
||||
if (typeof err === 'string') return err
|
||||
return JSON.stringify(err)
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle successful execution and merge with existing calendars", async () => {
|
||||
it('should handle successful execution and merge with existing calendars', async () => {
|
||||
getState.mockReturnValue({
|
||||
calendars: {
|
||||
list: {
|
||||
"cal-existing": {
|
||||
id: "cal-existing",
|
||||
color: { light: "red", dark: "#FFF" },
|
||||
events: { "event-1": {} },
|
||||
},
|
||||
},
|
||||
'cal-existing': {
|
||||
id: 'cal-existing',
|
||||
color: { light: 'red', dark: '#FFF' },
|
||||
events: { 'event-1': {} }
|
||||
}
|
||||
}
|
||||
},
|
||||
user: {
|
||||
userData: { openpaasId: "user-123" },
|
||||
},
|
||||
});
|
||||
userData: { openpaasId: 'user-123' }
|
||||
}
|
||||
})
|
||||
|
||||
const mockCalendarsResponse = {
|
||||
_embedded: {
|
||||
"dav:calendar": [{ id: "cal-existing" }, { id: "cal-new" }],
|
||||
},
|
||||
};
|
||||
mockedGetCalendars.mockResolvedValue(mockCalendarsResponse);
|
||||
'dav:calendar': [{ id: 'cal-existing' }, { id: 'cal-new' }]
|
||||
}
|
||||
}
|
||||
mockedGetCalendars.mockResolvedValue(mockCalendarsResponse)
|
||||
|
||||
mockedNormalizeCalendar
|
||||
.mockReturnValueOnce({
|
||||
cal: { "dav:name": "Existing Cal", "apple:color": "blue" },
|
||||
id: "cal-existing",
|
||||
ownerId: "user-123",
|
||||
description: "old cal",
|
||||
cal: { 'dav:name': 'Existing Cal', 'apple:color': 'blue' },
|
||||
id: 'cal-existing',
|
||||
ownerId: 'user-123',
|
||||
description: 'old cal',
|
||||
delegated: false,
|
||||
link: "/link/1",
|
||||
link: '/link/1',
|
||||
visibility: 1,
|
||||
access: 3,
|
||||
access: 3
|
||||
})
|
||||
.mockReturnValueOnce({
|
||||
cal: { "dav:name": "New Cal" },
|
||||
id: "cal-new",
|
||||
ownerId: "user-456",
|
||||
description: "new cal",
|
||||
cal: { 'dav:name': 'New Cal' },
|
||||
id: 'cal-new',
|
||||
ownerId: 'user-456',
|
||||
description: 'new cal',
|
||||
delegated: true,
|
||||
link: "/link/2",
|
||||
link: '/link/2',
|
||||
visibility: 2,
|
||||
access: 2,
|
||||
invite: [{ href: "", principal: "", access: 3, inviteStatus: 1 }],
|
||||
});
|
||||
invite: [{ href: '', principal: '', access: 3, inviteStatus: 1 }]
|
||||
})
|
||||
|
||||
mockedFetchOwnerData
|
||||
.mockResolvedValueOnce({
|
||||
firstname: "John",
|
||||
lastname: "Doe",
|
||||
emails: ["john@example.com"],
|
||||
firstname: 'John',
|
||||
lastname: 'Doe',
|
||||
emails: ['john@example.com']
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
firstname: "Jane",
|
||||
lastname: "Smith",
|
||||
emails: ["jane@example.com"],
|
||||
});
|
||||
firstname: 'Jane',
|
||||
lastname: 'Smith',
|
||||
emails: ['jane@example.com']
|
||||
})
|
||||
|
||||
const thunk = getCalendarsListAsync();
|
||||
const result = await thunk(dispatch, getState, undefined);
|
||||
const thunk = getCalendarsListAsync()
|
||||
const result = await thunk(dispatch, getState, undefined)
|
||||
|
||||
expect(result.type).toBe("calendars/getCalendars/fulfilled");
|
||||
expect(result.type).toBe('calendars/getCalendars/fulfilled')
|
||||
const payload = result.payload as {
|
||||
importedCalendars: any;
|
||||
errors: string;
|
||||
};
|
||||
importedCalendars: any
|
||||
errors: string
|
||||
}
|
||||
|
||||
expect(payload.errors).toBe("");
|
||||
expect(Object.keys(payload.importedCalendars)).toHaveLength(2);
|
||||
expect(payload.errors).toBe('')
|
||||
expect(Object.keys(payload.importedCalendars)).toHaveLength(2)
|
||||
|
||||
expect(payload.importedCalendars["cal-existing"]).toMatchObject({
|
||||
id: "cal-existing",
|
||||
color: { light: "blue", dark: "#FFF" },
|
||||
events: { "event-1": {} },
|
||||
});
|
||||
expect(payload.importedCalendars['cal-existing']).toMatchObject({
|
||||
id: 'cal-existing',
|
||||
color: { light: 'blue', dark: '#FFF' },
|
||||
events: { 'event-1': {} }
|
||||
})
|
||||
|
||||
expect(payload.importedCalendars["cal-new"]).toMatchObject({
|
||||
id: "cal-new",
|
||||
name: "New Cal",
|
||||
owner: { firstname: "Jane", lastname: "Smith" },
|
||||
});
|
||||
expect(payload.importedCalendars['cal-new']).toMatchObject({
|
||||
id: 'cal-new',
|
||||
name: 'New Cal',
|
||||
owner: { firstname: 'Jane', lastname: 'Smith' }
|
||||
})
|
||||
|
||||
expect(mockedGetOpenPaasUser).not.toHaveBeenCalled(); // User ID existed in state
|
||||
expect(mockedGetCalendars).toHaveBeenCalledWith("user-123");
|
||||
});
|
||||
expect(mockedGetOpenPaasUser).not.toHaveBeenCalled() // User ID existed in state
|
||||
expect(mockedGetCalendars).toHaveBeenCalledWith('user-123')
|
||||
})
|
||||
|
||||
it("should fetch user using getOpenPaasUser if openpaasId is not in state", async () => {
|
||||
getState.mockReturnValue({ calendars: {}, user: {} });
|
||||
mockedGetOpenPaasUser.mockResolvedValue({ id: "fetched-user-123" });
|
||||
mockedGetCalendars.mockResolvedValue({ _embedded: { "dav:calendar": [] } });
|
||||
it('should fetch user using getOpenPaasUser if openpaasId is not in state', async () => {
|
||||
getState.mockReturnValue({ calendars: {}, user: {} })
|
||||
mockedGetOpenPaasUser.mockResolvedValue({ id: 'fetched-user-123' })
|
||||
mockedGetCalendars.mockResolvedValue({ _embedded: { 'dav:calendar': [] } })
|
||||
|
||||
const thunk = getCalendarsListAsync();
|
||||
await thunk(dispatch, getState, undefined);
|
||||
const thunk = getCalendarsListAsync()
|
||||
await thunk(dispatch, getState, undefined)
|
||||
|
||||
expect(mockedGetOpenPaasUser).toHaveBeenCalled();
|
||||
expect(mockedGetCalendars).toHaveBeenCalledWith("fetched-user-123");
|
||||
});
|
||||
expect(mockedGetOpenPaasUser).toHaveBeenCalled()
|
||||
expect(mockedGetCalendars).toHaveBeenCalledWith('fetched-user-123')
|
||||
})
|
||||
|
||||
it("should handle error when API call fails", async () => {
|
||||
getState.mockReturnValue({ calendars: {}, user: {} });
|
||||
mockedGetOpenPaasUser.mockResolvedValue({ id: "fetched-user-123" });
|
||||
it('should handle error when API call fails', async () => {
|
||||
getState.mockReturnValue({ calendars: {}, user: {} })
|
||||
mockedGetOpenPaasUser.mockResolvedValue({ id: 'fetched-user-123' })
|
||||
|
||||
mockedGetCalendars.mockRejectedValue({
|
||||
response: { status: 500 },
|
||||
message: "Server Error",
|
||||
});
|
||||
message: 'Server Error'
|
||||
})
|
||||
|
||||
// toRejectedError is imported from a mocked module; provide the expected return
|
||||
const { toRejectedError } = jest.requireMock("@/utils/errorUtils") as {
|
||||
toRejectedError: jest.Mock;
|
||||
};
|
||||
const { toRejectedError } = jest.requireMock('@/utils/errorUtils') as {
|
||||
toRejectedError: jest.Mock
|
||||
}
|
||||
toRejectedError.mockReturnValueOnce({
|
||||
status: 500,
|
||||
message: "Server Error",
|
||||
});
|
||||
message: 'Server Error'
|
||||
})
|
||||
|
||||
const thunk = getCalendarsListAsync();
|
||||
const result = await thunk(dispatch, getState, undefined);
|
||||
const thunk = getCalendarsListAsync()
|
||||
const result = await thunk(dispatch, getState, undefined)
|
||||
|
||||
expect(result.type).toBe("calendars/getCalendars/rejected");
|
||||
expect(result.type).toBe('calendars/getCalendars/rejected')
|
||||
expect(result.payload).toEqual({
|
||||
status: 500,
|
||||
message: "Server Error",
|
||||
});
|
||||
});
|
||||
message: 'Server Error'
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle error when fetching owner data fails", async () => {
|
||||
it('should handle error when fetching owner data fails', async () => {
|
||||
getState.mockReturnValue({
|
||||
calendars: {},
|
||||
user: { userData: { openpaasId: "user-123" } },
|
||||
});
|
||||
user: { userData: { openpaasId: 'user-123' } }
|
||||
})
|
||||
mockedGetCalendars.mockResolvedValue({
|
||||
_embedded: { "dav:calendar": [{ id: "cal-1" }] },
|
||||
});
|
||||
_embedded: { 'dav:calendar': [{ id: 'cal-1' }] }
|
||||
})
|
||||
mockedNormalizeCalendar.mockReturnValue({
|
||||
cal: { "dav:name": "Error Cal" },
|
||||
id: "cal-1",
|
||||
ownerId: "error-123",
|
||||
});
|
||||
cal: { 'dav:name': 'Error Cal' },
|
||||
id: 'cal-1',
|
||||
ownerId: 'error-123'
|
||||
})
|
||||
|
||||
// fetchOwnerData fails
|
||||
mockedFetchOwnerData.mockRejectedValueOnce(new Error("Network Error"));
|
||||
mockedFetchOwnerData.mockRejectedValueOnce(new Error('Network Error'))
|
||||
|
||||
const thunk = getCalendarsListAsync();
|
||||
const result = await thunk(dispatch, getState, undefined);
|
||||
const thunk = getCalendarsListAsync()
|
||||
const result = await thunk(dispatch, getState, undefined)
|
||||
|
||||
const payload = result.payload as any;
|
||||
expect(mockedFetchOwnerData).toHaveBeenCalledWith("error-123");
|
||||
expect(payload.importedCalendars["cal-1"].owner).toEqual({
|
||||
firstname: "",
|
||||
lastname: "Unknown User",
|
||||
emails: [],
|
||||
});
|
||||
const payload = result.payload as any
|
||||
expect(mockedFetchOwnerData).toHaveBeenCalledWith('error-123')
|
||||
expect(payload.importedCalendars['cal-1'].owner).toEqual({
|
||||
firstname: '',
|
||||
lastname: 'Unknown User',
|
||||
emails: []
|
||||
})
|
||||
// Errors array should contain the error
|
||||
expect(payload.errors).toContain("Network Error");
|
||||
});
|
||||
expect(payload.errors).toContain('Network Error')
|
||||
})
|
||||
|
||||
it("should return owner data mapping properly (including resource: true)", async () => {
|
||||
it('should return owner data mapping properly (including resource: true)', async () => {
|
||||
getState.mockReturnValue({
|
||||
calendars: {},
|
||||
user: { userData: { openpaasId: "user-123" } },
|
||||
});
|
||||
user: { userData: { openpaasId: 'user-123' } }
|
||||
})
|
||||
mockedGetCalendars.mockResolvedValue({
|
||||
_embedded: { "dav:calendar": [{ id: "cal-1" }] },
|
||||
});
|
||||
_embedded: { 'dav:calendar': [{ id: 'cal-1' }] }
|
||||
})
|
||||
mockedNormalizeCalendar.mockReturnValue({
|
||||
cal: { "dav:name": "Resource Cal" },
|
||||
id: "cal-1",
|
||||
ownerId: "resource-123",
|
||||
});
|
||||
cal: { 'dav:name': 'Resource Cal' },
|
||||
id: 'cal-1',
|
||||
ownerId: 'resource-123'
|
||||
})
|
||||
|
||||
// fetchOwnerData succeeds and returns a resource config structure
|
||||
mockedFetchOwnerData.mockResolvedValueOnce({
|
||||
firstname: "Creator",
|
||||
lastname: "User",
|
||||
firstname: 'Creator',
|
||||
lastname: 'User',
|
||||
emails: [],
|
||||
resource: true,
|
||||
});
|
||||
resource: true
|
||||
})
|
||||
|
||||
const thunk = getCalendarsListAsync();
|
||||
const result = await thunk(dispatch, getState, undefined);
|
||||
const thunk = getCalendarsListAsync()
|
||||
const result = await thunk(dispatch, getState, undefined)
|
||||
|
||||
const payload = result.payload as any;
|
||||
expect(mockedFetchOwnerData).toHaveBeenCalledWith("resource-123");
|
||||
expect(payload.importedCalendars["cal-1"].owner).toEqual({
|
||||
firstname: "Creator",
|
||||
lastname: "User",
|
||||
const payload = result.payload as any
|
||||
expect(mockedFetchOwnerData).toHaveBeenCalledWith('resource-123')
|
||||
expect(payload.importedCalendars['cal-1'].owner).toEqual({
|
||||
firstname: 'Creator',
|
||||
lastname: 'User',
|
||||
emails: [],
|
||||
resource: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
resource: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
import { fetchOwnerData } from "@/features/Calendars/services/helpers";
|
||||
import { getResourceDetails, getUserDetails } from "@/features/User/userAPI";
|
||||
import { fetchOwnerData } from '@/features/Calendars/services/helpers'
|
||||
import { getResourceDetails, getUserDetails } from '@/features/User/userAPI'
|
||||
|
||||
jest.mock("@/features/User/userAPI");
|
||||
jest.mock('@/features/User/userAPI')
|
||||
|
||||
const mockedGetUserDetails = getUserDetails as jest.Mock;
|
||||
const mockedGetResourceDetails = getResourceDetails as jest.Mock;
|
||||
const mockedGetUserDetails = getUserDetails as jest.Mock
|
||||
const mockedGetResourceDetails = getResourceDetails as jest.Mock
|
||||
|
||||
describe("helpers", () => {
|
||||
describe('helpers', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("fetchOwnerData", () => {
|
||||
it("should return user details successfully", async () => {
|
||||
describe('fetchOwnerData', () => {
|
||||
it('should return user details successfully', async () => {
|
||||
const mockUser = {
|
||||
firstname: "John",
|
||||
lastname: "Doe",
|
||||
emails: ["john@example.com"],
|
||||
};
|
||||
mockedGetUserDetails.mockResolvedValueOnce(mockUser);
|
||||
firstname: 'John',
|
||||
lastname: 'Doe',
|
||||
emails: ['john@example.com']
|
||||
}
|
||||
mockedGetUserDetails.mockResolvedValueOnce(mockUser)
|
||||
|
||||
const result = await fetchOwnerData("user-123");
|
||||
const result = await fetchOwnerData('user-123')
|
||||
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledWith("user-123");
|
||||
expect(mockedGetResourceDetails).not.toHaveBeenCalled();
|
||||
expect(result).toEqual(mockUser);
|
||||
});
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledWith('user-123')
|
||||
expect(mockedGetResourceDetails).not.toHaveBeenCalled()
|
||||
expect(result).toEqual(mockUser)
|
||||
})
|
||||
|
||||
it("should fetch resource details and its creator when user is not found", async () => {
|
||||
const mockResource = { creator: "creator-456" };
|
||||
it('should fetch resource details and its creator when user is not found', async () => {
|
||||
const mockResource = { creator: 'creator-456' }
|
||||
const mockCreator = {
|
||||
firstname: "Creator",
|
||||
lastname: "User",
|
||||
emails: ["creator@example.com"],
|
||||
};
|
||||
firstname: 'Creator',
|
||||
lastname: 'User',
|
||||
emails: ['creator@example.com']
|
||||
}
|
||||
|
||||
// Mock getUserDetails to fail with 404 for the initial call
|
||||
mockedGetUserDetails.mockRejectedValueOnce({
|
||||
response: { status: 404 },
|
||||
});
|
||||
response: { status: 404 }
|
||||
})
|
||||
|
||||
// Mock getResourceDetails to succeed and return a creator ID
|
||||
mockedGetResourceDetails.mockResolvedValueOnce(mockResource);
|
||||
mockedGetResourceDetails.mockResolvedValueOnce(mockResource)
|
||||
|
||||
// Mock getUserDetails to succeed when called for the creator
|
||||
mockedGetUserDetails.mockResolvedValueOnce(mockCreator);
|
||||
mockedGetUserDetails.mockResolvedValueOnce(mockCreator)
|
||||
|
||||
const result = await fetchOwnerData("resource-123");
|
||||
const result = await fetchOwnerData('resource-123')
|
||||
|
||||
expect(mockedGetUserDetails).toHaveBeenNthCalledWith(1, "resource-123");
|
||||
expect(mockedGetResourceDetails).toHaveBeenCalledWith("resource-123");
|
||||
expect(mockedGetUserDetails).toHaveBeenNthCalledWith(2, "creator-456");
|
||||
expect(mockedGetUserDetails).toHaveBeenNthCalledWith(1, 'resource-123')
|
||||
expect(mockedGetResourceDetails).toHaveBeenCalledWith('resource-123')
|
||||
expect(mockedGetUserDetails).toHaveBeenNthCalledWith(2, 'creator-456')
|
||||
expect(result).toEqual({
|
||||
...mockCreator,
|
||||
resource: true,
|
||||
administrators: undefined,
|
||||
resourceIcon: undefined,
|
||||
});
|
||||
});
|
||||
resourceIcon: undefined
|
||||
})
|
||||
})
|
||||
|
||||
it("should throw error when getUserDetails fails with non-404 error", async () => {
|
||||
const mockError = { response: { status: 500 } };
|
||||
mockedGetUserDetails.mockRejectedValueOnce(mockError);
|
||||
it('should throw error when getUserDetails fails with non-404 error', async () => {
|
||||
const mockError = { response: { status: 500 } }
|
||||
mockedGetUserDetails.mockRejectedValueOnce(mockError)
|
||||
|
||||
await expect(fetchOwnerData("user-123")).rejects.toEqual(mockError);
|
||||
await expect(fetchOwnerData('user-123')).rejects.toEqual(mockError)
|
||||
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledWith("user-123");
|
||||
expect(mockedGetResourceDetails).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledWith('user-123')
|
||||
expect(mockedGetResourceDetails).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should throw error when getResourceDetails fails", async () => {
|
||||
const mockError = new Error("Resource not found");
|
||||
it('should throw error when getResourceDetails fails', async () => {
|
||||
const mockError = new Error('Resource not found')
|
||||
|
||||
// Mock getUserDetails to fail with 404 for the initial call
|
||||
mockedGetUserDetails.mockRejectedValueOnce({
|
||||
response: { status: 404 },
|
||||
});
|
||||
response: { status: 404 }
|
||||
})
|
||||
|
||||
// Mock getResourceDetails to fail
|
||||
mockedGetResourceDetails.mockRejectedValueOnce(mockError);
|
||||
mockedGetResourceDetails.mockRejectedValueOnce(mockError)
|
||||
|
||||
await expect(fetchOwnerData("resource-123")).rejects.toEqual(mockError);
|
||||
await expect(fetchOwnerData('resource-123')).rejects.toEqual(mockError)
|
||||
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledWith("resource-123");
|
||||
expect(mockedGetResourceDetails).toHaveBeenCalledWith("resource-123");
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledTimes(1); // Only called once
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledWith('resource-123')
|
||||
expect(mockedGetResourceDetails).toHaveBeenCalledWith('resource-123')
|
||||
expect(mockedGetUserDetails).toHaveBeenCalledTimes(1) // Only called once
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user