test(core): add tests for events utils (#13265)

This adds tests for the following utils found in `core`:

- event utils
- getAggregateWorkingHours
- getCalendarsEvents

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
James Garbutt
2024-01-19 13:22:28 +00:00
committed by GitHub
co-authored by Keith Williams
parent 74803dc122
commit 26e50d24b0
3 changed files with 930 additions and 0 deletions
+394
View File
@@ -0,0 +1,394 @@
import type { TFunction } from "next-i18next";
import { describe, expect, it, vi } from "vitest";
import * as event from "./event";
describe("event tests", () => {
describe("fn: getEventName", () => {
it("should return event_between_users message if no name", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
t: tFunc as TFunction,
});
expect(result).toBe("foo");
const lastCall = tFunc.mock.lastCall;
expect(lastCall).toEqual([
"event_between_users",
{
eventName: "example event type",
host: "example host",
attendeeName: "example attendee",
interpolation: {
escapeValue: false,
},
},
]);
});
it("should return event_between_users message if no name with team set", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
teamName: "example team name",
t: tFunc as TFunction,
});
expect(result).toBe("foo");
const lastCall = tFunc.mock.lastCall;
expect(lastCall).toEqual([
"event_between_users",
{
eventName: "example event type",
host: "example team name",
attendeeName: "example attendee",
interpolation: {
escapeValue: false,
},
},
]);
});
it("should return event name if no vars used", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "example event name",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("example event name");
});
it("should support templating of event type", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "event type: {Event type title}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("event type: example event type");
});
it("should support templating of scheduler", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "scheduler: {Scheduler}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("scheduler: example attendee");
});
it("should support templating of organiser", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "organiser: {Organiser}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("organiser: example host");
});
it("should support templating of user", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "user: {USER}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("user: example attendee");
});
it("should support templating of attendee", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "attendee: {ATTENDEE}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("attendee: example attendee");
});
it("should support templating of host", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "host: {HOST}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("host: example host");
});
it("should support templating of attendee with host/attendee", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "host or attendee: {HOST/ATTENDEE}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("host or attendee: example attendee");
});
it("should support templating of host with host/attendee", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName(
{
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "host or attendee: {HOST/ATTENDEE}",
t: tFunc as TFunction,
},
true
);
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("host or attendee: example host");
});
it("should support templating of custom booking fields", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "custom field: {customField}",
bookingFields: {
customField: "example custom field",
},
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("custom field: example custom field");
});
it("should support templating of custom booking fields with values", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "custom field: {customField}",
bookingFields: {
customField: {
value: "example custom field",
},
},
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("custom field: example custom field");
});
it("should support templating of custom booking fields with non-string values", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "custom field: {customField}",
bookingFields: {
customField: {
value: 808,
},
},
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("custom field: 808");
});
it("should support templating of custom booking fields with no value", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "custom field: {customField}",
bookingFields: {
customField: {
value: undefined,
},
},
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("custom field: ");
});
it("should support templating of location via {Location}", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
location: "attendeeInPerson",
eventName: "location: {Location}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("location: in_person_attendee_address");
});
it("should support templating of location via {LOCATION}", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
location: "attendeeInPerson",
eventName: "location: {LOCATION}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("location: in_person_attendee_address");
});
it("should strip location template if none set", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
eventName: "location: {Location}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("location: ");
});
it("should strip location template if empty", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
location: "",
eventName: "location: {Location}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("location: ");
});
it("should template {Location} as passed location if unknown type", () => {
const tFunc = vi.fn(() => "foo");
const result = event.getEventName({
attendeeName: "example attendee",
eventType: "example event type",
host: "example host",
location: "unknownNonsense",
eventName: "location: {Location}",
t: tFunc as TFunction,
});
expect(tFunc).not.toHaveBeenCalled();
expect(result).toBe("location: unknownNonsense");
});
});
describe("fn: validateCustomEventName", () => {
it("should be valid when no variables used", () => {
expect(event.validateCustomEventName("foo", "error message")).toBe(true);
});
[
"Event type title",
"Organiser",
"Scheduler",
"Location",
"LOCATION",
"HOST/ATTENDEE",
"HOST",
"ATTENDEE",
"USER",
].forEach((value) => {
it(`should support {${value}} variable`, () => {
expect(event.validateCustomEventName(`foo {${value}} bar`, "error message")).toBe(true);
expect(event.validateCustomEventName(`{${value}} bar`, "error message")).toBe(true);
expect(event.validateCustomEventName(`foo {${value}}`, "error message")).toBe(true);
});
});
it("should support booking field variables", () => {
expect(
event.validateCustomEventName("foo{customField}bar", "error message", {
customField: true,
})
).toBe(true);
});
it("should return error when invalid variable used", () => {
expect(event.validateCustomEventName("foo{nonsenseField}bar", "error message")).toBe("error message");
});
});
});
@@ -0,0 +1,326 @@
import { describe, it, expect } from "vitest";
import type { WorkingHours } from "@calcom/types/schedule";
import { getAggregateWorkingHours } from "./getAggregateWorkingHours";
describe("getAggregateWorkingHours", () => {
it("should return all schedules if no scheduling type", () => {
const workingHours: WorkingHours[] = [
{
days: [1, 2, 3],
startTime: 0,
endTime: 720,
},
];
const result = getAggregateWorkingHours(
[
{
busy: [],
timeZone: "Europe/London",
workingHours,
dateOverrides: [],
},
{
busy: [],
timeZone: "Europe/London",
workingHours,
dateOverrides: [],
},
],
null
);
expect(result).toEqual([...workingHours, ...workingHours]);
});
it("should return all schedules if no fixed users exist", () => {
const workingHours: WorkingHours[] = [
{
days: [1, 2, 3],
startTime: 0,
endTime: 720,
},
];
const result = getAggregateWorkingHours(
[
{
busy: [],
timeZone: "Europe/London",
workingHours,
dateOverrides: [],
},
{
busy: [],
timeZone: "Europe/London",
workingHours,
dateOverrides: [],
},
],
"MANAGED"
);
expect(result).toEqual([...workingHours, ...workingHours]);
});
it("should consider all schedules fixed if collective", () => {
const workingHoursA: WorkingHours[] = [
{
days: [1, 2],
startTime: 0,
endTime: 200,
},
];
const workingHoursB: WorkingHours[] = [
{
days: [2, 3],
startTime: 100,
endTime: 300,
},
];
const result = getAggregateWorkingHours(
[
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursA,
dateOverrides: [],
user: {
isFixed: false,
},
},
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursB,
dateOverrides: [],
user: {
isFixed: false,
},
},
],
"COLLECTIVE"
);
expect(result).toEqual([
{
days: [2],
startTime: 100,
endTime: 200,
},
]);
});
it("should include loose host hours", () => {
const workingHoursA: WorkingHours[] = [
{
days: [1, 2],
startTime: 0,
endTime: 200,
},
];
const workingHoursB: WorkingHours[] = [
{
days: [2, 3],
startTime: 100,
endTime: 300,
},
];
const result = getAggregateWorkingHours(
[
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursA,
dateOverrides: [],
user: {
isFixed: false,
},
},
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursB,
dateOverrides: [],
user: {
isFixed: true,
},
},
],
"GROUP"
);
expect(result).toEqual([
{
days: [2],
startTime: 100,
endTime: 200,
userId: undefined,
},
]);
});
it("should return last user's hours if no intersection", () => {
const workingHoursA: WorkingHours[] = [
{
days: [1],
startTime: 0,
endTime: 200,
},
];
const workingHoursB: WorkingHours[] = [
{
days: [2],
startTime: 100,
endTime: 300,
},
];
const result = getAggregateWorkingHours(
[
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursA,
dateOverrides: [],
user: {
isFixed: true,
},
},
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursB,
dateOverrides: [],
user: {
isFixed: true,
},
},
],
"GROUP"
);
expect(result).toEqual([...workingHoursB]);
});
it("should include user IDs when not collective", () => {
const workingHoursA: WorkingHours[] = [
{
days: [1, 2],
startTime: 0,
endTime: 200,
userId: "userA",
},
];
const workingHoursB: WorkingHours[] = [
{
days: [2, 3],
startTime: 100,
endTime: 300,
userId: "userB",
},
];
const result = getAggregateWorkingHours(
[
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursA,
dateOverrides: [],
user: {
isFixed: true,
},
},
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursB,
dateOverrides: [],
user: {
isFixed: true,
},
},
],
"GROUP"
);
expect(result).toEqual([
{
userId: "userA",
days: [2],
startTime: 100,
endTime: 200,
},
]);
});
it("should handle multiple intersections", () => {
const workingHoursA: WorkingHours[] = [
{
days: [1, 2],
startTime: 0,
endTime: 200,
},
{
days: [3, 4],
startTime: 100,
endTime: 300,
},
];
const workingHoursB: WorkingHours[] = [
{
days: [2, 3],
startTime: 100,
endTime: 300,
},
{
days: [4, 5],
startTime: 0,
endTime: 200,
},
];
const result = getAggregateWorkingHours(
[
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursA,
dateOverrides: [],
user: {
isFixed: true,
},
},
{
busy: [],
timeZone: "Europe/London",
workingHours: workingHoursB,
dateOverrides: [],
user: {
isFixed: true,
},
},
],
"GROUP"
);
expect(result).toEqual([
{
days: [2],
startTime: 100,
endTime: 200,
userId: undefined,
},
{
days: [3],
startTime: 100,
endTime: 300,
userId: undefined,
},
{
days: [4],
startTime: 100,
endTime: 200,
userId: undefined,
},
]);
});
});
+210
View File
@@ -0,0 +1,210 @@
import type { SelectedCalendar } from "@prisma/client";
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import GoogleCalendarService from "@calcom/app-store/googlecalendar/lib/CalendarService";
import OfficeCalendarService from "@calcom/app-store/office365calendar/lib/CalendarService";
import logger from "@calcom/lib/logger";
import type { EventBusyDate } from "@calcom/types/Calendar";
import type { CredentialPayload } from "@calcom/types/Credential";
import getCalendarsEvents from "./getCalendarsEvents";
describe("getCalendarsEvents", () => {
let credential: CredentialPayload;
beforeEach(() => {
vi.spyOn(logger.constructor.prototype, "debug");
credential = {
id: 303,
type: "google_calendar",
key: {
scope: "example scope",
token_type: "Bearer",
expiry_date: Date.now() + 84000,
access_token: "access token",
refresh_token: "refresh token",
},
userId: 808,
teamId: null,
appId: "exampleApp",
subscriptionId: null,
paymentStatus: null,
billingCycleStart: null,
invalid: false,
};
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should return empty array if no calendar credentials", async () => {
const result = await getCalendarsEvents(
[
{
...credential,
type: "totally_unrelated",
},
],
"2010-12-01",
"2010-12-02",
[]
);
expect(result).toEqual([]);
});
it("should return unknown calendars as empty", async () => {
const result = await getCalendarsEvents(
[
{
...credential,
type: "unknown_calendar",
},
],
"2010-12-01",
"2010-12-02",
[]
);
expect(result).toEqual([[]]);
});
it("should return unmatched calendars as empty", async () => {
const selectedCalendar: SelectedCalendar = {
credentialId: 100,
externalId: "externalId",
integration: "office365_calendar",
userId: 200,
};
const result = await getCalendarsEvents(
[
{
...credential,
type: "google_calendar",
},
],
"2010-12-01",
"2010-12-02",
[selectedCalendar]
);
expect(result).toEqual([[]]);
});
it("should return availability from selected calendar", async () => {
const availability: EventBusyDate[] = [
{
start: new Date(2010, 11, 2),
end: new Date(2010, 11, 3),
},
{
start: new Date(2010, 11, 2, 4),
end: new Date(2010, 11, 2, 16),
},
];
const getAvailabilitySpy = vi
.spyOn(GoogleCalendarService.prototype, "getAvailability")
.mockReturnValue(Promise.resolve(availability));
const selectedCalendar: SelectedCalendar = {
credentialId: 100,
externalId: "externalId",
integration: "google_calendar",
userId: 200,
};
const result = await getCalendarsEvents(
[
{
...credential,
type: "google_calendar",
},
],
"2010-12-01",
"2010-12-04",
[selectedCalendar]
);
expect(getAvailabilitySpy).toHaveBeenCalledWith("2010-12-01", "2010-12-04", [selectedCalendar]);
expect(result).toEqual([
availability.map((av) => ({
...av,
source: "exampleApp",
})),
]);
});
it("should return availability from multiple calendars", async () => {
const googleAvailability: EventBusyDate[] = [
{
start: new Date(2010, 11, 2),
end: new Date(2010, 11, 3),
},
];
const officeAvailability: EventBusyDate[] = [
{
start: new Date(2010, 11, 2, 4),
end: new Date(2010, 11, 2, 16),
},
];
const getGoogleAvailabilitySpy = vi
.spyOn(GoogleCalendarService.prototype, "getAvailability")
.mockReturnValue(Promise.resolve(googleAvailability));
const getOfficeAvailabilitySpy = vi
.spyOn(OfficeCalendarService.prototype, "getAvailability")
.mockReturnValue(Promise.resolve(officeAvailability));
const selectedGoogleCalendar: SelectedCalendar = {
credentialId: 100,
externalId: "externalId",
integration: "google_calendar",
userId: 200,
};
const selectedOfficeCalendar: SelectedCalendar = {
credentialId: 100,
externalId: "externalId",
integration: "office365_calendar",
userId: 200,
};
const result = await getCalendarsEvents(
[
{
...credential,
type: "google_calendar",
},
{
...credential,
type: "office365_calendar",
key: {
access_token: "access",
refresh_token: "refresh",
expires_in: Date.now() + 86400,
},
},
],
"2010-12-01",
"2010-12-04",
[selectedGoogleCalendar, selectedOfficeCalendar]
);
expect(getGoogleAvailabilitySpy).toHaveBeenCalledWith("2010-12-01", "2010-12-04", [
selectedGoogleCalendar,
]);
expect(getOfficeAvailabilitySpy).toHaveBeenCalledWith("2010-12-01", "2010-12-04", [
selectedOfficeCalendar,
]);
expect(result).toEqual([
googleAvailability.map((av) => ({
...av,
source: "exampleApp",
})),
officeAvailability.map((av) => ({
...av,
source: "exampleApp",
})),
]);
});
});