Files
calendar/packages/embeds/embed-core/playwright/lib/testUtils.ts
T
fc592a17ac feat: Add 'Disable on Prefill' option for fields (#15715)
* feat:add prefilcheck check box in the field creation

* chore:formatted the li8 translations

* feat:add e2e test case for this feature

* refactor:reanmed the key from prefilledCheck to disableOnPrefill and moved the readOnly logic to formbuilderField

* chore:fixed the type check

* fix:if prefilled has error. do not disabble it

* chore:removed the e2e tests for prefill in e2e

* chore:revert back to old changes

* chore:remove unwanted code frome2e

* Support name field as well

* refactor:handled the feedback changes. still unit test cases pending

* feat: add unit tests for  disable field on prefill logic

* chore: resolved the type check and typos

* chore:renamed the files and type clean up

* Apply suggestions from code review

* chore:renaming test files and moving the disableonprefill hook to formbuilderFeild.

* refactor: add unit test for serach params and form values check and minor code formatting and file name changes

* fix: validating the input value partially at the intialising phase. so that form response values and  from ui values are same and making sure invalid data not sent to backend on submit.

* chore:fix the type check issue

* fix: field value valdiation at initilisation and add the  unit test cases for that change

* chore:add comments to the tests

* chore:resolve type check

* chore: moved the tests to the formBuilder

* Simplify codebase by ensuring select and multiselect changes the value when no option is found

* Relocate and refactor tests

* handle special cases

* Dont disable for dirty fields

* Update schema.prisma

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Hariom <hariombalhara@gmail.com>
2024-08-18 17:34:59 +00:00

172 lines
6.3 KiB
TypeScript

import type { Page, Frame } from "@playwright/test";
import { expect } from "@playwright/test";
import prisma from "@calcom/prisma";
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 ? 15000 : 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"]');
}
export async function assertNoRequestIsBlocked(page: Page) {
page.on("requestfailed", (request) => {
const error = request.failure()?.errorText;
// Identifies that the request is blocked by the browser due to COEP restrictions
if (error?.includes("ERR_BLOCKED_BY_RESPONSE")) {
throw new Error(`Request Blocked: ${request.url()}. Error: ${error}`);
}
});
}