From 486817a688f3de79afce98904fccd08062652102 Mon Sep 17 00:00:00 2001 From: Keith Williams Date: Wed, 31 Dec 2025 14:58:20 -0300 Subject: [PATCH] fix: add timeout: 0 to E2E assertions that check for absence of elements (#26353) This fixes hanging E2E tests where assertions like toHaveCount(0) and not.toBeVisible() would wait for the full timeout (10s in CI, 120s locally) before failing, instead of failing immediately. These assertions are 'state check' assertions that verify an element is already absent or hidden, rather than waiting for it to become so. Adding { timeout: 0 } makes them fail immediately if the condition is not met. Files updated: - locale.e2e.ts: 16 instances of toHaveCount(0) - booking-seats.e2e.ts: 8 instances of toHaveCount(0) - organization-redirection.e2e.ts: 3 instances of toHaveCount(0) - organization-creation-flows.e2e.ts: 5 instances of not.toBeVisible() - insights-charts.e2e.ts: 1 instance of toHaveCount(0) - bookings-list.e2e.ts: 1 instance of toHaveCount(0) - availability.e2e.ts: 1 instance of toHaveCount(0) - managed-event-types.e2e.ts: 1 instance of toHaveCount(0) - team-invitation.e2e.ts: 1 instance of toHaveCount(0) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/web/playwright/availability.e2e.ts | 2 +- apps/web/playwright/booking-seats.e2e.ts | 16 ++++++------ apps/web/playwright/bookings-list.e2e.ts | 2 +- apps/web/playwright/insights-charts.e2e.ts | 2 +- apps/web/playwright/locale.e2e.ts | 26 +++++++++---------- .../web/playwright/managed-event-types.e2e.ts | 2 +- .../organization-creation-flows.e2e.ts | 10 +++---- .../organization-redirection.e2e.ts | 6 ++--- .../playwright/team/team-invitation.e2e.ts | 2 +- 9 files changed, 34 insertions(+), 34 deletions(-) diff --git a/apps/web/playwright/availability.e2e.ts b/apps/web/playwright/availability.e2e.ts index cabed0e278..f0ae7dc846 100644 --- a/apps/web/playwright/availability.e2e.ts +++ b/apps/web/playwright/availability.e2e.ts @@ -124,7 +124,7 @@ test.describe("Availability", () => { await submitAndWaitForResponse(page, "/api/trpc/availability/schedule.delete?batch=1", { action: () => page.locator('[data-testid="delete-schedule"]').click(), }); - await expect(page.locator('[data-testid="schedules"] > li').nth(1)).toHaveCount(0); + await expect(page.locator('[data-testid="schedules"] > li').nth(1)).toHaveCount(0, { timeout: 0 }); }); await test.step("Cannot delete the last schedule", async () => { await page.locator('[data-testid="schedules"] > li').nth(0).getByTestId("schedule-more").click(); diff --git a/apps/web/playwright/booking-seats.e2e.ts b/apps/web/playwright/booking-seats.e2e.ts index d4cf1a3ed5..80fbd72c86 100644 --- a/apps/web/playwright/booking-seats.e2e.ts +++ b/apps/web/playwright/booking-seats.e2e.ts @@ -64,14 +64,14 @@ test.describe("Booking with Seats", () => { await test.step("Attendee #2 shouldn't be able to cancel booking using only booking/uid", async () => { await page.goto(`/booking/${booking.uid}`); - await expect(page.locator("[text=Cancel]")).toHaveCount(0); + await expect(page.locator("[text=Cancel]")).toHaveCount(0, { timeout: 0 }); }); await test.step("Attendee #2 shouldn't be able to cancel booking using randomString for seatReferenceUId", async () => { await page.goto(`/booking/${booking.uid}?seatReferenceUid=${randomString(10)}`); // expect cancel button to don't be in the page - await expect(page.locator("[text=Cancel]")).toHaveCount(0); + await expect(page.locator("[text=Cancel]")).toHaveCount(0, { timeout: 0 }); }); }); @@ -82,7 +82,7 @@ test.describe("Booking with Seats", () => { { name: "John Third", email: "third+seats@cal.com", timeZone: "Europe/Berlin" }, ]); await page.goto(`/booking/${booking.uid}?cancel=true`); - await expect(page.locator("[text=Cancel]")).toHaveCount(0); + await expect(page.locator("[text=Cancel]")).toHaveCount(0, { timeout: 0 }); // expect login text to be in the page, not data-testid await expect(page.locator("text=Login")).toHaveCount(1); @@ -99,7 +99,7 @@ test.describe("Booking with Seats", () => { await page.goto(`/booking/${booking.uid}?cancel=true`); // expect login button to don't be in the page - await expect(page.locator("text=Login")).toHaveCount(0); + await expect(page.locator("text=Login")).toHaveCount(0, { timeout: 0 }); // fill reason for cancellation await page.fill('[data-testid="cancel_reason"]', "Double booked!"); @@ -183,7 +183,7 @@ test.describe("Reschedule for booking with seats", () => { await page.locator('[data-testid="day"][data-disabled="false"]').nth(1).click(); // Validate that the number of seats its 10 - await expect(page.locator("text=9 / 10 Seats available")).toHaveCount(0); + await expect(page.locator("text=9 / 10 Seats available")).toHaveCount(0, { timeout: 0 }); }); test("Should cancel with seats but event should be still accessible and with one less attendee/seat", async ({ @@ -325,7 +325,7 @@ test.describe("Reschedule for booking with seats", () => { // No attendees should be displayed only the one that it's cancelling const notFoundSecondAttendee = await page.locator('p[data-testid="attendee-email-second+seats@cal.com"]'); - await expect(notFoundSecondAttendee).toHaveCount(0); + await expect(notFoundSecondAttendee).toHaveCount(0, { timeout: 0 }); const foundFirstAttendee = await page.locator('p[data-testid="attendee-email-first+seats@cal.com"]'); await expect(foundFirstAttendee).toHaveCount(1); @@ -365,7 +365,7 @@ test.describe("Reschedule for booking with seats", () => { const getBooking = await booking.self(); await page.goto(`/booking/${booking.uid}`); - await expect(page.locator('[data-testid="reschedule"]')).toHaveCount(0); + await expect(page.locator('[data-testid="reschedule"]')).toHaveCount(0, { timeout: 0 }); // expect login text to be in the page, not data-testid await expect(page.locator("text=Login")).toHaveCount(1); @@ -382,7 +382,7 @@ test.describe("Reschedule for booking with seats", () => { await page.goto(`/booking/${booking.uid}`); // expect login button to don't be in the page - await expect(page.locator("text=Login")).toHaveCount(0); + await expect(page.locator("text=Login")).toHaveCount(0, { timeout: 0 }); // reschedule-link click await page.locator('[data-testid="reschedule-link"]').click(); diff --git a/apps/web/playwright/bookings-list.e2e.ts b/apps/web/playwright/bookings-list.e2e.ts index 2a8c3b7c65..ca7134915a 100644 --- a/apps/web/playwright/bookings-list.e2e.ts +++ b/apps/web/playwright/bookings-list.e2e.ts @@ -455,7 +455,7 @@ test.describe("Bookings", () => { .click(); await bookingsGetResponse2; - await expect(page.locator('[data-testid="booking-item"]')).toHaveCount(0); + await expect(page.locator('[data-testid="booking-item"]')).toHaveCount(0, { timeout: 0 }); }); test.describe("Filter Dropdown Item Search", () => { diff --git a/apps/web/playwright/insights-charts.e2e.ts b/apps/web/playwright/insights-charts.e2e.ts index 8860355d34..eb87071600 100644 --- a/apps/web/playwright/insights-charts.e2e.ts +++ b/apps/web/playwright/insights-charts.e2e.ts @@ -282,7 +282,7 @@ test.describe("Insights > Charts Loading", () => { // Verify no charts are in error state const errorCharts = page.locator('[data-testid="chart-card"][data-loading-state="error"]'); - await expect(errorCharts).toHaveCount(0); + await expect(errorCharts).toHaveCount(0, { timeout: 0 }); }); }); }); diff --git a/apps/web/playwright/locale.e2e.ts b/apps/web/playwright/locale.e2e.ts index 89175eec31..ec4206bf62 100644 --- a/apps/web/playwright/locale.e2e.ts +++ b/apps/web/playwright/locale.e2e.ts @@ -239,7 +239,7 @@ test.describe("authorized user sees correct translations (de)", async () => { { const locator = page.getByText("Event Types", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -258,7 +258,7 @@ test.describe("authorized user sees correct translations (de)", async () => { { const locator = page.getByText("No upcoming bookings", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -277,7 +277,7 @@ test.describe("authorized user sees correct translations (de)", async () => { { const locator = page.getByText("No upcoming bookings", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); }); @@ -311,7 +311,7 @@ test.describe("authorized user sees correct translations (pt-br)", async () => { { const locator = page.getByText("Event Types", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -330,7 +330,7 @@ test.describe("authorized user sees correct translations (pt-br)", async () => { { const locator = page.getByText("Bookings", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -349,7 +349,7 @@ test.describe("authorized user sees correct translations (pt-br)", async () => { { const locator = page.getByText("Bookings", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); }); @@ -383,7 +383,7 @@ test.describe("authorized user sees correct translations (ar)", async () => { { const locator = page.getByText("Event Types", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -402,7 +402,7 @@ test.describe("authorized user sees correct translations (ar)", async () => { { const locator = page.getByText("Bookings", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -421,7 +421,7 @@ test.describe("authorized user sees correct translations (ar)", async () => { { const locator = page.getByText("Bookings", { exact: true }); - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); }); @@ -463,7 +463,7 @@ test.describe("authorized user sees changed translations (de->ar)", async () => { const locator = page.getByText("Allgemein", { exact: true }); // "general" - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -482,7 +482,7 @@ test.describe("authorized user sees changed translations (de->ar)", async () => { const locator = page.getByText("Allgemein", { exact: true }); // "general" - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); }); @@ -522,7 +522,7 @@ test.describe("authorized user sees changed translations (de->pt-BR) [locale1]", { const locator = page.getByText("Allgemein", { exact: true }); // "general" - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); @@ -541,7 +541,7 @@ test.describe("authorized user sees changed translations (de->pt-BR) [locale1]", { const locator = page.getByText("Allgemein", { exact: true }); // "general" - await expect(locator).toHaveCount(0); + await expect(locator).toHaveCount(0, { timeout: 0 }); } }); }); diff --git a/apps/web/playwright/managed-event-types.e2e.ts b/apps/web/playwright/managed-event-types.e2e.ts index 2e6898dabd..f4042ec9c3 100644 --- a/apps/web/playwright/managed-event-types.e2e.ts +++ b/apps/web/playwright/managed-event-types.e2e.ts @@ -129,7 +129,7 @@ test.describe("Managed Event Types", () => { // Proceed to unlock and check that it got unlocked titleLockIndicator.click(); - await expect(titleLockIndicator.locator("[data-state='checked']")).toHaveCount(0); + await expect(titleLockIndicator.locator("[data-state='checked']")).toHaveCount(0, { timeout: 0 }); await expect(titleLockIndicator.locator("[data-state='unchecked']")).toHaveCount(1); // Save changes diff --git a/apps/web/playwright/organization/organization-creation-flows.e2e.ts b/apps/web/playwright/organization/organization-creation-flows.e2e.ts index 95ed2f0f65..5870446b13 100644 --- a/apps/web/playwright/organization/organization-creation-flows.e2e.ts +++ b/apps/web/playwright/organization/organization-creation-flows.e2e.ts @@ -242,8 +242,8 @@ test.describe("Organization Creation Flows - Comprehensive Suite", () => { // Verify billing fields are NOT visible to regular users if (IS_TEAM_BILLING_ENABLED) { - await expect(page.locator("input[name=seats]")).not.toBeVisible(); - await expect(page.locator("input[name=pricePerSeat]")).not.toBeVisible(); + await expect(page.locator("input[name=seats]")).not.toBeVisible({ timeout: 0 }); + await expect(page.locator("input[name=pricePerSeat]")).not.toBeVisible({ timeout: 0 }); // Verify "Upgrade to Organizations" UI is shown await expect(page.getByText(/upgrade to organizations/i)).toBeVisible(); @@ -439,9 +439,9 @@ test.describe("Organization Creation Flows - Comprehensive Suite", () => { await page.goto("/settings/organizations/new"); // Should NOT see admin billing fields - await expect(page.locator("input[name=seats]")).not.toBeVisible(); - await expect(page.locator("input[name=pricePerSeat]")).not.toBeVisible(); - await expect(page.locator("#billingPeriod")).not.toBeVisible(); + await expect(page.locator("input[name=seats]")).not.toBeVisible({ timeout: 0 }); + await expect(page.locator("input[name=pricePerSeat]")).not.toBeVisible({ timeout: 0 }); + await expect(page.locator("#billingPeriod")).not.toBeVisible({ timeout: 0 }); // Should see upgrade/pricing UI await expect(page.getByText(/upgrade to organizations/i)).toBeVisible(); diff --git a/apps/web/playwright/organization/organization-redirection.e2e.ts b/apps/web/playwright/organization/organization-redirection.e2e.ts index 2d25b7c202..e2d09e6dc4 100644 --- a/apps/web/playwright/organization/organization-redirection.e2e.ts +++ b/apps/web/playwright/organization/organization-redirection.e2e.ts @@ -57,7 +57,7 @@ test.describe("Unpublished Organization Redirection", () => { await expect(page.getByTestId("empty-screen")).toBeVisible(); // Ensure that the team name is not displayed. - await expect(page.getByTestId("team-name")).toHaveCount(0); + await expect(page.getByTestId("team-name")).toHaveCount(0, { timeout: 0 }); }); }); @@ -101,8 +101,8 @@ test.describe("Unpublished Organization Redirection", () => { await expect(page.getByTestId("empty-screen")).toBeVisible(); // Ensure user profile elements are not visible. - await expect(page.locator('[data-testid="name-title"]')).toHaveCount(0); - await expect(page.locator('[data-testid="event-types"]')).toHaveCount(0); + await expect(page.locator('[data-testid="name-title"]')).toHaveCount(0, { timeout: 0 }); + await expect(page.locator('[data-testid="event-types"]')).toHaveCount(0, { timeout: 0 }); }); }); diff --git a/apps/web/playwright/team/team-invitation.e2e.ts b/apps/web/playwright/team/team-invitation.e2e.ts index dd958496c1..73d02c198e 100644 --- a/apps/web/playwright/team/team-invitation.e2e.ts +++ b/apps/web/playwright/team/team-invitation.e2e.ts @@ -74,7 +74,7 @@ test.describe("Team", () => { await page.goto(`/settings/teams/${team.id}/settings`); await expect( page.locator(`[data-testid="email-${invitedUserEmail.replace("@", "")}-pending"]`) - ).toHaveCount(0); + ).toHaveCount(0, { timeout: 0 }); }); await test.step("To the team by invite link", async () => {