cfb2b59584
* [#523] added way to manage attendance for delegated calendars * [DELEGATION] #523 & #524 changed eventURL calculation for delegated events to have the rigth one * [#524] added drag and drop authorisation to delegated event * [#524] added tests * [#524] support for the permission access * [#524] no edit when delegated event is not public
232 lines
6.8 KiB
TypeScript
232 lines
6.8 KiB
TypeScript
import {
|
|
eventToFullCalendarFormat,
|
|
getCalendarVisibility,
|
|
} from "@/components/Calendar/utils/calendarUtils";
|
|
import { Calendar, DelegationAccess } from "@/features/Calendars/CalendarTypes";
|
|
import { AclEntry } from "@/features/Calendars/types/CalendarData";
|
|
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
|
import { userOrganiser } from "@/features/User/userDataTypes";
|
|
|
|
describe("getCalendarVisibility", () => {
|
|
it("returns 'public' when {DAV:}authenticated has {DAV:}read", () => {
|
|
const acl: AclEntry[] = [
|
|
{
|
|
privilege: "{DAV:}read",
|
|
principal: "{DAV:}authenticated",
|
|
protected: true,
|
|
},
|
|
];
|
|
expect(getCalendarVisibility(acl)).toBe("public");
|
|
});
|
|
|
|
it("returns 'private' when {DAV:}authenticated only has read-free-busy", () => {
|
|
const acl: AclEntry[] = [
|
|
{
|
|
privilege: "{urn:ietf:params:xml:ns:caldav}read-free-busy",
|
|
principal: "{DAV:}authenticated",
|
|
protected: true,
|
|
},
|
|
];
|
|
expect(getCalendarVisibility(acl)).toBe("private");
|
|
});
|
|
|
|
it("returns 'private' when {DAV:}authenticated is not present", () => {
|
|
const acl: AclEntry[] = [
|
|
{
|
|
privilege: "{DAV:}read",
|
|
principal: "principals/users/123",
|
|
protected: true,
|
|
},
|
|
];
|
|
expect(getCalendarVisibility(acl)).toBe("private");
|
|
});
|
|
|
|
it("ignores non-authenticated principals and still returns 'private'", () => {
|
|
const acl: AclEntry[] = [
|
|
{
|
|
privilege: "{DAV:}read",
|
|
principal: "principals/users/other",
|
|
protected: true,
|
|
},
|
|
{
|
|
privilege: "{DAV:}write",
|
|
principal: "principals/users/other",
|
|
protected: true,
|
|
},
|
|
];
|
|
expect(getCalendarVisibility(acl)).toBe("private");
|
|
});
|
|
|
|
it("stops early when it finds a {DAV:}read for {DAV:}authenticated", () => {
|
|
const acl: AclEntry[] = [
|
|
{
|
|
privilege: "{DAV:}read",
|
|
principal: "{DAV:}authenticated",
|
|
protected: true,
|
|
},
|
|
{
|
|
privilege: "{DAV:}write",
|
|
principal: "{DAV:}authenticated",
|
|
protected: true,
|
|
},
|
|
];
|
|
expect(getCalendarVisibility(acl)).toBe("public");
|
|
});
|
|
});
|
|
|
|
jest.mock("twake-i18n", () => ({
|
|
useI18n: () => ({ t: (key: string) => key }),
|
|
}));
|
|
|
|
const baseEvent: CalendarEvent = {
|
|
uid: "event-1",
|
|
calId: "user1/cal1",
|
|
title: "Test Event",
|
|
start: "2024-01-15T10:00:00",
|
|
end: "2024-01-15T11:00:00",
|
|
allday: false,
|
|
organizer: { cal_address: "owner@example.com" } as userOrganiser,
|
|
color: { light: "#FF0000" },
|
|
class: "PUBLIC",
|
|
} as unknown as CalendarEvent;
|
|
|
|
const makeCalendar = (overrides: Partial<Calendar> = {}): Calendar =>
|
|
({
|
|
id: "user1/cal1",
|
|
name: "Test Calendar",
|
|
delegated: false,
|
|
access: {} as DelegationAccess,
|
|
owner: { emails: ["alice@example.com"], firstname: "Alice" },
|
|
color: { light: "#FF0000" },
|
|
events: {},
|
|
...overrides,
|
|
}) as Calendar;
|
|
|
|
const callFormat = (
|
|
event: CalendarEvent,
|
|
calendars: Record<string, Calendar>,
|
|
userAddress = "alice@example.com",
|
|
userId = "user1"
|
|
) =>
|
|
eventToFullCalendarFormat([event], [], userId, userAddress, false, calendars);
|
|
|
|
describe("eventToFullCalendarFormat - editable flag", () => {
|
|
describe("personal calendar (non-delegated)", () => {
|
|
const calendar = makeCalendar({ delegated: false });
|
|
|
|
it("is editable when user is organizer", () => {
|
|
const event = {
|
|
...baseEvent,
|
|
organizer: { cal_address: "alice@example.com" },
|
|
} as CalendarEvent;
|
|
const [result] = callFormat(event, { "user1/cal1": calendar });
|
|
expect(result.editable).toBe(true);
|
|
});
|
|
|
|
it("is not editable when user is not organizer", () => {
|
|
const event = {
|
|
...baseEvent,
|
|
organizer: { cal_address: "other@example.com" },
|
|
} as CalendarEvent;
|
|
const [result] = callFormat(event, { "user1/cal1": calendar });
|
|
expect(result.editable).toBe(false);
|
|
});
|
|
|
|
it("is not editable when pending", () => {
|
|
const event = {
|
|
...baseEvent,
|
|
organizer: { cal_address: "alice@example.com" },
|
|
} as CalendarEvent;
|
|
const [result] = eventToFullCalendarFormat(
|
|
[event],
|
|
[],
|
|
"user1",
|
|
"alice@example.com",
|
|
true,
|
|
{ "user1/cal1": calendar }
|
|
);
|
|
expect(result.editable).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("delegated calendar", () => {
|
|
const writeDelegatedCalendar = makeCalendar({
|
|
id: "user2/cal1",
|
|
delegated: true,
|
|
access: { write: true } as DelegationAccess,
|
|
owner: { emails: ["owner@example.com"], firstname: "Owner" },
|
|
});
|
|
const readDelegatedCalendar = makeCalendar({
|
|
id: "user2/cal1",
|
|
delegated: true,
|
|
access: { read: true } as DelegationAccess,
|
|
owner: { emails: ["owner@example.com"], firstname: "Owner" },
|
|
});
|
|
const event = { ...baseEvent, calId: "user2/cal1" };
|
|
|
|
it("is editable when owner is organizer and delegated user has write rights", () => {
|
|
const [result] = callFormat(
|
|
{
|
|
...event,
|
|
organizer: { cal_address: "owner@example.com" },
|
|
} as CalendarEvent,
|
|
{ "user2/cal1": writeDelegatedCalendar }
|
|
);
|
|
expect(result.editable).toBe(true);
|
|
});
|
|
|
|
it("is not editable when owner is organizer but delegated user only has read access", () => {
|
|
const [result] = callFormat(
|
|
{
|
|
...event,
|
|
organizer: { cal_address: "owner@example.com" },
|
|
} as CalendarEvent,
|
|
{ "user2/cal1": readDelegatedCalendar }
|
|
);
|
|
expect(result.editable).toBe(false);
|
|
});
|
|
|
|
it("is not editable when owner is not organizer", () => {
|
|
const [result] = callFormat(
|
|
{
|
|
...event,
|
|
organizer: { cal_address: "someone-else@example.com" },
|
|
} as CalendarEvent,
|
|
{ "user2/cal1": writeDelegatedCalendar }
|
|
);
|
|
expect(result.editable).toBe(false);
|
|
});
|
|
|
|
it("is not editable when pending even if owner is organizer", () => {
|
|
const [result] = eventToFullCalendarFormat(
|
|
[
|
|
{
|
|
...event,
|
|
organizer: { cal_address: "owner@example.com" },
|
|
} as CalendarEvent,
|
|
],
|
|
[],
|
|
"user1",
|
|
"alice@example.com",
|
|
true,
|
|
{ "user2/cal1": writeDelegatedCalendar }
|
|
);
|
|
expect(result.editable).toBe(false);
|
|
});
|
|
|
|
it("uses owner email not logged-in user email for organizer check", () => {
|
|
// logged-in user is alice, owner is owner@example.com
|
|
// organizer matches owner → editable
|
|
const [result] = callFormat(
|
|
{
|
|
...event,
|
|
organizer: { cal_address: "owner@example.com" },
|
|
} as CalendarEvent,
|
|
{ "user2/cal1": writeDelegatedCalendar },
|
|
"alice@example.com" // logged-in user, should NOT be used for delegated
|
|
);
|
|
expect(result.editable).toBe(true);
|
|
});
|
|
});
|
|
});
|