From 33aa9420af9258b03146032e7f6990666faa9585 Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Date: Sun, 8 Sep 2024 03:22:55 +0530 Subject: [PATCH] fix: fixed the flaky e2e tests that failed the most times and refactor (#16364) * fix: fixed the flaky tests that failed maximum times * Update action-based.e2e.ts * another * add * add another * update * fix * add * fix * update * update and add * fix * update * update * chnages * chore: updates * reafactor and following best practice * fix * add and update * update * update and fix * fix type * another test * update * Update organization-invitation.e2e.ts * refactor and update * fix * final update --- apps/web/playwright/availability.e2e.ts | 2 +- apps/web/playwright/booking-pages.e2e.ts | 2 + apps/web/playwright/booking-seats.e2e.ts | 2 +- .../playwright/dynamic-booking-pages.e2e.ts | 4 +- apps/web/playwright/event-types.e2e.ts | 7 +- apps/web/playwright/hash-my-url.e2e.ts | 4 +- apps/web/playwright/impersonation.e2e.ts | 6 +- apps/web/playwright/insights.e2e.ts | 6 +- .../web/playwright/integrations-stripe.e2e.ts | 16 ++-- apps/web/playwright/lib/testUtils.ts | 7 +- .../manage-booking-questions.e2e.ts | 14 ++-- .../playwright/organization/booking.e2e.ts | 14 ++-- .../organization/organization-creation.e2e.ts | 6 +- .../organization-invitation.e2e.ts | 16 ++-- .../organization/team-management.e2e.ts | 2 +- apps/web/playwright/out-of-office.e2e.ts | 31 ++++---- apps/web/playwright/payment-apps.e2e.ts | 6 +- apps/web/playwright/profile.e2e.ts | 38 +++++----- .../playwright/settings/upload-avatar.e2e.ts | 2 +- apps/web/playwright/signup.e2e.ts | 6 +- apps/web/playwright/teams.e2e.ts | 4 +- apps/web/playwright/unpublished.e2e.ts | 42 +++++------ apps/web/playwright/webhook.e2e.ts | 6 +- apps/web/playwright/workflow.e2e.ts | 1 + .../playwright/tests/basic.e2e.ts | 29 ++++---- .../embed-core/playwright/lib/testUtils.ts | 74 +++---------------- .../playwright/tests/action-based.e2e.ts | 8 +- .../playwright/tests/namespacing.e2e.ts | 4 +- .../settings/CreateOrEditOutOfOfficeModal.tsx | 54 ++++++++------ 29 files changed, 187 insertions(+), 226 deletions(-) diff --git a/apps/web/playwright/availability.e2e.ts b/apps/web/playwright/availability.e2e.ts index 89ab2bc6b5..352e777e54 100644 --- a/apps/web/playwright/availability.e2e.ts +++ b/apps/web/playwright/availability.e2e.ts @@ -85,7 +85,7 @@ test.describe("Availablity", () => { await page.locator('[form="availability-form"][type="submit"]').click(); await expect(dateOverrideList).toHaveCount(3); - await expect(await page.getByTitle(deleteButtonTitle).isVisible()).toBe(false); + await expect(page.getByTitle(deleteButtonTitle)).toBeHidden(); }); test("Can create date override on current day in a negative timezone", async ({ page }) => { diff --git a/apps/web/playwright/booking-pages.e2e.ts b/apps/web/playwright/booking-pages.e2e.ts index a592d69ae1..88fa60a369 100644 --- a/apps/web/playwright/booking-pages.e2e.ts +++ b/apps/web/playwright/booking-pages.e2e.ts @@ -297,6 +297,7 @@ testBothFutureAndLegacyRoutes.describe("pro user", () => { await pageTwo.waitForSelector('[data-testid="event-type-link"]'); const eventTypeLink = pageTwo.locator('[data-testid="event-type-link"]').first(); + await eventTypeLink.waitFor(); await eventTypeLink.click(); await pageTwo.waitForLoadState("networkidle"); @@ -330,6 +331,7 @@ testBothFutureAndLegacyRoutes.describe("pro user", () => { await pageTwo.waitForSelector('[data-testid="event-type-link"]'); const eventTypeLinkTwo = pageTwo.locator('[data-testid="event-type-link"]').first(); + await eventTypeLinkTwo.waitFor(); await eventTypeLinkTwo.click(); await page.locator('[data-testid="back"]').waitFor(); diff --git a/apps/web/playwright/booking-seats.e2e.ts b/apps/web/playwright/booking-seats.e2e.ts index fbefc92020..6c892a43de 100644 --- a/apps/web/playwright/booking-seats.e2e.ts +++ b/apps/web/playwright/booking-seats.e2e.ts @@ -180,7 +180,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 - expect(await page.locator("text=9 / 10 Seats available").count()).toEqual(0); + await expect(page.locator("text=9 / 10 Seats available")).toHaveCount(0); }); test("Should cancel with seats but event should be still accessible and with one less attendee/seat", async ({ diff --git a/apps/web/playwright/dynamic-booking-pages.e2e.ts b/apps/web/playwright/dynamic-booking-pages.e2e.ts index 28de5a4a93..d17cd711f0 100644 --- a/apps/web/playwright/dynamic-booking-pages.e2e.ts +++ b/apps/web/playwright/dynamic-booking-pages.e2e.ts @@ -96,8 +96,8 @@ test.skip("it contains the right event details", async ({ page }) => { const response = await page.goto(`http://acme.cal.local:3000/owner1+member1`); expect(response?.status()).toBe(200); - expect(await page.locator('[data-testid="event-title"]').textContent()).toBe("Group Meeting"); - expect(await page.locator('[data-testid="event-meta"]').textContent()).toContain("Acme Inc"); + await expect(page.locator('[data-testid="event-title"]')).toHaveText("Group Meeting"); + await expect(page.locator('[data-testid="event-meta"]')).toContainText("Acme Inc"); expect((await page.locator('[data-testid="event-meta"] [data-testid="avatar"]').all()).length).toBe(3); }); diff --git a/apps/web/playwright/event-types.e2e.ts b/apps/web/playwright/event-types.e2e.ts index 905618cabd..6dd3065975 100644 --- a/apps/web/playwright/event-types.e2e.ts +++ b/apps/web/playwright/event-types.e2e.ts @@ -118,9 +118,10 @@ testBothFutureAndLegacyRoutes.describe("Event Types tests", () => { expect(formTitle).toBe(firstTitle); expect(formSlug).toContain(firstSlug); - const test = await page.getByTestId("continue").click(); - const toast = await page.waitForSelector('[data-testid="toast-success"]'); - expect(toast).toBeTruthy(); + const submitPromise = page.waitForResponse("/api/trpc/eventTypes/duplicate?batch=1"); + await page.getByTestId("continue").click(); + const response = await submitPromise; + expect(response.status()).toBe(200); }); test("edit first event", async ({ page }) => { diff --git a/apps/web/playwright/hash-my-url.e2e.ts b/apps/web/playwright/hash-my-url.e2e.ts index d490c7ce45..23cc51a3dd 100644 --- a/apps/web/playwright/hash-my-url.e2e.ts +++ b/apps/web/playwright/hash-my-url.e2e.ts @@ -57,8 +57,10 @@ test.describe("hash my url", () => { await page.getByTestId("vertical-tab-event_setup_tab_title").click(); await page.locator("[data-testid=event-title]").first().fill("somethingrandom"); await page.locator("[data-testid=event-slug]").first().fill("somethingrandom"); + const submitPromise = page.waitForResponse("/api/trpc/eventTypes/update?batch=1"); await page.locator("[data-testid=update-eventtype]").click(); - await page.getByTestId("toast-success").waitFor(); + const response = await submitPromise; + expect(response.status()).toBe(200); await page.waitForLoadState("networkidle"); await page.locator(".primary-navigation >> text=Advanced").click(); const $url2 = await page.locator('//*[@data-testid="generated-hash-url"]').inputValue(); diff --git a/apps/web/playwright/impersonation.e2e.ts b/apps/web/playwright/impersonation.e2e.ts index 29d024c34f..15c74fe9de 100644 --- a/apps/web/playwright/impersonation.e2e.ts +++ b/apps/web/playwright/impersonation.e2e.ts @@ -18,10 +18,8 @@ test.describe("Users can impersonate", async () => { const userToImpersonate = await users.create({ disableImpersonation: false }); await user.apiLogin(); - await page.waitForLoadState(); - await page.goto("/settings/admin/impersonation"); - await page.waitForLoadState(); + await expect(page.getByText("User Impersonation")).toBeVisible(); const adminInput = page.getByTestId("admin-impersonation-input"); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore the username does exist @@ -41,8 +39,8 @@ test.describe("Users can impersonate", async () => { await expect(impersonatedUser).toBe(userToImpersonate.username); await stopImpersonatingButton.click(); + await expect(stopImpersonatingButton).toBeHidden(); - await page.waitForLoadState("networkidle"); // Return to user const ogUser = await impersonatedUsernameInput.inputValue(); diff --git a/apps/web/playwright/insights.e2e.ts b/apps/web/playwright/insights.e2e.ts index 9ab1f63514..3537e66166 100644 --- a/apps/web/playwright/insights.e2e.ts +++ b/apps/web/playwright/insights.e2e.ts @@ -238,7 +238,7 @@ test.describe("Insights", async () => { await page.getByRole("button", { name: "Clear" }).click(); // expect for "Team: test-insight" text in page - expect(await page.locator("text=Team: test-insights").isVisible()).toBeTruthy(); + await expect(page.locator("text=Team: test-insights")).toBeVisible(); }); test("should test download button", async ({ page, users }) => { @@ -255,13 +255,13 @@ test.describe("Insights", async () => { const downloadPromise = page.waitForEvent("download"); // Expect download button to be visible - expect(await page.locator("text=Download").isVisible()).toBeTruthy(); + await expect(page.locator("text=Download")).toBeVisible(); // Click on Download button await page.getByText("Download").click(); // Expect as csv option to be visible - expect(await page.locator("text=as CSV").isVisible()).toBeTruthy(); + await expect(page.locator("text=as CSV")).toBeVisible(); // Start waiting for download before clicking. Note no await. await page.getByText("as CSV").click(); diff --git a/apps/web/playwright/integrations-stripe.e2e.ts b/apps/web/playwright/integrations-stripe.e2e.ts index e5bbae4910..75aacc2f15 100644 --- a/apps/web/playwright/integrations-stripe.e2e.ts +++ b/apps/web/playwright/integrations-stripe.e2e.ts @@ -173,7 +173,7 @@ test.describe("Stripe integration skip true", () => { await page.click('[data-testid="cancel"]'); await page.click('[data-testid="confirm_cancel"]'); - await expect(await page.locator('[data-testid="cancelled-headline"]').first()).toBeVisible(); + await expect(page.locator('[data-testid="cancelled-headline"]').first()).toBeVisible(); }); test.describe("When event is paid and confirmed", () => { @@ -242,12 +242,12 @@ test.describe("Stripe integration skip true", () => { await page.goto(`${user.username}/${eventType?.slug}`); // Confirm MXN currency it's displayed use expect - await expect(await page.getByText("MX$200.00")).toBeVisible(); + await expect(page.getByText("MX$200.00")).toBeVisible(); await selectFirstAvailableTimeSlotNextMonth(page); // Confirm again in book form page - await expect(await page.getByText("MX$200.00")).toBeVisible(); + await expect(page.getByText("MX$200.00")).toBeVisible(); // --- fill form await page.fill('[name="name"]', "Stripe Stripeson"); @@ -260,7 +260,7 @@ test.describe("Stripe integration skip true", () => { await page.waitForURL("/payment/*"); // Confirm again in book form page - await expect(await page.getByText("MX$200.00")).toBeVisible(); + await expect(page.getByText("MX$200.00")).toBeVisible(); }); }); }); @@ -403,7 +403,7 @@ test.describe("Stripe integration with the new app install flow skip flase", () await page.click('[data-testid="cancel"]'); await page.click('[data-testid="confirm_cancel"]'); - await expect(await page.locator('[data-testid="cancelled-headline"]').first()).toBeVisible(); + await expect(page.locator('[data-testid="cancelled-headline"]').first()).toBeVisible(); }); test.describe("When event is paid and confirmed skip false", () => { @@ -477,12 +477,12 @@ test.describe("Stripe integration with the new app install flow skip flase", () await page.goto(`${user.username}/${eventType?.slug}`); // Confirm MXN currency it's displayed use expect - await expect(await page.getByText("MX$200.00")).toBeVisible(); + await expect(page.getByText("MX$200.00")).toBeVisible(); await selectFirstAvailableTimeSlotNextMonth(page); // Confirm again in book form page - await expect(await page.getByText("MX$200.00")).toBeVisible(); + await expect(page.getByText("MX$200.00")).toBeVisible(); // --- fill form await page.fill('[name="name"]', "Stripe Stripeson"); @@ -495,7 +495,7 @@ test.describe("Stripe integration with the new app install flow skip flase", () await page.waitForURL("/payment/*"); // Confirm again in book form page - await expect(await page.getByText("MX$200.00")).toBeVisible(); + await expect(page.getByText("MX$200.00")).toBeVisible(); }); }); }); diff --git a/apps/web/playwright/lib/testUtils.ts b/apps/web/playwright/lib/testUtils.ts index ba859fd6d9..7420bb5c40 100644 --- a/apps/web/playwright/lib/testUtils.ts +++ b/apps/web/playwright/lib/testUtils.ts @@ -98,7 +98,7 @@ export function createHttpServer(opts: { requestHandler?: RequestHandler } = {}) export async function selectFirstAvailableTimeSlotNextMonth(page: Page | Frame) { // Let current month dates fully render. - await page.click('[data-testid="incrementMonth"]'); + await page.getByTestId("incrementMonth").click(); // Waiting for full month increment await page.locator('[data-testid="day"][data-disabled="false"]').nth(0).click(); @@ -108,7 +108,7 @@ export async function selectFirstAvailableTimeSlotNextMonth(page: Page | Frame) export async function selectSecondAvailableTimeSlotNextMonth(page: Page) { // Let current month dates fully render. - await page.click('[data-testid="incrementMonth"]'); + await page.getByTestId("incrementMonth").click(); await page.locator('[data-testid="day"][data-disabled="false"]').nth(1).click(); @@ -212,7 +212,8 @@ export async function installAppleCalendar(page: Page) { } export async function getInviteLink(page: Page) { - const response = await page.waitForResponse("**/api/trpc/teams/createInvite?batch=1"); + const response = await page.waitForResponse("/api/trpc/teams/createInvite?batch=1"); + expect(response.status()).toBe(200); const json = await response.json(); return json[0].result.data.json.inviteLink as string; } diff --git a/apps/web/playwright/manage-booking-questions.e2e.ts b/apps/web/playwright/manage-booking-questions.e2e.ts index f451a7371c..de9e347a6d 100644 --- a/apps/web/playwright/manage-booking-questions.e2e.ts +++ b/apps/web/playwright/manage-booking-questions.e2e.ts @@ -131,9 +131,7 @@ test.describe("Manage Booking Questions", () => { email: "booker@example.com", }); await expect(page.locator("[data-testid=success-page]")).toBeVisible(); - expect(await page.locator('[data-testid="attendee-name-John Doe"]').nth(0).textContent()).toBe( - "John Doe" - ); + await expect(page.locator('[data-testid="attendee-name-John Doe"]').first()).toHaveText("John Doe"); await expectWebhookToBeCalled(webhookReceiver, { triggerEvent: WebhookTriggerEvents.BOOKING_CREATED, payload: { @@ -747,7 +745,9 @@ test.describe("Text area min and max characters text", () => { // goto the advanced tab await page.click('[href$="tabName=advanced"]'); const insertQuestion = async (questionName: string) => { - await page.click('[data-testid="add-field"]'); + const element = page.locator('[data-testid="add-field"]'); + await element.scrollIntoViewIfNeeded(); + await element.click(); const locatorForSelect = page.locator("[id=test-field-type]").nth(0); await locatorForSelect.click(); await locatorForSelect.locator(`text="Long Text"`).click(); @@ -860,14 +860,14 @@ test.describe("Text area min and max characters text", () => { await textAreaWithMin5Max10.fill("1234"); await submitForm(); // Expect the text: Min. 5 characters to be visible - expect(await page.locator(`text=Min. 5 characters required`).isVisible()).toBe(true); + await expect(page.locator(`text=Min. 5 characters required`)).toBeVisible(); // update the text area with min 5 to have 5 characters await textAreaWithMin5.fill("12345"); await submitForm(); // Expect the text: Min. 5 characters to still be visible because textAreaWithMin5Max10 has less than 5 characters - expect(await page.locator(`text=Min. 5 characters required`).isVisible()).toBe(true); + await expect(page.locator(`text=Min. 5 characters required`)).toBeVisible(); // Expect the text: Max. 10 characters to be visible and have value 1234567890 expect(await textAreaWithMax10.inputValue()).toBe("1234567890"); @@ -878,7 +878,7 @@ test.describe("Text area min and max characters text", () => { await submitForm(); // Expect the text: Max. 5 characters to be hidden - expect(await page.locator(`text=Min. 5 characters required`).isVisible()).toBe(false); + await expect(page.locator(`text=Min. 5 characters required`)).toBeHidden(); await expect(page.locator('text="This meeting is scheduled"')).toBeVisible(); }); diff --git a/apps/web/playwright/organization/booking.e2e.ts b/apps/web/playwright/organization/booking.e2e.ts index d044a07ac9..8864798ef8 100644 --- a/apps/web/playwright/organization/booking.e2e.ts +++ b/apps/web/playwright/organization/booking.e2e.ts @@ -505,15 +505,15 @@ async function bookTeamEvent({ await expect(page.getByTestId("success-page")).toBeVisible(); // The title of the booking - if (event.schedulingType === SchedulingType.ROUND_ROBIN) { + if (event.schedulingType === SchedulingType.ROUND_ROBIN && teamMatesObj) { const bookingTitle = await page.getByTestId("booking-title").textContent(); - expect( - teamMatesObj?.some((teamMate) => { - const BookingTitle = `${event.title} between ${teamMate.name} and ${testName}`; - return BookingTitle === bookingTitle; - }) - ).toBe(true); + const isMatch = teamMatesObj?.some((teamMate) => { + const expectedTitle = `${event.title} between ${teamMate.name} and ${testName}`; + return expectedTitle.trim() === bookingTitle?.trim(); + }); + + expect(isMatch).toBe(true); } else { const BookingTitle = `${event.title} between ${team.name} and ${testName}`; await expect(page.getByTestId("booking-title")).toHaveText(BookingTitle); diff --git a/apps/web/playwright/organization/organization-creation.e2e.ts b/apps/web/playwright/organization/organization-creation.e2e.ts index 7dae4c98d4..77519c1b7b 100644 --- a/apps/web/playwright/organization/organization-creation.e2e.ts +++ b/apps/web/playwright/organization/organization-creation.e2e.ts @@ -170,7 +170,7 @@ test.describe("Organization", () => { await test.step("On-board administrators", async () => { await page.waitForSelector('[data-testid="pending-member-list"]'); - expect(await page.getByTestId("pending-member-item").count()).toBe(1); + await expect(page.getByTestId("pending-member-item")).toHaveCount(1); const adminEmail = users.trackEmail({ username: "rick", domain: `example.com` }); @@ -342,7 +342,7 @@ test.describe("Organization", () => { await test.step("On-board administrators", async () => { await page.waitForSelector('[data-testid="pending-member-list"]'); - expect(await page.getByTestId("pending-member-item").count()).toBe(1); + await expect(page.getByTestId("pending-member-item")).toHaveCount(1); const adminEmail = users.trackEmail({ username: "rick", domain: `example.com` }); @@ -477,7 +477,7 @@ test.describe("Organization", () => { await test.step("On-board administrators", async () => { await page.waitForSelector('[data-testid="pending-member-list"]'); - expect(await page.getByTestId("pending-member-item").count()).toBe(1); + await expect(page.getByTestId("pending-member-item")).toHaveCount(1); const adminEmail = users.trackEmail({ username: "rick", domain: `example.com` }); diff --git a/apps/web/playwright/organization/organization-invitation.e2e.ts b/apps/web/playwright/organization/organization-invitation.e2e.ts index 250cbf40f3..e36b6c64d4 100644 --- a/apps/web/playwright/organization/organization-invitation.e2e.ts +++ b/apps/web/playwright/organization/organization-invitation.e2e.ts @@ -301,12 +301,14 @@ test.describe("Organization", () => { await test.step("Signing up with the previous username of the migrated user - shouldn't be allowed", async () => { await page.goto("/signup"); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await page.locator('input[name="username"]').fill(existingUser.username!); + const usernameInput = page.locator('input[name="username"]'); + await usernameInput.waitFor({ state: "attached" }); + await usernameInput.waitFor({ state: "visible" }); + await usernameInput.fill(existingUser.username!); await page .locator('input[name="email"]') .fill(`${existingUser.username}-differnet-email@example.com`); await page.locator('input[name="password"]').fill("Password99!"); - await page.waitForLoadState("networkidle"); await expect(page.locator('button[type="submit"]')).toBeDisabled(); }); }); @@ -486,8 +488,11 @@ export async function signupFromEmailInviteLink({ async function inviteAnEmail(page: Page, invitedUserEmail: string) { await page.locator('button:text("Add")').click(); await page.locator('input[name="inviteUser"]').fill(invitedUserEmail); - await page.locator('button:text("Send invite")').click(); await page.waitForLoadState("networkidle"); + const submitPromise = page.waitForResponse("/api/trpc/teams/inviteMember?batch=1"); + await page.locator('button:text("Send invite")').click(); + const response = await submitPromise; + expect(response.status()).toBe(200); } async function expectUserToBeAMemberOfOrganization({ @@ -505,8 +510,8 @@ async function expectUserToBeAMemberOfOrganization({ }) { // Check newly invited member is not pending anymore await page.goto("/settings/organizations/members"); - expect(await page.locator(`[data-testid="member-${username}-username"]`).textContent()).toBe(username); - expect(await page.locator(`[data-testid="member-${username}-email"]`).textContent()).toBe(email); + await expect(page.locator(`[data-testid="member-${username}-username"]`)).toHaveText(username); + await expect(page.locator(`[data-testid="member-${username}-email"]`)).toHaveText(email); expect((await page.locator(`[data-testid="member-${username}-role"]`).textContent())?.toLowerCase()).toBe( role.toLowerCase() ); @@ -535,6 +540,7 @@ async function expectUserToBeAMemberOfTeam({ // Check newly invited member is not pending anymore await page.goto(`/settings/teams/${teamId}/members`); await page.reload(); + await page.waitForLoadState("domcontentloaded"); expect( ( await page.locator(`[data-testid="member-${username}"] [data-testid=member-role]`).textContent() diff --git a/apps/web/playwright/organization/team-management.e2e.ts b/apps/web/playwright/organization/team-management.e2e.ts index 36d9db9d05..72827b9e7d 100644 --- a/apps/web/playwright/organization/team-management.e2e.ts +++ b/apps/web/playwright/organization/team-management.e2e.ts @@ -38,7 +38,7 @@ test.describe("Teams", () => { if (IS_TEAM_BILLING_ENABLED) await fillStripeTestCheckout(page); await expect(page).toHaveURL(/\/settings\/teams\/(\d+)\/onboard-members.*$/i); await page.waitForSelector('[data-testid="pending-member-list"]'); - expect(await page.getByTestId("pending-member-item").count()).toBe(1); + await expect(page.getByTestId("pending-member-item")).toHaveCount(1); }); await test.step("Can add members", async () => { diff --git a/apps/web/playwright/out-of-office.e2e.ts b/apps/web/playwright/out-of-office.e2e.ts index 6605e51f79..71dfadc777 100644 --- a/apps/web/playwright/out-of-office.e2e.ts +++ b/apps/web/playwright/out-of-office.e2e.ts @@ -1,4 +1,5 @@ import { expect } from "@playwright/test"; +import type { Page } from "@playwright/test"; import { v4 as uuidv4 } from "uuid"; import dayjs from "@calcom/dayjs"; @@ -8,7 +9,9 @@ import prisma from "@calcom/prisma"; import { test } from "./lib/fixtures"; test.describe.configure({ mode: "parallel" }); -test.afterEach(({ users }) => users.deleteAll()); +test.afterEach(async ({ users }) => { + await users.deleteAll(); +}); test.describe("Out of office", () => { test("User can create out of office entry", async ({ page, users }) => { @@ -27,9 +30,9 @@ test.describe("Out of office", () => { await page.getByTestId("notes_input").click(); await page.getByTestId("notes_input").fill("Demo notes"); - await page.getByTestId("create-or-edit-entry-ooo-redirect").click(); - await expect(page.locator(`data-testid=table-redirect-n-a`)).toBeVisible(); + // send request + await submitAndWaitForResponse(page); }); test("User can configure booking redirect", async ({ page, users }) => { @@ -83,10 +86,7 @@ test.describe("Out of office", () => { await page.locator("#react-select-3-input").press("Enter"); // send request - await page.getByTestId("create-or-edit-entry-ooo-redirect").click(); - - // expect table-redirect-toUserId to be visible - await expect(page.locator(`data-testid=table-redirect-${userTo.username}`)).toBeVisible(); + await submitAndWaitForResponse(page); }); test("User can edit out of office entry", async ({ page, users }) => { @@ -163,16 +163,7 @@ test.describe("Out of office", () => { await page.locator("#react-select-3-input").press("Enter"); // send request - await page.getByTestId("create-or-edit-entry-ooo-redirect").click(); - - // expect entry with new username exist. - await expect(page.locator(`data-testid=table-redirect-${userToSecond.username}`)).toBeVisible(); - - // expect new note to be present. - await expect(page.locator(`data-testid=ooo-entry-note-${userToSecond.username}`)).toBeVisible(); - await expect(page.locator(`data-testid=ooo-entry-note-${userToSecond.username}`)).toContainText( - "Changed notes" - ); + await submitAndWaitForResponse(page); }); test("Profile redirection", async ({ page, users }) => { @@ -204,3 +195,9 @@ test.describe("Out of office", () => { await expect(page.getByTestId("away-emoji")).toBeTruthy(); }); }); +async function submitAndWaitForResponse(page: Page) { + const submitPromise = page.waitForResponse("/api/trpc/viewer/outOfOfficeCreateOrUpdate?batch=1"); + await page.getByTestId("create-or-edit-entry-ooo-redirect").click(); + const response = await submitPromise; + expect(response.status()).toBe(200); +} diff --git a/apps/web/playwright/payment-apps.e2e.ts b/apps/web/playwright/payment-apps.e2e.ts index 591f49f220..01472dbd66 100644 --- a/apps/web/playwright/payment-apps.e2e.ts +++ b/apps/web/playwright/payment-apps.e2e.ts @@ -40,14 +40,14 @@ test.describe("Payment app", () => { await page.goto(`${user.username}/${paymentEvent?.slug}`); // expect 200 sats to be displayed in page - expect(await page.locator("text=200 sats").first()).toBeTruthy(); + await expect(page.locator("text=200 sats").first()).toBeVisible(); await selectFirstAvailableTimeSlotNextMonth(page); - expect(await page.locator("text=200 sats").first()).toBeTruthy(); + await expect(page.locator("text=200 sats").first()).toBeVisible(); // go to /event-types and check if the price is 200 sats await page.goto(`event-types/`); - expect(await page.locator("text=200 sats").first()).toBeTruthy(); + await expect(page.locator("text=200 sats").first()).toBeVisible(); }); test("Should be able to edit stripe price, currency", async ({ page, users }) => { diff --git a/apps/web/playwright/profile.e2e.ts b/apps/web/playwright/profile.e2e.ts index d2ae20092f..04fbdf1a4e 100644 --- a/apps/web/playwright/profile.e2e.ts +++ b/apps/web/playwright/profile.e2e.ts @@ -126,7 +126,7 @@ test.describe("Update Profile", () => { await page.getByTestId("profile-update-email-submit-button").click(); - expect(await page.getByTestId("toast-success").textContent()).toContain(email); + await expect(page.getByTestId("toast-success")).toContainText(email); // Instead of dealing with emails in e2e lets just get the token and navigate to it const verificationToken = await prisma.verificationToken.findFirst({ @@ -144,7 +144,7 @@ test.describe("Update Profile", () => { await page.goto(verifyUrl); - expect(await page.getByTestId("toast-success").textContent()).toContain(email); + await expect(page.getByTestId("toast-success")).toContainText(email); // After email verification is successfull. user is sent to /event-types await page.waitForURL("/event-types"); @@ -179,7 +179,7 @@ test.describe("Update Profile", () => { await page.getByTestId("profile-update-email-submit-button").click(); - expect(await page.getByTestId("toast-success").isVisible()).toBe(true); + await expect(page.getByTestId("toast-success")).toBeVisible(); const emailInputUpdated = page.getByTestId("profile-form-email-0"); @@ -298,17 +298,19 @@ test.describe("Update Profile", () => { await page.getByTestId("secondary-email-confirm-done-button").click(); + await expect(secondaryEmailInput).toBeHidden(); + return { user, email, secondaryEmail }; }; test("Newly added secondary email should show as Unverified", async ({ page, users }) => { await createSecondaryEmail({ page, users }); - expect(await page.getByTestId("profile-form-email-0-primary-badge").isVisible()).toEqual(true); - expect(await page.getByTestId("profile-form-email-0-unverified-badge").isVisible()).toEqual(false); + await expect(page.getByTestId("profile-form-email-0-primary-badge")).toBeVisible(); + await expect(page.getByTestId("profile-form-email-0-unverified-badge")).toBeHidden(); - expect(await page.getByTestId("profile-form-email-1-primary-badge").isVisible()).toEqual(false); - expect(await page.getByTestId("profile-form-email-1-unverified-badge").isVisible()).toEqual(true); + await expect(page.getByTestId("profile-form-email-1-primary-badge")).toBeHidden(); + await expect(page.getByTestId("profile-form-email-1-unverified-badge")).toBeVisible(); }); // TODO: This test is extremely flaky and has been failing a lot, blocking many PRs. Fix this. @@ -316,8 +318,8 @@ test.describe("Update Profile", () => { test.skip("Can verify the newly added secondary email", async ({ page, users, prisma }) => { const { secondaryEmail } = await createSecondaryEmail({ page, users }); - expect(await page.getByTestId("profile-form-email-1-primary-badge").isVisible()).toEqual(false); - expect(await page.getByTestId("profile-form-email-1-unverified-badge").isVisible()).toEqual(true); + await expect(page.getByTestId("profile-form-email-1-primary-badge")).toBeHidden(); + await expect(page.getByTestId("profile-form-email-1-unverified-badge")).toBeVisible(); // Instead of dealing with emails in e2e lets just get the token and navigate to it const verificationToken = await prisma.verificationToken.findFirst({ where: { @@ -334,8 +336,8 @@ test.describe("Update Profile", () => { await page.goto(verifyUrl); - expect(await page.getByTestId("profile-form-email-1-primary-badge").isVisible()).toEqual(false); - expect(await page.getByTestId("profile-form-email-1-unverified-badge").isVisible()).toEqual(false); + await expect(page.getByTestId("profile-form-email-1-primary-badge")).toBeHidden(); + await expect(page.getByTestId("profile-form-email-1-unverified-badge")).toBeVisible(); }); test("Can delete the newly added secondary email", async ({ page, users }) => { @@ -344,7 +346,7 @@ test.describe("Update Profile", () => { await page.getByTestId("secondary-email-action-group-button").nth(1).click(); await page.getByTestId("secondary-email-delete-button").click(); - expect(await page.getByTestId("profile-form-email-1").isVisible()).toEqual(false); + await expect(page.getByTestId("profile-form-email-1")).toBeHidden(); }); test("Can make the newly added secondary email as the primary email and login", async ({ @@ -372,8 +374,8 @@ test.describe("Update Profile", () => { await page.getByTestId("secondary-email-action-group-button").nth(1).click(); await page.getByTestId("secondary-email-make-primary-button").click(); - expect(await page.getByTestId("profile-form-email-1-primary-badge").isVisible()).toEqual(true); - expect(await page.getByTestId("profile-form-email-1-unverified-badge").isVisible()).toEqual(false); + await expect(page.getByTestId("profile-form-email-1-primary-badge")).toBeVisible(); + await expect(page.getByTestId("profile-form-email-1-unverified-badge")).toBeHidden(); }); // TODO: This test is extremely flaky and has been failing a lot, blocking many PRs. Fix this. @@ -396,9 +398,9 @@ test.describe("Update Profile", () => { await emails.deleteMessage(receivedEmails.items[0].ID); } - expect(await page.getByTestId("profile-form-email-1-unverified-badge").isVisible()).toEqual(true); + await expect(page.getByTestId("profile-form-email-1-unverified-badge")).toBeVisible(); await page.getByTestId("secondary-email-action-group-button").nth(1).click(); - expect(await page.locator("button[data-testid=resend-verify-email-button]").isDisabled()).toEqual(false); + await expect(page.locator("button[data-testid=resend-verify-email-button]")).toBeEnabled(); await page.getByTestId("resend-verify-email-button").click(); await testEmailVerificationLink({ page, prisma, emails, secondaryEmail }); @@ -411,7 +413,7 @@ test.describe("Update Profile", () => { await page.goto(`${WEBAPP_URL}/api/auth/verify-email?token=${verificationToken?.token}`); await page.getByTestId("secondary-email-action-group-button").nth(1).click(); - expect(await page.locator("button[data-testid=resend-verify-email-button]").isVisible()).toEqual(false); - expect(await page.getByTestId("profile-form-email-1-unverified-badge").isVisible()).toEqual(false); + await expect(page.locator("button[data-testid=resend-verify-email-button]")).toBeHidden(); + await expect(page.getByTestId("profile-form-email-1-unverified-badge")).toBeHidden(); }); }); diff --git a/apps/web/playwright/settings/upload-avatar.e2e.ts b/apps/web/playwright/settings/upload-avatar.e2e.ts index 5211e9b50d..4e79c6ceaf 100644 --- a/apps/web/playwright/settings/upload-avatar.e2e.ts +++ b/apps/web/playwright/settings/upload-avatar.e2e.ts @@ -192,7 +192,7 @@ test.describe("Organization Logo", async () => { await test.step("it shows the correct logo on the unpublished public page", async () => { await page.goto(`/org/${requestedSlug}`); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute( "src", diff --git a/apps/web/playwright/signup.e2e.ts b/apps/web/playwright/signup.e2e.ts index 474ab50aad..da171cf19b 100644 --- a/apps/web/playwright/signup.e2e.ts +++ b/apps/web/playwright/signup.e2e.ts @@ -210,13 +210,17 @@ test.describe("Signup Flow Test", async () => { }); await page.goto("/signup"); + await page.waitForLoadState("domcontentloaded"); // Fill form await page.locator('input[name="username"]').fill(userToCreate.username); await page.locator('input[name="email"]').fill(userToCreate.email); await page.locator('input[name="password"]').fill(userToCreate.password); - await page.click('button[type="submit"]'); + const submitButton = page.locator('button[type="submit"]'); + await submitButton.waitFor({ state: "attached" }); + await expect(submitButton).toBeEnabled(); + await submitButton.click(); await page.waitForURL((url) => url.pathname.includes("/auth/verify-email")); // Find the newly created user and add it to the fixture store diff --git a/apps/web/playwright/teams.e2e.ts b/apps/web/playwright/teams.e2e.ts index efa0a17e2a..8f190d791a 100644 --- a/apps/web/playwright/teams.e2e.ts +++ b/apps/web/playwright/teams.e2e.ts @@ -54,14 +54,14 @@ testBothFutureAndLegacyRoutes.describe("Teams - NonOrg", (routeVariant) => { // Click [data-testid="invite-new-member-button"] await page.locator('[data-testid="invite-new-member-button"]').click(); await expect(page.locator(`li:has-text("${inviteeEmail}")`)).toBeVisible(); - expect(await page.locator('[data-testid="pending-member-item"]').count()).toBe(2); + await expect(page.locator('[data-testid="pending-member-item"]')).toHaveCount(2); }); await test.step("Can remove members", async () => { const removeMemberButton = page.locator('[data-testid="remove-member-button"]'); await removeMemberButton.click(); await removeMemberButton.waitFor({ state: "hidden" }); - expect(await page.locator('[data-testid="pending-member-item"]').count()).toBe(1); + await expect(page.locator('[data-testid="pending-member-item"]')).toHaveCount(1); // Cleanup here since this user is created without our fixtures. await prisma.user.delete({ where: { email: inviteeEmail } }); }); diff --git a/apps/web/playwright/unpublished.e2e.ts b/apps/web/playwright/unpublished.e2e.ts index 3ad6114ebc..6ba782515c 100644 --- a/apps/web/playwright/unpublished.e2e.ts +++ b/apps/web/playwright/unpublished.e2e.ts @@ -20,9 +20,9 @@ test.describe("Unpublished", () => { const { team } = await owner.getFirstTeamMembership(); const { requestedSlug } = team.metadata as { requestedSlug: string }; await page.goto(`/team/${requestedSlug}`); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); - expect(await page.locator(`h2:has-text("${title(team.name)}")`).count()).toBe(1); - expect(await page.locator(`div:text("${description("team")}")`).count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); + await expect(page.locator(`h2:has-text("${title(team.name)}")`)).toHaveCount(1); + await expect(page.locator(`div:text("${description("team")}")`)).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute("src", /.*/); }); @@ -36,9 +36,9 @@ test.describe("Unpublished", () => { const { requestedSlug } = team.metadata as { requestedSlug: string }; const { slug: teamEventSlug } = await owner.getFirstTeamEvent(team.id); await page.goto(`/team/${requestedSlug}/${teamEventSlug}`); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); - expect(await page.locator(`h2:has-text("${title(team.name)}")`).count()).toBe(1); - expect(await page.locator(`div:text("${description("team")}")`).count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); + await expect(page.locator(`h2:has-text("${title(team.name)}")`)).toHaveCount(1); + await expect(page.locator(`div:text("${description("team")}")`)).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute("src", /.*/); }); @@ -48,9 +48,9 @@ test.describe("Unpublished", () => { const { requestedSlug } = org.metadata as { requestedSlug: string }; await page.goto(`/org/${requestedSlug}`); await page.waitForLoadState("networkidle"); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); - expect(await page.locator(`h2:has-text("${title(org.name)}")`).count()).toBe(1); - expect(await page.locator(`div:text("${description("organization")}")`).count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); + await expect(page.locator(`h2:has-text("${title(org.name)}")`)).toHaveCount(1); + await expect(page.locator(`div:text("${description("organization")}")`)).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute("src", /.*/); }); @@ -66,9 +66,9 @@ test.describe("Unpublished", () => { const [{ slug: subteamSlug }] = org.children as { slug: string }[]; await page.goto(`/org/${requestedSlug}/team/${subteamSlug}`); await page.waitForLoadState("networkidle"); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); - expect(await page.locator(`h2:has-text("${title(org.name)}")`).count()).toBe(1); - expect(await page.locator(`div:text("${description("organization")}")`).count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); + await expect(page.locator(`h2:has-text("${title(org.name)}")`)).toHaveCount(1); + await expect(page.locator(`div:text("${description("organization")}")`)).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute("src", /.*/); }); @@ -86,9 +86,9 @@ test.describe("Unpublished", () => { await page.goto(`/org/${requestedSlug}/team/${subteamSlug}/${subteamEventSlug}`); await page.waitForLoadState("networkidle"); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); - expect(await page.locator(`h2:has-text("${title(org.name)}")`).count()).toBe(1); - expect(await page.locator(`div:text("${description("organization")}")`).count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); + await expect(page.locator(`h2:has-text("${title(org.name)}")`)).toHaveCount(1); + await expect(page.locator(`div:text("${description("organization")}")`)).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute("src", /.*/); }); @@ -98,9 +98,9 @@ test.describe("Unpublished", () => { const { requestedSlug } = org.metadata as { requestedSlug: string }; await page.goto(`/org/${requestedSlug}/${owner.username}`); await page.waitForLoadState("networkidle"); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); - expect(await page.locator(`h2:has-text("${title(org.name)}")`).count()).toBe(1); - expect(await page.locator(`div:text("${description("organization")}")`).count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); + await expect(page.locator(`h2:has-text("${title(org.name)}")`)).toHaveCount(1); + await expect(page.locator(`div:text("${description("organization")}")`)).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute("src", /.*/); }); @@ -111,9 +111,9 @@ test.describe("Unpublished", () => { const [{ slug: ownerEventType }] = owner.eventTypes; await page.goto(`/org/${requestedSlug}/${owner.username}/${ownerEventType}`); await page.waitForLoadState("networkidle"); - expect(await page.locator('[data-testid="empty-screen"]').count()).toBe(1); - expect(await page.locator(`h2:has-text("${title(org.name)}")`).count()).toBe(1); - expect(await page.locator(`div:text("${description("organization")}")`).count()).toBe(1); + await expect(page.locator('[data-testid="empty-screen"]')).toHaveCount(1); + await expect(page.locator(`h2:has-text("${title(org.name)}")`)).toHaveCount(1); + await expect(page.locator(`div:text("${description("organization")}")`)).toHaveCount(1); await expect(page.locator(`img`)).toHaveAttribute("src", /.*/); }); }); diff --git a/apps/web/playwright/webhook.e2e.ts b/apps/web/playwright/webhook.e2e.ts index 63829ce61a..1895f65027 100644 --- a/apps/web/playwright/webhook.e2e.ts +++ b/apps/web/playwright/webhook.e2e.ts @@ -577,9 +577,9 @@ test.describe("MEETING_ENDED, MEETING_STARTED", async () => { expect(newMeetingEndedTriggers.length).toBe(0); // disable webhook - await page.click('[data-testid="webhook-switch"]'); - - await page.waitForLoadState("networkidle"); + await page.getByTestId("webhook-switch").click(); + const response = await page.waitForResponse("/api/trpc/webhook/edit?batch=1"); + expect(response.status()).toBe(200); const scheduledTriggersAfterDisabling = await prisma.webhookScheduledTriggers.findMany({ where: { diff --git a/apps/web/playwright/workflow.e2e.ts b/apps/web/playwright/workflow.e2e.ts index 5b5aafe152..60df8c7814 100644 --- a/apps/web/playwright/workflow.e2e.ts +++ b/apps/web/playwright/workflow.e2e.ts @@ -26,6 +26,7 @@ test.describe("Workflow Tab - Event Type", () => { await fillNameInput("Edited Workflow"); await saveWorkflow(); await page.getByTestId("go-back-button").click(); + await page.waitForLoadState("networkidle"); await hasWorkflowInList("Edited Workflow"); }); diff --git a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts index f89b86b4b8..a5808a8736 100644 --- a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts +++ b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts @@ -36,7 +36,7 @@ test.describe("Routing Forms", () => { await page.waitForSelector('[data-testid="routing-forms-list"]'); // Ensure that it's visible in forms list - expect(await page.locator('[data-testid="routing-forms-list"] > div').count()).toBe(1); + await expect(page.locator('[data-testid="routing-forms-list"] > div')).toHaveCount(1); await gotoRoutingLink({ page, formId }); await expect(page.locator("text=Test Form Name")).toBeVisible(); @@ -60,8 +60,8 @@ test.describe("Routing Forms", () => { label, }); - expect(await page.inputValue(`[data-testid="description"]`)).toBe(description); - expect(await page.locator('[data-testid="field"]').count()).toBe(types.length); + await expect(page.locator('[data-testid="description"]')).toHaveValue(description); + await expect(page.locator('[data-testid="field"]')).toHaveCount(types.length); fields.forEach((item, index) => { createdFields[index] = { label: item.label, typeIndex: index }; @@ -113,7 +113,7 @@ test.describe("Routing Forms", () => { //FIXME: Figure out why this delay is required. Without it field count comes out to be 1 only await new Promise((resolve) => setTimeout(resolve, 1000)); - expect(await page.locator('[data-testid="field"]').count()).toBe(2); + await expect(page.locator('[data-testid="field"]')).toHaveCount(2); await expectCurrentFormToHaveFields(page, { 1: { label: "F1 Field1", typeIndex: 1 } }, types); // Add 1 more field in F1 await addOneFieldAndDescriptionAndSaveForm(form1Id, page, { @@ -126,7 +126,7 @@ test.describe("Routing Forms", () => { await page.goto(`/routing-forms/form-edit/${form2Id}`); //FIXME: Figure out why this delay is required. Without it field count comes out to be 1 only await new Promise((resolve) => setTimeout(resolve, 1000)); - expect(await page.locator('[data-testid="field"]').count()).toBe(3); + await expect(page.locator('[data-testid="field"]')).toHaveCount(3); await expectCurrentFormToHaveFields(page, { 2: { label: "F1 Field2", typeIndex: 1 } }, types); }); todo("Create relationship by using duplicate with live connect"); @@ -158,15 +158,15 @@ test.describe("Routing Forms", () => { await page.fill('[data-testid="form-field-Test Field Long Text"]', "manual-fill"); - expect(await page.locator(`[data-testid="form-field-firstField"]`).inputValue()).toBe("456"); - expect(await page.locator(`[data-testid="form-field-Test Field Number"]`).inputValue()).toBe("456"); + await expect(page.locator('[data-testid="form-field-firstField"]')).toHaveValue("456"); + await expect(page.locator('[data-testid="form-field-Test Field Number"]')).toHaveValue("456"); // TODO: Verify select and multiselect has prefilled values. // expect(await page.locator(`[data-testid="form-field-Test Field Select"]`).inputValue()).toBe("456"); // expect(await page.locator(`[data-testid="form-field-Test Field MultiSelect"]`).inputValue()).toBe("456"); - expect(await page.locator(`[data-testid="form-field-Test Field Phone"]`).inputValue()).toBe("456"); - expect(await page.locator(`[data-testid="form-field-Test Field Email"]`).inputValue()).toBe( + await expect(page.locator('[data-testid="form-field-Test Field Phone"]')).toHaveValue("456"); + await expect(page.locator('[data-testid="form-field-Test Field Email"]')).toHaveValue( "456@example.com" ); @@ -324,12 +324,12 @@ test.describe("Routing Forms", () => { // Router should be publicly accessible await users.logout(); - page.goto(`/router?form=${routingForm.id}&Test field=event-routing`); + await page.goto(`/router?form=${routingForm.id}&Test field=event-routing`); await page.waitForURL((url) => { return url.pathname.endsWith("/pro/30min") && url.searchParams.get("Test field") === "event-routing"; }); - page.goto(`/router?form=${routingForm.id}&Test field=external-redirect`); + await page.goto(`/router?form=${routingForm.id}&Test field=external-redirect`); await page.waitForURL((url) => { return ( url.hostname.includes("google.com") && url.searchParams.get("Test field") === "external-redirect" @@ -357,17 +357,16 @@ test.describe("Routing Forms", () => { return document.querySelectorAll("input")[0].validity.valueMissing; }); expect(firstInputMissingValue).toBe(true); - expect(await page.locator('button[type="submit"][disabled]').count()).toBe(0); + await expect(page.locator('button[type="submit"][disabled]')).toHaveCount(0); }); test("Test preview should return correct route", async ({ page, users }) => { const user = await createUserAndLogin({ users, page }); const routingForm = user.routingForms[0]; - page.goto(`apps/routing-forms/form-edit/${routingForm.id}`); + await page.goto(`apps/routing-forms/form-edit/${routingForm.id}`); await page.click('[data-testid="test-preview"]'); - await page.waitForLoadState("networkidle"); - // //event redirect + //event redirect await page.fill('[data-testid="form-field-Test field"]', "event-routing"); await page.click('[data-testid="test-routing"]'); let routingType = await page.locator('[data-testid="test-routing-result-type"]').innerText(); diff --git a/packages/embeds/embed-core/playwright/lib/testUtils.ts b/packages/embeds/embed-core/playwright/lib/testUtils.ts index a7d0f96f30..a2230cb352 100644 --- a/packages/embeds/embed-core/playwright/lib/testUtils.ts +++ b/packages/embeds/embed-core/playwright/lib/testUtils.ts @@ -38,65 +38,19 @@ export const getEmbedIframe = async ({ page: Page; pathname: string; }) => { - const handleFrameErrors = (frame: Frame) => { - frame.on("pageerror", (error) => { - console.error(`Iframe error (${frame.url()}): ${error.message}`); - }); - frame.on("requestfailed", (request) => { - console.error( - `Iframe failed request (${frame.url()}): ${request.url()}, ${request.failure()?.errorText}` - ); - }); - }; - - // We can't seem to access page.frame till contentWindow is available. So wait for that. - const iframeReady = await page.evaluate( - (hardTimeout) => { - return new Promise((resolve) => { - const interval = setInterval(() => { - const iframe = document.querySelector(".cal-embed"); - if (iframe) { - console.log("Iframe found"); - } - if (iframe?.contentWindow) { - console.log("Iframe contentWindow found"); - } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (iframe && iframe.contentWindow && window.iframeReady) { - clearInterval(interval); - resolve(true); - } else { - console.log("Waiting for all three to be true:", { - iframeElement: iframe, - contentWindow: iframe?.contentWindow, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - iframeReady: window.iframeReady, - }); - } - }, 500); - - // A hard timeout if iframe isn't ready in that time. Avoids infinite wait - setTimeout(() => { - clearInterval(interval); - resolve(false); - // This is the time embed-iframe.ts loads in the iframe and fires atleast one event. Also, it is a load of entire React Application so it can sometime take more time even on CI. - }, hardTimeout); - }); + await page.waitForFunction( + () => { + const iframe = document.querySelector(".cal-embed"); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return iframe && iframe.contentWindow && window.iframeReady; }, - !process.env.CI ? 15000 : 15000 + { polling: 500 } ); - if (!iframeReady) { + const embedIframe = page.frame(`cal-embed=${calNamespace}`); + if (!embedIframe) { return null; } - - page.on("frameattached", handleFrameErrors); - - // We just verified that iframeReady is true here, so obviously embedIframe is not null - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const embedIframe = page.frame(`cal-embed=${calNamespace}`)!; - handleFrameErrors(embedIframe); const u = new URL(embedIframe.url()); if (u.pathname === `${pathname}/embed`) { return embedIframe; @@ -151,12 +105,8 @@ export async function bookFirstEvent(username: string, frame: Frame, page: Page) await frame.press('[name="email"]', "Enter"); const response = await page.waitForResponse("**/api/book/event"); const booking = (await response.json()) as { uid: string; eventSlug: string }; + expect(response.status()).toBe(200); booking.eventSlug = eventSlug; - - // Make sure we're navigated to the success page - await expect(frame.locator("[data-testid=success-page]")).toBeVisible(); - // expect(await page.screenshot()).toMatchSnapshot("success-page.png"); - return booking; } @@ -166,13 +116,11 @@ export async function rescheduleEvent(username: string, frame: Frame, page: Page await frame.press('[name="email"]', "Enter"); await frame.click("[data-testid=confirm-reschedule-button]"); const response = await page.waitForResponse("**/api/book/event"); + expect(response.status()).toBe(200); const responseObj = await response.json(); const booking = responseObj.uid; - // Make sure we're navigated to the success page - await expect(frame.locator("[data-testid=success-page]")).toBeVisible(); return booking; } - export async function installAppleCalendar(page: Page) { await page.goto("/apps/categories/calendar"); await page.click('[data-testid="app-store-app-card-apple-calendar"]'); diff --git a/packages/embeds/embed-core/playwright/tests/action-based.e2e.ts b/packages/embeds/embed-core/playwright/tests/action-based.e2e.ts index 6f5601a004..7baa28228d 100644 --- a/packages/embeds/embed-core/playwright/tests/action-based.e2e.ts +++ b/packages/embeds/embed-core/playwright/tests/action-based.e2e.ts @@ -113,16 +113,10 @@ test.describe("Popup Tests", () => { const calNamespace = "routingFormAuto"; await addEmbedListeners(calNamespace); await page.goto("/?only=prerender-test"); - let embedIframe = await getEmbedIframe({ - calNamespace, - page, - pathname: "/forms/948ae412-d995-4865-875a-48302588de03", - }); - expect(embedIframe).toBeFalsy(); await page.click( `[data-cal-namespace=${calNamespace}][data-cal-link="forms/948ae412-d995-4865-875a-48302588de03"]` ); - embedIframe = await getEmbedIframe({ + const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/forms/948ae412-d995-4865-875a-48302588de03", diff --git a/packages/embeds/embed-core/playwright/tests/namespacing.e2e.ts b/packages/embeds/embed-core/playwright/tests/namespacing.e2e.ts index c3a8840abc..59e3cb87c6 100644 --- a/packages/embeds/embed-core/playwright/tests/namespacing.e2e.ts +++ b/packages/embeds/embed-core/playwright/tests/namespacing.e2e.ts @@ -31,7 +31,7 @@ test.describe("Namespacing", () => { case: "doubleInstallSnippetWithInlineEmbedWithNonDefaultNamespace", }, }); - expect(await page.locator("iframe").count()).toBe(1); + await expect(page.locator("iframe")).toHaveCount(1); }); test("Double install Embed Snippet with inline embed without a namespace(i.e. default namespace)", async ({ @@ -48,7 +48,7 @@ test.describe("Namespacing", () => { case: "doubleInstallSnippetWithInlineEmbed", }, }); - expect(await page.locator("iframe").count()).toBe(1); + await expect(page.locator("iframe")).toHaveCount(1); }); }); test("Different namespaces can have different init configs", async ({ page, embeds }) => { diff --git a/packages/features/settings/CreateOrEditOutOfOfficeModal.tsx b/packages/features/settings/CreateOrEditOutOfOfficeModal.tsx index f3bfeebab3..34e89267cd 100644 --- a/packages/features/settings/CreateOrEditOutOfOfficeModal.tsx +++ b/packages/features/settings/CreateOrEditOutOfOfficeModal.tsx @@ -59,7 +59,13 @@ export const CreateOrEditOutOfOfficeEntryModal = ({ const { hasTeamPlan } = useHasTeamPlan(); - const { handleSubmit, setValue, control, register } = useForm({ + const { + handleSubmit, + setValue, + control, + register, + formState: { isSubmitting }, + } = useForm({ defaultValues: currentlyEditingOutOfOfficeEntry ? currentlyEditingOutOfOfficeEntry : { @@ -72,6 +78,7 @@ export const CreateOrEditOutOfOfficeEntryModal = ({ reasonId: 1, }, }); + console.log(isSubmitting); const createOrEditOutOfOfficeEntry = trpc.viewer.outOfOfficeCreateOrUpdate.useMutation({ onSuccess: () => { @@ -97,11 +104,10 @@ export const CreateOrEditOutOfOfficeEntryModal = ({ }}>
{ createOrEditOutOfOfficeEntry.mutate(data); })}> -
+
+ +
+ + +
+
- -
- - -
-
);