diff --git a/apps/web/playwright/booking-pages.e2e.ts b/apps/web/playwright/booking-pages.e2e.ts index 87ac1dcf51..63071b8f64 100644 --- a/apps/web/playwright/booking-pages.e2e.ts +++ b/apps/web/playwright/booking-pages.e2e.ts @@ -53,7 +53,7 @@ test.describe("free user", () => { // book same time spot again await bookTimeSlot(page); - await expect(page.locator("[data-testid=booking-fail]")).toBeVisible({ timeout: 1000 }); + await page.locator("[data-testid=booking-fail]").waitFor({ state: "visible" }); }); }); diff --git a/apps/web/playwright/lib/testUtils.ts b/apps/web/playwright/lib/testUtils.ts index f401dca0f9..b9cf3850d6 100644 --- a/apps/web/playwright/lib/testUtils.ts +++ b/apps/web/playwright/lib/testUtils.ts @@ -1,5 +1,6 @@ import type { Frame, Page } from "@playwright/test"; import { expect } from "@playwright/test"; +import EventEmitter from "events"; import type { IncomingMessage, ServerResponse } from "http"; import { createServer } from "http"; // eslint-disable-next-line no-restricted-imports @@ -35,7 +36,27 @@ export function createHttpServer(opts: { requestHandler?: RequestHandler } = {}) res.end(); }, } = opts; + const eventEmitter = new EventEmitter(); const requestList: Request[] = []; + + const waitForRequestCount = (count: number) => + new Promise((resolve) => { + if (requestList.length === count) { + resolve(); + return; + } + + const pushHandler = () => { + if (requestList.length !== count) { + return; + } + eventEmitter.off("push", pushHandler); + resolve(); + }; + + eventEmitter.on("push", pushHandler); + }); + const server = createServer((req, res) => { const buffer: unknown[] = []; @@ -49,6 +70,7 @@ export function createHttpServer(opts: { requestHandler?: RequestHandler } = {}) _req.body = json; requestList.push(_req); + eventEmitter.emit("push"); requestHandler({ req: _req, res }); }); }); @@ -58,34 +80,16 @@ export function createHttpServer(opts: { requestHandler?: RequestHandler } = {}) // eslint-disable-next-line @typescript-eslint/no-explicit-any const port: number = (server.address() as any).port; const url = `http://localhost:${port}`; + return { port, close: () => server.close(), requestList, url, + waitForRequestCount, }; } -/** - * When in need to wait for any period of time you can use waitFor, to wait for your expectations to pass. - */ -export async function waitFor(fn: () => Promise | unknown, opts: { timeout?: number } = {}) { - let finished = false; - const timeout = opts.timeout ?? 5000; // 5s - const timeStart = Date.now(); - while (!finished) { - try { - await fn(); - finished = true; - } catch { - if (Date.now() - timeStart >= timeout) { - throw new Error("waitFor timed out"); - } - await new Promise((resolve) => setTimeout(resolve, 0)); - } - } -} - export async function selectFirstAvailableTimeSlotNextMonth(page: Page | Frame) { // Let current month dates fully render. await page.click('[data-testid="incrementMonth"]'); diff --git a/apps/web/playwright/manage-booking-questions.e2e.ts b/apps/web/playwright/manage-booking-questions.e2e.ts index 4091acfa1a..9f0d4762ae 100644 --- a/apps/web/playwright/manage-booking-questions.e2e.ts +++ b/apps/web/playwright/manage-booking-questions.e2e.ts @@ -8,7 +8,7 @@ import { WebhookTriggerEvents } from "@calcom/prisma/enums"; import type { CalendarEvent } from "@calcom/types/Calendar"; import { test } from "./lib/fixtures"; -import { createHttpServer, waitFor, selectFirstAvailableTimeSlotNextMonth } from "./lib/testUtils"; +import { createHttpServer, selectFirstAvailableTimeSlotNextMonth } from "./lib/testUtils"; async function getLabelText(field: Locator) { return await field.locator("label").first().locator("span").first().innerText(); @@ -215,13 +215,7 @@ test.describe("Manage Booking Questions", () => { async function runTestStepsCommonForTeamAndUserEventType( page: Page, context: PlaywrightTestArgs["context"], - webhookReceiver: { - port: number; - close: () => import("http").Server; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - requestList: (import("http").IncomingMessage & { body?: any })[]; - url: string; - } + webhookReceiver: Awaited> ) { await page.click('[href$="tabName=advanced"]'); @@ -311,12 +305,11 @@ async function runTestStepsCommonForTeamAndUserEventType( await page.locator('[data-testid="field-response"][data-fob-field="how-are-you"]').innerText() ).toBe("I am great!"); - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); const [request] = webhookReceiver.requestList; + // @ts-expect-error body is unknown const payload = request.body.payload; expect(payload.responses).toMatchObject({ @@ -667,9 +660,7 @@ async function expectWebhookToBeCalled( }; } ) { - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); const [request] = webhookReceiver.requestList; const body = request.body; diff --git a/apps/web/playwright/webhook.e2e.ts b/apps/web/playwright/webhook.e2e.ts index d5e1d5b512..074ffbfd5c 100644 --- a/apps/web/playwright/webhook.e2e.ts +++ b/apps/web/playwright/webhook.e2e.ts @@ -10,7 +10,6 @@ import { bookOptinEvent, createHttpServer, selectFirstAvailableTimeSlotNextMonth, - waitFor, gotoRoutingLink, createUserWithSeatedEventAndAttendees, } from "./lib/testUtils"; @@ -78,10 +77,7 @@ test.describe("BOOKING_CREATED", async () => { await page.fill('[name="email"]', "test@example.com"); await page.press('[name="email"]', "Enter"); - // --- check that webhook was called - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); const [request] = webhookReceiver.requestList; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -209,10 +205,8 @@ test.describe("BOOKING_REJECTED", async () => { await page.click('[data-testid="rejection-confirm"]'); await page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/confirm")); - // --- check that webhook was called - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); + const [request] = webhookReceiver.requestList; // eslint-disable-next-line @typescript-eslint/no-explicit-any const body = request.body as any; @@ -332,9 +326,8 @@ test.describe("BOOKING_REQUESTED", async () => { // --- check that webhook was called - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); + const [request] = webhookReceiver.requestList; // eslint-disable-next-line @typescript-eslint/no-explicit-any const body = request.body as any; @@ -442,9 +435,7 @@ test.describe("BOOKING_RESCHEDULED", async () => { expect(newBooking).not.toBeNull(); // --- check that webhook was called - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); const [request] = webhookReceiver.requestList; @@ -520,9 +511,7 @@ test.describe("BOOKING_RESCHEDULED", async () => { expect(newBooking).not.toBeNull(); // --- check that webhook was called - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); const [firstRequest] = webhookReceiver.requestList; @@ -541,9 +530,7 @@ test.describe("BOOKING_RESCHEDULED", async () => { await expect(page).toHaveURL(/.*booking/); - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(2); - }); + await webhookReceiver.waitForRequestCount(2); const [_, secondRequest] = webhookReceiver.requestList; @@ -597,9 +584,8 @@ test.describe("FORM_SUBMITTED", async () => { await page.fill(`[data-testid="form-field-${fieldName}"]`, "John Doe"); page.click('button[type="submit"]'); - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + await webhookReceiver.waitForRequestCount(1); + const [request] = webhookReceiver.requestList; // eslint-disable-next-line @typescript-eslint/no-explicit-any const body = request.body as any; @@ -656,9 +642,9 @@ test.describe("FORM_SUBMITTED", async () => { const fieldName = "name"; await page.fill(`[data-testid="form-field-${fieldName}"]`, "John Doe"); page.click('button[type="submit"]'); - await waitFor(() => { - expect(webhookReceiver.requestList.length).toBe(1); - }); + + await webhookReceiver.waitForRequestCount(1); + const [request] = webhookReceiver.requestList; // eslint-disable-next-line @typescript-eslint/no-explicit-any const body = request.body as any;