diff --git a/apps/web/playwright/bookings-list.e2e.ts b/apps/web/playwright/bookings-list.e2e.ts index ca7134915a..b371fd4253 100644 --- a/apps/web/playwright/bookings-list.e2e.ts +++ b/apps/web/playwright/bookings-list.e2e.ts @@ -1,9 +1,7 @@ -import { expect } from "@playwright/test"; -import type { Page } from "@playwright/test"; - import { prisma } from "@calcom/prisma"; import { BookingStatus, MembershipRole, SchedulingType } from "@calcom/prisma/enums"; - +import type { Page } from "@playwright/test"; +import { expect } from "@playwright/test"; import { addFilter } from "./filter-helpers"; import { createTeamEventType } from "./fixtures/users"; import type { Fixtures } from "./lib/fixtures"; @@ -567,6 +565,187 @@ test.describe("Bookings", () => { } }); }); + + test.describe("Team filter", () => { + test("Team filter shows bookings for direct team event types", async ({ page, users, bookings }) => { + const owner = await users.create( + { name: "Team Owner" }, + { + hasTeam: true, + teamRole: MembershipRole.OWNER, + } + ); + + const { team } = await owner.getFirstTeamMembership(); + const teamEvent = await owner.getFirstTeamEvent(team.id); + + const teamBookingFixture = await createBooking({ + title: "Team Event Booking", + bookingsFixture: bookings, + relativeDate: 5, + organizer: owner, + organizerEventType: teamEvent, + attendees: [{ name: "Test Attendee", email: "attendee@example.com", timeZone: "Europe/Berlin" }], + }); + const teamBooking = await teamBookingFixture.self(); + + const personalBookingFixture = await createBooking({ + title: "Personal Event Booking", + bookingsFixture: bookings, + relativeDate: 4, + organizer: owner, + organizerEventType: owner.eventTypes[0], + attendees: [{ name: "Personal Attendee", email: "personal@example.com", timeZone: "Europe/Berlin" }], + }); + await personalBookingFixture.self(); + + await owner.apiLogin(); + const bookingsGetResponse = page.waitForResponse((response) => + /\/api\/trpc\/bookings\/get.*/.test(response.url()) + ); + await page.goto(`/bookings/upcoming`, { waitUntil: "domcontentloaded" }); + await bookingsGetResponse; + + await addFilter(page, "teamId"); + + const bookingsGetResponse2 = page.waitForResponse( + (response) => response.url().includes("/api/trpc/bookings/get?batch=1") && response.status() === 200 + ); + await page.locator(`[data-testid="select-filter-options-teamId"] [role="option"]`).first().click(); + await bookingsGetResponse2; + + const upcomingBookingsTable = page.locator('[data-testid="upcoming-bookings"]'); + const bookingListItems = upcomingBookingsTable.locator('[data-testid="booking-item"]'); + const bookingListCount = await bookingListItems.count(); + + expect(bookingListCount).toBe(1); + await expect(bookingListItems.first().locator(`text=${teamBooking!.title}`)).toBeVisible(); + }); + + test("Team filter shows bookings for managed event types (child events)", async ({ + page, + users, + bookings, + }) => { + const { adminUser, memberUser } = await setupManagedEvent({ users }); + + const memberManagedEvent = await memberUser.getFirstEventAsOwner(); + + const managedEventBookingFixture = await createBooking({ + title: "Managed Event Booking", + bookingsFixture: bookings, + relativeDate: 5, + organizer: memberUser, + organizerEventType: memberManagedEvent, + attendees: [{ name: "Managed Attendee", email: "managed@example.com", timeZone: "Europe/Berlin" }], + }); + const managedEventBooking = await managedEventBookingFixture.self(); + + const personalBookingFixture = await createBooking({ + title: "Personal Event Booking", + bookingsFixture: bookings, + relativeDate: 4, + organizer: adminUser, + organizerEventType: adminUser.eventTypes[0], + attendees: [{ name: "Personal Attendee", email: "personal@example.com", timeZone: "Europe/Berlin" }], + }); + await personalBookingFixture.self(); + + await adminUser.apiLogin(); + const bookingsGetResponse = page.waitForResponse((response) => + /\/api\/trpc\/bookings\/get.*/.test(response.url()) + ); + await page.goto(`/bookings/upcoming`, { waitUntil: "domcontentloaded" }); + await bookingsGetResponse; + + await addFilter(page, "teamId"); + + const bookingsGetResponse2 = page.waitForResponse( + (response) => response.url().includes("/api/trpc/bookings/get?batch=1") && response.status() === 200 + ); + await page.locator(`[data-testid="select-filter-options-teamId"] [role="option"]`).first().click(); + await bookingsGetResponse2; + + const upcomingBookingsTable = page.locator('[data-testid="upcoming-bookings"]'); + const bookingListItems = upcomingBookingsTable.locator('[data-testid="booking-item"]'); + const bookingListCount = await bookingListItems.count(); + + expect(bookingListCount).toBe(1); + await expect(bookingListItems.first().locator(`text=${managedEventBooking!.title}`)).toBeVisible(); + }); + + test("Team filter excludes bookings from other teams", async ({ page, users, bookings }) => { + const owner = await users.create( + { name: "Multi-Team Owner" }, + { + hasTeam: true, + numberOfTeams: 2, + teamRole: MembershipRole.OWNER, + } + ); + + const memberships = await prisma.membership.findMany({ + where: { userId: owner.id }, + include: { team: true }, + orderBy: { teamId: "asc" }, + }); + + const team1 = memberships[0].team; + const team2 = memberships[1].team; + + const team1Event = await createTeamEventType( + { id: owner.id }, + { id: team1.id }, + { teamEventSlug: "team1-event" } + ); + const team2Event = await createTeamEventType( + { id: owner.id }, + { id: team2.id }, + { teamEventSlug: "team2-event" } + ); + + const team1BookingFixture = await createBooking({ + title: "Team 1 Booking", + bookingsFixture: bookings, + relativeDate: 5, + organizer: owner, + organizerEventType: team1Event, + attendees: [{ name: "Team1 Attendee", email: "team1@example.com", timeZone: "Europe/Berlin" }], + }); + await team1BookingFixture.self(); + + const team2BookingFixture = await createBooking({ + title: "Team 2 Booking", + bookingsFixture: bookings, + relativeDate: 4, + organizer: owner, + organizerEventType: team2Event, + attendees: [{ name: "Team2 Attendee", email: "team2@example.com", timeZone: "Europe/Berlin" }], + }); + await team2BookingFixture.self(); + + await owner.apiLogin(); + const bookingsGetResponse = page.waitForResponse((response) => + /\/api\/trpc\/bookings\/get.*/.test(response.url()) + ); + await page.goto(`/bookings/upcoming`, { waitUntil: "domcontentloaded" }); + await bookingsGetResponse; + + await addFilter(page, "teamId"); + + const bookingsGetResponse2 = page.waitForResponse( + (response) => response.url().includes("/api/trpc/bookings/get?batch=1") && response.status() === 200 + ); + await page.locator(`[data-testid="select-filter-options-teamId"] [role="option"]`).first().click(); + await bookingsGetResponse2; + + const upcomingBookingsTable = page.locator('[data-testid="upcoming-bookings"]'); + const bookingListItems = upcomingBookingsTable.locator('[data-testid="booking-item"]'); + const bookingListCount = await bookingListItems.count(); + + expect(bookingListCount).toBe(1); + }); + }); }); async function createBooking({