Files
calendar/packages/embeds/embed-core/playwright/lib/testUtils.ts
T

168 lines
6.1 KiB
TypeScript

import type { Page, Frame } from "@playwright/test";
import { test, expect } from "@playwright/test";
// eslint-disable-next-line no-restricted-imports
import prisma from "@calcom/prisma";
export function todo(title: string) {
// eslint-disable-next-line @typescript-eslint/no-empty-function, playwright/no-skipped-test
test.skip(title, () => {});
}
export const deleteAllBookingsByEmail = async (email: string) =>
await prisma.booking.deleteMany({
where: {
attendees: {
some: {
email: email,
},
},
},
});
export const getBooking = async (bookingId: string) => {
const booking = await prisma.booking.findUnique({
where: {
uid: bookingId,
},
include: {
attendees: true,
},
});
if (!booking) {
throw new Error("Booking not found");
}
return booking;
};
export const getEmbedIframe = async ({
calNamespace,
page,
pathname,
}: {
calNamespace: string;
page: Page;
pathname: string;
}) => {
// 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<HTMLIFrameElement>(".cal-embed");
// 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);
});
},
!process.env.CI ? 150000 : 15000
);
if (!iframeReady) {
return null;
}
// 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}`)!;
const u = new URL(embedIframe.url());
if (u.pathname === `${pathname}/embed`) {
return embedIframe;
}
console.log(`Embed iframe url pathname match. Expected: "${pathname}/embed"`, `Actual: ${u.pathname}`);
return null;
};
async function selectFirstAvailableTimeSlotNextMonth(frame: Frame, page: Page) {
await frame.click('[data-testid="incrementMonth"]');
// @TODO: Find a better way to make test wait for full month change render to end
// so it can click up on the right day, also when done, resolve other todos as well
// The problem is that the Month Text changes instantly but we don't know when the corresponding dates are visible
// Waiting for full month increment
await frame.waitForTimeout(1000);
// expect(await page.screenshot()).toMatchSnapshot("availability-page-2.png");
// TODO: Find out why the first day is always booked on tests
await frame.locator('[data-testid="day"][data-disabled="false"]').nth(1).click();
await frame.click('[data-testid="time"]');
}
export async function bookFirstEvent(username: string, frame: Frame, page: Page) {
// Click first event type on Profile Page
await frame.click('[data-testid="event-type-link"]');
await frame.waitForURL((url) => {
// Wait for reaching the event page
const matches = url.pathname.match(new RegExp(`/${username}/(.+)$`));
if (!matches || !matches[1]) {
return false;
}
if (matches[1] === "embed") {
return false;
}
return true;
});
// Let current month dates fully render.
// There is a bug where if we don't let current month fully render and quickly click go to next month, current month get's rendered
// This doesn't seem to be replicable with the speed of a person, only during automation.
// It would also allow correct snapshot to be taken for current month.
await frame.waitForTimeout(1000);
// expect(await page.screenshot()).toMatchSnapshot("availability-page-1.png");
// Remove /embed from the end if present.
const eventSlug = new URL(frame.url()).pathname.replace(/\/embed$/, "");
await selectFirstAvailableTimeSlotNextMonth(frame, page);
// expect(await page.screenshot()).toMatchSnapshot("booking-page.png");
// --- fill form
await frame.fill('[name="name"]', "Embed User");
await frame.fill('[name="email"]', "embed-user@example.com");
await frame.press('[name="email"]', "Enter");
const response = await page.waitForResponse("**/api/book/event");
const booking = (await response.json()) as { uid: string; eventSlug: string };
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;
}
export async function rescheduleEvent(username: string, frame: Frame, page: Page) {
await selectFirstAvailableTimeSlotNextMonth(frame, page);
// --- fill form
await frame.press('[name="email"]', "Enter");
await frame.click("[data-testid=confirm-reschedule-button]");
const response = await page.waitForResponse("**/api/book/event");
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"]');
await page.waitForURL("/apps/apple-calendar");
await page.click('[data-testid="install-app-button"]');
}