"Manage Booking Questions" - Add a comprehensive test (#7465)
* Add first test * Add test for team event as wellpull/7562/head
parent
a2fd5ba2a2
commit
6f8ea490d0
|
@ -220,6 +220,7 @@ function EventTypeSingleLayout({
|
|||
<Tooltip content={t("preview")}>
|
||||
<Button
|
||||
color="secondary"
|
||||
data-testid="preview-button"
|
||||
target="_blank"
|
||||
variant="icon"
|
||||
href={permalink}
|
||||
|
|
|
@ -561,7 +561,12 @@ export default function Success(props: SuccessProps) {
|
|||
<>
|
||||
<div className="mt-9 font-medium">{label}</div>
|
||||
<div className="col-span-2 mb-2 mt-9">
|
||||
<p className="break-words">{response.toString()}</p>
|
||||
<p
|
||||
className="break-words"
|
||||
data-testid="field-response"
|
||||
data-fob-field={field.name}>
|
||||
{response.toString()}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -44,9 +44,6 @@ const createTeamAndAddUser = async (
|
|||
slug: `team-${workerInfo.workerIndex}-${Date.now()}`,
|
||||
},
|
||||
});
|
||||
if (!team) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { role = MembershipRole.OWNER, id: userId } = user;
|
||||
await prisma.membership.create({
|
||||
|
@ -54,8 +51,10 @@ const createTeamAndAddUser = async (
|
|||
teamId: team.id,
|
||||
userId,
|
||||
role: role,
|
||||
accepted: true,
|
||||
},
|
||||
});
|
||||
return team;
|
||||
};
|
||||
|
||||
// creates a user fixture instance and stores the collection
|
||||
|
@ -246,7 +245,29 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => {
|
|||
include: userIncludes,
|
||||
});
|
||||
if (scenario.hasTeam) {
|
||||
await createTeamAndAddUser({ user: { id: user.id, role: "OWNER" } }, workerInfo);
|
||||
const team = await createTeamAndAddUser({ user: { id: user.id, role: "OWNER" } }, workerInfo);
|
||||
await prisma.eventType.create({
|
||||
data: {
|
||||
team: {
|
||||
connect: {
|
||||
id: team.id,
|
||||
},
|
||||
},
|
||||
users: {
|
||||
connect: {
|
||||
id: _user.id,
|
||||
},
|
||||
},
|
||||
owner: {
|
||||
connect: {
|
||||
id: _user.id,
|
||||
},
|
||||
},
|
||||
title: "Team Event - 30min",
|
||||
slug: "team-event-30min",
|
||||
length: 30,
|
||||
},
|
||||
});
|
||||
}
|
||||
const userFixture = createUserFixture(user, store.page!);
|
||||
store.users.push(userFixture);
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
import type { Page, PlaywrightTestArgs } from "@playwright/test";
|
||||
import { expect } from "@playwright/test";
|
||||
import { WebhookTriggerEvents } from "@prisma/client";
|
||||
import type { createUsersFixture } from "playwright/fixtures/users";
|
||||
import { uuid } from "short-uuid";
|
||||
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
import { createHttpServer, waitFor, selectFirstAvailableTimeSlotNextMonth } from "./lib/testUtils";
|
||||
|
||||
test.describe("Manage Booking Questions", () => {
|
||||
test.afterEach(async ({ users }) => {
|
||||
await users.deleteAll();
|
||||
});
|
||||
|
||||
test.describe("For User EventType", () => {
|
||||
test("Do a booking with a user added question and verify a few thing in b/w", async ({
|
||||
page,
|
||||
users,
|
||||
context,
|
||||
}, testInfo) => {
|
||||
// Considering there are many steps in it, it would need more than default test timeout
|
||||
test.setTimeout(testInfo.timeout * 3);
|
||||
const user = await createAndLoginUserWithEventTypes({ users });
|
||||
|
||||
const webhookReceiver = await addWebhook(user);
|
||||
|
||||
await test.step("Go to EventType Page ", async () => {
|
||||
const $eventTypes = page.locator("[data-testid=event-types] > li a");
|
||||
const firstEventTypeElement = $eventTypes.first();
|
||||
|
||||
await firstEventTypeElement.click();
|
||||
});
|
||||
|
||||
await test.step("Add Question and see that it's shown on Booking Page at appropriate position", async () => {
|
||||
await addQuestionAndSave({
|
||||
page,
|
||||
question: {
|
||||
name: "how_are_you",
|
||||
type: "Name",
|
||||
label: "How are you?",
|
||||
placeholder: "I'm fine, thanks",
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
const allFieldsLocator = await expectSystemFieldsToBeThere(page);
|
||||
const userFieldLocator = allFieldsLocator.nth(5);
|
||||
|
||||
await expect(userFieldLocator.locator('[name="how_are_you"]')).toBeVisible();
|
||||
// There are 2 labels right now. Will be one in future. The second one is hidden
|
||||
expect(await userFieldLocator.locator("label").nth(0).innerText()).toBe("How are you?");
|
||||
await expect(userFieldLocator.locator("input[type=text]")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
await test.step("Hide Question and see that it's not shown on Booking Page", async () => {
|
||||
await toggleQuestionAndSave({
|
||||
name: "how_are_you",
|
||||
page,
|
||||
});
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
const formBuilderFieldLocator = page.locator('[data-fob-field-name="how_are_you"]');
|
||||
await expect(formBuilderFieldLocator).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
await test.step("Show Question Again", async () => {
|
||||
await toggleQuestionAndSave({
|
||||
name: "how_are_you",
|
||||
page,
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Try to book without providing "How are you?" response', async () => {
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
await bookTimeSlot({ page, name: "Booker", email: "booker@example.com" });
|
||||
await expectErrorToBeThereFor({ page, name: "how_are_you" });
|
||||
});
|
||||
});
|
||||
|
||||
await test.step("Do a booking", async () => {
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
const formBuilderFieldLocator = page.locator('[data-fob-field-name="how_are_you"]');
|
||||
await expect(formBuilderFieldLocator).toBeVisible();
|
||||
expect(
|
||||
await formBuilderFieldLocator.locator('[name="how_are_you"]').getAttribute("placeholder")
|
||||
).toBe("I'm fine, thanks");
|
||||
expect(await formBuilderFieldLocator.locator("label").nth(0).innerText()).toBe("How are you?");
|
||||
await formBuilderFieldLocator.locator('[name="how_are_you"]').fill("I am great!");
|
||||
await bookTimeSlot({ page, name: "Booker", email: "booker@example.com" });
|
||||
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
|
||||
|
||||
expect(
|
||||
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);
|
||||
});
|
||||
|
||||
const [request] = webhookReceiver.requestList;
|
||||
|
||||
const payload = (request.body as any).payload as any;
|
||||
|
||||
expect(payload.responses).toMatchObject({
|
||||
name: "Booker",
|
||||
email: "booker@example.com",
|
||||
how_are_you: "I am great!",
|
||||
});
|
||||
|
||||
expect(payload.location).toBe("integrations:daily");
|
||||
|
||||
expect(payload.attendees[0]).toMatchObject({
|
||||
name: "Booker",
|
||||
email: "booker@example.com",
|
||||
});
|
||||
|
||||
expect(payload.userFieldsResponses).toMatchObject({
|
||||
how_are_you: "I am great!",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("For Team EventType", () => {
|
||||
test("Do a booking with a user added question and verify a few thing in b/w", async ({
|
||||
page,
|
||||
users,
|
||||
context,
|
||||
}, testInfo) => {
|
||||
// Considering there are many steps in it, it would need more than default test timeout
|
||||
test.setTimeout(testInfo.timeout * 3);
|
||||
const user = await createAndLoginUserWithEventTypes({ users });
|
||||
|
||||
const webhookReceiver = await addWebhook(user);
|
||||
|
||||
await test.step("Go to First Team Event", async () => {
|
||||
const $eventTypes = page.locator("[data-testid=event-types]").nth(1).locator("li a");
|
||||
const firstEventTypeElement = $eventTypes.first();
|
||||
|
||||
await firstEventTypeElement.click();
|
||||
});
|
||||
|
||||
await runTestStepsCommonForTeamAndUserEventType(page, context, webhookReceiver);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function runTestStepsCommonForTeamAndUserEventType(
|
||||
page: Page,
|
||||
context: PlaywrightTestArgs["context"],
|
||||
webhookReceiver: {
|
||||
port: number;
|
||||
close: () => import("http").Server;
|
||||
requestList: (import("http").IncomingMessage & { body?: unknown })[];
|
||||
url: string;
|
||||
}
|
||||
) {
|
||||
await test.step("Add Question and see that it's shown on Booking Page at appropriate position", async () => {
|
||||
await addQuestionAndSave({
|
||||
page,
|
||||
question: {
|
||||
name: "how_are_you",
|
||||
type: "Name",
|
||||
label: "How are you?",
|
||||
placeholder: "I'm fine, thanks",
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
const allFieldsLocator = await expectSystemFieldsToBeThere(page);
|
||||
const userFieldLocator = allFieldsLocator.nth(5);
|
||||
|
||||
await expect(userFieldLocator.locator('[name="how_are_you"]')).toBeVisible();
|
||||
// There are 2 labels right now. Will be one in future. The second one is hidden
|
||||
expect(await userFieldLocator.locator("label").nth(0).innerText()).toBe("How are you?");
|
||||
await expect(userFieldLocator.locator("input[type=text]")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
await test.step("Hide Question and see that it's not shown on Booking Page", async () => {
|
||||
await toggleQuestionAndSave({
|
||||
name: "how_are_you",
|
||||
page,
|
||||
});
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
const formBuilderFieldLocator = page.locator('[data-fob-field-name="how_are_you"]');
|
||||
await expect(formBuilderFieldLocator).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
await test.step("Show Question Again", async () => {
|
||||
await toggleQuestionAndSave({
|
||||
name: "how_are_you",
|
||||
page,
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Try to book without providing "How are you?" response', async () => {
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
await bookTimeSlot({ page, name: "Booker", email: "booker@example.com" });
|
||||
await expectErrorToBeThereFor({ page, name: "how_are_you" });
|
||||
});
|
||||
});
|
||||
|
||||
await test.step("Do a booking", async () => {
|
||||
await doOnFreshPreview(page, context, async (page) => {
|
||||
const formBuilderFieldLocator = page.locator('[data-fob-field-name="how_are_you"]');
|
||||
await expect(formBuilderFieldLocator).toBeVisible();
|
||||
expect(await formBuilderFieldLocator.locator('[name="how_are_you"]').getAttribute("placeholder")).toBe(
|
||||
"I'm fine, thanks"
|
||||
);
|
||||
expect(await formBuilderFieldLocator.locator("label").nth(0).innerText()).toBe("How are you?");
|
||||
await formBuilderFieldLocator.locator('[name="how_are_you"]').fill("I am great!");
|
||||
await bookTimeSlot({ page, name: "Booker", email: "booker@example.com" });
|
||||
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
|
||||
|
||||
expect(
|
||||
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);
|
||||
});
|
||||
|
||||
const [request] = webhookReceiver.requestList;
|
||||
|
||||
const payload = (request.body as any).payload as any;
|
||||
|
||||
expect(payload.responses).toMatchObject({
|
||||
name: "Booker",
|
||||
email: "booker@example.com",
|
||||
how_are_you: "I am great!",
|
||||
});
|
||||
|
||||
expect(payload.location).toBe("integrations:daily");
|
||||
|
||||
expect(payload.attendees[0]).toMatchObject({
|
||||
name: "Booker",
|
||||
email: "booker@example.com",
|
||||
});
|
||||
|
||||
expect(payload.userFieldsResponses).toMatchObject({
|
||||
how_are_you: "I am great!",
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function expectSystemFieldsToBeThere(page: Page) {
|
||||
const allFieldsLocator = page.locator("[data-fob-field-name]:not(.hidden)");
|
||||
const nameLocator = allFieldsLocator.nth(0);
|
||||
const emailLocator = allFieldsLocator.nth(1);
|
||||
// Location isn't rendered unless explicitly set which isn't the case here
|
||||
// const locationLocator = allFieldsLocator.nth(2);
|
||||
const additionalNotes = allFieldsLocator.nth(3);
|
||||
const guestsLocator = allFieldsLocator.nth(4);
|
||||
|
||||
await expect(nameLocator.locator('[name="name"]')).toBeVisible();
|
||||
await expect(emailLocator.locator('[name="email"]')).toBeVisible();
|
||||
|
||||
await expect(additionalNotes.locator('[name="notes"]')).toBeVisible();
|
||||
await expect(guestsLocator.locator("button")).toBeVisible();
|
||||
return allFieldsLocator;
|
||||
}
|
||||
|
||||
//TODO: Add one question for each type and see they are rendering labels and only once and are showing appropriate native component
|
||||
// Verify webhook is sent with the correct data, DB is correct (including metadata)
|
||||
|
||||
//TODO: Verify that prefill works
|
||||
async function bookTimeSlot({ page, name, email }: { page: Page; name: string; email: string }) {
|
||||
// --- fill form
|
||||
await page.fill('[name="name"]', name);
|
||||
await page.fill('[name="email"]', email);
|
||||
await page.press('[name="email"]', "Enter");
|
||||
}
|
||||
/**
|
||||
* 'option' starts from 1
|
||||
*/
|
||||
async function selectOption({
|
||||
page,
|
||||
selector,
|
||||
optionText,
|
||||
}: {
|
||||
page: Page;
|
||||
selector: { selector: string; nth: number };
|
||||
optionText: string;
|
||||
}) {
|
||||
const locatorForSelect = page.locator(selector.selector).nth(selector.nth);
|
||||
await locatorForSelect.click();
|
||||
await locatorForSelect.locator(`text="${optionText}"`).click();
|
||||
}
|
||||
|
||||
async function addQuestionAndSave({
|
||||
page,
|
||||
question,
|
||||
}: {
|
||||
page: Page;
|
||||
question: {
|
||||
name?: string;
|
||||
type?: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
};
|
||||
}) {
|
||||
await page.click('[href$="tabName=advanced"]');
|
||||
await page.click('[data-testid="add-field"]');
|
||||
|
||||
if (question.type !== undefined) {
|
||||
await selectOption({
|
||||
page,
|
||||
selector: {
|
||||
selector: "[id=test-field-type]",
|
||||
nth: 0,
|
||||
},
|
||||
optionText: question.type,
|
||||
});
|
||||
}
|
||||
|
||||
if (question.name !== undefined) {
|
||||
await page.fill('[name="name"]', question.name);
|
||||
}
|
||||
|
||||
if (question.label !== undefined) {
|
||||
await page.fill('[name="label"]', question.label);
|
||||
}
|
||||
|
||||
if (question.placeholder !== undefined) {
|
||||
await page.fill('[name="placeholder"]', question.placeholder);
|
||||
}
|
||||
|
||||
if (question.required !== undefined) {
|
||||
// await page.fill('[name="name"]', question.required);
|
||||
}
|
||||
|
||||
await page.click('[data-testid="field-add-save"]');
|
||||
await saveEventType(page);
|
||||
}
|
||||
|
||||
async function expectErrorToBeThereFor({ page, name }: { page: Page; name: string }) {
|
||||
await expect(page.locator(`[data-testid=error-message-${name}]`)).toHaveCount(1);
|
||||
// TODO: We should either verify the error message or error code in the test so we know that the correct error is shown
|
||||
// Checking for the error message isn't well maintainable as translation can change and we might want to verify in non english language as well.
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a fresh preview window and runs the callback on it giving it the preview tab's `page`
|
||||
*/
|
||||
async function doOnFreshPreview(
|
||||
page: Page,
|
||||
context: PlaywrightTestArgs["context"],
|
||||
callback: (page: Page) => Promise<void>
|
||||
) {
|
||||
const previewTabPage = await openBookingFormInPreviewTab(context, page);
|
||||
await callback(previewTabPage);
|
||||
await previewTabPage.close();
|
||||
}
|
||||
|
||||
async function toggleQuestionAndSave({ name, page }: { name: string; page: Page }) {
|
||||
await page.locator(`[data-testid="field-${name}"]`).locator('[data-testid="toggle-field"]').click();
|
||||
await saveEventType(page);
|
||||
}
|
||||
|
||||
async function createAndLoginUserWithEventTypes({ users }: { users: ReturnType<typeof createUsersFixture> }) {
|
||||
const user = await users.create(null, {
|
||||
hasTeam: true,
|
||||
});
|
||||
await user.login();
|
||||
return user;
|
||||
}
|
||||
|
||||
async function openBookingFormInPreviewTab(context: PlaywrightTestArgs["context"], page: Page) {
|
||||
const previewTabPromise = context.waitForEvent("page");
|
||||
await page.locator('[data-testid="preview-button"]').click();
|
||||
const previewTabPage = await previewTabPromise;
|
||||
await previewTabPage.waitForLoadState();
|
||||
await selectFirstAvailableTimeSlotNextMonth(previewTabPage);
|
||||
await previewTabPage.waitForNavigation({
|
||||
url: (url) => url.pathname.endsWith("/book"),
|
||||
});
|
||||
return previewTabPage;
|
||||
}
|
||||
|
||||
async function saveEventType(page: Page) {
|
||||
await page.locator("[data-testid=update-eventtype]").click();
|
||||
}
|
||||
|
||||
async function addWebhook(user: Awaited<ReturnType<typeof createAndLoginUserWithEventTypes>>) {
|
||||
const webhookReceiver = createHttpServer();
|
||||
await prisma.webhook.create({
|
||||
data: {
|
||||
id: uuid(),
|
||||
userId: user.id,
|
||||
subscriberUrl: webhookReceiver.url,
|
||||
eventTriggers: [
|
||||
WebhookTriggerEvents.BOOKING_CREATED,
|
||||
WebhookTriggerEvents.BOOKING_CANCELLED,
|
||||
WebhookTriggerEvents.BOOKING_RESCHEDULED,
|
||||
],
|
||||
},
|
||||
});
|
||||
return webhookReceiver;
|
||||
}
|
|
@ -286,6 +286,7 @@ export const FormBuilder = function FormBuilder({
|
|||
return (
|
||||
<li
|
||||
key={index}
|
||||
data-testid={`field-${field.name}`}
|
||||
className="group relative flex items-center justify-between border-b p-4 last:border-b-0">
|
||||
<button
|
||||
type="button"
|
||||
|
@ -322,6 +323,7 @@ export const FormBuilder = function FormBuilder({
|
|||
{field.editable !== "user-readonly" && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
data-testid="toggle-field"
|
||||
disabled={field.editable === "system"}
|
||||
tooltip={field.editable === "system" ? t("form_builder_system_field_cant_toggle") : ""}
|
||||
checked={!field.hidden}
|
||||
|
@ -356,7 +358,12 @@ export const FormBuilder = function FormBuilder({
|
|||
);
|
||||
})}
|
||||
</ul>
|
||||
<Button color="minimal" onClick={addField} className="mt-4" StartIcon={FiPlus}>
|
||||
<Button
|
||||
color="minimal"
|
||||
data-testid="add-field"
|
||||
onClick={addField}
|
||||
className="mt-4"
|
||||
StartIcon={FiPlus}>
|
||||
{addFieldLabel}
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -405,6 +412,7 @@ export const FormBuilder = function FormBuilder({
|
|||
}}>
|
||||
<SelectField
|
||||
defaultValue={FieldTypes[3]} // "text" as defaultValue
|
||||
id="test-field-type"
|
||||
isDisabled={
|
||||
fieldForm.getValues("editable") === "system" ||
|
||||
fieldForm.getValues("editable") === "system-but-optional"
|
||||
|
@ -473,7 +481,9 @@ export const FormBuilder = function FormBuilder({
|
|||
/>
|
||||
<DialogFooter>
|
||||
<DialogClose color="secondary">Cancel</DialogClose>
|
||||
<Button type="submit">{isFieldEditMode ? t("save") : t("add")}</Button>
|
||||
<Button data-testid="field-add-save" type="submit">
|
||||
{isFieldEditMode ? t("save") : t("add")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Form>
|
||||
</div>
|
||||
|
@ -684,9 +694,7 @@ export const FormBuilderField = ({
|
|||
const { t } = useLocale();
|
||||
const { control, formState } = useFormContext();
|
||||
return (
|
||||
<div
|
||||
data-form-builder-field-name={field.name}
|
||||
className={classNames(className, field.hidden ? "hidden" : "")}>
|
||||
<div data-fob-field-name={field.name} className={classNames(className, field.hidden ? "hidden" : "")}>
|
||||
<Controller
|
||||
control={control}
|
||||
// Make it a variable
|
||||
|
@ -718,7 +726,7 @@ export const FormBuilderField = ({
|
|||
}
|
||||
return (
|
||||
<div
|
||||
data-field-name={field.name}
|
||||
data-testid={`error-message-${field.name}`}
|
||||
className="mt-2 flex items-center text-sm text-red-700 ">
|
||||
<FiInfo className="h-3 w-3 ltr:mr-2 rtl:ml-2" />
|
||||
<p>{t(message)}</p>
|
||||
|
|
|
@ -8,7 +8,15 @@ dotEnv.config({ path: ".env" });
|
|||
|
||||
const outputDir = path.join(__dirname, "test-results");
|
||||
|
||||
const DEFAULT_NAVIGATION_TIMEOUT = 15000;
|
||||
// Dev Server on local can be slow to start up and process requests. So, keep timeouts really high on local, so that tests run reliably locally
|
||||
|
||||
// So, if not in CI, keep the timers high, if the test is stuck somewhere and there is unnecessary wait developer can see in browser that it's stuck
|
||||
const DEFAULT_NAVIGATION_TIMEOUT = process.env.CI ? 15000 : 50000;
|
||||
const DEFAULT_EXPECT_TIMEOUT = process.env.CI ? 10000 : 50000;
|
||||
|
||||
// Test Timeout can hit due to slow expect, slow navigation.
|
||||
// So, it should me much higher than sum of expect and navigation timeouts as there can be many async expects and navigations in a single test
|
||||
const DEFAULT_TEST_TIMEOUT = process.env.CI ? 60000 : 120000;
|
||||
|
||||
const headless = !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS;
|
||||
|
||||
|
@ -36,7 +44,7 @@ const config: PlaywrightTestConfig = {
|
|||
forbidOnly: !!process.env.CI,
|
||||
retries: 2,
|
||||
workers: os.cpus().length,
|
||||
timeout: 60_000,
|
||||
timeout: DEFAULT_TEST_TIMEOUT,
|
||||
maxFailures: headless ? 10 : undefined,
|
||||
fullyParallel: true,
|
||||
reporter: [
|
||||
|
@ -58,6 +66,9 @@ const config: PlaywrightTestConfig = {
|
|||
name: "@calcom/web",
|
||||
testDir: "./apps/web/playwright",
|
||||
testMatch: /.*\.e2e\.tsx?/,
|
||||
expect: {
|
||||
timeout: DEFAULT_EXPECT_TIMEOUT,
|
||||
},
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
/** If navigation takes more than this, then something's wrong, let's fail fast. */
|
||||
|
@ -68,6 +79,9 @@ const config: PlaywrightTestConfig = {
|
|||
name: "@calcom/app-store",
|
||||
testDir: "./packages/app-store/",
|
||||
testMatch: /.*\.e2e\.tsx?/,
|
||||
expect: {
|
||||
timeout: DEFAULT_EXPECT_TIMEOUT,
|
||||
},
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
/** If navigation takes more than this, then something's wrong, let's fail fast. */
|
||||
|
|
Loading…
Reference in New Issue