Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / applications / calendar / src / app / helpers / event.test.ts
blobc822f6ee3639c354637c558a24c598fb60b8294e
1 import { MAX_ATTENDEES } from '@proton/shared/lib/calendar/constants';
2 import { ADDRESS_SEND } from '@proton/shared/lib/constants';
3 import { addressBuilder } from '@proton/testing/lib/builders';
5 import {
6     getCanChangeCalendarOfEvent,
7     getCanDeleteEvent,
8     getCanDuplicateEvent,
9     getCanEditEvent,
10     getCanEditSharedEventData,
11     getCanReplyToEvent,
12     getCannotSaveEvent,
13     getIsAvailableCalendar,
14 } from './event';
16 describe('getCannotSaveEvent()', () => {
17     test('Member cannot create invites in a shared calendar', () => {
18         const isOwnedCalendar = false;
19         const isOrganizer = true;
20         const numberOfAttendees = MAX_ATTENDEES - 1;
21         const canEditSharedEventData = true;
23         expect(getCannotSaveEvent({ isOwnedCalendar, isOrganizer, numberOfAttendees, canEditSharedEventData })).toEqual(
24             true
25         );
26     });
28     test('Owner can create invites in a personal calendar', () => {
29         const isOwnedCalendar = true;
30         const isOrganizer = true;
31         const numberOfAttendees = MAX_ATTENDEES - 1;
32         const canEditSharedEventData = true;
34         expect(getCannotSaveEvent({ isOwnedCalendar, isOrganizer, numberOfAttendees, canEditSharedEventData })).toEqual(
35             false
36         );
37     });
39     test('Owner cannot create invites with too many participants', () => {
40         const isOwnedCalendar = true;
41         const isOrganizer = true;
42         const numberOfAttendees = MAX_ATTENDEES + 1;
43         const canEditSharedEventData = true;
45         expect(
46             getCannotSaveEvent({
47                 isOwnedCalendar,
48                 isOrganizer,
49                 numberOfAttendees,
50                 canEditSharedEventData,
51                 maxAttendees: MAX_ATTENDEES,
52             })
53         ).toEqual(true);
54     });
56     test('Attendee can add notifications to invite in a personal calendar', () => {
57         const isOwnedCalendar = true;
58         const isOrganizer = false;
59         const numberOfAttendees = MAX_ATTENDEES - 1;
60         const canEditSharedEventData = false;
62         expect(getCannotSaveEvent({ isOwnedCalendar, isOrganizer, numberOfAttendees, canEditSharedEventData })).toEqual(
63             false
64         );
65     });
67     test('Attendee can add notifications to invite in a shared calendar', () => {
68         const isOwnedCalendar = false;
69         const isOrganizer = false;
70         const numberOfAttendees = MAX_ATTENDEES - 1;
71         const canEditSharedEventData = false;
73         expect(getCannotSaveEvent({ isOwnedCalendar, isOrganizer, numberOfAttendees, canEditSharedEventData })).toEqual(
74             false
75         );
76     });
78     test('Member can add notifications to an invite she organized in a shared calendar', () => {
79         const isOwnedCalendar = false;
80         const isOrganizer = true;
81         const numberOfAttendees = 10;
82         const canEditSharedEventData = false;
84         expect(getCannotSaveEvent({ isOwnedCalendar, isOrganizer, numberOfAttendees, canEditSharedEventData })).toEqual(
85             false
86         );
87     });
88 });
90 describe('getCanEditEvent()', () => {
91     test('User can edit events only if the calendar is of known type and is not disabled', () => {
92         const combinations = [];
93         for (let i = 0; i < 2 ** 2; i++) {
94             const isUnknownCalendar = !!(1 & i);
95             const isCalendarDisabled = !!(2 & i);
97             combinations.push({ isUnknownCalendar, isCalendarDisabled });
98         }
99         combinations.forEach(({ isUnknownCalendar, isCalendarDisabled }) => {
100             expect(
101                 getCanEditEvent({
102                     isUnknownCalendar,
103                     isCalendarDisabled,
104                 })
105             ).toEqual(!isCalendarDisabled && !isUnknownCalendar);
106         });
107     });
110 describe('getCanDeleteEvent()', () => {
111     test('User cannot delete events in subscribed or other read-only calendars', () => {
112         const combinations = [];
113         for (let i = 0; i < 2 ** 2; i++) {
114             const isOwnedCalendar = !!(1 & i);
115             const isInvitation = !!(2 & i);
117             combinations.push({ isOwnedCalendar, isInvitation });
118         }
119         combinations.forEach(({ isOwnedCalendar, isInvitation }) => {
120             expect(
121                 getCanDeleteEvent({
122                     isOwnedCalendar,
123                     isCalendarWritable: false,
124                     isInvitation,
125                 })
126             ).toEqual(false);
127         });
128     });
130     test('Attendee can delete invites in her own calendar', () => {
131         const isOwnedCalendar = true;
132         const isCalendarWritable = true;
133         const isInvitation = true;
135         expect(getCanDeleteEvent({ isOwnedCalendar, isCalendarWritable, isInvitation })).toEqual(true);
136     });
138     test('Member cannot delete invites in a shared calendar with edit rights', () => {
139         const isOwnedCalendar = false;
140         const isCalendarWritable = true;
141         const isInvitation = true;
143         expect(getCanDeleteEvent({ isOwnedCalendar, isCalendarWritable, isInvitation })).toEqual(false);
144     });
146     test('Member cannot delete invites in a shared calendar with view-only rights', () => {
147         const isOwnedCalendar = false;
148         const isCalendarWritable = false;
149         const isInvitation = true;
151         expect(getCanDeleteEvent({ isOwnedCalendar, isCalendarWritable, isInvitation })).toEqual(false);
152     });
155 describe('getCanEditSharedEventData()', () => {
156     const activeAddress = addressBuilder();
157     const cannotSendAddress = {
158         ...activeAddress,
159         Send: ADDRESS_SEND.SEND_NO,
160     };
162     test('Owner can edit shared event data of events which are not invitations', () => {
163         expect(
164             getCanEditSharedEventData({
165                 isOwnedCalendar: true,
166                 isCalendarWritable: true,
167                 isOrganizer: false,
168                 isAttendee: false,
169                 isInvitation: false,
170                 selfAddress: undefined,
171             })
172         ).toEqual(true);
173     });
175     test('Owner can edit shared event data of events she organizes if the address is active', () => {
176         expect(
177             getCanEditSharedEventData({
178                 isOwnedCalendar: true,
179                 isCalendarWritable: true,
180                 isOrganizer: true,
181                 isAttendee: false,
182                 isInvitation: false,
183                 selfAddress: activeAddress,
184             })
185         ).toEqual(true);
186     });
188     test('Owner cannot edit shared event data of events she organizes if the address cannot send', () => {
189         expect(
190             getCanEditSharedEventData({
191                 isOwnedCalendar: true,
192                 isCalendarWritable: true,
193                 isOrganizer: true,
194                 isAttendee: false,
195                 isInvitation: true,
196                 selfAddress: cannotSendAddress,
197             })
198         ).toEqual(false);
199     });
201     test('User cannot edit shared event data in subscribed calendars or other read-only calendars', () => {
202         const combinations = [];
203         for (let i = 0; i < 2 ** 3; i++) {
204             const isOrganizer = !!(1 & i);
205             const isAttendee = !!(2 & i);
206             const isInvitation = isOrganizer || isAttendee || !!(4 & i);
207             const selfAddress = isOrganizer || isAttendee ? activeAddress : undefined;
209             combinations.push({ isOrganizer, isAttendee, isInvitation, selfAddress });
210         }
211         combinations.forEach(({ isOrganizer, isAttendee, isInvitation, selfAddress }) => {
212             expect(
213                 getCanEditSharedEventData({
214                     isOwnedCalendar: true,
215                     isCalendarWritable: false,
216                     isOrganizer,
217                     isAttendee,
218                     isInvitation,
219                     selfAddress,
220                 })
221             ).toEqual(false);
222         });
223     });
225     test('Member cannot edit shared event data in shared calendars with view-only rights', () => {
226         const combinations = [];
227         for (let i = 0; i < 2 ** 3; i++) {
228             const isOrganizer = !!(1 & i);
229             const isAttendee = !!(2 & i);
230             const isInvitation = isOrganizer || isAttendee || !!(4 & i);
231             const selfAddress = isOrganizer || isAttendee ? activeAddress : undefined;
233             combinations.push({ isOrganizer, isAttendee, isInvitation, selfAddress });
234         }
235         combinations.forEach(({ isOrganizer, isAttendee, isInvitation, selfAddress }) => {
236             expect(
237                 getCanEditSharedEventData({
238                     isOwnedCalendar: false,
239                     isCalendarWritable: false,
240                     isOrganizer,
241                     isAttendee,
242                     isInvitation,
243                     selfAddress,
244                 })
245             ).toEqual(false);
246         });
247     });
249     test('Member with edit rights cannot edit shared event data of invitations', () => {
250         const combinations = [];
251         for (let i = 0; i < 2 ** 2; i++) {
252             const isOrganizer = !!(1 & i);
253             const isAttendee = !!(2 & i);
254             const selfAddress = isOrganizer || isAttendee ? activeAddress : undefined;
256             combinations.push({ isOrganizer, isAttendee, selfAddress });
257         }
258         combinations.forEach(({ isOrganizer, isAttendee, selfAddress }) => {
259             expect(
260                 getCanEditSharedEventData({
261                     isOwnedCalendar: false,
262                     isCalendarWritable: true,
263                     isOrganizer,
264                     isAttendee,
265                     isInvitation: true,
266                     selfAddress,
267                 })
268             ).toEqual(false);
269         });
270     });
272     test('Member with view-only rights cannot edit invitations', () => {
273         const combinations = [];
274         for (let i = 0; i < 2 ** 2; i++) {
275             const isOrganizer = !!(1 & i);
276             const isAttendee = !!(2 & i);
277             const selfAddress = isOrganizer || isAttendee ? activeAddress : undefined;
279             combinations.push({ isOrganizer, isAttendee, selfAddress });
280         }
281         combinations.forEach(({ isOrganizer, isAttendee, selfAddress }) => {
282             expect(
283                 getCanEditSharedEventData({
284                     isOwnedCalendar: false,
285                     isCalendarWritable: false,
286                     isOrganizer,
287                     isAttendee,
288                     isInvitation: true,
289                     selfAddress,
290                 })
291             ).toEqual(false);
292         });
293     });
295     test('User cannot edit invitations in owned calendars if she is not organizing or attending', () => {
296         expect(
297             getCanEditSharedEventData({
298                 isOwnedCalendar: true,
299                 isCalendarWritable: true,
300                 isOrganizer: false,
301                 isAttendee: false,
302                 isInvitation: true,
303                 selfAddress: undefined,
304             })
305         ).toEqual(false);
306     });
309 describe('getCanChangeCalendar()', () => {
310     test('User can change calendar of events that are not invitations', () => {
311         const combinations = [];
312         for (let i = 0; i < 2 ** 2; i++) {
313             const isOwnedCalendar = !!(1 & i);
314             const isSingleEdit = !!(2 & i);
316             combinations.push({ isOwnedCalendar, isSingleEdit });
317         }
318         combinations.forEach(({ isOwnedCalendar, isSingleEdit }) => {
319             expect(
320                 getCanChangeCalendarOfEvent({
321                     isCreateEvent: false,
322                     isOwnedCalendar,
323                     isCalendarWritable: true,
324                     isSingleEdit,
325                     isInvitation: false,
326                     isAttendee: false,
327                     isOrganizer: false,
328                 })
329             ).toEqual(true);
330         });
331     });
333     test('User creating event can change calendar', () => {
334         const combinations = [];
335         for (let i = 0; i < 2 ** 2; i++) {
336             const isOwnedCalendar = !!(1 & i);
337             const isOrganizer = !!(2 & i);
338             const isInvitation = isOrganizer;
340             combinations.push({ isOwnedCalendar, isOrganizer, isInvitation });
341         }
342         combinations.forEach(({ isOwnedCalendar, isInvitation, isOrganizer }) => {
343             expect(
344                 getCanChangeCalendarOfEvent({
345                     isCreateEvent: true,
346                     isOwnedCalendar,
347                     isCalendarWritable: true,
348                     isSingleEdit: false,
349                     isInvitation,
350                     isAttendee: false,
351                     isOrganizer,
352                 })
353             ).toEqual(true);
354         });
355     });
357     test('User cannot change calendar of event in non-writable calendar', () => {
358         const combinations = [];
359         for (let i = 0; i < 2 ** 5; i++) {
360             const isOwnedCalendar = !!(1 & i);
361             const isSingleEdit = !!(2 & i);
362             const isOrganizer = !!(4 & i);
363             const isAttendee = isOrganizer ? false : !!(8 & i);
364             const isInvitation = isOrganizer || isAttendee ? true : !!(16 & i);
366             combinations.push({ isOwnedCalendar, isSingleEdit, isInvitation, isOrganizer, isAttendee });
367         }
368         combinations.forEach(({ isOwnedCalendar, isSingleEdit, isInvitation, isOrganizer, isAttendee }) => {
369             expect(
370                 getCanChangeCalendarOfEvent({
371                     isCreateEvent: false,
372                     isOwnedCalendar,
373                     isCalendarWritable: false,
374                     isSingleEdit,
375                     isInvitation,
376                     isAttendee,
377                     isOrganizer,
378                 })
379             ).toEqual(false);
380         });
381     });
383     test('Member cannot change calendar of non-organized invitation in shared calendar', () => {
384         const combinations = [];
385         for (let i = 0; i < 2 ** 2; i++) {
386             const isSingleEdit = !!(1 & i);
387             const isAttendee = !!(2 & i);
389             combinations.push({ isSingleEdit, isAttendee });
390         }
391         combinations.forEach(({ isSingleEdit, isAttendee }) => {
392             expect(
393                 getCanChangeCalendarOfEvent({
394                     isCreateEvent: false,
395                     isOwnedCalendar: false,
396                     isCalendarWritable: true,
397                     isSingleEdit,
398                     isInvitation: true,
399                     isAttendee,
400                     isOrganizer: false,
401                 })
402             ).toEqual(false);
403         });
404     });
406     test('Organizer cannot change calendar existing invitation', () => {
407         const combinations = [];
408         for (let i = 0; i < 2 ** 3; i++) {
409             const isOwnedCalendar = !!(1 & i);
410             const isCalendarWritable = !!(2 & i);
411             const isSingleEdit = !!(4 & i);
413             combinations.push({ isOwnedCalendar, isCalendarWritable, isSingleEdit });
414         }
415         combinations.forEach(({ isOwnedCalendar, isCalendarWritable, isSingleEdit }) => {
416             expect(
417                 getCanChangeCalendarOfEvent({
418                     isCreateEvent: false,
419                     isOwnedCalendar,
420                     isCalendarWritable,
421                     isSingleEdit,
422                     isInvitation: true,
423                     isAttendee: false,
424                     isOrganizer: true,
425                 })
426             ).toEqual(false);
427         });
428     });
430     test('Attendee can change calendar in owned calendars if the event is not a single edit', () => {
431         expect(
432             getCanChangeCalendarOfEvent({
433                 isCreateEvent: false,
434                 isOwnedCalendar: true,
435                 isCalendarWritable: true,
436                 isSingleEdit: false,
437                 isInvitation: true,
438                 isAttendee: true,
439                 isOrganizer: false,
440             })
441         ).toEqual(true);
442         expect(
443             getCanChangeCalendarOfEvent({
444                 isCreateEvent: false,
445                 isOwnedCalendar: true,
446                 isCalendarWritable: true,
447                 isSingleEdit: true,
448                 isInvitation: true,
449                 isAttendee: true,
450                 isOrganizer: false,
451             })
452         ).toEqual(false);
453     });
456 describe('getIsAvailableCalendar()', () => {
457     test('User cannot change calendar of events to subscribed or other read-only calendars', () => {
458         const combinations = [];
459         for (let i = 0; i < 2 ** 2; i++) {
460             const isOwnedCalendar = !!(1 & i);
461             const isInvitation = !!(2 & i);
463             combinations.push({ isOwnedCalendar, isInvitation });
464         }
465         combinations.forEach(({ isOwnedCalendar, isInvitation }) => {
466             expect(
467                 getIsAvailableCalendar({
468                     isOwnedCalendar,
469                     isCalendarWritable: false,
470                     isInvitation,
471                 })
472             ).toEqual(false);
473         });
474     });
476     test('Invitations can only be changed to owned calendars', () => {
477         const combinations = [];
478         for (let i = 0; i < 2 ** 2; i++) {
479             const isOwnedCalendar = !!(1 & i);
480             const isCalendarWritable = isOwnedCalendar || !!(2 & i);
482             combinations.push({ isOwnedCalendar, isCalendarWritable });
483         }
484         combinations.forEach(({ isOwnedCalendar, isCalendarWritable }) => {
485             expect(
486                 getIsAvailableCalendar({
487                     isOwnedCalendar,
488                     isCalendarWritable,
489                     isInvitation: true,
490                 })
491             ).toEqual(isOwnedCalendar);
492         });
493     });
495     test('Events that are not invitations can be changed to writable calendars', () => {
496         const combinations = [];
497         for (let i = 0; i < 2 ** 1; i++) {
498             const isOwnedCalendar = !!(1 & i);
499             const isCalendarWritable = isOwnedCalendar || !!(2 & i);
501             combinations.push({ isOwnedCalendar, isCalendarWritable });
502         }
503         combinations.forEach(({ isOwnedCalendar, isCalendarWritable }) => {
504             expect(
505                 getIsAvailableCalendar({
506                     isOwnedCalendar,
507                     isCalendarWritable,
508                     isInvitation: true,
509                 })
510             ).toEqual(isCalendarWritable);
511         });
512     });
515 describe('getCanDuplicateEvent()', () => {
516     test('User cannot duplicate events in unknown calendars', () => {
517         const combinations = [];
518         for (let i = 0; i < 2 ** 4; i++) {
519             const isSubscribedCalendar = !!(1 & i);
520             const isOwnedCalendar = !!(2 & i);
521             const isOrganizer = !!(4 & i);
522             const isInvitation = !!(8 & i);
524             combinations.push({ isSubscribedCalendar, isOwnedCalendar, isOrganizer, isInvitation });
525         }
526         combinations.forEach(({ isSubscribedCalendar, isOwnedCalendar, isOrganizer, isInvitation }) => {
527             expect(
528                 getCanDuplicateEvent({
529                     isUnknownCalendar: true,
530                     isSubscribedCalendar,
531                     isOwnedCalendar,
532                     isInvitation,
533                     isOrganizer,
534                 })
535             ).toEqual(false);
536         });
537     });
539     test('User cannot duplicate events in subscribed calendars', () => {
540         const combinations = [];
541         for (let i = 0; i < 2 ** 2; i++) {
542             const isOrganizer = !!(1 & i);
543             const isInvitation = isOrganizer || !!(2 & i);
545             combinations.push({ isOrganizer, isInvitation });
546         }
547         combinations.forEach(({ isOrganizer, isInvitation }) => {
548             expect(
549                 getCanDuplicateEvent({
550                     isUnknownCalendar: false,
551                     isSubscribedCalendar: true,
552                     isOwnedCalendar: true,
553                     isInvitation,
554                     isOrganizer,
555                 })
556             ).toEqual(false);
557         });
558     });
560     test('Owner can duplicate events that are not invitations', () => {
561         expect(
562             getCanDuplicateEvent({
563                 isUnknownCalendar: false,
564                 isSubscribedCalendar: false,
565                 isOwnedCalendar: true,
566                 isInvitation: false,
567                 isOrganizer: false,
568             })
569         ).toEqual(true);
570     });
572     test('Owner can duplicate invitations that she is organizing', () => {
573         expect(
574             getCanDuplicateEvent({
575                 isUnknownCalendar: false,
576                 isSubscribedCalendar: false,
577                 isOwnedCalendar: true,
578                 isInvitation: true,
579                 isOrganizer: true,
580             })
581         ).toEqual(true);
582     });
584     test('Owner cannot duplicate invitations if she is not the organizer', () => {
585         expect(
586             getCanDuplicateEvent({
587                 isUnknownCalendar: false,
588                 isSubscribedCalendar: false,
589                 isOwnedCalendar: true,
590                 isInvitation: true,
591                 isOrganizer: false,
592             })
593         ).toEqual(false);
594     });
596     test('Member can duplicate events that are not invitations', () => {
597         expect(
598             getCanDuplicateEvent({
599                 isUnknownCalendar: false,
600                 isSubscribedCalendar: false,
601                 isOwnedCalendar: false,
602                 isInvitation: false,
603                 isOrganizer: false,
604             })
605         ).toEqual(true);
606     });
608     test('Member cannot duplicate invitations', () => {
609         const combinations = [];
610         for (let i = 0; i < 2 ** 1; i++) {
611             const isOrganizer = !!(1 & i);
613             combinations.push({ isOrganizer });
614         }
615         combinations.forEach(({ isOrganizer }) => {
616             expect(
617                 getCanDuplicateEvent({
618                     isUnknownCalendar: false,
619                     isSubscribedCalendar: false,
620                     isOwnedCalendar: false,
621                     isInvitation: true,
622                     isOrganizer,
623                 })
624             ).toEqual(false);
625         });
626     });
629 describe('getCanReplyToEvent()', () => {
630     test('User can reply to events he is invited in one of his own personal calendars if and only if the event is not cancelled', () => {
631         const combinations = [];
632         for (let i = 0; i < 2 ** 1; i++) {
633             const isCancelled = !!(1 & i);
635             combinations.push({ isCancelled });
636         }
637         combinations.forEach(({ isCancelled }) => {
638             expect(
639                 getCanReplyToEvent({
640                     isOwnedCalendar: true,
641                     isCalendarWritable: true,
642                     isAttendee: true,
643                     isCancelled,
644                 })
645             ).toEqual(!isCancelled);
646         });
647     });
649     test('User cannot reply to events he is not attending', () => {
650         const combinations = [];
651         for (let i = 0; i < 2 ** 3; i++) {
652             const isOwnedCalendar = !!(1 & i);
653             const isCalendarWritable = isOwnedCalendar || !!(2 & i);
654             const isCancelled = !!(4 & i);
656             combinations.push({ isOwnedCalendar, isCalendarWritable, isCancelled });
657         }
658         combinations.forEach(({ isOwnedCalendar, isCalendarWritable, isCancelled }) => {
659             expect(
660                 getCanReplyToEvent({
661                     isOwnedCalendar,
662                     isCalendarWritable,
663                     isAttendee: false,
664                     isCancelled,
665                 })
666             ).toEqual(false);
667         });
668     });
670     test('User cannot reply to invitations on subscribed calendars', () => {
671         const combinations = [];
672         for (let i = 0; i < 2 ** 1; i++) {
673             const isCancelled = !!(1 & i);
675             combinations.push({ isCancelled });
676         }
677         combinations.forEach(({ isCancelled }) => {
678             expect(
679                 getCanReplyToEvent({
680                     isOwnedCalendar: true,
681                     isCalendarWritable: false,
682                     isAttendee: true,
683                     isCancelled,
684                 })
685             ).toEqual(false);
686         });
687     });
689     test('User cannot reply to invitations on shared calendars, with or without edit rights', () => {
690         const combinations = [];
691         for (let i = 0; i < 2 ** 2; i++) {
692             const isCalendarWritable = !!(1 & i);
693             const isCancelled = !!(2 & i);
695             combinations.push({ isCalendarWritable, isCancelled });
696         }
697         combinations.forEach(({ isCalendarWritable, isCancelled }) => {
698             expect(
699                 getCanReplyToEvent({
700                     isOwnedCalendar: false,
701                     isCalendarWritable,
702                     isAttendee: true,
703                     isCancelled,
704                 })
705             ).toEqual(false);
706         });
707     });