import { getSampleUserInSession } from "../utils/bookingScenario/getSampleUserInSession"; import { setupAndTeardown } from "../utils/bookingScenario/setupAndTeardown"; import { createBookingScenario, getGoogleCalendarCredential, TestData, getOrganizer, getBooker, getScenarioData, getMockBookingAttendee, getDate, } from "@calcom/web/test/utils/bookingScenario/bookingScenario"; import { expectBookingRequestRescheduledEmails } from "@calcom/web/test/utils/bookingScenario/expects"; import type { Request, Response } from "express"; import type { NextApiRequest, NextApiResponse } from "next"; import { describe } from "vitest"; import { SchedulingType } from "@calcom/prisma/enums"; import { BookingStatus } from "@calcom/prisma/enums"; import type { TRequestRescheduleInputSchema } from "@calcom/trpc/server/routers/viewer/bookings/requestReschedule.schema"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { test } from "@calcom/web/test/fixtures/fixtures"; export type CustomNextApiRequest = NextApiRequest & Request; export type CustomNextApiResponse = NextApiResponse & Response; describe("Handler: requestReschedule", () => { setupAndTeardown(); describe("User Event Booking", () => { test(`should be able to request-reschedule for a user booking 1. RequestReschedule emails go to both attendee and the person requesting the reschedule`, async ({ emails, }) => { const { requestRescheduleHandler } = await import( "@calcom/trpc/server/routers/viewer/bookings/requestReschedule.handler" ); const booker = getBooker({ email: "booker@example.com", name: "Booker", }); const organizer = getOrganizer({ name: "Organizer", email: "organizer@example.com", id: 101, schedules: [TestData.schedules.IstWorkHours], credentials: [getGoogleCalendarCredential()], selectedCalendars: [TestData.selectedCalendars.google], }); const { dateString: plus1DateString } = getDate({ dateIncrement: 1 }); const bookingUid = "MOCKED_BOOKING_UID"; const eventTypeSlug = "event-type-1"; await createBookingScenario( getScenarioData({ webhooks: [ { userId: organizer.id, eventTriggers: ["BOOKING_CREATED"], subscriberUrl: "http://my-webhook.example.com", active: true, eventTypeId: 1, appId: null, }, ], eventTypes: [ { id: 1, slug: eventTypeSlug, slotInterval: 45, length: 45, users: [ { id: 101, }, ], }, ], bookings: [ { uid: bookingUid, eventTypeId: 1, userId: 101, status: BookingStatus.ACCEPTED, startTime: `${plus1DateString}T05:00:00.000Z`, endTime: `${plus1DateString}T05:15:00.000Z`, attendees: [ getMockBookingAttendee({ id: 2, name: booker.name, email: booker.email, // Booker's locale when the fresh booking happened earlier locale: "hi", // Booker's timezone when the fresh booking happened earlier timeZone: "Asia/Kolkata", noShow: false, }), ], }, ], organizer, apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]], }) ); const loggedInUser = { organizationId: null, id: 101, username: "reschedule-requester", name: "Reschedule Requester", email: "reschedule-requester@example.com", }; await requestRescheduleHandler( getTrpcHandlerData({ user: loggedInUser, input: { bookingId: bookingUid, rescheduleReason: "", }, }) ); expectBookingRequestRescheduledEmails({ booking: { uid: bookingUid, }, booker, organizer: organizer, loggedInUser, emails, bookNewTimePath: `/${organizer.username}/${eventTypeSlug}`, }); }); }); describe("Team Event Booking", () => { test(`should be able to request-reschedule for a team event booking 1. RequestReschedule emails go to both attendee and the person requesting the reschedule`, async ({ emails, }) => { const { requestRescheduleHandler } = await import( "@calcom/trpc/server/routers/viewer/bookings/requestReschedule.handler" ); const booker = getBooker({ email: "booker@example.com", name: "Booker", }); const organizer = getOrganizer({ name: "Organizer", email: "organizer@example.com", id: 101, teams: [ { membership: { accepted: true, }, team: { id: 1, name: "Team 1", slug: "team-1", }, }, ], schedules: [TestData.schedules.IstWorkHours], credentials: [getGoogleCalendarCredential()], selectedCalendars: [TestData.selectedCalendars.google], }); const { dateString: plus1DateString } = getDate({ dateIncrement: 1 }); const bookingUid = "MOCKED_BOOKING_UID"; const eventTypeSlug = "event-type-1"; await createBookingScenario( getScenarioData({ webhooks: [ { userId: organizer.id, eventTriggers: ["BOOKING_CREATED"], subscriberUrl: "http://my-webhook.example.com", active: true, eventTypeId: 1, appId: null, }, ], eventTypes: [ { id: 1, slug: eventTypeSlug, slotInterval: 45, teamId: 1, schedulingType: SchedulingType.COLLECTIVE, length: 45, users: [ { id: 101, }, ], }, ], bookings: [ { uid: bookingUid, eventTypeId: 1, userId: 101, status: BookingStatus.ACCEPTED, startTime: `${plus1DateString}T05:00:00.000Z`, endTime: `${plus1DateString}T05:15:00.000Z`, attendees: [ getMockBookingAttendee({ id: 2, name: booker.name, email: booker.email, // Booker's locale when the fresh booking happened earlier locale: "hi", // Booker's timezone when the fresh booking happened earlier timeZone: "Asia/Kolkata", noShow: false, }), ], }, ], organizer, apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]], }) ); const loggedInUser = { organizationId: null, id: 101, username: "reschedule-requester", name: "Reschedule Requester", email: "reschedule-requester@example.com", }; await requestRescheduleHandler( getTrpcHandlerData({ user: loggedInUser, input: { bookingId: bookingUid, rescheduleReason: "", }, }) ); expectBookingRequestRescheduledEmails({ booking: { uid: bookingUid, }, booker, organizer: organizer, loggedInUser, emails, bookNewTimePath: "/team/team-1/event-type-1", }); }); test.todo("Verify that the email should go to organizer as well as the team members"); }); }); function getTrpcHandlerData({ input, user, }: { input: TRequestRescheduleInputSchema; user: Partial, "id" | "email" | "username">> & Pick, "id" | "email" | "username">; }) { return { ctx: { user: { ...getSampleUserInSession(), ...user, avatarUrl: user.avatarUrl || null, profile: { upId: "", id: 1, name: "", avatarUrl: "", startTime: 0, endTime: 0, username: user.username || "", organizationId: null, organization: null, bufferTime: 5, avatar: null, }, } as unknown as NonNullable, }, input: input, }; }