test: Round Robin Team Booking (#10413)
This commit is contained in:
@@ -409,7 +409,9 @@ export default function Success(props: SuccessProps) {
|
||||
</>
|
||||
)}
|
||||
<div className="font-medium">{t("what")}</div>
|
||||
<div className="col-span-2 mb-6 last:mb-0">{eventName}</div>
|
||||
<div className="col-span-2 mb-6 last:mb-0" data-testid="booking-title">
|
||||
{eventName}
|
||||
</div>
|
||||
<div className="font-medium">{t("when")}</div>
|
||||
<div className="col-span-2 mb-6 last:mb-0">
|
||||
{reschedule && !!formerTime && (
|
||||
|
||||
@@ -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<Prisma.EventType, "id">) =>
|
||||
setupEventWithPrice(eventType, store.page),
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user