diff --git a/apps/web/pages/booking/[uid].tsx b/apps/web/pages/booking/[uid].tsx index fe3ab436a9..dc0d14977b 100644 --- a/apps/web/pages/booking/[uid].tsx +++ b/apps/web/pages/booking/[uid].tsx @@ -409,7 +409,9 @@ export default function Success(props: SuccessProps) { )}
{t("what")}
-
{eventName}
+
+ {eventName} +
{t("when")}
{reschedule && !!formerTime && ( diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts index acb364e27a..2af6ac85a1 100644 --- a/apps/web/playwright/fixtures/users.ts +++ b/apps/web/playwright/fixtures/users.ts @@ -6,9 +6,10 @@ import { hashSync as hash } from "bcryptjs"; import dayjs from "@calcom/dayjs"; import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability"; import { prisma } from "@calcom/prisma"; +import { SchedulingType } from "@calcom/prisma/enums"; import { MembershipRole } from "@calcom/prisma/enums"; -import { selectFirstAvailableTimeSlotNextMonth } from "../lib/testUtils"; +import { selectFirstAvailableTimeSlotNextMonth, teamEventSlug, teamEventTitle } from "../lib/testUtils"; import type { TimeZoneEnum } from "./types"; // Don't import hashPassword from app as that ends up importing next-auth and initializing it before NEXTAUTH_URL can be updated during tests. @@ -42,7 +43,7 @@ const createTeamAndAddUser = async ( ) => { const team = await prisma.team.create({ data: { - name: "", + name: `user-id-${user.id}'s Team`, slug: `team-${workerInfo.workerIndex}-${Date.now()}`, }, }); @@ -68,6 +69,10 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => { scenario: { seedRoutingForms?: boolean; hasTeam?: true; + teammates?: CustomUserOpts[]; + schedulingType?: SchedulingType; + teamEventTitle?: string; + teamEventSlug?: string; } = {} ) => { const _user = await prisma.user.create({ @@ -212,7 +217,7 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => { }); if (scenario.hasTeam) { const team = await createTeamAndAddUser({ user: { id: user.id, role: "OWNER" } }, workerInfo); - await prisma.eventType.create({ + const teamEvent = await prisma.eventType.create({ data: { team: { connect: { @@ -229,12 +234,48 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => { id: _user.id, }, }, - schedulingType: "COLLECTIVE", - title: "Team Event - 30min", - slug: "team-event-30min", + schedulingType: scenario.schedulingType ?? SchedulingType.COLLECTIVE, + title: scenario.teamEventTitle ?? teamEventTitle, + slug: scenario.teamEventSlug ?? teamEventSlug, length: 30, }, }); + if (scenario.teammates) { + // Create Teammate users + for (const teammateObj of scenario.teammates) { + const teamUser = await prisma.user.create({ + data: createUser(workerInfo, teammateObj), + }); + + // Add teammates to the team + await prisma.membership.create({ + data: { + teamId: team.id, + userId: teamUser.id, + role: MembershipRole.MEMBER, + accepted: true, + }, + }); + + // Add teammate to the host list of team event + await prisma.host.create({ + data: { + userId: teamUser.id, + eventTypeId: teamEvent.id, + isFixed: scenario.schedulingType === SchedulingType.COLLECTIVE ? true : false, + }, + }); + + const teammateFixture = createUserFixture( + await prisma.user.findUniqueOrThrow({ + where: { id: teamUser.id }, + include: userIncludes, + }), + store.page + ); + store.users.push(teammateFixture); + } + } } const userFixture = createUserFixture(user, store.page); store.users.push(userFixture); @@ -271,6 +312,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => { }))!; return { id: user.id, + name: user.name, username: user.username, eventTypes: user.eventTypes, routingForms: user.routingForms, @@ -280,6 +322,12 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => { logout: async () => { await page.goto("/auth/logout"); }, + getTeam: async () => { + return prisma.membership.findFirstOrThrow({ + where: { userId: user.id }, + include: { team: true }, + }); + }, getPaymentCredential: async () => getPaymentCredential(store.page), setupEventWithPrice: async (eventType: Pick) => setupEventWithPrice(eventType, store.page), diff --git a/apps/web/playwright/lib/testUtils.ts b/apps/web/playwright/lib/testUtils.ts index ab0e7d92db..4ef9eaaa43 100644 --- a/apps/web/playwright/lib/testUtils.ts +++ b/apps/web/playwright/lib/testUtils.ts @@ -18,6 +18,9 @@ type RequestHandler = (opts: RequestHandlerOptions) => void; export const testEmail = "test@example.com"; export const testName = "Test Testson"; +export const teamEventTitle = "Team Event - 30min"; +export const teamEventSlug = "team-event-30min"; + export function createHttpServer(opts: { requestHandler?: RequestHandler } = {}) { const { requestHandler = ({ res }) => { diff --git a/apps/web/playwright/teams.e2e.ts b/apps/web/playwright/teams.e2e.ts index c25b56d73a..8b6e72e254 100644 --- a/apps/web/playwright/teams.e2e.ts +++ b/apps/web/playwright/teams.e2e.ts @@ -1,10 +1,17 @@ import { expect } from "@playwright/test"; import { prisma } from "@calcom/prisma"; -import { MembershipRole } from "@calcom/prisma/enums"; +import { SchedulingType } from "@calcom/prisma/enums"; import { test } from "./lib/fixtures"; -import { bookTimeSlot, selectFirstAvailableTimeSlotNextMonth, testName } from "./lib/testUtils"; +import { + bookTimeSlot, + selectFirstAvailableTimeSlotNextMonth, + teamEventSlug, + teamEventTitle, + testName, + todo, +} from "./lib/testUtils"; test.describe.configure({ mode: "parallel" }); @@ -66,84 +73,63 @@ test.describe("Teams", () => { }); test("Can create a booking for Collective EventType", async ({ page, users }) => { const ownerObj = { username: "pro-user", name: "pro-user" }; - const teamMate1Obj = { username: "teammate-1", name: "teammate-1" }; - const teamMate2Obj = { username: "teammate-2", name: "teammate-2" }; + const teamMatesObj = [ + { username: "teammate-1", name: "teammate-1" }, + { username: "teammate-2", name: "teammate-2" }, + { username: "teammate-3", name: "teammate-3" }, + { username: "teammate-4", name: "teammate-4" }, + ]; - const owner = await users.create(ownerObj, { hasTeam: true }); - const teamMate1 = await users.create(teamMate1Obj); - const teamMate2 = await users.create(teamMate2Obj); - teamMate1.username, teamMate2.username; - // TODO: Create a fixture and follow DRY - const { team } = await prisma.membership.findFirstOrThrow({ - where: { userId: owner.id }, - include: { team: true }, + const owner = await users.create(ownerObj, { + hasTeam: true, + teammates: teamMatesObj, + schedulingType: SchedulingType.COLLECTIVE, }); + const { team } = await owner.getTeam(); - // Add teamMate1 to the team - await prisma.membership.create({ - data: { - teamId: team.id, - userId: teamMate1.id, - role: MembershipRole.MEMBER, - accepted: true, - }, - }); - - // Add teamMate2 to the team - await prisma.membership.create({ - data: { - teamId: team.id, - userId: teamMate2.id, - role: MembershipRole.MEMBER, - accepted: true, - }, - }); - // No need to remove membership row manually as it will be deleted with the user, at the end of the test. - - const { id: eventTypeId } = await prisma.eventType.findFirstOrThrow({ - where: { userId: owner.id, teamId: team.id }, - select: { id: true }, - }); - - // Assign owner and teammates to the eventtype host list - await prisma.host.create({ - data: { - userId: owner.id, - eventTypeId, - isFixed: true, - }, - }); - await prisma.host.create({ - data: { - userId: teamMate1.id, - eventTypeId, - isFixed: true, - }, - }); - await prisma.host.create({ - data: { - userId: teamMate2.id, - eventTypeId, - isFixed: true, - }, - }); - - await page.goto(`/team/${team.slug}/team-event-30min`); + await page.goto(`/team/${team.slug}/${teamEventSlug}`); await selectFirstAvailableTimeSlotNextMonth(page); await bookTimeSlot(page); await expect(page.locator("[data-testid=success-page]")).toBeVisible(); await expect(page.locator(`[data-testid="attendee-name-${testName}"]`)).toHaveText(testName); - // The first user added to the host list will be the host name - await expect(page.locator(`[data-testid="host-name-${ownerObj.name}"]`)).toHaveText(ownerObj.name); + for (const teammate of teamMatesObj) { + await expect(page.getByText(teammate.name, { exact: true })).toBeVisible(); + } - // The remaining hosts will be shown as the attendees - await expect(page.locator(`[data-testid="attendee-name-${teamMate1Obj.name}"]`)).toHaveText( - teamMate1Obj.name - ); - await expect(page.locator(`[data-testid="attendee-name-${teamMate2Obj.name}"]`)).toHaveText( - teamMate2Obj.name - ); // TODO: Assert whether the user received an email }); + test("Can create a booking for Round Robin EventType", async ({ page, users }) => { + const ownerObj = { username: "pro-user", name: "pro-user" }; + const teamMatesObj = [ + { username: "teammate-1", name: "teammate-1" }, + { username: "teammate-2", name: "teammate-2" }, + { username: "teammate-3", name: "teammate-3" }, + { username: "teammate-4", name: "teammate-4" }, + ]; + const owner = await users.create(ownerObj, { + hasTeam: true, + teammates: teamMatesObj, + schedulingType: SchedulingType.ROUND_ROBIN, + }); + + const { team } = await owner.getTeam(); + + await page.goto(`/team/${team.slug}/${teamEventSlug}`); + await selectFirstAvailableTimeSlotNextMonth(page); + await bookTimeSlot(page); + await expect(page.locator("[data-testid=success-page]")).toBeVisible(); + + // The person who booked the meeting should be in the attendee list + await expect(page.locator(`[data-testid="attendee-name-${testName}"]`)).toHaveText(testName); + + // The title of the booking + const BookingTitle = `${teamEventTitle} between ${team.name} and ${testName}`; + await expect(page.locator("[data-testid=booking-title]")).toHaveText(BookingTitle); + + // TODO: Assert one of the teamates from the list to be the host + // TODO: Assert whether the user received an email + }); + todo("Reschedule a Collective EventType booking"); + todo("Reschedule a Round Robin EventType booking"); });