* move dailyApiFetcher.ts to app store package * update imports * bookingLocationService * revert * move findFieldValueByIdentifier * wip * fix * fix * fix * fix * fix * fix * fix * move to features package * update imports * update imports * move * fix typechecks * move getCalendarLinks * mv event test
215 lines
8.0 KiB
TypeScript
215 lines
8.0 KiB
TypeScript
import type { TFunction } from "i18next";
|
|
import { createEvent } from "ics";
|
|
import { RRule } from "rrule";
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
|
|
import dayjs from "@calcom/dayjs";
|
|
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
|
|
|
|
import { getCalendarLinks, CalendarLinkType } from "./getCalendarLinks";
|
|
|
|
// Mock dependencies
|
|
vi.mock("ics", () => ({
|
|
createEvent: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@calcom/lib/isRecurringEvent", () => ({
|
|
parseRecurringEvent: vi.fn(),
|
|
}));
|
|
|
|
describe("getCalendarLinks", () => {
|
|
// Mock data for tests
|
|
const mockStartTime = new Date("2023-01-15T10:00:00Z");
|
|
const mockEndTime = new Date("2023-01-15T11:00:00Z");
|
|
const mockDayjsStartTime = dayjs(mockStartTime);
|
|
const mockDayjsEndTime = dayjs(mockEndTime);
|
|
|
|
// Mock translation function
|
|
const mockT = ((key: string) => key) as TFunction;
|
|
|
|
// Mock createEvent implementation
|
|
const mockIcsValue = "BEGIN:VCALENDAR\nVERSION:2.0\nEND:VCALENDAR";
|
|
|
|
// Baseline mock objects that can be reused across tests
|
|
const baseMockBooking = {
|
|
startTime: mockStartTime,
|
|
endTime: mockEndTime,
|
|
location: "Test Location",
|
|
title: "Test Title",
|
|
responses: {},
|
|
metadata: {},
|
|
};
|
|
|
|
const baseMockEventType = {
|
|
recurringEvent: {},
|
|
description: "Test Description",
|
|
eventName: "Test Event",
|
|
isDynamic: false,
|
|
length: 60,
|
|
team: null,
|
|
users: [{ name: "Test User" }],
|
|
title: "Test Title",
|
|
};
|
|
|
|
beforeEach(() => {
|
|
// Reset mocks before each test
|
|
vi.resetAllMocks();
|
|
|
|
// Default mock implementations
|
|
(createEvent as jest.Mock).mockReturnValue({ value: mockIcsValue });
|
|
(parseRecurringEvent as jest.Mock).mockReturnValue(null);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("should return all calendar links", async () => {
|
|
const result = getCalendarLinks({ booking: baseMockBooking, eventType: baseMockEventType, t: mockT });
|
|
const googleLink = result.find((link) => link.id === CalendarLinkType.GOOGLE_CALENDAR);
|
|
const microsoftOfficeLink = result.find((link) => link.id === CalendarLinkType.MICROSOFT_OFFICE);
|
|
const microsoftOutlookLink = result.find((link) => link.id === CalendarLinkType.MICROSOFT_OUTLOOK);
|
|
const icsLink = result.find((link) => link.id === CalendarLinkType.ICS);
|
|
|
|
expect(result).toHaveLength(4);
|
|
expect(googleLink).toBeDefined();
|
|
expect(microsoftOfficeLink).toBeDefined();
|
|
expect(microsoftOutlookLink).toBeDefined();
|
|
expect(icsLink).toBeDefined();
|
|
|
|
(function verifyGoogleLink() {
|
|
expect(googleLink?.link).toContain("https://calendar.google.com/calendar/r/eventedit");
|
|
expect(googleLink?.link).toContain("dates=");
|
|
expect(googleLink?.link).toContain(
|
|
`${mockDayjsStartTime.utc().format("YYYYMMDDTHHmmss[Z]")}/${mockDayjsEndTime
|
|
.utc()
|
|
.format("YYYYMMDDTHHmmss[Z]")}`
|
|
);
|
|
expect(googleLink?.link).toContain(`details=${encodeURIComponent(baseMockEventType.description)}`);
|
|
})();
|
|
|
|
(function verifyMicrosoftOfficeLink() {
|
|
expect(microsoftOfficeLink?.link).toContain("https://outlook.office.com/calendar/0/deeplink/compose");
|
|
expect(microsoftOfficeLink?.link).toContain("enddt=");
|
|
expect(microsoftOfficeLink?.link).toContain("startdt=");
|
|
expect(microsoftOfficeLink?.link).toContain("body=Test%20Description");
|
|
})();
|
|
|
|
(function verifymicrosoftOutlookLink() {
|
|
expect(microsoftOutlookLink?.link).toContain("https://outlook.live.com/calendar/0/deeplink/compose");
|
|
expect(microsoftOutlookLink?.link).toContain("enddt=");
|
|
expect(microsoftOutlookLink?.link).toContain("startdt=");
|
|
expect(microsoftOutlookLink?.link).toContain("body=Test%20Description");
|
|
})();
|
|
|
|
(function verifyIcsLink() {
|
|
expect(icsLink).toBeDefined();
|
|
expect(icsLink?.link).toContain("data:text/calendar");
|
|
expect(icsLink?.link).toContain(encodeURIComponent(mockIcsValue));
|
|
expect(createEvent).toHaveBeenCalled();
|
|
expect(createEvent).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
description: baseMockEventType.description,
|
|
})
|
|
);
|
|
})();
|
|
});
|
|
|
|
it("should use videoCallUrl from metadata when available", async () => {
|
|
const videoCallUrl = "https://zoom.com/test";
|
|
const booking = {
|
|
...baseMockBooking,
|
|
metadata: { videoCallUrl },
|
|
};
|
|
|
|
const result = getCalendarLinks({ booking, eventType: baseMockEventType, t: mockT });
|
|
|
|
// Check that all links contain the videoCallUrl
|
|
const googleLink = result.find((link) => link.id === CalendarLinkType.GOOGLE_CALENDAR);
|
|
const microsoftOfficeLink = result.find((link) => link.id === CalendarLinkType.MICROSOFT_OFFICE);
|
|
const microsoftOutlookLink = result.find((link) => link.id === CalendarLinkType.MICROSOFT_OUTLOOK);
|
|
|
|
expect(googleLink?.link).toContain(`location=${encodeURIComponent(videoCallUrl)}`);
|
|
expect(microsoftOfficeLink?.link).toContain(`location=${encodeURIComponent(videoCallUrl)}`);
|
|
expect(microsoftOutlookLink?.link).toContain(`location=${encodeURIComponent(videoCallUrl)}`);
|
|
});
|
|
|
|
it("should handle custom title from dynamic event", async () => {
|
|
const customTitle = "Custom Dynamic Title";
|
|
const booking = {
|
|
...baseMockBooking,
|
|
responses: { title: customTitle, name: "Test Attendee" },
|
|
};
|
|
|
|
const eventType = {
|
|
...baseMockEventType,
|
|
isDynamic: true, // This makes it use the custom title
|
|
};
|
|
|
|
const result = getCalendarLinks({ booking, eventType, t: mockT });
|
|
|
|
const googleLink = result.find((link) => link.id === CalendarLinkType.GOOGLE_CALENDAR);
|
|
expect(googleLink?.link).toContain(`details=${encodeURIComponent(eventType.description)}`);
|
|
expect(googleLink?.link).toContain(`text=${customTitle}`);
|
|
|
|
// Check Office 365 link
|
|
const microsoftOfficeLink = result.find((link) => link.id === CalendarLinkType.MICROSOFT_OFFICE);
|
|
expect(microsoftOfficeLink?.link).toContain("body=Test%20Description");
|
|
expect(microsoftOfficeLink?.link).toContain(`subject=${customTitle}`);
|
|
|
|
// Check Outlook link
|
|
const microsoftOutlookLink = result.find((link) => link.id === CalendarLinkType.MICROSOFT_OUTLOOK);
|
|
expect(microsoftOutlookLink?.link).toContain("body=Test%20Description");
|
|
expect(microsoftOutlookLink?.link).toContain(`subject=${encodeURIComponent(customTitle)}`);
|
|
});
|
|
|
|
it("should handle recurring events - Only Google Calendar supports at the moment", async () => {
|
|
// Mock a recurring event rule
|
|
const mockRecurringRule = {
|
|
freq: 2, // WEEKLY
|
|
interval: 1,
|
|
count: 5,
|
|
};
|
|
|
|
(parseRecurringEvent as jest.Mock).mockReturnValue(mockRecurringRule);
|
|
|
|
const eventType = {
|
|
...baseMockEventType,
|
|
recurringEvent: { count: 5, freq: 2, interval: 1 },
|
|
};
|
|
|
|
const mockRRuleString = "FREQ=WEEKLY;INTERVAL=1;COUNT=5";
|
|
vi.spyOn(RRule.prototype, "toString").mockReturnValue(mockRRuleString);
|
|
|
|
const result = getCalendarLinks({ booking: baseMockBooking, eventType, t: mockT });
|
|
const googleLink = result.find((link) => link.id === "googleCalendar");
|
|
|
|
expect(googleLink).toBeDefined();
|
|
expect(googleLink?.link).toContain(`recur=${encodeURIComponent(mockRRuleString)}`);
|
|
});
|
|
|
|
it("should handle error in ICS generation", async () => {
|
|
// Mock createEvent to return an error
|
|
(createEvent as jest.Mock).mockReturnValue({ error: new Error("ICS generation failed") });
|
|
|
|
// Mock console.error to avoid test output noise
|
|
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
|
|
|
const result = getCalendarLinks({ booking: baseMockBooking, eventType: baseMockEventType, t: mockT });
|
|
const icsLink = result.find((link) => link.id === "ics");
|
|
|
|
expect(icsLink).toBeDefined();
|
|
expect(icsLink?.link).toBe("");
|
|
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
|
|
// Verify createEvent was called with the correct description
|
|
expect(createEvent).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
description: baseMockEventType.description,
|
|
})
|
|
);
|
|
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
});
|