Test/Embed/Reschedule (#6056)
* Add reschedule embed test * Add comments
This commit is contained in:
parent
7fad76ae23
commit
e57f734e79
|
@ -18,7 +18,7 @@ yarn dev
|
|||
|
||||
## Running Tests
|
||||
|
||||
Ensure that the main App is running on port 3000 (e.g. yarn dx) already and then run the following command:
|
||||
Ensure that the main App is running on port 3000 (e.g. yarn dx) already. Also ensure dev server for embed-core is running and then run the following command:
|
||||
Start the server on 3100 port
|
||||
|
||||
```bash
|
||||
|
@ -31,6 +31,8 @@ And from another terminal you can run the following command to execute tests:
|
|||
yarn embed-tests-quick
|
||||
```
|
||||
|
||||
Note: `getEmbedIframe` and `addEmbedListeners` work as a team but they only support opening up embed in a fresh load. Opening an embed closing it and then opening another embed isn't supported yet.
|
||||
|
||||
## Shipping to Production
|
||||
|
||||
```bash
|
||||
|
|
|
@ -97,7 +97,10 @@
|
|||
<button data-cal-namespace="popupTeamLinkLightTheme" data-cal-config='{"theme":"light"}' data-cal-link="team/seeded-team/collective-seeded-team-event">Book with Test Team[Light Theme]</button>
|
||||
<button data-cal-namespace="popupTeamLinkDarkTheme" data-cal-config='{"theme":"dark"}' data-cal-link="team/seeded-team/collective-seeded-team-event">Book with Test Team[Dark Theme]</button>
|
||||
<button data-cal-namespace="popupTeamLinksList" data-cal-link="team/seeded-team/">See Team Links [Auto Theme]</button>
|
||||
<button data-cal-namespace="popupReschedule" data-cal-link="reschedule/qm3kwt3aTnVD7vmP9tiT2f">Reschedule Event[Auto Theme]</button>
|
||||
<script>
|
||||
let popupRescheduleId = new URL(document.URL).searchParams.get("popupRescheduleId") || "qm3kwt3aTnVD7vmP9tiT2f"
|
||||
document.write(`<button data-cal-namespace="popupReschedule" data-cal-link="reschedule/${popupRescheduleId}">Reschedule Event[Auto Theme]</button>`)
|
||||
</script>
|
||||
<button data-cal-namespace="popupPaidEvent" data-cal-link="pro/paid">Book Paid Event [Auto Theme]</button>
|
||||
<button data-cal-namespace="popupHideEventTypeDetails" data-cal-link="free/30min">Book Free Event [Auto Theme][uiConfig.hideEventTypeDetails=true]</button>
|
||||
<button data-cal-namespace="routingFormAuto" data-cal-link="forms/948ae412-d995-4865-875a-48302588de03">Book through Routing Form [Auto Theme]</button>
|
||||
|
|
|
@ -137,9 +137,9 @@ expect.extend({
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
let iframeReadyCheckInterval;
|
||||
const iframeReadyEventDetail = await new Promise(async (resolve) => {
|
||||
setInterval(async () => {
|
||||
iframeReadyCheckInterval = setInterval(async () => {
|
||||
const iframeReadyEventDetail = await getActionFiredDetails({
|
||||
calNamespace,
|
||||
actionType: "linkReady",
|
||||
|
@ -150,6 +150,8 @@ expect.extend({
|
|||
}, 500);
|
||||
});
|
||||
|
||||
clearInterval(iframeReadyCheckInterval);
|
||||
|
||||
//At this point we know that window.initialBodyVisibility would be set as DOM would already have been ready(because linkReady event can only fire after that)
|
||||
const {
|
||||
visibility: visibilityBefore,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { test as base, Page } from "@playwright/test";
|
||||
|
||||
interface Fixtures {
|
||||
export interface Fixtures {
|
||||
addEmbedListeners: (calNamespace: string) => Promise<void>;
|
||||
getActionFiredDetails: (a: { calNamespace: string; actionType: string }) => Promise<any>;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ import { Page, Frame, test, expect } from "@playwright/test";
|
|||
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: {
|
||||
|
@ -33,31 +35,41 @@ export const getBooking = async (bookingId: string) => {
|
|||
|
||||
export const getEmbedIframe = async ({ page, pathname }: { page: Page; pathname: string }) => {
|
||||
// We can't seem to access page.frame till contentWindow is available. So wait for that.
|
||||
await page.evaluate(() => {
|
||||
const iframeReady = await page.evaluate(() => {
|
||||
return new Promise((resolve) => {
|
||||
const iframe = document.querySelector(".cal-embed") as HTMLIFrameElement;
|
||||
if (!iframe) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
const interval = setInterval(() => {
|
||||
const iframe = document.querySelector(".cal-embed") as HTMLIFrameElement | null;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (iframe.contentWindow && window.iframeReady) {
|
||||
if (iframe && iframe.contentWindow && window.iframeReady) {
|
||||
clearInterval(interval);
|
||||
resolve(true);
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
console.log("Iframe Status:", !!iframe, !!iframe?.contentWindow, window.iframeReady);
|
||||
}
|
||||
}, 10);
|
||||
}, 500);
|
||||
|
||||
// A hard timeout if iframe isn't ready in that time. Avoids infinite wait
|
||||
setTimeout(() => {
|
||||
clearInterval(interval);
|
||||
resolve(false);
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
const embedIframe = page.frame("cal-embed");
|
||||
if (!embedIframe) {
|
||||
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")!;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -91,7 +103,7 @@ export async function bookFirstEvent(username: string, frame: Frame, page: Page)
|
|||
// 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");
|
||||
|
||||
const eventSlug = new URL(frame.url()).pathname;
|
||||
await selectFirstAvailableTimeSlotNextMonth(frame, page);
|
||||
await frame.waitForNavigation({
|
||||
url(url) {
|
||||
|
@ -104,10 +116,36 @@ export async function bookFirstEvent(username: string, frame: Frame, page: Page)
|
|||
await frame.fill('[name="email"]', "embed-user@example.com");
|
||||
await frame.press('[name="email"]', "Enter");
|
||||
const response = await page.waitForResponse("**/api/book/event");
|
||||
const responseObj = await response.json();
|
||||
const bookingId = responseObj.uid;
|
||||
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 bookingId;
|
||||
|
||||
//NOTE: frame.click('body') won't work here. Because the way it works, it clicks on the center of the body tag which is an element inside the popup view and that won't close the popup
|
||||
await frame.evaluate(() => {
|
||||
// Closes popup - if it is a popup. If not a popup, it will just do nothing
|
||||
document.body.click();
|
||||
});
|
||||
|
||||
return booking;
|
||||
}
|
||||
|
||||
export async function rescheduleEvent(username, frame, page) {
|
||||
await selectFirstAvailableTimeSlotNextMonth(frame, page);
|
||||
await frame.waitForNavigation({
|
||||
url(url) {
|
||||
return url.pathname.includes(`/${username}/book`);
|
||||
},
|
||||
});
|
||||
// --- 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;
|
||||
}
|
||||
|
|
|
@ -1,66 +1,131 @@
|
|||
import { expect } from "@playwright/test";
|
||||
import { expect, Page } from "@playwright/test";
|
||||
|
||||
import { test } from "../fixtures/fixtures";
|
||||
import { todo, getEmbedIframe, bookFirstEvent, getBooking, deleteAllBookingsByEmail } from "../lib/testUtils";
|
||||
import { Fixtures, test } from "../fixtures/fixtures";
|
||||
import {
|
||||
todo,
|
||||
getEmbedIframe,
|
||||
bookFirstEvent,
|
||||
getBooking,
|
||||
deleteAllBookingsByEmail,
|
||||
rescheduleEvent,
|
||||
} from "../lib/testUtils";
|
||||
|
||||
test("should open embed iframe on click - Configured with light theme", async ({
|
||||
page,
|
||||
async function bookFirstFreeUserEventThroughEmbed({
|
||||
addEmbedListeners,
|
||||
page,
|
||||
getActionFiredDetails,
|
||||
}) => {
|
||||
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||
|
||||
const calNamespace = "prerendertestLightTheme";
|
||||
}: {
|
||||
addEmbedListeners: Fixtures["addEmbedListeners"];
|
||||
page: Page;
|
||||
getActionFiredDetails: Fixtures["getActionFiredDetails"];
|
||||
}) {
|
||||
const embedButtonLocator = page.locator('[data-cal-link="free"]').first();
|
||||
await page.goto("/");
|
||||
// Obtain cal namespace from the element being clicked itself, so that addEmbedListeners always listen to correct namespace
|
||||
const calNamespace = (await embedButtonLocator.getAttribute("data-cal-namespace")) || "";
|
||||
await addEmbedListeners(calNamespace);
|
||||
await page.goto("/?only=prerender-test");
|
||||
let embedIframe = await getEmbedIframe({ page, pathname: "/free" });
|
||||
expect(embedIframe).toBeFalsy();
|
||||
// Goto / again so that initScript attached using addEmbedListeners can work now.
|
||||
await page.goto("/");
|
||||
|
||||
await page.click('[data-cal-link="free?light&popup"]');
|
||||
await embedButtonLocator.click();
|
||||
|
||||
embedIframe = await getEmbedIframe({ page, pathname: "/free" });
|
||||
const embedIframe = await getEmbedIframe({ page, pathname: "/free" });
|
||||
|
||||
await expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
|
||||
pathname: "/free",
|
||||
});
|
||||
expect(await page.screenshot()).toMatchSnapshot("event-types-list.png");
|
||||
if (!embedIframe) {
|
||||
throw new Error("Embed iframe not found");
|
||||
}
|
||||
const bookingId = await bookFirstEvent("free", embedIframe, page);
|
||||
const booking = await getBooking(bookingId);
|
||||
const booking = await bookFirstEvent("free", embedIframe, page);
|
||||
return booking;
|
||||
}
|
||||
|
||||
expect(booking.attendees.length).toBe(1);
|
||||
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||
});
|
||||
|
||||
todo("Floating Button Test with Dark Theme");
|
||||
|
||||
todo("Floating Button Test with Light Theme");
|
||||
|
||||
todo("Add snapshot test for embed iframe");
|
||||
|
||||
test("should open Routing Forms embed on click", async ({
|
||||
page,
|
||||
addEmbedListeners,
|
||||
getActionFiredDetails,
|
||||
}) => {
|
||||
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||
|
||||
const calNamespace = "routingFormAuto";
|
||||
await addEmbedListeners(calNamespace);
|
||||
await page.goto("/?only=prerender-test");
|
||||
let embedIframe = await getEmbedIframe({ 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({ page, pathname: "/forms/948ae412-d995-4865-875a-48302588de03" });
|
||||
if (!embedIframe) {
|
||||
throw new Error("Routing Form embed iframe not found");
|
||||
}
|
||||
await expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
|
||||
pathname: "/forms/948ae412-d995-4865-875a-48302588de03",
|
||||
test.describe("Popup Tests", () => {
|
||||
test.afterEach(async () => {
|
||||
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||
});
|
||||
test("should open embed iframe on click - Configured with light theme", async ({
|
||||
page,
|
||||
addEmbedListeners,
|
||||
getActionFiredDetails,
|
||||
}) => {
|
||||
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||
|
||||
const calNamespace = "prerendertestLightTheme";
|
||||
await addEmbedListeners(calNamespace);
|
||||
await page.goto("/?only=prerender-test");
|
||||
let embedIframe = await getEmbedIframe({ page, pathname: "/free" });
|
||||
expect(embedIframe).toBeFalsy();
|
||||
|
||||
await page.click('[data-cal-link="free?light&popup"]');
|
||||
|
||||
embedIframe = await getEmbedIframe({ page, pathname: "/free" });
|
||||
|
||||
await expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
|
||||
pathname: "/free",
|
||||
});
|
||||
expect(await page.screenshot()).toMatchSnapshot("event-types-list.png");
|
||||
if (!embedIframe) {
|
||||
throw new Error("Embed iframe not found");
|
||||
}
|
||||
const { uid: bookingId } = await bookFirstEvent("free", embedIframe, page);
|
||||
const booking = await getBooking(bookingId);
|
||||
|
||||
expect(booking.attendees.length).toBe(1);
|
||||
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||
});
|
||||
|
||||
test("should be able to reschedule", async ({ page, addEmbedListeners, getActionFiredDetails }) => {
|
||||
const booking = await test.step("Create a booking", async () => {
|
||||
return await bookFirstFreeUserEventThroughEmbed({
|
||||
page,
|
||||
addEmbedListeners,
|
||||
getActionFiredDetails,
|
||||
});
|
||||
});
|
||||
|
||||
await test.step("Reschedule the booking", async () => {
|
||||
await addEmbedListeners("popupReschedule");
|
||||
await page.goto(`/?popupRescheduleId=${booking.uid}`);
|
||||
await page.click('[data-cal-namespace="popupReschedule"]');
|
||||
|
||||
const embedIframe = await getEmbedIframe({ page, pathname: booking.eventSlug });
|
||||
if (!embedIframe) {
|
||||
throw new Error("Embed iframe not found");
|
||||
}
|
||||
await rescheduleEvent("free", embedIframe, page);
|
||||
});
|
||||
});
|
||||
|
||||
todo("Floating Button Test with Dark Theme");
|
||||
|
||||
todo("Floating Button Test with Light Theme");
|
||||
|
||||
todo("Add snapshot test for embed iframe");
|
||||
|
||||
test("should open Routing Forms embed on click", async ({
|
||||
page,
|
||||
addEmbedListeners,
|
||||
getActionFiredDetails,
|
||||
}) => {
|
||||
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||
|
||||
const calNamespace = "routingFormAuto";
|
||||
await addEmbedListeners(calNamespace);
|
||||
await page.goto("/?only=prerender-test");
|
||||
let embedIframe = await getEmbedIframe({ 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({ page, pathname: "/forms/948ae412-d995-4865-875a-48302588de03" });
|
||||
if (!embedIframe) {
|
||||
throw new Error("Routing Form embed iframe not found");
|
||||
}
|
||||
await expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
|
||||
pathname: "/forms/948ae412-d995-4865-875a-48302588de03",
|
||||
});
|
||||
await expect(embedIframe.locator("text=Seeded Form - Pro")).toBeVisible();
|
||||
});
|
||||
await expect(embedIframe.locator("text=Seeded Form - Pro")).toBeVisible();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user