Compare commits

...
32 Commits
Author SHA1 Message Date
Omar López 8685862617 v3.8.5
release.yml / release (push) Canceled after 0s
2024-02-29 10:26:06 -07:00
zomars 7422488b5d Update yarn.lock 2024-02-28 15:51:40 -07:00
zomars c3e1481d6a Update yarn.lock 2024-02-28 15:41:35 -07:00
Rushikesh a1d1883b34 Large padding on small screens for embedded booking widgets. (#13855) 2024-02-28 14:17:06 +00:00
Crowdin Bot f2ae94a190 New Crowdin translations by Github Action 2024-02-28 14:12:14 +00:00
Alex van Andel b82a597645 fix: invalid type check occupied seats (#13830)
* fix: Type check failure in occupiedSeats

* fix: Invalid handling of occupied seats hidden by earlier error
2024-02-28 11:07:34 -03:00
sean-brydonandUdit Takkar 5ba0ee8e85 fix: impersonation detecting orgs correctly. (#13889)
* feat:fix correctly detect impersonation organization

* chore: remove logs

* Update packages/features/ee/impersonation/lib/ImpersonationProvider.ts

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-02-28 12:16:46 +00:00
PritamandUdit Takkar 1463f97f14 fix: Currency Not Reflecting Correctly #13845 (#13847)
* fix currency problem

* fix type errors

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-02-28 17:14:26 +05:30
Nikhil Anand 34fea35e83 fix: text overflowing in username change popup #13846 (#13911) 2024-02-28 15:06:41 +05:30
Alex van AndelandUdit Takkar 2d75048037 fix: Test flakiness due to unfilled name/email (#13900)
* fix: Test flakiness due to unfilled name/email

* Better toast-success checks

* Revert "test: Create E2E tests for bookings with custom/required Phone + other questions (#11502)"

This reverts commit 614741d207.

* Revert "test: Create E2E tests for bookings with custom/required Multiple Emails + other questions  (teste2e-multipleEmailQuestion) (#11565)"

This reverts commit d1d50b0d91.

* Revert "test: Create E2E tests for bookings with custom/required Long Text + other questions (teste2e-longTextQuestion) (#11559)"

This reverts commit 96810b5ba1.

* Revert "test: Check the webhook event tab and your funtionalities (#12433)"

This reverts commit 9db9204088.

* Re-instate regularBookings

* Fixtures used by other tests

* Removed dead code from regularBookings.ts

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-02-28 06:07:32 +00:00
Carina WollendorferandCarinaWolli b549735334 santize right before sending mail (#13876)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-02-27 17:57:26 -03:00
Anwar SadathandCarinaWolli 86c379817e fix: Fix calendar event included in the workflow, doesn't show the meeting link (#13872)
* Fix calendar event included in the workflow, doesn't show the meeting link

* Fix type error

* fix public video call url

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-02-27 15:50:40 +00:00
Udit Takkar 5d1976295c fix: hover state (#13862)
release.yml / release (push) Canceled after 0s
2024-02-27 14:32:59 +00:00
Morgan e9b1fa1f50 v3.8.4 2024-02-27 16:31:14 +02:00
Udit Takkar 80080dda7a fix: missing userPrimaryEmail in workflows (#13875)
* fix: missing userPrimaryEmail in workflows

* chore

* fix: type err
2024-02-27 09:17:34 -05:00
Udit Takkar 27c25e1f95 chore: use transaction (#13874)
* fix: use transaction

* chore
2024-02-27 10:17:46 -03:00
Shyam Raghuwanshi 858be0f5d8 fix ics-feedcalendar logo not showing (#13886) 2024-02-27 11:43:25 +00:00
Hariom Balhara 68d0090f50 Make sure that state doesnt reset (#13880) 2024-02-27 07:51:11 -03:00
Udit Takkar a43fa13b11 fix: infinite rendering in event setup tab (#13877) 2024-02-26 22:18:58 +00:00
Pritam 2bf6da30b3 fix periodDays dirtystate (#13851) 2024-02-26 19:17:53 +00:00
Alex van Andel d85e0b51e4 fix: Prevent duplicate bookings with idempotency key (#13865)
* fix: Prevent duplicate bookings with idempotency key

* Add idempotencyKey to builder.ts type fix

* Add tests for new idempotency key (booking create)

* Add endTime to idempotency key, it should be possible to book different durations

* Move reschedule before the create so the idempotency key is unset
2024-02-26 18:33:22 +00:00
Carina WollendorferandCarinaWolli 55a317d677 fix: video call url in event location for recurring events (#13842)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-02-26 10:44:07 -05:00
Crowdin Bot 5fff8fc58c New Crowdin translations by Github Action 2024-02-26 14:36:48 +00:00
Udit Takkar 0d68767d0a fix: seed script (#13868) 2024-02-26 14:31:25 +00:00
Shyam Raghuwanshi 2051e87436 fix: username overflow (#13832) 2024-02-23 23:01:43 +00:00
Udit Takkar 77a157a12b fix: show go back button after expiry (#13833) 2024-02-23 22:20:46 +00:00
Omar López c4f25f2c44 build: deploy script for gradual migration (#13834) 2024-02-23 12:38:13 -07:00
Peer Richelsen d967facb63 feat: demodesk app (#13733) 2024-02-23 10:14:37 -05:00
c6dad39fb0 test: Create E2E tests for Analytics Apps (teste2e-analyticsApps) (#13695)
* refactor

* refactor

* refactor

* refactor

* fix failing check

* Update apps.ts

* fix failing check

* fix failing check

* fix failing check

---------

Co-authored-by: gitstart-calcom <gitstart-calcom@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
2024-02-22 21:54:49 -03:00
zomars eec7f4d7d4 Update yarn.lock 2024-02-22 15:00:52 -07:00
Udit Takkar 95d5824ba9 fix: og image (#13829) 2024-02-22 10:46:33 -07:00
Somay Chauhan 5f95a4d041 fix: New Team Members Join Org as OWNER/ADMIN (#13822) 2024-02-22 11:46:28 -05:00
88 changed files with 1629 additions and 5117 deletions
@@ -360,10 +360,12 @@ export const EventLimitsTab = () => {
title={t("limit_future_bookings")}
description={t("limit_future_bookings_description")}
checked={isChecked}
onCheckedChange={(bool) =>
// formMethods.setValue("periodType", bool ? "ROLLING" : "UNLIMITED", { shouldDirty: true })
onChange(bool ? "ROLLING" : "UNLIMITED")
}>
onCheckedChange={(bool) => {
if (bool && !formMethods.getValues("periodDays")) {
formMethods.setValue("periodDays", 30, { shouldDirty: true });
}
return onChange(bool ? "ROLLING" : "UNLIMITED");
}}>
<div className="border-subtle rounded-b-lg border border-t-0 p-6">
<RadioGroup.Root
value={watchPeriodType}
@@ -318,7 +318,7 @@ export const EventSetupTab = (
/>
</div>
<ErrorMessage
errors={formMethods.formState.errors.locations?.[index]}
errors={formMethods.formState.errors?.locations?.[index]}
name={eventLocationType.defaultValueVariable}
className="text-error my-1 ml-6 text-sm"
as="div"
@@ -160,7 +160,7 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
<div className="bg-subtle flex w-full flex-wrap gap-6 rounded-sm px-2 py-3 text-sm">
<div>
<p className="text-subtle">{t("current_username")}</p>
<p className="text-emphasis mt-1" data-testid="current-username">
<p className="text-emphasis mt-1 break-all" data-testid="current-username">
{currentUsername}
</p>
</div>
@@ -168,7 +168,7 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
<p className="text-subtle" data-testid="new-username">
{t("new_username")}
</p>
<p className="text-emphasis mt-1">{inputUsernameValue}</p>
<p className="text-emphasis mt-1 break-all">{inputUsernameValue}</p>
</div>
</div>
</div>
@@ -266,7 +266,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
beforeEventBuffer: eventType.beforeEventBuffer,
eventName: eventType.eventName || "",
scheduleName: eventType.scheduleName,
periodDays: eventType.periodDays || 30,
periodDays: eventType.periodDays,
requiresBookerEmailVerification: eventType.requiresBookerEmailVerification,
seatsPerTimeSlot: eventType.seatsPerTimeSlot,
seatsShowAttendees: eventType.seatsShowAttendees,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "3.8.3",
"version": "3.8.5",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -95,6 +95,7 @@ const IntegrationsContainer = ({
className="mb-6"
actions={
<Button
data-testid="add-apps"
href={variant ? `/apps/categories/${variant}` : "/apps"}
color="secondary"
StartIcon={Plus}>
@@ -374,7 +374,7 @@ const ProfileView = () => {
</div>
<DialogFooter showDivider>
<Button
data-testId="profile-update-email-submit-button"
data-testid="profile-update-email-submit-button"
color="primary"
loading={confirmPasswordMutation.isPending}
onClick={(e) => onConfirmPassword(e)}>
@@ -511,7 +511,7 @@ const ProfileForm = ({
<TextField
label={t("email")}
hint={t("change_email_hint")}
data-testId="profile-form-email"
data-testid="profile-form-email"
{...formMethods.register("email")}
/>
</div>
@@ -535,7 +535,7 @@ const ProfileForm = ({
disabled={isDisabled}
color="primary"
type="submit"
data-testId="profile-submit-button">
data-testid="profile-submit-button">
{t("update")}
</Button>
</SectionBottomActions>
+1 -1
View File
@@ -123,7 +123,7 @@ function TeamPage({
(mem) => mem.subteams?.includes(ch.slug) && mem.accepted
).length;
return (
<li key={i} className="hover:bg-muted w-full">
<li key={i} className="hover:bg-muted w-full rounded-md">
<Link href={`/${ch.slug}`} className="flex items-center justify-between">
<div className="flex items-center px-5 py-5">
<div className="ms-3 inline-block truncate">
@@ -0,0 +1,35 @@
import { loginUser } from "../../fixtures/regularBookings";
import { test } from "../../lib/fixtures";
const ALL_APPS = ["fathom", "matomo", "plausible", "ga4", "gtm", "metapixel"];
test.describe("Check analytics Apps", () => {
test.beforeEach(async ({ page, users }) => {
await loginUser(users);
await page.goto("/apps/");
});
test("Check analytics Apps", async ({ appsPage, page }) => {
await appsPage.goToAppsCategory("analytics");
await appsPage.installApp("fathom");
await appsPage.goBackToAppsPage();
await appsPage.installApp("matomo");
await appsPage.goBackToAppsPage();
await appsPage.installApp("plausible");
await appsPage.goBackToAppsPage();
await appsPage.installApp("ga4");
await appsPage.goBackToAppsPage();
await appsPage.installApp("gtm");
await appsPage.goBackToAppsPage();
await appsPage.installApp("metapixel");
await appsPage.goBackToAppsPage();
await page.goto("/event-types");
await appsPage.goToEventType("30 min");
await appsPage.goToAppsTab();
await appsPage.verifyAppsInfo(0);
for (const app of ALL_APPS) {
await appsPage.activeApp(app);
}
await appsPage.verifyAppsInfo(6);
});
});
+29
View File
@@ -175,6 +175,35 @@ testBothFutureAndLegacyRoutes.describe("pro user", () => {
await expect(page.locator('[data-testid="empty-screen"]')).toBeVisible();
});
test("can book an unconfirmed event multiple times", async ({ page, users }) => {
await page.locator('[data-testid="event-type-link"]:has-text("Opt in")').click();
await selectFirstAvailableTimeSlotNextMonth(page);
const pageUrl = page.url();
await bookTimeSlot(page);
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
// go back to the booking page to re-book.
await page.goto(pageUrl);
await bookTimeSlot(page, { email: "test2@example.com" });
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
});
test("cannot book an unconfirmed event multiple times with the same email", async ({ page, users }) => {
await page.locator('[data-testid="event-type-link"]:has-text("Opt in")').click();
await selectFirstAvailableTimeSlotNextMonth(page);
const pageUrl = page.url();
await bookTimeSlot(page);
// go back to the booking page to re-book.
await page.goto(pageUrl);
await bookTimeSlot(page);
await expect(page.getByText("Could not book the meeting.")).toBeVisible();
});
test("can book with multiple guests", async ({ page, users }) => {
const additionalGuests = ["test@gmail.com", "test2@gmail.com"];
@@ -1,483 +0,0 @@
import { loginUser } from "../../fixtures/regularBookings";
import { test } from "../../lib/fixtures";
test.describe("Booking With Address Question and Each Other Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test.beforeEach(async ({ page, users }) => {
await loginUser(users);
await page.goto("/event-types");
});
test.describe("Booking With Address Question and Checkbox Group Question", () => {
test("Address required and checkbox group required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Checkbox Group question (both required)",
secondQuestion: "checkbox",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and checkbox group not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Checkbox Group question (only address required)",
secondQuestion: "checkbox",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and Checkbox Question", () => {
test("Address required and checkbox required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Checkbox question (both required)",
secondQuestion: "boolean",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Addres and checkbox not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Checkbox question (only address required)",
secondQuestion: "boolean",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and Long text Question", () => {
test("Addres required and Long Text required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Long Text question (both required)",
secondQuestion: "textarea",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and Long Text not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", false, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Long Text question (only address required)",
secondQuestion: "textarea",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and Multi email Question", () => {
test("Address required and Multi email required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multiemail question (both required)",
secondQuestion: "multiemail",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and Multi email not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
false,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multiemail question (only address required)",
secondQuestion: "multiemail",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and multiselect Question", () => {
test("Address required and multiselect text required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multi Select question (both required)",
secondQuestion: "multiselect",
options: { ...bookingOptions, isMultiSelect: true },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and multiselect text not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multi Select question (only address required)",
secondQuestion: "multiselect",
options: { ...bookingOptions, isMultiSelect: true, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and Number Question", () => {
test("Address required and Number required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("number", "number-test", "number test", true, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Number question (both required)",
secondQuestion: "number",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and Number not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("number", "number-test", "number test", false, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Number question (only address required)",
secondQuestion: "number",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and Phone Question", () => {
test("Address required and Phone required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone-test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multi Select question (both required)",
secondQuestion: "phone",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and Phone not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("phone", "phone-test", "phone test", false, "phone-test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multi Select question (only address required)",
secondQuestion: "phone",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and Radio group Question", () => {
test("Address required and Radio group required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Radio question (both required)",
secondQuestion: "radio",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and Radio group not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Radio question (only address required)",
secondQuestion: "radio",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and select Question", () => {
test("Address required and select required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Select question (both required)",
secondQuestion: "select",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and select not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("select", "select-test", "select test", false, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Select question (both required)",
secondQuestion: "select",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Address Question and Short text question", () => {
test("Address required and Short text required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("text", "text-test", "text test", true, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multi Select question (both required)",
secondQuestion: "text",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Address and Short text not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.addQuestion("text", "text-test", "text test", false, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "address",
fillText: "Test Address question and Multi Select question (only address required)",
secondQuestion: "text",
options: { ...bookingOptions, isRequired: true },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
});
@@ -1,110 +0,0 @@
/* eslint-disable playwright/no-conditional-in-test */
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe("Booking With All Questions", () => {
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
const bookingOptions = { isAllRequired: true };
test("Selecting and filling all questions as required", async ({ bookingPage }) => {
const allQuestions = [
"phone",
"address",
"checkbox",
"boolean",
"textarea",
"multiemail",
"multiselect",
"number",
"radio",
"select",
"text",
];
for (const question of allQuestions) {
if (
question !== "number" &&
question !== "select" &&
question !== "checkbox" &&
question !== "boolean" &&
question !== "multiselect" &&
question !== "radio"
) {
await bookingPage.addQuestion(
question,
`${question}-test`,
`${question} test`,
true,
`${question} test`
);
} else {
await bookingPage.addQuestion(question, `${question}-test`, `${question} test`, true);
}
await bookingPage.checkField(question);
}
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAllQuestions(eventTypePage, allQuestions, bookingOptions);
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Selecting and filling all questions as optional", async ({ bookingPage }) => {
const allQuestions = [
"phone",
"address",
"checkbox",
"boolean",
"textarea",
"multiemail",
"multiselect",
"number",
"radio",
"select",
"text",
];
for (const question of allQuestions) {
if (
question !== "number" &&
question !== "select" &&
question !== "checkbox" &&
question !== "boolean" &&
question !== "multiselect" &&
question !== "radio"
) {
await bookingPage.addQuestion(
question,
`${question}-test`,
`${question} test`,
false,
`${question} test`
);
} else {
await bookingPage.addQuestion(question, `${question}-test`, `${question} test`, false);
}
await bookingPage.checkField(question, { isOptional: true });
}
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAllQuestions(eventTypePage, allQuestions, {
...bookingOptions,
isAllRequired: false,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
@@ -1,445 +0,0 @@
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe("Booking With Checkbox Group Question and Each Other Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
test.describe("Booking With Checkbox Group Question and Address Question", () => {
test("Checkbox Group required and Address required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Address question (both required)",
secondQuestion: "address",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and Address not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("address", "address-test", "address test", false, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Address question (only checkbox required)",
secondQuestion: "address",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test.describe("Booking With Checkbox Group Question and Phone Question", () => {
test("Checkbox Group required and Phone required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and phone question (both required)",
secondQuestion: "phone",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and Phone not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("phone", "phone-test", "phone test", false, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Phone question (only checkbox required)",
secondQuestion: "phone",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and checkbox Question", () => {
test("Checkbox Group required and checkbox required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and checkbox question (both required)",
secondQuestion: "boolean",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and checkbox not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and checkbox (only checkbox required)",
secondQuestion: "boolean",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and Long text Question", () => {
test("Checkbox Group required and Long text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Long Text question (both required)",
secondQuestion: "textarea",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and Long text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", false, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Long Text question (only checkbox required)",
secondQuestion: "textarea",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and Multi email Question", () => {
test("Checkbox Group required and Multi email required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Multi Email question (both required)",
secondQuestion: "multiemail",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and Multi email not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
false,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Multi Email question (only checkbox required)",
secondQuestion: "multiemail",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and multiselect Question", () => {
test("Checkbox Group required and multiselect text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Multi Select question (both required)",
secondQuestion: "multiselect",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and multiselect text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Multi Select question (only checkbox required)",
secondQuestion: "multiselect",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and Number Question", () => {
test("Checkbox Group required and Number required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("number", "number-test", "number test", true, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Number question (both required)",
secondQuestion: "number",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and Number not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("number", "number-test", "number test", false, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Number question (only checkbox required)",
secondQuestion: "number",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and Radio group Question", () => {
test("Checkbox Group required and Radio group required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Radio question (both required)",
secondQuestion: "radio",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and Radio group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("radio", "radio-test", "radio test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Radio question (only checkbox required)",
secondQuestion: "radio",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and select Question", () => {
test("Checkbox Group required and select required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Select question (both required)",
secondQuestion: "select",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and select not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("select", "select-test", "select test", false, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Select question (only checkbox required)",
secondQuestion: "select",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Checkbox Group Question and Short text question", () => {
test("Checkbox Group required and Short Text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("text", "text-test", "text test", true, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Text question (both required)",
secondQuestion: "text",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Checkbox Group and Short Text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.addQuestion("text", "text-test", "text test", false, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "checkbox",
fillText: "Test Checkbox Group question and Text question (only checkbox required)",
secondQuestion: "text",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
});
});
@@ -1,54 +0,0 @@
import { test } from "../../lib/fixtures";
test.describe("Collective event type", () => {
const teamEventTitle = "Test Managed Event Type";
let userId: number;
test.beforeEach(async ({ page, users, bookingPage }) => {
const userFixture = await users.create(
{ name: "testuser" },
{ hasTeam: true, schedulingType: "COLLECTIVE", teamEventTitle }
);
await userFixture.apiLogin();
userId = userFixture.id;
await page.goto("/event-types");
await bookingPage.goToEventType(teamEventTitle);
});
test("Book a Collective event type", async ({ bookingPage }) => {
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillEmailAndName(eventTypePage);
await bookingPage.confirmBooking(eventTypePage);
await bookingPage.backToBookings(eventTypePage);
await bookingPage.assertLabelWithCorrectTeamName(eventTypePage, teamEventTitle);
await bookingPage.assertBookingWithCorrectTitleAndDescription(eventTypePage, {
profileName: "testuser",
bookingName: teamEventTitle,
teamName: `user-id-${userId}'s Team`,
});
await bookingPage.clickOnBooking(eventTypePage, teamEventTitle);
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBookingWithReason(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Book a Collective event type (with added guest)", async ({ bookingPage }) => {
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillEmailAndName(eventTypePage);
await bookingPage.addGuests(eventTypePage, { guests: ["test@example.com"] });
await bookingPage.confirmBooking(eventTypePage);
await bookingPage.backToBookings(eventTypePage);
await bookingPage.assertLabelWithCorrectTeamName(eventTypePage, teamEventTitle);
await bookingPage.assertBookingWithCorrectTitleAndDescription(eventTypePage, {
profileName: "testuser",
bookingName: teamEventTitle,
teamName: `user-id-${userId}'s Team`,
aditionalGuestEmail: "test@example.com",
});
await bookingPage.clickOnBooking(eventTypePage, teamEventTitle);
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBookingWithReason(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
@@ -1,483 +0,0 @@
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe("Booking With Long Text Question and Each Other Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test.beforeEach(async ({ page, users }) => {
await loginUser(users);
await page.goto("/event-types");
});
test("Long Text and Address required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Address question (both required)",
secondQuestion: "address",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and Address not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("address", "address-test", "address test", false, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Address question (only Long Text required)",
secondQuestion: "address",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test.describe("Booking With Long Text Question and Checkbox Group Question", () => {
test("Long Text and Checkbox Group required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Checkbox Group question (both required)",
secondQuestion: "checkbox",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and Checkbox Group not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Checkbox Group question (only Long Text required)",
secondQuestion: "checkbox",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Long Text Question and checkbox Question", () => {
test("Long Text and checkbox required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Checkbox question (only Long Text required)",
secondQuestion: "boolean",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and checkbox not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Checkbox question (only Long Text required)",
secondQuestion: "boolean",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Long Text Question and Multiple email Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test("Long Text and Multiple email required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Multiple email question (both required)",
secondQuestion: "multiemail",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and Multiple email not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
false,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Multiple email question (only Long Text required)",
secondQuestion: "multiemail",
options: { hasPlaceholder: true, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Long Text Question and multiselect Question", () => {
test("Long Text and multiselect text required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and multiselect question (both required)",
secondQuestion: "multiselect",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and multiselect text not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and multiselect question (only long text required)",
secondQuestion: "multiselect",
options: { hasPlaceholder: false, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Long Text Question and Number Question", () => {
test("Long Text and Number required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Number question (both required)",
secondQuestion: "multiselect",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test("Long Text required and Number not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Number question (only Long Textß required)",
secondQuestion: "multiselect",
options: { hasPlaceholder: false, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test.describe("Booking With Long Text Question and Phone Question", () => {
test("Long Text and Phone required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Phone question (both required)",
secondQuestion: "phone",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and Phone not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("phone", "phone-test", "phone test", false, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Phone question (only Long Text required)",
secondQuestion: "phone",
options: { hasPlaceholder: false, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Long Text Question and Radio group Question", () => {
test("Long Text and Radio group required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Radio Group question (both required)",
secondQuestion: "radio",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and Radio group not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Radio Group question (only Long Text required)",
secondQuestion: "radio",
options: { hasPlaceholder: false, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Long Text Question and select Question", () => {
test("Long Text and select required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("select", "select-test", "select test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Select question (both required)",
secondQuestion: "select",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and select not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("select", "select-test", "select test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Select question (only Long Text required)",
secondQuestion: "select",
options: { hasPlaceholder: false, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Long Text Question and Short text question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test("Long Text and Short text required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("text", "text-test", "text test", true, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Text question (both required)",
secondQuestion: "text",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Long Text required and Short text not required", async ({ bookingPage }) => {
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.addQuestion("text", "text-test", "text test", false, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "textarea",
fillText: "Test Long Text question and Text question (only Long Text required)",
secondQuestion: "text",
options: { hasPlaceholder: false, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
});
@@ -1,553 +0,0 @@
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe("Booking With Multiple Email Question and Each Other Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
test.describe("Booking With Multiple Email Question and Address Question", () => {
test("Multiple Email required and Address required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Address question (both required)",
secondQuestion: "address",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and Address not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("address", "address-test", "address test", false, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Address question (only Multiple Email required)",
secondQuestion: "address",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple Email Question and checkbox group Question", () => {
test("Multiple Email required and checkbox group required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and checkbox group question (both required)",
secondQuestion: "checkbox",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and checkbox group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and checkbox group question (only Multiple Email required)",
secondQuestion: "checkbox",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple Email Question and checkbox Question", () => {
test("Multiple Email required and checkbox required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and checkbox question (both required)",
secondQuestion: "boolean",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and checkbox not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and checkbox (only Multiple Email required)",
secondQuestion: "boolean",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple Email Question and Long text Question", () => {
test("Multiple Email required and Long text required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Long Text question (both required)",
secondQuestion: "textarea",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and Long text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", false, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Long Text question (only Multiple Email required)",
secondQuestion: "textarea",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple Email Question and multiselect Question", () => {
test("Multiple Email required and multiselect text required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Multi Select question (both required)",
secondQuestion: "multiselect",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and multiselect text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Multi Select question (only Multiple Email required)",
secondQuestion: "multiselect",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple Email Question and Number Question", () => {
test("Multiple Email required and Number required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("number", "number-test", "number test", true, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Number question (both required)",
secondQuestion: "number",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and Number not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("number", "number-test", "number test", false, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Number question (only Multiple Email required)",
secondQuestion: "number",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple email Question and phone Question", () => {
test("Multiple email required and Phone required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Phone question (both required)",
secondQuestion: "phone",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple email and Phone not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("phone", "phone-test", "phone test", false, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Phone question (both required)",
secondQuestion: "phone",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple Email Question and Radio group Question", () => {
test("Multiple Email required and Radio group required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Radio question (both required)",
secondQuestion: "radio",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and Radio group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("radio", "radio-test", "radio test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Radio question (only Multiple Email required)",
secondQuestion: "radio",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test.describe("Booking With Multiple Email Question and select Question", () => {
test("Multiple Email required and select required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Select question (both required)",
secondQuestion: "select",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and select not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("select", "select-test", "select test", false, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Select question (only Multiple Email required)",
secondQuestion: "select",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Multiple Email Question and Short text question", () => {
test("Multiple Email required and Short text required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("text", "text-test", "text test", true, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Text question (both required)",
secondQuestion: "text",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Multiple Email and Short text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.addQuestion("text", "text-test", "text test", false, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "multiemail",
fillText: "Test Multiple Email question and Text question (only Multiple Email required)",
secondQuestion: "text",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
});
});
@@ -1,446 +0,0 @@
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe("Booking With Phone Question and Each Other Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
test.describe("Booking With Phone Question and Address Question", () => {
test("Phone and Address required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Address question (both required)",
secondQuestion: "address",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and Address not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("address", "address-test", "address test", false, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Address question (only phone required)",
secondQuestion: "address",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test.describe("Booking With Phone Question and checkbox group Question", () => {
const bookingOptions = { hasPlaceholder: false, isRequired: true };
test("Phone and checkbox group required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and checkbox group question (both required)",
secondQuestion: "checkbox",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and checkbox group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and checkbox group question (only phone required)",
secondQuestion: "checkbox",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and checkbox Question", () => {
test("Phone and checkbox required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and checkbox question (both required)",
secondQuestion: "boolean",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and checkbox not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and checkbox (only phone required)",
secondQuestion: "boolean",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and Long text Question", () => {
test("Phone and Long text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Long Text question (both required)",
secondQuestion: "textarea",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and Long text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", false, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Long Text question (only phone required)",
secondQuestion: "textarea",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and Multi email Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test("Phone and Multi email required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Multi Email question (both required)",
secondQuestion: "multiemail",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and Multi email not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
false,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Multi Email question (only phone required)",
secondQuestion: "multiemail",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and multiselect Question", () => {
test("Phone and multiselect text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Multi Select question (both required)",
secondQuestion: "multiselect",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and multiselect text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Multi Select question (only phone required)",
secondQuestion: "multiselect",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and Number Question", () => {
test("Phone and Number required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("number", "number-test", "number test", true, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Number question (both required)",
secondQuestion: "number",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and Number not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("number", "number-test", "number test", false, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Number question (only phone required)",
secondQuestion: "number",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and Radio group Question", () => {
test("Phone and Radio group required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Radio question (both required)",
secondQuestion: "radio",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and Radio group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Radio question (only phone required)",
secondQuestion: "radio",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and select Question", () => {
test("Phone and select required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Select question (both required)",
secondQuestion: "select",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and select not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("select", "select-test", "select test", false, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Select question (only phone required)",
secondQuestion: "select",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Phone Question and Short text question", () => {
test("Phone and Short text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("text", "text-test", "text test", true, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Text question (both required)",
secondQuestion: "text",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Phone required and Short text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.addQuestion("text", "text-test", "text test", false, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "phone",
fillText: "Test Phone question and Text question (only phone required)",
secondQuestion: "text",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
});
});
@@ -1,444 +0,0 @@
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe("Booking With Radio Question and Each Other Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
test.describe("Booking With Radio Question and Address Question", () => {
test("Radio required and Address required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Address question (both required)",
secondQuestion: "address",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and Address not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("address", "address-test", "address test", false, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Address question (only radio required)",
secondQuestion: "address",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test.describe("Booking With Radio Question and checkbox group Question", () => {
test("Radio required and checkbox group required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and checkbox group question (both required)",
secondQuestion: "checkbox",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and checkbox group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and checkbox group question (only radio required)",
secondQuestion: "checkbox",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and checkbox Question", () => {
test("Radio required and checkbox required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and checkbox question (both required)",
secondQuestion: "boolean",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and checkbox not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and checkbox (only radio required)",
secondQuestion: "boolean",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and Long text Question", () => {
test("Radio required and Long text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Long Text question (both required)",
secondQuestion: "textarea",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and Long text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", false, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Long Text question (only radio required)",
secondQuestion: "textarea",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and Multi email Question", () => {
test("Radio required and Multi email required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Multi Email question (both required)",
secondQuestion: "multiemail",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and Multi email not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
false,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Multi Email question (only radio required)",
secondQuestion: "multiemail",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and multiselect Question", () => {
test("Radio required and multiselect text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Multi Select question (both required)",
secondQuestion: "multiselect",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and multiselect text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Multi Select question (only radio required)",
secondQuestion: "multiselect",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and Number Question", () => {
test("Radio required and Number required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("number", "number-test", "number test", true, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Number question (both required)",
secondQuestion: "number",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and Number not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("number", "number-test", "number test", false, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Number question (only radio required)",
secondQuestion: "number",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and Phone Question", () => {
test("Radio required and Phone required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Phone question (both required)",
secondQuestion: "phone",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and Phone not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("phone", "phone-test", "phone test", false, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Phone question (only radio required)",
secondQuestion: "phone",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and select Question", () => {
test("Radio required and select required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Select question (both required)",
secondQuestion: "select",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and select not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("select", "select-test", "select test", false, "select test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Select question (only radio required)",
secondQuestion: "select",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Radio Question and Short text question", () => {
test("Radio required and Short text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("text", "text-test", "text test", true, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Text question (both required)",
secondQuestion: "text",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Radio and Short text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.addQuestion("text", "text-test", "text test", false, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "radio",
fillText: "Test Radio question and Text question (only radio required)",
secondQuestion: "text",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
});
});
@@ -1,28 +0,0 @@
/* eslint-disable playwright/no-conditional-in-test */
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe.configure({ mode: "serial" });
test.describe("Booking with recurring checked", () => {
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("recurring");
});
test("Updates event type with recurring events", async ({ page, bookingPage }) => {
await bookingPage.updateRecurringTab("2", "3");
await bookingPage.updateEventType();
await page.getByRole("link", { name: "Event Types" }).click();
await bookingPage.assertRepeatEventType();
});
test("Updates and shows recurring schedule correctly in booking page", async ({ bookingPage }) => {
await bookingPage.updateRecurringTab("2", "3");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.fillRecurringFieldAndConfirm(eventTypePage);
});
});
@@ -1,107 +0,0 @@
/* eslint-disable playwright/no-conditional-in-test */
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
const resolutions = [
{
width: 1920,
height: 1080,
},
{
width: 1280,
height: 720,
},
{
width: 640,
height: 480,
},
];
test.describe("Booking With All Questions", () => {
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
const allQuestions = [
"phone",
"address",
"checkbox",
"boolean",
"textarea",
"multiemail",
"multiselect",
"number",
"radio",
"select",
"text",
];
for (const question of allQuestions) {
if (
question !== "number" &&
question !== "select" &&
question !== "checkbox" &&
question !== "boolean" &&
question !== "multiselect" &&
question !== "radio"
) {
await bookingPage.addQuestion(
question,
`${question}-test`,
`${question} test`,
true,
`${question} test`
);
} else {
await bookingPage.addQuestion(question, `${question}-test`, `${question} test`, true);
}
await bookingPage.checkField(question);
}
});
for (const resolution of resolutions) {
test(`Booking with ${resolution.width}x${resolution.height} resolution and all questions`, async ({
bookingPage,
}) => {
await bookingPage.setResolution(resolution.width, resolution.height);
await bookingPage.updateEventType();
});
}
});
test.describe("Booking With no questions", () => {
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
for (const resolution of resolutions) {
test(`Booking with ${resolution.width}x${resolution.height} resolution and no questions`, async ({
bookingPage,
}) => {
await bookingPage.setResolution(resolution.width, resolution.height);
await bookingPage.updateEventType();
});
}
});
test.describe("Booking page with no questions", () => {
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
for (const resolution of resolutions) {
test(`Booking page with ${resolution.width}x${resolution.height} resolution`, async ({ bookingPage }) => {
const eventTypePage = await bookingPage.previewEventType();
await eventTypePage.setViewportSize({ width: resolution.width, height: resolution.height });
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.confirmBooking(eventTypePage);
});
}
});
@@ -1,441 +0,0 @@
import { loginUser } from "../fixtures/regularBookings";
import { test } from "../lib/fixtures";
test.describe("Booking With Phone Question and Each Other Question", () => {
const bookingOptions = { hasPlaceholder: true, isRequired: true };
test.beforeEach(async ({ page, users, bookingPage }) => {
await loginUser(users);
await page.goto("/event-types");
await bookingPage.goToEventType("30 min");
await bookingPage.goToTab("event_advanced_tab_title");
});
test.describe("Booking With Select Question and Address Question", () => {
test("Select required and Address required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("address", "address-test", "address test", true, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and Address question (both required)",
secondQuestion: "address",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and Address not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("address", "address-test", "address test", false, "address test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and Address question (only select required)",
secondQuestion: "address",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and checkbox group Question", () => {
test("Select required and checkbox group required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and Checkbox question (both required)",
secondQuestion: "checkbox",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and checkbox group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("checkbox", "checkbox-test", "checkbox test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and Checkbox question (only Select required)",
secondQuestion: "checkbox",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and checkbox Question", () => {
test("Select required and checkbox required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and boolean question (both required)",
secondQuestion: "boolean",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and checkbox not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("boolean", "boolean-test", "boolean test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and boolean question (only select required)",
secondQuestion: "boolean",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and Long text Question", () => {
test("Select required and Long text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", true, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and textarea question (both required)",
secondQuestion: "textarea",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and Long text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("textarea", "textarea-test", "textarea test", false, "textarea test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and textarea question (only select required)",
secondQuestion: "textarea",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and Multi email Question", () => {
test("Select required and Multi email required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
true,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and multiemail question (both required)",
secondQuestion: "multiemail",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and Multi email not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion(
"multiemail",
"multiemail-test",
"multiemail test",
false,
"multiemail test"
);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and multiemail question (only select required)",
secondQuestion: "multiemail",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and multiselect Question", () => {
test("Select required and multiselect text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and multiselect question (both required)",
secondQuestion: "multiselect",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and multiselect text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("multiselect", "multiselect-test", "multiselect test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and multiselect question (only select required)",
secondQuestion: "multiselect",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and Number Question", () => {
test("Select required and Number required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("number", "number-test", "number test", true, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and number question (both required)",
secondQuestion: "number",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and Number not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("number", "number-test", "number test", false, "number test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and number question (only select required)",
secondQuestion: "number",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and Phone Question", () => {
test("Select required and select required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("phone", "phone-test", "phone test", true, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and phone question (both required)",
secondQuestion: "phone",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and Phone not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("phone", "phone-test", "phone test", false, "phone test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and phone question (only select required)",
secondQuestion: "phone",
options: { ...bookingOptions, isRequired: false },
});
});
});
test.describe("Booking With Select Question and Radio group Question", () => {
test("Select required and Radio group required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", true);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and radio question (both required)",
secondQuestion: "radio",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and Radio group not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("radio", "radio-test", "radio test", false);
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and radio question (only select required)",
secondQuestion: "radio",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
test.describe("Booking With Select Question and Short text question", () => {
test("Select required and Short text required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("text", "text-test", "text test", true, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and text question (both required)",
secondQuestion: "text",
options: bookingOptions,
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
test("Select and Short text not required", async ({ bookingPage }) => {
await bookingPage.addQuestion("select", "select-test", "select test", true, "select test");
await bookingPage.addQuestion("text", "text-test", "text test", false, "text test");
await bookingPage.updateEventType();
const eventTypePage = await bookingPage.previewEventType();
await bookingPage.selectTimeSlot(eventTypePage);
await bookingPage.fillAndConfirmBooking({
eventTypePage,
placeholderText: "Please share anything that will help prepare for our meeting.",
question: "select",
fillText: "Test Select question and text question (only select required)",
secondQuestion: "text",
options: { ...bookingOptions, isRequired: false },
});
await bookingPage.rescheduleBooking(eventTypePage);
await bookingPage.assertBookingRescheduled(eventTypePage);
await bookingPage.cancelBooking(eventTypePage);
await bookingPage.assertBookingCanceled(eventTypePage);
});
});
});
+29
View File
@@ -0,0 +1,29 @@
import { expect, type Page } from "@playwright/test";
export function createAppsFixture(page: Page) {
return {
goToAppsCategory: async (category: string) => {
await page.getByTestId(`app-store-category-${category}`).nth(1).click();
await page.goto("apps/categories/analytics");
},
installApp: async (app: string) => {
await page.getByTestId(`app-store-app-card-${app}`).click();
await page.getByTestId("install-app-button").click();
},
goBackToAppsPage: async () => {
await page.getByTestId("add-apps").click();
},
goToEventType: async (eventType: string) => {
await page.getByRole("link", { name: eventType }).click();
},
goToAppsTab: async () => {
await page.getByTestId("vertical-tab-apps").click();
},
activeApp: async (app: string) => {
await page.getByTestId(`${app}-app-switch`).click();
},
verifyAppsInfo: async (activeApps: number) => {
await expect(page.locator(`text=6 apps, ${activeApps} active`)).toBeVisible();
},
};
}
@@ -1,183 +1,14 @@
import { expect, type Page } from "@playwright/test";
import dayjs from "@calcom/dayjs";
import { WEBAPP_URL } from "@calcom/lib/constants";
import type { MembershipRole } from "@calcom/prisma/enums";
import { localize } from "../lib/testUtils";
import type { createUsersFixture } from "./users";
const reschedulePlaceholderText = "Let others know why you need to reschedule";
export const scheduleSuccessfullyText = "This meeting is scheduled";
const EMAIL = "test@test.com";
const EMAIL2 = "test2@test.com";
const PHONE = "+55 (32) 983289947";
type BookingOptions = {
hasPlaceholder?: boolean;
isReschedule?: boolean;
isRequired?: boolean;
isAllRequired?: boolean;
isMultiSelect?: boolean;
};
interface QuestionActions {
[key: string]: () => Promise<void>;
}
type teamBookingtypes = { isManagedType?: boolean; isRoundRobinType?: boolean; isCollectiveType?: boolean };
type customLocators = {
shouldChangeSelectLocator: boolean;
shouldUseLastRadioGroupLocator: boolean;
shouldUseFirstRadioGroupLocator: boolean;
shouldChangeMultiSelectLocator: boolean;
};
type fillAndConfirmBookingParams = {
eventTypePage: Page;
placeholderText: string;
question: string;
fillText: string;
secondQuestion: string;
options: BookingOptions;
};
type UserFixture = ReturnType<typeof createUsersFixture>;
function isLastDayOfMonth(): boolean {
const today = dayjs();
const endOfMonth = today.endOf("month");
return today.isSame(endOfMonth, "day");
}
const fillQuestion = async (eventTypePage: Page, questionType: string, customLocators: customLocators) => {
const questionActions: QuestionActions = {
phone: async () => {
await eventTypePage.locator('input[name="phone-test"]').clear();
await eventTypePage.locator('input[name="phone-test"]').fill(PHONE);
},
multiemail: async () => {
await eventTypePage.getByRole("button", { name: `${questionType} test` }).click();
await eventTypePage.getByPlaceholder(`${questionType} test`).fill(EMAIL);
await eventTypePage.getByTestId("add-another-guest").last().click();
await eventTypePage.getByPlaceholder(`${questionType} test`).last().fill(EMAIL2);
},
checkbox: async () => {
if (customLocators.shouldUseLastRadioGroupLocator || customLocators.shouldChangeMultiSelectLocator) {
await eventTypePage.getByLabel("Option 1").last().click();
await eventTypePage.getByLabel("Option 2").last().click();
} else if (customLocators.shouldUseFirstRadioGroupLocator) {
await eventTypePage.getByLabel("Option 1").first().click();
await eventTypePage.getByLabel("Option 2").first().click();
} else {
await eventTypePage.getByLabel("Option 1").click();
await eventTypePage.getByLabel("Option 2").click();
}
},
multiselect: async () => {
if (customLocators.shouldChangeMultiSelectLocator) {
await eventTypePage.getByLabel("multi-select-dropdown").click();
await eventTypePage.getByTestId("select-option-Option 1").click();
} else {
await eventTypePage.getByLabel("multi-select-dropdown").last().click();
await eventTypePage.getByTestId("select-option-Option 1").click();
}
},
boolean: async () => {
await eventTypePage.getByLabel(`${questionType} test`).check();
},
radio: async () => {
await eventTypePage.locator('[id="radio-test\\.option\\.0\\.radio"]').click();
},
select: async () => {
if (customLocators.shouldChangeSelectLocator) {
await eventTypePage.getByLabel("select-dropdown").first().click();
await eventTypePage.getByTestId("select-option-Option 1").click();
} else {
await eventTypePage.getByLabel("select-dropdown").last().click();
await eventTypePage.getByTestId("select-option-Option 1").click();
}
},
number: async () => {
await eventTypePage.getByPlaceholder(`${questionType} test`).click();
await eventTypePage.getByPlaceholder(`${questionType} test`).fill("123");
},
address: async () => {
await eventTypePage.getByPlaceholder(`${questionType} test`).click();
await eventTypePage.getByPlaceholder(`${questionType} test`).fill("address test");
},
textarea: async () => {
await eventTypePage.getByPlaceholder(`${questionType} test`).click();
await eventTypePage.getByPlaceholder(`${questionType} test`).fill("textarea test");
},
text: async () => {
await eventTypePage.getByPlaceholder(`${questionType} test`).click();
await eventTypePage.getByPlaceholder(`${questionType} test`).fill("text test");
},
};
if (questionActions[questionType]) {
await questionActions[questionType]();
}
};
const fillAllQuestions = async (eventTypePage: Page, questions: string[], options: BookingOptions) => {
if (options.isAllRequired) {
for (const question of questions) {
switch (question) {
case "email":
await eventTypePage.getByPlaceholder("Email").click();
await eventTypePage.getByPlaceholder("Email").fill(EMAIL);
break;
case "phone":
await eventTypePage.getByPlaceholder("Phone test").click();
await eventTypePage.getByPlaceholder("Phone test").fill(PHONE);
break;
case "address":
await eventTypePage.getByPlaceholder("Address test").click();
await eventTypePage.getByPlaceholder("Address test").fill("123 Main St, City, Country");
break;
case "textarea":
await eventTypePage.getByPlaceholder("Textarea test").click();
await eventTypePage.getByPlaceholder("Textarea test").fill("This is a sample text for textarea.");
break;
case "select":
await eventTypePage.getByLabel("select-dropdown").last().click();
await eventTypePage.getByTestId("select-option-Option 1").click();
break;
case "multiselect":
// select-dropdown
await eventTypePage.getByLabel("multi-select-dropdown").click();
await eventTypePage.getByTestId("select-option-Option 1").click();
break;
case "number":
await eventTypePage.getByLabel("number test").click();
await eventTypePage.getByLabel("number test").fill("123");
break;
case "radio":
await eventTypePage.getByRole("radiogroup").getByText("Option 1").check();
break;
case "text":
await eventTypePage.getByPlaceholder("Text test").click();
await eventTypePage.getByPlaceholder("Text test").fill("Sample text");
break;
case "checkbox":
await eventTypePage.getByLabel("Option 1").first().check();
await eventTypePage.getByLabel("Option 2").first().check();
break;
case "boolean":
await eventTypePage.getByLabel(`${question} test`).check();
break;
case "multiemail":
await eventTypePage.getByRole("button", { name: "multiemail test" }).click();
await eventTypePage.getByPlaceholder("multiemail test").fill(EMAIL);
break;
}
}
}
};
export async function loginUser(users: UserFixture) {
const pro = await users.create({ name: "testuser" });
await pro.apiLogin();
@@ -191,17 +22,6 @@ export async function loginUserWithTeam(users: UserFixture, role: MembershipRole
await pro.apiLogin();
}
const goToNextMonthIfNoAvailabilities = async (eventTypePage: Page) => {
try {
if (isLastDayOfMonth()) {
await eventTypePage.getByTestId("view_next_month").waitFor({ timeout: 6000 });
await eventTypePage.getByTestId("view_next_month").click();
}
} catch (err) {
console.info("No need to click on view next month button");
}
};
export function createBookingPageFixture(page: Page) {
return {
goToEventType: async (eventType: string) => {
@@ -216,308 +36,19 @@ export function createBookingPageFixture(page: Page) {
goToTab: async (tabName: string) => {
await page.getByTestId(`vertical-tab-${tabName}`).click();
},
goToEventTypesPage: async () => {
await page.goto("/event-types");
},
setResolution: async (width: number, height: number) => {
await page.setViewportSize({ width: width, height: height });
},
addQuestion: async (
questionType: string,
identifier: string,
label: string,
isRequired: boolean,
placeholder?: string
) => {
await page.getByTestId("add-field").click();
await page.getByTestId("test-field-type").click();
await page.getByTestId(`select-option-${questionType}`).click();
await page.getByLabel("Identifier").dblclick();
await page.getByLabel("Identifier").fill(identifier);
await page.getByLabel("Label").click();
await page.getByLabel("Label").fill(label);
if (placeholder) {
await page.getByLabel("Placeholder").click();
await page.getByLabel("Placeholder").fill(placeholder);
}
if (!isRequired) {
await page.getByRole("radio", { name: "No" }).click();
}
await page.getByTestId("field-add-save").click();
},
updateRecurringTab: async (repeatWeek: string, maxEvents: string) => {
const repeatText = (await localize("en"))("repeats_every");
const maximumOf = (await localize("en"))("for_a_maximum_of");
await page.getByTestId("recurring-event-check").click();
await page
.getByTestId("recurring-event-collapsible")
.locator("div")
.filter({ hasText: repeatText })
.getByRole("spinbutton")
.fill(repeatWeek);
await page
.getByTestId("recurring-event-collapsible")
.locator("div")
.filter({ hasText: maximumOf })
.getByRole("spinbutton")
.fill(maxEvents);
},
updateEventType: async () => {
await page.getByTestId("update-eventtype").click();
const toast = await page.waitForSelector('[data-testid="toast-success"]');
expect(toast).toBeTruthy();
},
previewEventType: async () => {
const eventtypePromise = page.waitForEvent("popup");
await page.getByTestId("preview-button").click();
return eventtypePromise;
},
selectTimeSlot: async (eventTypePage: Page) => {
await goToNextMonthIfNoAvailabilities(eventTypePage);
await eventTypePage.getByTestId("time").first().click();
},
clickReschedule: async () => {
await page.getByText("Reschedule").click();
},
selectFirstAvailableTime: async () => {
await page.getByTestId("time").first().click();
},
fillRescheduleReasonAndConfirm: async () => {
await page.getByPlaceholder(reschedulePlaceholderText).click();
await page.getByPlaceholder(reschedulePlaceholderText).fill("Test reschedule");
await page.getByTestId("confirm-reschedule-button").click();
},
fillRecurringFieldAndConfirm: async (eventTypePage: Page) => {
await eventTypePage.getByTestId("occurrence-input").click();
await eventTypePage.getByTestId("occurrence-input").fill("2");
await goToNextMonthIfNoAvailabilities(eventTypePage);
await eventTypePage.getByTestId("time").first().click();
await expect(eventTypePage.getByTestId("recurring-dates")).toBeVisible();
},
cancelBookingWithReason: async (page: Page) => {
await page.getByTestId("cancel").click();
await page.getByTestId("cancel_reason").fill("Test cancel");
await page.getByTestId("confirm_cancel").click();
},
assertBookingCanceled: async (page: Page) => {
await expect(page.getByTestId("cancelled-headline")).toBeVisible();
},
rescheduleBooking: async (eventTypePage: Page) => {
await goToNextMonthIfNoAvailabilities(eventTypePage);
await eventTypePage.getByText("Reschedule").click();
while (await eventTypePage.getByRole("button", { name: "View next" }).isVisible()) {
await eventTypePage.getByRole("button", { name: "View next" }).click();
}
await eventTypePage.getByTestId("time").first().click();
await eventTypePage.getByPlaceholder(reschedulePlaceholderText).click();
await eventTypePage.getByPlaceholder(reschedulePlaceholderText).fill("Test reschedule");
await eventTypePage.getByTestId("confirm-reschedule-button").click();
await eventTypePage.waitForTimeout(400);
if (
await eventTypePage.getByRole("heading", { name: "Could not reschedule the meeting." }).isVisible()
) {
await eventTypePage.getByTestId("back").click();
await eventTypePage.getByTestId("time").last().click();
await eventTypePage.getByTestId("confirm-reschedule-button").click();
}
},
assertBookingRescheduled: async (page: Page) => {
await expect(page.getByText(scheduleSuccessfullyText)).toBeVisible();
},
assertRepeatEventType: async () => {
await expect(page.getByTestId("repeat-eventtype")).toBeVisible();
},
cancelBooking: async (eventTypePage: Page) => {
await eventTypePage.getByTestId("cancel").click();
await eventTypePage.getByTestId("cancel_reason").fill("Test cancel");
await eventTypePage.getByTestId("confirm_cancel").click();
await expect(eventTypePage.getByTestId("cancelled-headline")).toBeVisible();
},
confirmBooking: async (eventTypePage: Page) => {
await eventTypePage.getByTestId("confirm-book-button").click();
await eventTypePage.waitForURL("booking/**");
await expect(eventTypePage.getByText(scheduleSuccessfullyText)).toBeVisible();
},
fillAndConfirmBooking: async ({
eventTypePage,
placeholderText,
question,
fillText,
secondQuestion,
options,
}: fillAndConfirmBookingParams) => {
const confirmButton = options.isReschedule ? "confirm-reschedule-button" : "confirm-book-button";
await expect(eventTypePage.getByText(`${secondQuestion} test`).first()).toBeVisible();
await eventTypePage.getByPlaceholder(placeholderText).fill(fillText);
// Change the selector for specifics cases related to select question
const shouldChangeSelectLocator = (question: string, secondQuestion: string): boolean =>
question === "select" && ["multiemail", "multiselect", "address"].includes(secondQuestion);
const shouldUseLastRadioGroupLocator = (question: string, secondQuestion: string): boolean =>
question === "radio" && secondQuestion === "checkbox";
const shouldUseFirstRadioGroupLocator = (question: string, secondQuestion: string): boolean =>
question === "checkbox" && secondQuestion === "radio";
const shouldChangeMultiSelectLocator = (question: string, secondQuestion: string): boolean =>
question === "multiselect" &&
["address", "checkbox", "multiemail", "select"].includes(secondQuestion);
const customLocators = {
shouldChangeSelectLocator: shouldChangeSelectLocator(question, secondQuestion),
shouldUseLastRadioGroupLocator: shouldUseLastRadioGroupLocator(question, secondQuestion),
shouldUseFirstRadioGroupLocator: shouldUseFirstRadioGroupLocator(question, secondQuestion),
shouldChangeMultiSelectLocator: shouldChangeMultiSelectLocator(question, secondQuestion),
};
// Fill the first question
await fillQuestion(eventTypePage, question, customLocators);
// Fill the second question if is required
options.isRequired && (await fillQuestion(eventTypePage, secondQuestion, customLocators));
await eventTypePage.getByTestId(confirmButton).click();
await eventTypePage.waitForTimeout(400);
if (await eventTypePage.getByRole("heading", { name: "Could not book the meeting." }).isVisible()) {
await eventTypePage.getByTestId("back").click();
await eventTypePage.getByTestId("time").last().click();
await fillQuestion(eventTypePage, question, customLocators);
options.isRequired && (await fillQuestion(eventTypePage, secondQuestion, customLocators));
await eventTypePage.getByTestId(confirmButton).click();
}
const scheduleSuccessfullyPage = eventTypePage.getByText(scheduleSuccessfullyText);
await scheduleSuccessfullyPage.waitFor({ state: "visible" });
await expect(scheduleSuccessfullyPage).toBeVisible();
},
fillAllQuestions: async (eventTypePage: Page, questions: string[], options: BookingOptions) => {
const confirmButton = options.isReschedule ? "confirm-reschedule-button" : "confirm-book-button";
await fillAllQuestions(eventTypePage, questions, options);
await eventTypePage.getByTestId(confirmButton).click();
const scheduleSuccessfullyPage = eventTypePage.getByText(scheduleSuccessfullyText);
await scheduleSuccessfullyPage.waitFor({ state: "visible" });
await expect(scheduleSuccessfullyPage).toBeVisible();
},
addGuests: async (eventTypePage: Page, options: { guests: string[] }) => {
await eventTypePage.getByTestId("add-guests").click();
for (const guest of options.guests) {
await eventTypePage.getByPlaceholder("Email").fill(guest);
}
},
createTeam: async (name: string) => {
await page.getByTestId("teams-test").click();
await page.getByTestId("new-team-btn").click();
await page.getByPlaceholder("Acme Inc.").click();
await page.getByPlaceholder("Acme Inc.").fill(name);
await page.getByTestId("continue-button").click();
await page.getByTestId("publish-button").click();
await page.getByTestId("vertical-tab-Back").click();
},
createTeamEventType: async (name: string, options: teamBookingtypes) => {
await page.getByTestId("new-event-type").click();
await page.getByTestId("option-0").click();
// We first simulate to create a default event type to check if managed option is not available
const managedEventDescription = (await localize("en"))("managed_event_description");
const roundRobinEventDescription = (await localize("en"))("round_robin_description");
const collectiveEventDescription = (await localize("en"))("collective_description");
const quickChatText = (await localize("en"))("quick_chat");
await expect(page.locator("div").filter({ hasText: managedEventDescription })).toBeHidden();
await page.getByTestId("dialog-rejection").click();
await page.getByTestId("new-event-type").click();
await page.getByTestId("option-team-1").click();
await page.getByPlaceholder(quickChatText).fill(name);
if (options.isCollectiveType) {
await page
.locator("div")
.filter({ hasText: `Collective${collectiveEventDescription}` })
.getByRole("radio")
.first()
.click();
}
if (options.isRoundRobinType) {
await page
.locator("div")
.filter({ hasText: `Round Robin${roundRobinEventDescription}` })
.getByRole("radio")
.nth(1)
.click();
}
if (options.isManagedType) {
await page
.locator("div")
.filter({ hasText: `Managed Event${managedEventDescription}` })
.getByRole("radio")
.last()
.click();
const managedEventClarification = (await localize("en"))("managed_event_url_clarification");
await expect(page.getByText(managedEventClarification)).toBeVisible();
}
const continueText = (await localize("en"))("continue");
const toast = page.getByTestId("toast-success");
await page.getByRole("button", { name: continueText }).click();
expect(toast).toBeTruthy();
await page.getByTestId("update-eventtype").click();
},
assertLabelWithCorrectTeamName: async (page: Page, teamName: string) => {
await expect(page.getByRole("link", { name: teamName }).first()).toBeVisible();
},
assertBookingWithCorrectTitleAndDescription: async (
page: Page,
options: { profileName: string; bookingName: string; teamName: string; aditionalGuestEmail?: string }
) => {
options.aditionalGuestEmail
? await expect(
page.getByRole("link", {
name: `${options.bookingName} between ${options.teamName} and ${options.profileName} You , ${options.profileName} and ${options.aditionalGuestEmail}`,
})
).toBeVisible()
: await expect(
page.getByRole("link", {
name: `${options.bookingName} between ${options.teamName} and ${options.profileName} You and ${options.profileName}`,
})
).toBeVisible();
},
clickOnBooking: async (page: Page, teamName: string) => {
await page.getByRole("link", { name: teamName }).first().click();
},
checkField: async (question: string, options?: { isOptional: boolean }) => {
if (options?.isOptional) {
await expect(page.getByTestId(`field-${question}-test`).getByTestId("optional")).toBeVisible();
} else {
await expect(page.getByTestId(`field-${question}-test`).getByTestId("required")).toBeVisible();
}
await expect(page.getByTestId(`field-${question}-test`)).toBeVisible();
},
checkRequiresConfirmation: async () => {
// Check existence of the icon
await expect(page.getByTestId("requires-confirmation-title").locator("svg")).toBeVisible();
@@ -526,7 +57,6 @@ export function createBookingPageFixture(page: Page) {
await expect(confirmationSwitch).toBeVisible();
await confirmationSwitch.click();
},
checkRequiresBookerEmailVerification: async () => {
await expect(page.getByTestId("requires-booker-email-verification-title").locator("svg")).toBeVisible();
@@ -535,7 +65,6 @@ export function createBookingPageFixture(page: Page) {
await expect(emailSwitch).toBeVisible();
await emailSwitch.click();
},
checkHideNotes: async () => {
await expect(page.getByTestId("disable-notes-title").locator("svg")).toBeVisible();
@@ -544,7 +73,6 @@ export function createBookingPageFixture(page: Page) {
await expect(hideNotesSwitch).toBeVisible();
await hideNotesSwitch.click();
},
checkRedirectOnBooking: async () => {
await expect(page.getByTestId("redirect-success-booking-title").locator("svg")).toBeVisible();
@@ -555,7 +83,6 @@ export function createBookingPageFixture(page: Page) {
await page.getByTestId("external-redirect-url").fill("https://cal.com");
await expect(page.getByTestId("redirect-url-warning")).toBeVisible();
},
checkEnablePrivateUrl: async () => {
await expect(page.getByTestId("hashedLinkCheck-title").locator("label div")).toBeVisible();
@@ -564,7 +91,6 @@ export function createBookingPageFixture(page: Page) {
await page.getByTestId("hashedLinkCheck").click();
await expect(page.getByTestId("generated-hash-url")).toBeVisible();
},
toggleOfferSeats: async () => {
await expect(page.getByTestId("offer-seats-toggle-title").locator("svg")).toBeVisible();
@@ -575,7 +101,6 @@ export function createBookingPageFixture(page: Page) {
await expect(seatSwitchField).toHaveValue("3");
await expect(page.getByTestId("show-attendees")).toBeVisible();
},
checkLockTimezone: async () => {
await expect(page.getByTestId("lock-timezone-toggle-title").locator("svg")).toBeVisible();
@@ -584,18 +109,9 @@ export function createBookingPageFixture(page: Page) {
await expect(lockSwitch).toBeVisible();
await lockSwitch.click();
},
checkEventType: async () => {
await expect(page.getByTestId("requires-confirmation-badge").last()).toBeVisible();
},
assertBookingIsVisible: async (name: string) => {
await expect(
page.getByRole("link", {
name,
})
).toBeVisible();
},
checkBufferTime: async () => {
const minutes = (await localize("en"))("minutes");
const fieldPlaceholder = page.getByPlaceholder("0");
@@ -629,7 +145,6 @@ export function createBookingPageFixture(page: Page) {
await page.getByTestId("select-option-60").click();
await expect(page.getByText(`60 ${minutes}`, { exact: true })).toBeVisible();
},
checkLimitBookingFrequency: async () => {
const fieldPlaceholder = page.getByPlaceholder("1").nth(1);
const limitFrequency = (await localize("en"))("limit_booking_frequency");
@@ -645,7 +160,6 @@ export function createBookingPageFixture(page: Page) {
await expect(fieldPlaceholder).toHaveValue("12");
await limitFrequencySwitch.click();
},
checkLimitBookingDuration: async () => {
const limitDuration = (await localize("en"))("limit_total_booking_duration");
const addlimit = (await localize("en"))("add_limit");
@@ -659,7 +173,6 @@ export function createBookingPageFixture(page: Page) {
await expect(page.getByTestId("add-limit")).toHaveCount(2);
await limitDurationSwitch.click();
},
checkLimitFutureBookings: async () => {
const limitFutureBookings = (await localize("en"))("limit_future_bookings");
const limitBookingsSwitch = page
@@ -672,7 +185,6 @@ export function createBookingPageFixture(page: Page) {
await expect(page.locator("#RANGE")).toBeChecked();
await limitBookingsSwitch.click();
},
checkOffsetTimes: async () => {
const offsetStart = (await localize("en"))("offset_start");
const offsetStartTimes = (await localize("en"))("offset_toggle");
@@ -685,54 +197,8 @@ export function createBookingPageFixture(page: Page) {
page.getByText("e.g. this will show time slots to your bookers at 9:10 AM instead of 9:00 AM")
).toBeVisible();
},
checkTimeSlotsCount: async (eventTypePage: Page, count: number) => {
await expect(eventTypePage.getByTestId("time")).toHaveCount(count);
},
createBookingWebhook: async (webhookReceiver: { url: string }, eventTitle: string) => {
const events = [
"Booking Canceled",
"Booking Created",
"Booking Rejected",
"Booking Requested",
"Booking Payment Initiated",
"Booking Rescheduled",
"Meeting Ended",
"Booking Paid",
"Recording Download Link Ready",
];
await page.goto(`${WEBAPP_URL}/event-types`);
await page.getByText(eventTitle).click();
await page.getByTestId("vertical-tab-webhooks").click();
await page.getByTestId("new_webhook").click();
await page.getByLabel("Subscriber URL").fill(webhookReceiver.url);
await page.getByRole("button", { name: "Ping test" }).click();
page
.getByTestId("dialog-creation")
.locator("div")
.filter({ hasText: '{ "ok": true, "status": 200, "message": "{}" }' })
.nth(2);
await page.getByRole("button", { name: "Create Webhook" }).click();
page.getByText(webhookReceiver.url);
events.forEach((event) => {
page.getByText(event);
});
},
rejectFirstBooking: async () => {
await page.goto("/bookings/unconfirmed");
await page.getByTestId("horizontal-tab-unconfirmed").click();
await page.getByTestId("booking-item").first().getByText("Reject").click();
await page.getByTestId("rejection-confirm").click();
},
fillEmailAndName: async (eventTypePage: Page) => {
const emailLabel = (await localize("en"))("email_address");
const nameLabel = (await localize("en"))("your_name");
await eventTypePage.getByLabel(emailLabel).fill(EMAIL);
await eventTypePage.getByLabel(nameLabel).fill("testuser");
},
};
}
+6
View File
@@ -4,6 +4,7 @@ import { test as base } from "@playwright/test";
import prisma from "@calcom/prisma";
import type { ExpectedUrlDetails } from "../../../../playwright.config";
import { createAppsFixture } from "../fixtures/apps";
import { createBookingsFixture } from "../fixtures/bookings";
import { createEmailsFixture } from "../fixtures/emails";
import { createEmbedsFixture } from "../fixtures/embeds";
@@ -32,6 +33,7 @@ export interface Fixtures {
workflowPage: ReturnType<typeof createWorkflowPageFixture>;
features: ReturnType<typeof createFeatureFixture>;
eventTypePage: ReturnType<typeof createEventTypeFixture>;
appsPage: ReturnType<typeof createAppsFixture>;
}
declare global {
@@ -104,4 +106,8 @@ export const test = base.extend<Fixtures>({
const eventTypePage = createEventTypeFixture(page);
await use(eventTypePage);
},
appsPage: async ({ page }, use) => {
const appsPage = createAppsFixture(page);
await use(appsPage);
},
});
+5 -13
View File
@@ -58,7 +58,7 @@ test.describe("Update Profile", () => {
await page.getByTestId("profile-update-email-submit-button").click();
const toastLocator = await page.waitForSelector('[data-testId="toast-success"]');
const toastLocator = await page.getByTestId("toast-success");
const textContent = await toastLocator.textContent();
@@ -84,7 +84,7 @@ test.describe("Update Profile", () => {
await page.goto(verifyUrl);
const errorLocator = await page.waitForSelector('[data-testId="toast-error"]');
const errorLocator = await page.getByTestId("toast-error");
expect(errorLocator).toBeDefined();
@@ -118,11 +118,7 @@ test.describe("Update Profile", () => {
await page.getByTestId("profile-update-email-submit-button").click();
const toastLocator = await page.waitForSelector('[data-testId="toast-success"]');
const textContent = await toastLocator.textContent();
expect(textContent).toContain(email);
expect(await page.getByTestId("toast-success").textContent()).toContain(email);
// Instead of dealing with emails in e2e lets just get the token and navigate to it
const verificationToken = await prisma.verificationToken.findFirst({
@@ -140,9 +136,7 @@ test.describe("Update Profile", () => {
await page.goto(verifyUrl);
const successLocator = await page.waitForSelector('[data-testId="toast-success"]');
expect(await successLocator.textContent()).toContain(email);
expect(await page.getByTestId("toast-success").textContent()).toContain(email);
// After email verification is successfull. user is sent to /event-types
await page.waitForURL("/event-types");
@@ -176,9 +170,7 @@ test.describe("Update Profile", () => {
await page.getByTestId("profile-update-email-submit-button").click();
const toastLocator = await page.waitForSelector('[data-testId="toast-success"]');
expect(await toastLocator.isVisible()).toBe(true);
expect(await page.getByTestId("toast-success").isVisible()).toBe(true);
const emailInputUpdated = page.getByTestId("profile-form-email");
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,5 @@
{
"identity_provider": "Azonosítás szolgáltató",
"trial_days_left": "Még $t(day, {\"count\": {{days}} }) maradt a PRO próbaidőszakodból",
"day_one": "{{count}} nap",
"day_other": "{{count}} nap",
@@ -7,6 +8,7 @@
"upgrade_now": "Frissíts most",
"accept_invitation": "Meghívó elfogadása",
"have_any_questions": "Van kérdésed? Itt vagyunk hogy segítsünk.",
"verify_email_email_button": "Email igazolása",
"organizer": "Szervező",
"rejection_reason": "Lemondás oka",
"help": "Segítség",
@@ -160,7 +162,7 @@
"yearly_one": "év",
"yearly_other": "év",
"name": "Név",
"logo": "Logo",
"logo": "Logó",
"error": "Hiba",
"type": "Típus",
"edit": "Szerkesztés",
@@ -13,6 +13,8 @@
"reset_password_subject": "{{appName}}: інструкції зі скидання пароля",
"verify_email_subject": "{{appName}}: підтвердьте свій обліковий запис",
"check_your_email": "Перевірте свою електронну пошту",
"old_email_address": "Стара адреса електронної пошти",
"new_email_address": "Нова адреса електронної пошти",
"verify_email_page_body": "Ми надіслали лист на адресу {{email}}. Підтвердьте свою електронну адресу, щоб уникнути проблем із доставкою листів і даних календаря із застосунку {{appName}}.",
"verify_email_banner_body": "Підтвердьте свою електронну адресу, щоб уникнути проблем із доставкою листів і даних календаря",
"verify_email_email_header": "Підтвердьте свою електронну адресу",
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# Only deploy to production
if [ "$VERCEL_ENV" != "production" ]; then exit 0; fi
checkRoute () {
if [ "$1" != '1' ]; then rm -rf $2; fi
}
# These conditionals are used to remove directories from the build that are not needed in production
# This is to reduce the size of the build and prevent OOM errors
checkRoute "$APP_ROUTER_EVENT_TYPES_ENABLED" app/future/event-types
checkRoute "$APP_ROUTER_SETTINGS_ADMIN_ENABLED" app/future/settings/admin
checkRoute "$APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED" app/future/apps/installed
checkRoute "$APP_ROUTER_APPS_SLUG_ENABLED" app/future/apps/\[slug\]
checkRoute "$APP_ROUTER_APPS_SLUG_SETUP_ENABLED" app/future/apps/\[slug\]/setup
checkRoute "$APP_ROUTER_APPS_CATEGORIES_ENABLED" app/future/apps/categories
checkRoute "$APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED" app/future/apps/categories/\[category\]
checkRoute "$APP_ROUTER_WORKFLOWS_ENABLED" app/future/workflows
checkRoute "$APP_ROUTER_SETTINGS_TEAMS_ENABLED" app/future/settings/teams
checkRoute "$APP_ROUTER_GETTING_STARTED_STEP_ENABLED" app/future/getting-started
checkRoute "$APP_ROUTER_APPS_ENABLED" app/future/apps
checkRoute "$APP_ROUTER_BOOKINGS_STATUS_ENABLED" app/future/bookings
checkRoute "$APP_ROUTER_VIDEO_ENABLED" app/future/video
checkRoute "$APP_ROUTER_TEAMS_ENABLED" app/future/teams
# These are routes that don't have and environment variable to enable or disable them
# Will stop removing gradually as we test and confirm that they are working
rm -rf \
app/future/[user]\
app/future/auth\
app/future/booking\
app/future/connect-and-join\
app/future/d\
app/future/enterprise\
app/future/insights\
app/future/maintenance\
app/future/more\
app/future/org\
app/future/payment\
app/future/reschedule\
app/future/routing-forms\
app/future/settings\
app/future/signup\
app/future/team
exit 1
@@ -14,6 +14,7 @@ import closecom_config_json from "./closecom/config.json";
import cron_config_json from "./cron/config.json";
import { metadata as dailyvideo__metadata_ts } from "./dailyvideo/_metadata";
import deel_config_json from "./deel/config.json";
import demodesk_config_json from "./demodesk/config.json";
import discord_config_json from "./discord/config.json";
import eightxeight_config_json from "./eightxeight/config.json";
import element_call_config_json from "./element-call/config.json";
@@ -97,6 +98,7 @@ export const appStoreMetadata = {
cron: cron_config_json,
dailyvideo: dailyvideo__metadata_ts,
deel: deel_config_json,
demodesk: demodesk_config_json,
discord: discord_config_json,
eightxeight: eightxeight_config_json,
"element-call": element_call_config_json,
@@ -14,6 +14,7 @@ export const apiHandlers = {
closecom: import("./closecom/api"),
cron: import("./cron/api"),
deel: import("./deel/api"),
demodesk: import("./demodesk/api"),
discord: import("./discord/api"),
eightxeight: import("./eightxeight/api"),
"element-call": import("./element-call/api"),
@@ -5,6 +5,7 @@
import around_config_json from "./around/config.json";
import campfire_config_json from "./campfire/config.json";
import { metadata as dailyvideo__metadata_ts } from "./dailyvideo/_metadata";
import demodesk_config_json from "./demodesk/config.json";
import discord_config_json from "./discord/config.json";
import eightxeight_config_json from "./eightxeight/config.json";
import element_call_config_json from "./element-call/config.json";
@@ -41,6 +42,7 @@ export const appStoreMetadata = {
around: around_config_json,
campfire: campfire_config_json,
dailyvideo: dailyvideo__metadata_ts,
demodesk: demodesk_config_json,
discord: discord_config_json,
eightxeight: eightxeight_config_json,
"element-call": element_call_config_json,
@@ -0,0 +1,30 @@
---
items:
- iframe: { src: https://www.youtube.com/embed/X6XVzJ0E8xY }
- 1.jpeg
- 2.webp
- 3.webp
- 4.webp
---
{DESCRIPTION}
### AI superpowers for your sales meetings
#### Coach and onboard sellers with AI-based insights
- Automatically record all meetings, GDPR & MiFID compliant
- Transcribe and summarize meetings using Demodesk’s AI
- Coach teams along conversational metrics and scorecards
- Automatically log key information into your CRM
#### Schedule more customer meetings, on auto-pilot
- Let customers schedule time without email back-and-forth
- Qualify website leads and route them to the right person
- Automate and control handoff processes in your team
- Automate admin work and sync interactions to your CRM
#### Run secure video meetings that convert into revenue
- Host professional, secure video meetings without downloads
- Engage customers with interactive screen sharing
- Coach sellers in real-time along standardized playbooks
- Automate preparation, follow-up and CRM documentation
+16
View File
@@ -0,0 +1,16 @@
import { createDefaultInstallation } from "@calcom/app-store/_utils/installation";
import type { AppDeclarativeHandler } from "@calcom/types/AppHandler";
import appConfig from "../config.json";
const handler: AppDeclarativeHandler = {
appType: appConfig.type,
variant: appConfig.variant,
slug: appConfig.slug,
supportsMultipleInstalls: false,
handlerType: "add",
createCredential: ({ appType, user, slug, teamId }) =>
createDefaultInstallation({ appType, user: user, slug, key: {}, teamId }),
};
export default handler;
+1
View File
@@ -0,0 +1 @@
export { default as add } from "./add";
+25
View File
@@ -0,0 +1,25 @@
{
"/*": "Don't modify slug - If required, do it using cli edit command",
"name": "Demodesk",
"slug": "demodesk",
"type": "demodesk_conferencing",
"logo": "icon.svg",
"url": "https://demodesk.com",
"variant": "conferencing",
"categories": ["conferencing"],
"publisher": "Cal.com, Inc.",
"email": "support@cal.com",
"appData": {
"location": {
"type": "integrations:{SLUG}_video",
"label": "{TITLE}",
"linkType": "static",
"organizerInputPlaceholder": "https://demodesk.com/meet/mylink",
"urlRegExp": "^http(s)?:\\/\\/(www\\.)?demodesk.com\\/[a-zA-Z0-9]*"
}
},
"description": "Run Professional Video Meetings, Coach Sales Teams in Real-Time with AI, And Schedule Meetings on Auto-Pilot. 100% GDPR Compliant, Enterprise Ready.",
"isTemplate": false,
"__createdUsingCli": true,
"__template": "event-type-location-video-static"
}
+1
View File
@@ -0,0 +1 @@
export * as api from "./api";
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"name": "@calcom/demodesk",
"version": "0.0.1",
"main": "./index.ts",
"dependencies": {
"@calcom/lib": "*"
},
"devDependencies": {
"@calcom/types": "*"
},
"description": "Run Professional Video Meetings, Coach Sales Teams in Real-Time with AI, And Schedule Meetings on Auto-Pilot. 100% GDPR Compliant, Enterprise Ready."
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@@ -0,0 +1,4 @@
<svg width="2484" height="2484" viewBox="0 0 2484 2484" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="2484" height="2484" rx="200" fill="#DDDDDD"/>
<path d="M1126.24 514.004H687.083C663.823 514.004 645 532.827 645 556.088V656.4C645 679.661 663.823 698.484 687.083 698.484H1109.75C1451.81 698.484 1699.98 941.881 1699.98 1241.1C1699.98 1365.7 1657.75 1480.59 1586.85 1572.29C1599.97 1525.62 1607.02 1476.42 1607.02 1425.58C1607.02 1126.36 1418.26 882.963 1025.89 882.963H687.083C663.823 882.963 645 901.786 645 925.047V1025.36C645 1048.62 663.823 1067.44 687.083 1067.44H1019.19C1270.01 1068.59 1412.48 1228.09 1412.48 1425.58C1412.48 1623.06 1270.01 1783.75 1019.19 1783.75H850.096V1294.04C850.096 1270.79 831.274 1251.96 808.014 1251.96H687.083C663.823 1251.96 645 1270.79 645 1294.04V1926.15C645 1949.4 663.823 1968.23 687.083 1968.23H1064.45H1064.53H1126.28C1579.28 1968.23 1894.59 1642.04 1894.59 1241.13C1894.59 840.229 1607.05 512.703 1126.24 514.004Z" fill="#2B2D28"/>
</svg>

After

Width:  |  Height:  |  Size: 998 B

@@ -21,6 +21,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
switchChecked={enabled}
teamId={eventType.team?.id || undefined}>
<TextField
dataTestid={app.slug}
name="Tracking ID"
value={trackingId}
disabled={disabled}
@@ -21,6 +21,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
switchChecked={enabled}
teamId={eventType.team?.id || undefined}>
<TextField
dataTestid={app.slug}
name="Tracking ID"
value={trackingId}
disabled={disabled}
@@ -264,6 +264,7 @@ export default class GoogleCalendarService implements Calendar {
calendarId: selectedCalendar,
eventId: event.id || "",
requestBody: {
location: getLocation(calEventRaw),
description: getRichDescription({
...calEventRaw,
}),
@@ -21,6 +21,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
switchChecked={enabled}
teamId={eventType.team?.id || undefined}>
<TextField
dataTestid={app.slug}
name="Tracking ID"
value={trackingId}
disabled={disabled}
@@ -2,6 +2,7 @@
"name": "ICS Feed",
"title": "ICS Feed",
"slug": "ics-feed",
"dirName":"ics-feedcalendar",
"type": "ics-feed_calendar",
"logo": "icon.svg",
"variant": "calendar",
@@ -23,6 +23,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
teamId={eventType.team?.id || undefined}>
<div className="flex flex-col gap-2">
<TextField
dataTestid={`${app.slug}-url`}
name="Matomo URL"
placeholder="Enter your Matomo URL here"
value={matomoUrl}
@@ -32,6 +33,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
}}
/>
<TextField
dataTestid={`${app.slug}-site-id`}
disabled={disabled}
name="Site ID"
placeholder="Enter your Site ID"
@@ -19,6 +19,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
switchChecked={enabled}
teamId={eventType.team?.id || undefined}>
<TextField
dataTestid={app.slug}
name="Pixel ID"
value={trackingId}
disabled={disabled}
@@ -23,6 +23,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
teamId={eventType.team?.id || undefined}>
<div className="flex flex-col gap-2">
<TextField
dataTestid={`${app.slug}-url`}
name="Plausible URL"
defaultValue="https://plausible.io/js/script.js"
placeholder="https://plausible.io/js/script.js"
@@ -33,6 +34,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
}}
/>
<TextField
dataTestid={`${app.slug}-tracking-id`}
disabled={disabled}
name="Tracked Domain"
placeholder="yourdomain.com"
@@ -22,7 +22,7 @@ const zeroDecimalCurrencies = [
export const convertToSmallestCurrencyUnit = (amount: number, currency: string) => {
// Special cases
if (zeroDecimalCurrencies.includes(currency)) {
if (zeroDecimalCurrencies.includes(currency.toUpperCase())) {
return amount;
}
return Math.round(amount * 100);
@@ -30,7 +30,7 @@ export const convertToSmallestCurrencyUnit = (amount: number, currency: string)
export const convertFromSmallestToPresentableCurrencyUnit = (amount: number, currency: string) => {
// Special cases
if (zeroDecimalCurrencies.includes(currency)) {
if (zeroDecimalCurrencies.includes(currency.toUpperCase())) {
return amount;
}
return amount / 100;
@@ -0,0 +1,16 @@
export const sanitizeDisplayName = (nameAndEmail: string) => {
const match = nameAndEmail.match(/^(.*?)\s<(.*)>$/);
if (match) {
const sanitizedName = sanitize(match[1]);
return `${sanitizedName} <${match[2]}>`;
}
return nameAndEmail;
};
const sanitize = (name: string) => {
const charsToReplace = /[;,"<>()]/g;
return name.replace(charsToReplace, " ");
};
+13
View File
@@ -9,6 +9,8 @@ import { serverConfig } from "@calcom/lib/serverConfig";
import { setTestEmail } from "@calcom/lib/testEmails";
import prisma from "@calcom/prisma";
import { sanitizeDisplayName } from "../lib/sanitizeDisplayName";
export default class BaseEmail {
name = "";
@@ -46,10 +48,21 @@ export default class BaseEmail {
}
const payload = await this.getNodeMailerPayload();
const from = "from" in payload ? (payload.from as string) : "";
const to = "to" in payload ? (payload.to as string) : "";
const sanitizedFrom = sanitizeDisplayName(from);
const sanitizedTo = sanitizeDisplayName(to);
const parseSubject = z.string().safeParse(payload?.subject);
const payloadWithUnEscapedSubject = {
headers: this.getMailerOptions().headers,
...payload,
...{
from: sanitizedFrom,
to: sanitizedTo,
},
...(parseSubject.success && { subject: decodeHTML(parseSubject.data) }),
};
await new Promise((resolve, reject) =>
@@ -1,5 +1,7 @@
import type { TFunction } from "next-i18next";
import { APP_NAME } from "@calcom/lib/constants";
import { renderEmail } from "..";
import BaseEmail from "./_base-email";
@@ -30,7 +32,7 @@ export default class DisabledAppEmail extends BaseEmail {
protected async getNodeMailerPayload(): Promise<Record<string, unknown>> {
return {
from: `Cal.com <${this.getMailerOptions().from}>`,
from: `${APP_NAME} <${this.getMailerOptions().from}>`,
to: this.email,
subject:
this.title && this.eventTypeId
@@ -1,5 +1,7 @@
import type { TFunction } from "next-i18next";
import { APP_NAME } from "@calcom/lib/constants";
import { renderEmail } from "..";
import BaseEmail from "./_base-email";
@@ -21,7 +23,7 @@ export default class SlugReplacementEmail extends BaseEmail {
protected async getNodeMailerPayload(): Promise<Record<string, unknown>> {
return {
from: `Cal.com <${this.getMailerOptions().from}>`,
from: `${APP_NAME} <${this.getMailerOptions().from}>`,
to: this.email,
subject: this.t("email_subject_slug_replacement", { slug: this.slug }),
html: await renderEmail("SlugReplacementEmail", {
+2 -3
View File
@@ -123,7 +123,8 @@ const BookerComponent = ({
const { bookerFormErrorRef, key, formEmail, bookingForm, errors: formErrors } = bookerForm;
const { handleBookEvent, hasInstantMeetingTokenExpired, errors, loadingStates, expiryTime } = bookings;
const { handleBookEvent, errors, loadingStates, expiryTime } = bookings;
const {
isEmailVerificationModalVisible,
setEmailVerificationModalVisible,
@@ -180,7 +181,6 @@ const BookerComponent = ({
/>
<RedirectToInstantMeetingModal
expiryTime={expiryTime}
hasInstantMeetingTokenExpired={hasInstantMeetingTokenExpired}
bookingId={parseInt(getQueryParam("bookingId") || "0")}
onGoBack={() => {
onGoBackInstantMeeting();
@@ -203,7 +203,6 @@ const BookerComponent = ({
formErrors,
handleBookEvent,
handleVerifyEmail,
hasInstantMeetingTokenExpired,
isEmailVerificationModalVisible,
key,
loadingStates,
@@ -55,13 +55,6 @@ export function Header({
if (isMobile || !enabledLayouts) return null;
// Only reason we create this component, is because it is used 3 times in this component,
// and this way we can't forget to update one of the props in all places :)
const LayoutToggleWithData = () => {
return enabledLayouts.length <= 1 ? null : (
<LayoutToggle onLayoutToggle={onLayoutToggle} layout={layout} enabledLayouts={enabledLayouts} />
);
};
// In month view we only show the layout toggle.
if (isMonthView) {
return (
@@ -78,7 +71,11 @@ export function Header({
) : (
renderOverlay?.()
)}
<LayoutToggleWithData />
<LayoutToggleWithData
layout={layout}
enabledLayouts={enabledLayouts}
onLayoutToggle={onLayoutToggle}
/>
</div>
);
}
@@ -140,7 +137,11 @@ export function Header({
{renderOverlay?.()}
<TimeFormatToggle />
<div className="fixed top-4 ltr:right-4 rtl:left-4">
<LayoutToggleWithData />
<LayoutToggleWithData
layout={layout}
enabledLayouts={enabledLayouts}
onLayoutToggle={onLayoutToggle}
/>
</div>
{/*
This second layout toggle is hidden, but needed to reserve the correct spot in the DIV
@@ -150,7 +151,11 @@ export function Header({
while it actually already was on place. That's why we have this element twice.
*/}
<div className="pointer-events-none opacity-0" aria-hidden>
<LayoutToggleWithData />
<LayoutToggleWithData
layout={layout}
enabledLayouts={enabledLayouts}
onLayoutToggle={onLayoutToggle}
/>
</div>
</div>
</div>
@@ -198,3 +203,17 @@ const LayoutToggle = ({
return <ToggleGroup onValueChange={onLayoutToggle} defaultValue={layout} options={layoutOptions} />;
};
const LayoutToggleWithData = ({
enabledLayouts,
onLayoutToggle,
layout,
}: {
enabledLayouts: BookerLayouts[];
onLayoutToggle: (layout: string) => void;
layout: string;
}) => {
return enabledLayouts.length <= 1 ? null : (
<LayoutToggle onLayoutToggle={onLayoutToggle} layout={layout} enabledLayouts={enabledLayouts} />
);
};
@@ -12,18 +12,17 @@ const handleBeforeUnload = (event: BeforeUnloadEvent) => {
};
export const RedirectToInstantMeetingModal = ({
hasInstantMeetingTokenExpired,
bookingId,
onGoBack,
expiryTime,
}: {
hasInstantMeetingTokenExpired: boolean;
bookingId: number;
onGoBack: () => void;
expiryTime?: Date;
}) => {
const { t } = useLocale();
const [timeRemaining, setTimeRemaining] = useState(calculateTimeRemaining());
const [hasInstantMeetingTokenExpired, setHasInstantMeetingTokenExpired] = useState(false);
function calculateTimeRemaining() {
const now = dayjs();
@@ -37,6 +36,7 @@ export const RedirectToInstantMeetingModal = ({
const timer = setInterval(() => {
setTimeRemaining(calculateTimeRemaining());
setHasInstantMeetingTokenExpired(expiryTime && new Date(expiryTime) < new Date());
}, 1000);
return () => {
@@ -56,7 +56,7 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBo
const { t, i18n } = useLocale();
const bookingSuccessRedirect = useBookingSuccessRedirect();
const bookerFormErrorRef = useRef<HTMLDivElement>(null);
const [expiryTime, setExpiryTime] = useState<Date | undefined>();
const [instantMeetingTokenExpiryTime, setExpiryTime] = useState<Date | undefined>();
const recurringEventCount = useBookerStore((state) => state.recurringEventCount);
const isInstantMeeting = useBookerStore((state) => state.isInstantMeeting);
const duration = useBookerStore((state) => state.selectedDuration);
@@ -66,13 +66,13 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBo
const isRescheduling = !!rescheduleUid && !!bookingData;
const bookingId = parseInt(getQueryParam("bookingId") || "0");
const hasInstantMeetingTokenExpired = expiryTime && new Date(expiryTime) < new Date();
const _instantBooking = trpc.viewer.bookings.getInstantBookingLocation.useQuery(
{
bookingId: bookingId,
},
{
enabled: !!bookingId && !hasInstantMeetingTokenExpired,
enabled: !!bookingId,
refetchInterval: 2000,
refetchIntervalInBackground: true,
}
@@ -80,6 +80,7 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBo
useEffect(
function refactorMeWithoutEffect() {
const data = _instantBooking.data;
if (!data) return;
try {
showToast(t("something_went_wrong_on_our_end"), "error");
@@ -301,11 +302,10 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBo
return {
handleBookEvent,
expiryTime,
expiryTime: instantMeetingTokenExpiryTime,
bookingForm,
bookerFormErrorRef,
errors,
loadingStates,
hasInstantMeetingTokenExpired: Boolean(hasInstantMeetingTokenExpired),
};
};
@@ -2,5 +2,5 @@ import { classNames } from "@calcom/lib";
export function getBookerWrapperClasses({ isEmbed }: { isEmbed: boolean }) {
// We don't want any margins for Embed. Any margin needed should be added by Embed user.
return classNames("flex h-full items-center justify-center", !isEmbed && "min-h-[100dvh]");
return classNames("flex h-full items-center justify-center", !isEmbed && "min-h-[calc(100dvh)]");
}
@@ -1627,6 +1627,19 @@ async function handler(
})
);
// update original rescheduled booking (no seats event)
if (!eventType.seatsPerTimeSlot && originalRescheduledBooking?.uid) {
await prisma.booking.update({
where: {
id: originalRescheduledBooking.id,
},
data: {
rescheduled: true,
status: BookingStatus.CANCELLED,
},
});
}
try {
booking = await createBooking({
originalRescheduledBooking,
@@ -1694,7 +1707,7 @@ async function handler(
err.message
);
if (err.code === "P2002") {
throw new HttpError({ statusCode: 409, message: "booking.conflict" });
throw new HttpError({ statusCode: 409, message: "booking_conflict" });
}
throw err;
}
@@ -1720,20 +1733,7 @@ async function handler(
evt = addVideoCallDataToEvent(originalRescheduledBooking.references, evt);
//update original rescheduled booking (no seats event)
if (!eventType.seatsPerTimeSlot) {
await prisma.booking.update({
where: {
id: originalRescheduledBooking.id,
},
data: {
rescheduled: true,
status: BookingStatus.CANCELLED,
},
});
}
const newDesinationCalendar = evt.destinationCalendar;
const newDestinationCalendar = evt.destinationCalendar;
evt.destinationCalendar = originalRescheduledBooking?.destinationCalendar
? [originalRescheduledBooking?.destinationCalendar]
@@ -1752,7 +1752,7 @@ async function handler(
originalRescheduledBooking.uid,
undefined,
changedOrganizer,
newDesinationCalendar
newDestinationCalendar
);
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
@@ -2291,8 +2291,7 @@ async function handler(
loggerWithEventDetails.error("Error while creating booking references", JSON.stringify({ error }));
}
const metadataFromEvent = videoCallUrl ? { videoCallUrl } : undefined;
const evtWithMetadata = { ...evt, metadata: metadataFromEvent, eventType: { slug: eventType.slug } };
const evtWithMetadata = { ...evt, metadata, eventType: { slug: eventType.slug } };
await scheduleMandatoryReminder(
evtWithMetadata,
@@ -111,7 +111,7 @@ export function EventList({ day }: Props) {
<div
key={`${event.id}-${eventStart.toISOString()}`}
className="absolute inset-x-1 "
data-testId={event.options?.["data-test-id"]}
data-testid={event.options?.["data-test-id"]}
style={{
marginLeft,
zIndex,
@@ -198,6 +198,7 @@ async function isReturningToSelf({ session, creds }: { session: Session | null;
role: true,
organizationId: true,
locale: true,
profiles: true,
teams: {
where: {
accepted: true, // Ensure they are apart of the team and not just invited.
@@ -213,7 +214,10 @@ async function isReturningToSelf({ session, creds }: { session: Session | null;
if (returningUser) {
// Skip for none org users
if (returningUser.role !== "ADMIN" && !returningUser.organizationId) return;
const inOrg =
returningUser.organizationId || // Keep for backwards compatability
returningUser.profiles.some((profile) => profile.organizationId !== undefined); // New way of seeing if the user has a profile in orgs.
if (returningUser.role !== "ADMIN" && !inOrg) return;
const hasTeams = returningUser.teams.length >= 1;
@@ -116,7 +116,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
switch (reminder.workflowStep.action) {
case WorkflowActions.EMAIL_HOST:
sendTo = reminder.booking.user?.email;
sendTo = reminder.booking?.userPrimaryEmail ?? reminder.booking.user?.email;
break;
case WorkflowActions.EMAIL_ATTENDEE:
sendTo = reminder.booking.attendees[0].email;
@@ -227,7 +227,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
html: emailContent.emailBody,
batchId: batchId,
sendAt: dayjs(reminder.scheduledDate).unix(),
replyTo: reminder.booking.user?.email,
replyTo: reminder.booking?.userPrimaryEmail ?? reminder.booking.user?.email,
attachments: reminder.workflowStep.includeCalendarEvent
? [
{
@@ -297,7 +297,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
html: emailContent.emailBody,
batchId: batchId,
sendAt: dayjs(reminder.scheduledDate).unix(),
replyTo: reminder.booking.user?.email,
replyTo: reminder.booking?.userPrimaryEmail ?? reminder.booking.user?.email,
},
{ sender: reminder.workflowStep?.sender }
)
@@ -23,6 +23,7 @@ type PartialBooking =
| "responses"
| "uid"
| "attendees"
| "userPrimaryEmail"
> & { eventType: Partial<EventType> | null } & { user: Partial<User> | null })
| null;
@@ -134,6 +135,7 @@ export async function getAllUnscheduledReminders(): Promise<PartialWorkflowRemin
endTime: true,
location: true,
description: true,
userPrimaryEmail: true,
user: {
select: {
email: true,
@@ -5,6 +5,7 @@ import type { DateArray } from "ics";
import { RRule } from "rrule";
import { v4 as uuidv4 } from "uuid";
import { guessEventLocationType } from "@calcom/app-store/locations";
import dayjs from "@calcom/dayjs";
import { preprocessNameFieldDataWithVariant } from "@calcom/features/form-builder/utils";
import logger from "@calcom/lib/logger";
@@ -33,6 +34,12 @@ function getiCalEventAsString(evt: BookingInfo, status?: ParticipationStatus) {
recurrenceRule = new RRule(evt.eventType.recurringEvent).toString().replace("RRULE:", "");
}
let location = bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl;
if (!location) {
location = guessEventLocationType(location)?.label || evt.location || "";
}
const icsEvent = createEvent({
uid,
startInputType: "utc",
@@ -44,7 +51,7 @@ function getiCalEventAsString(evt: BookingInfo, status?: ParticipationStatus) {
duration: { minutes: dayjs(evt.endTime).diff(dayjs(evt.startTime), "minute") },
title: evt.title,
description: evt.additionalNotes || "",
location: evt.location || "",
location,
organizer: { email: evt.organizer.email || "", name: evt.organizer.name },
attendees: [
{
+5 -2
View File
@@ -462,8 +462,10 @@ function UserDropdown({ small }: UserDropdownProps) {
</span>
{!small && (
<span className="flex flex-grow items-center gap-2">
<span className="line-clamp-1 flex-grow text-sm leading-none">
<span className="text-emphasis block font-medium">{user.name || "Nameless User"}</span>
<span className="w-24 flex-shrink-0 text-sm leading-none">
<span className="text-emphasis block truncate font-medium">
{user.name || "Nameless User"}
</span>
</span>
<ChevronDown
className="group-hover:text-subtle text-muted h-4 w-4 flex-shrink-0 rtl:mr-4"
@@ -729,6 +731,7 @@ const NavigationItem: React.FC<{
<Fragment>
<Tooltip side="right" content={t(item.name)} className="lg:hidden">
<Link
data-test-id={item.name}
href={item.href}
aria-label={t(item.name)}
className={classNames(
+1 -1
View File
@@ -83,7 +83,7 @@ export const SEO_IMG_DEFAULT = `${CAL_URL}/og-image.png`;
// parameters you pass to the /api/social/og/image endpoint, you wrap them in encodeURIComponent
// as well, otherwise the URL won't be valid.
export const SEO_IMG_OGIMG = `${CAL_URL}/_next/image?w=1200&q=100&url=${encodeURIComponent(
`${CAL_URL}/api/social/og/image`
`/api/social/og/image`
)}`;
export const SEO_IMG_OGIMG_VIDEO = `${CAL_URL}/video-og-image.png`;
export const IS_STRIPE_ENABLED = !!(
+5 -2
View File
@@ -1,11 +1,14 @@
import { convertFromSmallestToPresentableCurrencyUnit } from "@calcom/app-store/stripepayment/lib/currencyConversions";
export const formatPrice = (price: number, currency: string | undefined, locale = "en") => {
switch (currency) {
case "BTC":
return `${price} sats`;
default:
currency = currency?.toUpperCase() || "USD";
return `${Intl.NumberFormat(locale, {
style: "currency",
currency: currency?.toUpperCase() || "USD",
}).format(price / 100.0)}`;
currency: currency,
}).format(convertFromSmallestToPresentableCurrencyUnit(price, currency))}`;
}
};
+1
View File
@@ -38,6 +38,7 @@ export const buildBooking = (booking?: Partial<Booking>): Booking => {
uid,
userId: null,
eventTypeId: null,
idempotencyKey: null,
userPrimaryEmail: null,
title: faker.lorem.sentence(),
description: faker.lorem.paragraph(),
@@ -0,0 +1,38 @@
import { Prisma } from "@prisma/client";
import { v5 as uuidv5 } from "uuid";
import { BookingStatus } from "@calcom/prisma/enums";
export function bookingIdempotencyKeyExtension() {
return Prisma.defineExtension({
query: {
booking: {
async create({ args, query }) {
const uniqueEmailJoinInput: string[] = [];
if (args.data.attendees?.create && !Array.isArray(args.data.attendees?.create)) {
uniqueEmailJoinInput.push(args.data.attendees?.create.email);
}
if (args.data.attendees?.createMany && Array.isArray(args.data.attendees?.createMany.data)) {
uniqueEmailJoinInput.push(...args.data.attendees?.createMany.data.map((record) => record.email));
}
const idempotencyKey = uuidv5(
`${
args.data.eventType?.connect?.id
}.${args.data.startTime.valueOf()}.${args.data.endTime.valueOf()}.${uniqueEmailJoinInput.join(
","
)}`,
uuidv5.URL
);
args.data.idempotencyKey = idempotencyKey;
return query(args);
},
async update({ args, query }) {
if (args.data.status === BookingStatus.CANCELLED || args.data.status === BookingStatus.REJECTED) {
args.data.idempotencyKey = null;
}
return query(args);
},
},
},
});
}
+3
View File
@@ -2,6 +2,7 @@ import type { Prisma } from "@prisma/client";
import { PrismaClient as PrismaClientWithoutExtension } from "@prisma/client";
import { withAccelerate } from "@prisma/extension-accelerate";
import { bookingIdempotencyKeyExtension } from "./extensions/booking-idempotency-key";
import { excludePendingPaymentsExtension } from "./extensions/exclude-pending-payment-teams";
import { bookingReferenceMiddleware } from "./middleware";
@@ -21,6 +22,7 @@ const prismaWithoutClientExtensions =
export const customPrisma = (options?: Prisma.PrismaClientOptions) =>
new PrismaClientWithoutExtension({ ...prismaOptions, ...options })
.$extends(excludePendingPaymentsExtension())
.$extends(bookingIdempotencyKeyExtension())
.$extends(withAccelerate());
// If any changed on middleware server restart is required
@@ -31,6 +33,7 @@ bookingReferenceMiddleware(prismaWithoutClientExtensions);
// Specifically we get errors like `Type 'string | Date | null | undefined' is not assignable to type 'Exact<string | Date | null | undefined, string | Date | null | undefined>'`
const prismaWithClientExtensions = prismaWithoutClientExtensions
.$extends(excludePendingPaymentsExtension())
.$extends(bookingIdempotencyKeyExtension())
.$extends(withAccelerate());
export const prisma = globalForPrisma.prismaWithClientExtensions || prismaWithClientExtensions;
@@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[idempotencyKey]` on the table `Booking` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "Booking" ADD COLUMN "idempotencyKey" TEXT;
-- CreateIndex
CREATE UNIQUE INDEX "Booking_idempotencyKey_key" ON "Booking"("idempotencyKey");
+2
View File
@@ -462,6 +462,8 @@ enum BookingStatus {
model Booking {
id Int @id @default(autoincrement())
uid String @unique
// (optional) UID based on slot start/end time & email against duplicates
idempotencyKey String? @unique
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int?
// User's email at the time of booking
+17 -6
View File
@@ -38,13 +38,9 @@ export async function createUserAndEventType({
appId: string;
} | null)[];
}) {
const { password, ...restOfUser } = user;
const userData = {
...user,
password: {
create: {
hash: await hashPassword(user.password),
},
},
...restOfUser,
emailVerified: new Date(),
completedOnboarding: user.completedOnboarding ?? true,
locale: "en",
@@ -69,6 +65,21 @@ export async function createUserAndEventType({
create: userData,
});
await prisma.userPassword.upsert({
where: { userId: theUser.id },
update: {
hash: await hashPassword(user.password),
},
create: {
hash: await hashPassword(user.password),
user: {
connect: {
id: theUser.id,
},
},
},
});
console.log(
`👤 Upserted '${user.username}' with email "${user.email}" & password "${user.password}". Booking page 👉 ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}`
);
@@ -18,6 +18,7 @@ import { performance } from "@calcom/lib/server/perfObserver";
import { UserRepository } from "@calcom/lib/server/repository/user";
import getSlots from "@calcom/lib/slots";
import prisma, { availabilityUserSelect } from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import { SchedulingType } from "@calcom/prisma/enums";
import { BookingStatus } from "@calcom/prisma/enums";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
@@ -269,6 +270,31 @@ export function getRegularOrDynamicEventType(
: getEventType(input, organizationDetails);
}
const selectSelectedSlots = Prisma.validator<Prisma.SelectedSlotsDefaultArgs>()({
select: {
id: true,
slotUtcStartDate: true,
slotUtcEndDate: true,
userId: true,
isSeat: true,
eventTypeId: true,
},
});
type SelectedSlots = Prisma.SelectedSlotsGetPayload<typeof selectSelectedSlots>;
function applyOccupiedSeatsToCurrentSeats(currentSeats: CurrentSeats, occupiedSeats: SelectedSlots[]) {
const occupiedSeatsCount = countBy(occupiedSeats, (item) => item.slotUtcStartDate.toISOString());
Object.keys(occupiedSeatsCount).forEach((date) => {
currentSeats.push({
uid: uuid(),
startTime: dayjs(date).toDate(),
_count: { attendees: occupiedSeatsCount[date] },
});
});
return currentSeats;
}
export async function getAvailableSlots({ input, ctx }: GetScheduleOptions) {
const orgDetails = orgDomainConfig(ctx?.req);
if (process.env.INTEGRATION_TEST_MODE === "true") {
@@ -494,14 +520,7 @@ export async function getAvailableSlots({ input, ctx }: GetScheduleOptions) {
userId: { in: usersWithCredentials.map((user) => user.id) },
releaseAt: { gt: dayjs.utc().format() },
},
select: {
id: true,
slotUtcStartDate: true,
slotUtcEndDate: true,
userId: true,
isSeat: true,
eventTypeId: true,
},
...selectSelectedSlots,
})) || [];
await prisma.selectedSlots.deleteMany({
where: { eventTypeId: { equals: eventType.id }, id: { notIn: selectedSlots.map((item) => item.id) } },
@@ -534,16 +553,11 @@ export async function getAvailableSlots({ input, ctx }: GetScheduleOptions) {
);
}
if (occupiedSeats?.length && typeof availabilityCheckProps.currentSeats === undefined)
availabilityCheckProps.currentSeats = [];
const occupiedSeatsCount = countBy(occupiedSeats, (item) => item.slotUtcStartDate.toISOString());
Object.keys(occupiedSeatsCount).forEach((date) => {
(availabilityCheckProps.currentSeats as CurrentSeats).push({
uid: uuid(),
startTime: dayjs(date).toDate(),
_count: { attendees: occupiedSeatsCount[date] },
});
});
availabilityCheckProps.currentSeats = applyOccupiedSeatsToCurrentSeats(
availabilityCheckProps.currentSeats || [],
occupiedSeats
);
currentSeats = availabilityCheckProps.currentSeats;
}
availableTimeSlots = availableTimeSlots
@@ -23,17 +23,20 @@ export const deleteHandler = async ({ ctx, input }: DeleteOptions) => {
if (IS_TEAM_BILLING_ENABLED) await cancelTeamSubscriptionFromStripe(input.teamId);
// delete all memberships
await prisma.membership.deleteMany({
where: {
teamId: input.teamId,
},
});
const deletedTeam = await prisma.$transaction(async (tx) => {
// delete all memberships
await tx.membership.deleteMany({
where: {
teamId: input.teamId,
},
});
const deletedTeam = await prisma.team.delete({
where: {
id: input.teamId,
},
const deletedTeam = await tx.team.delete({
where: {
id: input.teamId,
},
});
return deletedTeam;
});
const deletedTeamMetadata = teamMetadataSchema.parse(deletedTeam.metadata);
@@ -285,7 +285,7 @@ export async function createNewUsersConnectToOrgIfExists({
data: {
teamId: parentId,
userId: createdUser.id,
role: input.role as MembershipRole,
role: MembershipRole.MEMBER,
accepted: autoAccept,
},
});
@@ -332,7 +332,7 @@ export async function createMemberships({
accepted,
teamId: parentId,
userId: invitee.id,
role: input.role as MembershipRole,
role: MembershipRole.MEMBER,
});
}
return data;
@@ -43,25 +43,26 @@ export const removeMemberHandler = async ({ ctx, input }: RemoveMemberOptions) =
message: "You can not remove yourself from a team you own.",
});
const membership = await ctx.prisma.membership.delete({
where: {
userId_teamId: { userId: input.memberId, teamId: input.teamId },
},
include: {
user: true,
team: true,
},
});
// remove user as host from team events associated with this membership
await ctx.prisma.host.deleteMany({
where: {
userId: input.memberId,
eventType: {
teamId: input.teamId,
const [membership] = await ctx.prisma.$transaction([
ctx.prisma.membership.delete({
where: {
userId_teamId: { userId: input.memberId, teamId: input.teamId },
},
},
});
include: {
user: true,
team: true,
},
}),
// remove user as host from team events associated with this membership
ctx.prisma.host.deleteMany({
where: {
userId: input.memberId,
eventType: {
teamId: input.teamId,
},
},
}),
]);
if (input.isOrg) {
log.debug("Removing a member from the organization");
+1 -1
View File
@@ -32,7 +32,7 @@ function OrganizationIndicator({
return (
<div className={classNames("absolute bottom-0 right-0 z-10", size === "lg" ? "h-6 w-6" : "h-10 w-10")}>
<img
data-testId="organization-logo"
data-testid="organization-logo"
src={organizationUrl}
alt={user.username || ""}
className="flex h-full items-center justify-center rounded-full"
@@ -81,6 +81,7 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function
onClickAddon,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
t: __t,
dataTestid,
...passThrough
} = props;
@@ -114,6 +115,7 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function
</Addon>
)}
<Input
data-testid={`${dataTestid}-input` ?? "input-field"}
id={id}
type={type}
placeholder={placeholder}
+1
View File
@@ -17,6 +17,7 @@ export type InputFieldProps = {
containerClassName?: string;
showAsteriskIndicator?: boolean;
t?: (key: string) => string;
dataTestid?: string;
onClickAddon?: () => void;
} & React.ComponentProps<typeof Input> & {
labelProps?: React.ComponentProps<typeof Label>;
@@ -78,7 +78,11 @@ const VerticalTabItem = ({
{props.isExternalLink ? <ExternalLink data-testid="external-link" /> : null}
</span>
{info && (
<Skeleton as="p" title={t(info)} className="max-w-44 mt-1 truncate text-xs font-normal">
<Skeleton
data-testid="apps-info"
as="p"
title={t(info)}
className="max-w-44 mt-1 truncate text-xs font-normal">
{t(info)}
</Skeleton>
)}
+4 -11
View File
@@ -17,21 +17,14 @@ export type PhoneInputProps = {
onChange: (value: string) => void;
};
function BasePhoneInput({ name, className = "", onChange, ...rest }: PhoneInputProps) {
useEffect(() => {
if (!rest.value) {
return;
}
const formattedValue = rest.value.trim().replace(/^\+?/, "+");
onChange(formattedValue);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function BasePhoneInput({ name, className = "", onChange, value, ...rest }: PhoneInputProps) {
const defaultCountry = useDefaultCountry();
return (
<PhoneInput
{...rest}
country={rest.value ? undefined : defaultCountry}
value={value ? value.trim().replace(/^\+?/, "+") : undefined}
country={value ? undefined : defaultCountry}
enableSearch
disableSearchIcon
inputProps={{
+514 -45
View File
@@ -3661,7 +3661,7 @@ __metadata:
swr: ^1.2.2
tailwindcss: ^3.3.3
typescript: ^4.9.4
zod: ^3.22.2
zod: ^3.22.4
languageName: unknown
linkType: soft
@@ -3723,6 +3723,15 @@ __metadata:
languageName: unknown
linkType: soft
"@calcom/demodesk@workspace:packages/app-store/demodesk":
version: 0.0.0-use.local
resolution: "@calcom/demodesk@workspace:packages/app-store/demodesk"
dependencies:
"@calcom/lib": "*"
"@calcom/types": "*"
languageName: unknown
linkType: soft
"@calcom/discord@workspace:packages/app-store/discord":
version: 0.0.0-use.local
resolution: "@calcom/discord@workspace:packages/app-store/discord"
@@ -4083,6 +4092,15 @@ __metadata:
languageName: unknown
linkType: soft
"@calcom/linear@workspace:packages/app-store/linear":
version: 0.0.0-use.local
resolution: "@calcom/linear@workspace:packages/app-store/linear"
dependencies:
"@calcom/lib": "*"
"@calcom/types": "*"
languageName: unknown
linkType: soft
"@calcom/make@workspace:packages/app-store/make":
version: 0.0.0-use.local
resolution: "@calcom/make@workspace:packages/app-store/make"
@@ -4404,6 +4422,7 @@ __metadata:
"@types/node": 16.9.1
"@types/react": 18.0.26
"@types/react-dom": ^18.0.9
"@types/swagger-ui-react": ^4.18.3
highlight.js: ^11.6.0
isarray: 2.0.5
next: ^13.5.4
@@ -4748,7 +4767,7 @@ __metadata:
"@radix-ui/react-tabs": ^1.0.0
"@radix-ui/react-tooltip": ^1.0.0
"@stripe/stripe-js": ^1.35.0
"@tanstack/react-query": ^4.3.9
"@tanstack/react-query": ^5.17.15
"@typeform/embed-react": ^1.2.4
"@types/bcryptjs": ^2.4.2
"@types/debounce": ^1.2.1
@@ -4777,6 +4796,7 @@ __metadata:
fathom-client: ^3.5.0
globby: ^13.1.3
graphql: ^16.8.0
graphql-codegen: ^0.4.0
graphql-request: ^6.1.0
gray-matter: ^4.0.3
gsap: ^3.11.0
@@ -4819,7 +4839,7 @@ __metadata:
typescript: ^4.9.4
wait-on: ^7.0.1
xml2js: ^0.6.0
zod: ^3.22.2
zod: ^3.22.4
languageName: unknown
linkType: soft
@@ -13041,13 +13061,6 @@ __metadata:
languageName: node
linkType: hard
"@tanstack/query-core@npm:4.36.1":
version: 4.36.1
resolution: "@tanstack/query-core@npm:4.36.1"
checksum: 47672094da20d89402d9fe03bb7b0462be73a76ff9ca715169738bc600a719d064d106d083a8eedae22a2c22de22f87d5eb5d31ef447aba771d9190f2117ed10
languageName: node
linkType: hard
"@tanstack/query-core@npm:5.17.19":
version: 5.17.19
resolution: "@tanstack/query-core@npm:5.17.19"
@@ -13055,25 +13068,6 @@ __metadata:
languageName: node
linkType: hard
"@tanstack/react-query@npm:^4.3.9":
version: 4.36.1
resolution: "@tanstack/react-query@npm:4.36.1"
dependencies:
"@tanstack/query-core": 4.36.1
use-sync-external-store: ^1.2.0
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
react-native: "*"
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
checksum: 1aff0a476859386f8d32253fa0d0bde7b81769a6d4d4d9cbd78778f0f955459a3bdb7ee27a0d2ee7373090f12998b45df80db0b5b313bd0a7a39d36c6e8e51c5
languageName: node
linkType: hard
"@tanstack/react-query@npm:^5.17.15":
version: 5.17.19
resolution: "@tanstack/react-query@npm:5.17.19"
@@ -14481,6 +14475,15 @@ __metadata:
languageName: node
linkType: hard
"@types/swagger-ui-react@npm:^4.18.3":
version: 4.18.3
resolution: "@types/swagger-ui-react@npm:4.18.3"
dependencies:
"@types/react": "*"
checksum: 4927314f1b0d68edf200ef15bca7555f12ec1bb8cc699fa397ef2f4e1e1873d17880b663bbc6d70e30149e50c3e7055178c1e0aad110520dfb31d781ab326dcb
languageName: node
linkType: hard
"@types/testing-library__jest-dom@npm:^5.9.1":
version: 5.14.8
resolution: "@types/testing-library__jest-dom@npm:5.14.8"
@@ -16543,6 +16546,16 @@ __metadata:
languageName: node
linkType: hard
"babel-runtime@npm:^6.11.6":
version: 6.26.0
resolution: "babel-runtime@npm:6.26.0"
dependencies:
core-js: ^2.4.0
regenerator-runtime: ^0.11.0
checksum: 8aeade94665e67a73c1ccc10f6fd42ba0c689b980032b70929de7a6d9a12eb87ef51902733f8fefede35afea7a5c3ef7e916a64d503446c1eedc9e3284bd3d50
languageName: node
linkType: hard
"bail@npm:^2.0.0":
version: 2.0.2
resolution: "bail@npm:2.0.2"
@@ -17288,6 +17301,16 @@ __metadata:
languageName: node
linkType: hard
"camel-case@npm:^3.0.0":
version: 3.0.0
resolution: "camel-case@npm:3.0.0"
dependencies:
no-case: ^2.2.0
upper-case: ^1.1.1
checksum: 4190ed6ab8acf4f3f6e1a78ad4d0f3f15ce717b6bfa1b5686d58e4bcd29960f6e312dd746b5fa259c6d452f1413caef25aee2e10c9b9a580ac83e516533a961a
languageName: node
linkType: hard
"camel-case@npm:^4.1.2":
version: 4.1.2
resolution: "camel-case@npm:4.1.2"
@@ -17323,6 +17346,13 @@ __metadata:
languageName: node
linkType: hard
"camelcase@npm:^3.0.0":
version: 3.0.0
resolution: "camelcase@npm:3.0.0"
checksum: ae4fe1c17c8442a3a345a6b7d2393f028ab7a7601af0c352ad15d1ab97ca75112e19e29c942b2a214898e160194829b68923bce30e018d62149c6d84187f1673
languageName: node
linkType: hard
"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1":
version: 5.3.1
resolution: "camelcase@npm:5.3.1"
@@ -17527,6 +17557,32 @@ __metadata:
languageName: node
linkType: hard
"change-case@npm:^3.0.0":
version: 3.1.0
resolution: "change-case@npm:3.1.0"
dependencies:
camel-case: ^3.0.0
constant-case: ^2.0.0
dot-case: ^2.1.0
header-case: ^1.0.0
is-lower-case: ^1.1.0
is-upper-case: ^1.1.0
lower-case: ^1.1.1
lower-case-first: ^1.0.0
no-case: ^2.3.2
param-case: ^2.1.0
pascal-case: ^2.0.0
path-case: ^2.1.0
sentence-case: ^2.1.0
snake-case: ^2.1.0
swap-case: ^1.1.0
title-case: ^2.1.0
upper-case: ^1.1.1
upper-case-first: ^1.1.0
checksum: d6f9f90a5f1d2a98294e06ea62f913fa0d7cfc289f188bf05662344da6128f5710b5c99ece83682c6a848db8d996b7348e09b2235dc3363afb6ae7142e7978e1
languageName: node
linkType: hard
"change-case@npm:^4.1.2":
version: 4.1.2
resolution: "change-case@npm:4.1.2"
@@ -17919,6 +17975,17 @@ __metadata:
languageName: node
linkType: hard
"cliui@npm:^3.2.0":
version: 3.2.0
resolution: "cliui@npm:3.2.0"
dependencies:
string-width: ^1.0.1
strip-ansi: ^3.0.1
wrap-ansi: ^2.0.0
checksum: c68d1dbc3e347bfe79ed19cc7f48007d5edd6cd8438342e32073e0b4e311e3c44e1f4f19221462bc6590de56c2df520e427533a9dde95dee25710bec322746ad
languageName: node
linkType: hard
"cliui@npm:^6.0.0":
version: 6.0.0
resolution: "cliui@npm:6.0.0"
@@ -18070,6 +18137,13 @@ __metadata:
languageName: node
linkType: hard
"code-point-at@npm:^1.0.0":
version: 1.1.0
resolution: "code-point-at@npm:1.1.0"
checksum: 17d5666611f9b16d64fdf48176d9b7fb1c7d1c1607a189f7e600040a11a6616982876af148230336adb7d8fe728a559f743a4e29db3747e3b1a32fa7f4529681
languageName: node
linkType: hard
"coffeescript@npm:^1.10.0":
version: 1.12.7
resolution: "coffeescript@npm:1.12.7"
@@ -18436,6 +18510,16 @@ __metadata:
languageName: node
linkType: hard
"constant-case@npm:^2.0.0":
version: 2.0.0
resolution: "constant-case@npm:2.0.0"
dependencies:
snake-case: ^2.1.0
upper-case: ^1.1.1
checksum: 893c793a425ebcd0744061c7f12650c655aae259b89d5654fb8eda42d22c3690716a4988ed03f2abe370b1ee7bfec44f8e4395e76e2f1458a8921982b15410ba
languageName: node
linkType: hard
"constant-case@npm:^3.0.4":
version: 3.0.4
resolution: "constant-case@npm:3.0.4"
@@ -18594,6 +18678,13 @@ __metadata:
languageName: node
linkType: hard
"core-js@npm:^2.4.0":
version: 2.6.12
resolution: "core-js@npm:2.6.12"
checksum: 44fa9934a85f8c78d61e0c8b7b22436330471ffe59ec5076fe7f324d6e8cf7f824b14b1c81ca73608b13bdb0fef035bd820989bf059767ad6fa13123bb8bd016
languageName: node
linkType: hard
"core-js@npm:^3":
version: 3.21.1
resolution: "core-js@npm:3.21.1"
@@ -19301,7 +19392,7 @@ __metadata:
languageName: node
linkType: hard
"decamelize@npm:1.2.0, decamelize@npm:^1.1.0, decamelize@npm:^1.2.0":
"decamelize@npm:1.2.0, decamelize@npm:^1.1.0, decamelize@npm:^1.1.1, decamelize@npm:^1.2.0":
version: 1.2.0
resolution: "decamelize@npm:1.2.0"
checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa
@@ -19895,6 +19986,15 @@ __metadata:
languageName: node
linkType: hard
"dot-case@npm:^2.1.0":
version: 2.1.1
resolution: "dot-case@npm:2.1.1"
dependencies:
no-case: ^2.2.0
checksum: 5c9d937245ff810a7ae788602e40c62e38cb515146ddf9b11c7f60cb02aae84859588761f1e8769d9e713609fae3c78dc99c8da9e0ee8e4d8b5c09a2fdf70328
languageName: node
linkType: hard
"dot-case@npm:^3.0.4":
version: 3.0.4
resolution: "dot-case@npm:3.0.4"
@@ -20198,7 +20298,7 @@ __metadata:
languageName: node
linkType: hard
"encoding@npm:0.1.13, encoding@npm:^0.1.13":
"encoding@npm:0.1.13, encoding@npm:^0.1.11, encoding@npm:^0.1.13":
version: 0.1.13
resolution: "encoding@npm:0.1.13"
dependencies:
@@ -20343,7 +20443,7 @@ __metadata:
languageName: node
linkType: hard
"error-ex@npm:^1.3.1":
"error-ex@npm:^1.2.0, error-ex@npm:^1.3.1":
version: 1.3.2
resolution: "error-ex@npm:1.3.2"
dependencies:
@@ -22017,6 +22117,16 @@ __metadata:
languageName: node
linkType: hard
"find-up@npm:^1.0.0":
version: 1.1.2
resolution: "find-up@npm:1.1.2"
dependencies:
path-exists: ^2.0.0
pinkie-promise: ^2.0.0
checksum: a2cb9f4c9f06ee3a1e92ed71d5aed41ac8ae30aefa568132f6c556fac7678a5035126153b59eaec68da78ac409eef02503b2b059706bdbf232668d7245e3240a
languageName: node
linkType: hard
"find-up@npm:^2.1.0":
version: 2.1.0
resolution: "find-up@npm:2.1.0"
@@ -22675,6 +22785,13 @@ __metadata:
languageName: node
linkType: hard
"get-caller-file@npm:^1.0.1":
version: 1.0.3
resolution: "get-caller-file@npm:1.0.3"
checksum: 2b90a7f848896abcebcdc0acc627a435bcf05b9cd280599bc980ebfcdc222416c3df12c24c4845f69adc4346728e8966f70b758f9369f3534182791dfbc25c05
languageName: node
linkType: hard
"get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5":
version: 2.0.5
resolution: "get-caller-file@npm:2.0.5"
@@ -23270,6 +23387,23 @@ __metadata:
languageName: node
linkType: hard
"graphql-codegen@npm:^0.4.0":
version: 0.4.0
resolution: "graphql-codegen@npm:0.4.0"
dependencies:
babel-runtime: ^6.11.6
change-case: ^3.0.0
graphql: ^0.7.0
inflected: ^1.1.7
mkdirp: ^0.5.1
node-fetch: ^1.5.3
yargs: ^5.0.0
bin:
graphql-codegen: ./lib/cli.js
checksum: 584ad3382de9ee3927cfd4fcc340abb2f8d4bbdacacebe9e56e34edf255c87eb1e191f2d7c4f3d1d5bb0ebd829b852e50efe91e967683a8b181dcd3b26ae6b6c
languageName: node
linkType: hard
"graphql-config@npm:^5.0.2":
version: 5.0.3
resolution: "graphql-config@npm:5.0.3"
@@ -23327,6 +23461,15 @@ __metadata:
languageName: node
linkType: hard
"graphql@npm:^0.7.0":
version: 0.7.2
resolution: "graphql@npm:0.7.2"
dependencies:
iterall: 1.0.2
checksum: 9b815b851d4fbc861609ce37e88e0be881d25fa2d5dc54f661379e600d103d4f051cee15a09799c4efa2d3590a61f64fc2e1d67332c7fdf8df2871c27b1c3f40
languageName: node
linkType: hard
"graphql@npm:^16.3.0":
version: 16.5.0
resolution: "graphql@npm:16.5.0"
@@ -23739,6 +23882,16 @@ __metadata:
languageName: node
linkType: hard
"header-case@npm:^1.0.0":
version: 1.0.1
resolution: "header-case@npm:1.0.1"
dependencies:
no-case: ^2.2.0
upper-case: ^1.1.3
checksum: fe1cc9a555ec9aabc2de80f4dd961a81c534fc23951694fef34297e59b0dd60f26647148731bf0dd3fdb3a1c688089d3cd147d7038db850e25be7c0a5fabb022
languageName: node
linkType: hard
"header-case@npm:^2.0.4":
version: 2.0.4
resolution: "header-case@npm:2.0.4"
@@ -24384,6 +24537,13 @@ __metadata:
languageName: node
linkType: hard
"inflected@npm:^1.1.7":
version: 1.1.7
resolution: "inflected@npm:1.1.7"
checksum: eac857f10871586006d629dc72bab05058f1967c10fae9d7391d627534566a8e5be0a1f690d2590102d890e4ef5647b73b7827380dc7bcf50faac4154f0ab084
languageName: node
linkType: hard
"inflight@npm:^1.0.4":
version: 1.0.6
resolution: "inflight@npm:1.0.6"
@@ -24613,6 +24773,13 @@ __metadata:
languageName: node
linkType: hard
"invert-kv@npm:^1.0.0":
version: 1.0.0
resolution: "invert-kv@npm:1.0.0"
checksum: aebeee31dda3b3d25ffd242e9a050926e7fe5df642d60953ab183aca1a7d1ffb39922eb2618affb0e850cf2923116f0da1345367759d88d097df5da1f1e1590e
languageName: node
linkType: hard
"ip@npm:^2.0.0":
version: 2.0.0
resolution: "ip@npm:2.0.0"
@@ -24906,6 +25073,15 @@ __metadata:
languageName: node
linkType: hard
"is-fullwidth-code-point@npm:^1.0.0":
version: 1.0.0
resolution: "is-fullwidth-code-point@npm:1.0.0"
dependencies:
number-is-nan: ^1.0.0
checksum: 4d46a7465a66a8aebcc5340d3b63a56602133874af576a9ca42c6f0f4bd787a743605771c5f246db77da96605fefeffb65fc1dbe862dcc7328f4b4d03edf5a57
languageName: node
linkType: hard
"is-fullwidth-code-point@npm:^3.0.0":
version: 3.0.0
resolution: "is-fullwidth-code-point@npm:3.0.0"
@@ -25203,7 +25379,7 @@ __metadata:
languageName: node
linkType: hard
"is-stream@npm:^1.1.0":
"is-stream@npm:^1.0.1, is-stream@npm:^1.1.0":
version: 1.1.0
resolution: "is-stream@npm:1.1.0"
checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae
@@ -25294,6 +25470,13 @@ __metadata:
languageName: node
linkType: hard
"is-utf8@npm:^0.2.0":
version: 0.2.1
resolution: "is-utf8@npm:0.2.1"
checksum: 167ccd2be869fc228cc62c1a28df4b78c6b5485d15a29027d3b5dceb09b383e86a3522008b56dcac14b592b22f0a224388718c2505027a994fd8471465de54b3
languageName: node
linkType: hard
"is-weakmap@npm:^2.0.1":
version: 2.0.1
resolution: "is-weakmap@npm:2.0.1"
@@ -25473,6 +25656,13 @@ __metadata:
languageName: node
linkType: hard
"iterall@npm:1.0.2":
version: 1.0.2
resolution: "iterall@npm:1.0.2"
checksum: f87cbb8e22e3681f4d7ba634e4ce7bd419a78b47ff7b01f57f90cf761d187cb845fcac0d4d7e4c7dfcbfb80467c8ea42a9f3691bb3aa909e3e3707a25735ed70
languageName: node
linkType: hard
"jackspeak@npm:^2.0.3, jackspeak@npm:^2.3.5":
version: 2.3.6
resolution: "jackspeak@npm:2.3.6"
@@ -26604,6 +26794,15 @@ __metadata:
languageName: node
linkType: hard
"lcid@npm:^1.0.0":
version: 1.0.0
resolution: "lcid@npm:1.0.0"
dependencies:
invert-kv: ^1.0.0
checksum: e8c7a4db07663068c5c44b650938a2bc41aa992037eebb69376214320f202c1250e70b50c32f939e28345fd30c2d35b8e8cd9a19d5932c398246a864ce54843d
languageName: node
linkType: hard
"leac@npm:^0.6.0":
version: 0.6.0
resolution: "leac@npm:0.6.0"
@@ -26953,6 +27152,19 @@ __metadata:
languageName: node
linkType: hard
"load-json-file@npm:^1.0.0":
version: 1.1.0
resolution: "load-json-file@npm:1.1.0"
dependencies:
graceful-fs: ^4.1.2
parse-json: ^2.2.0
pify: ^2.0.0
pinkie-promise: ^2.0.0
strip-bom: ^2.0.0
checksum: 0e4e4f380d897e13aa236246a917527ea5a14e4fc34d49e01ce4e7e2a1e08e2740ee463a03fb021c04f594f29a178f4adb994087549d7c1c5315fcd29bf9934b
languageName: node
linkType: hard
"load-json-file@npm:^4.0.0":
version: 4.0.0
resolution: "load-json-file@npm:4.0.0"
@@ -27096,6 +27308,13 @@ __metadata:
languageName: node
linkType: hard
"lodash.assign@npm:^4.1.0, lodash.assign@npm:^4.2.0":
version: 4.2.0
resolution: "lodash.assign@npm:4.2.0"
checksum: 75bbc6733c9f577c448031b4051f990f068802708891f94be9d4c2faffd6a9ec67a2c49671dafc908a068d35687765464853282842b4560b662e6c903d11cc90
languageName: node
linkType: hard
"lodash.camelcase@npm:^4.3.0":
version: 4.3.0
resolution: "lodash.camelcase@npm:4.3.0"
@@ -29654,6 +29873,15 @@ __metadata:
languageName: node
linkType: hard
"no-case@npm:^2.2.0, no-case@npm:^2.3.2":
version: 2.3.2
resolution: "no-case@npm:2.3.2"
dependencies:
lower-case: ^1.1.1
checksum: 856487731936fef44377ca74fdc5076464aba2e0734b56a4aa2b2a23d5b154806b591b9b2465faa59bb982e2b5c9391e3685400957fb4eeb38f480525adcf3dd
languageName: node
linkType: hard
"no-case@npm:^3.0.4":
version: 3.0.4
resolution: "no-case@npm:3.0.4"
@@ -29747,6 +29975,16 @@ __metadata:
languageName: node
linkType: hard
"node-fetch@npm:^1.5.3":
version: 1.7.3
resolution: "node-fetch@npm:1.7.3"
dependencies:
encoding: ^0.1.11
is-stream: ^1.0.1
checksum: 3bb0528c05d541316ebe52770d71ee25a6dce334df4231fd55df41a644143e07f068637488c18a5b0c43f05041dbd3346752f9e19b50df50569a802484544d5b
languageName: node
linkType: hard
"node-fetch@npm:^2.0.0, node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.9":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
@@ -30115,6 +30353,13 @@ __metadata:
languageName: node
linkType: hard
"number-is-nan@npm:^1.0.0":
version: 1.0.1
resolution: "number-is-nan@npm:1.0.1"
checksum: 13656bc9aa771b96cef209ffca31c31a03b507ca6862ba7c3f638a283560620d723d52e626d57892c7fff475f4c36ac07f0600f14544692ff595abff214b9ffb
languageName: node
linkType: hard
"nwsapi@npm:^2.2.4":
version: 2.2.4
resolution: "nwsapi@npm:2.2.4"
@@ -30585,6 +30830,15 @@ __metadata:
languageName: node
linkType: hard
"os-locale@npm:^1.4.0":
version: 1.4.0
resolution: "os-locale@npm:1.4.0"
dependencies:
lcid: ^1.0.0
checksum: 0161a1b6b5a8492f99f4b47fe465df9fc521c55ba5414fce6444c45e2500487b8ed5b40a47a98a2363fe83ff04ab033785300ed8df717255ec4c3b625e55b1fb
languageName: node
linkType: hard
"os-tmpdir@npm:~1.0.2":
version: 1.0.2
resolution: "os-tmpdir@npm:1.0.2"
@@ -30810,6 +31064,15 @@ __metadata:
languageName: node
linkType: hard
"param-case@npm:^2.1.0":
version: 2.1.1
resolution: "param-case@npm:2.1.1"
dependencies:
no-case: ^2.2.0
checksum: 3a63dcb8d8dc7995a612de061afdc7bb6fe7bd0e6db994db8d4cae999ed879859fd24389090e1a0d93f4c9207ebf8c048c870f468a3f4767161753e03cb9ab58
languageName: node
linkType: hard
"param-case@npm:^3.0.4":
version: 3.0.4
resolution: "param-case@npm:3.0.4"
@@ -30921,6 +31184,15 @@ __metadata:
languageName: node
linkType: hard
"parse-json@npm:^2.2.0":
version: 2.2.0
resolution: "parse-json@npm:2.2.0"
dependencies:
error-ex: ^1.2.0
checksum: dda78a63e57a47b713a038630868538f718a7ca0cd172a36887b0392ccf544ed0374902eb28f8bf3409e8b71d62b79d17062f8543afccf2745f9b0b2d2bb80ca
languageName: node
linkType: hard
"parse-json@npm:^4.0.0":
version: 4.0.0
resolution: "parse-json@npm:4.0.0"
@@ -31009,6 +31281,16 @@ __metadata:
languageName: node
linkType: hard
"pascal-case@npm:^2.0.0":
version: 2.0.1
resolution: "pascal-case@npm:2.0.1"
dependencies:
camel-case: ^3.0.0
upper-case-first: ^1.1.0
checksum: 4c539bf556572812f64a02fc6b544f3d2b51db12aed484e5162ed7f8ac2b366775d15e536091c890d71d82bdf9153128321f21574721b3a984bd85df9e519a35
languageName: node
linkType: hard
"pascal-case@npm:^3.1.2":
version: 3.1.2
resolution: "pascal-case@npm:3.1.2"
@@ -31052,6 +31334,15 @@ __metadata:
languageName: node
linkType: hard
"path-case@npm:^2.1.0":
version: 2.1.1
resolution: "path-case@npm:2.1.1"
dependencies:
no-case: ^2.2.0
checksum: eb1da508c28378715cbe4ce054ee5f83a570c5010f041f4cfb439c811f7a78e36c46f26a8d59b2594c3882b53db06ef26195519c27f86523dc5d19c2e29f306d
languageName: node
linkType: hard
"path-case@npm:^3.0.4":
version: 3.0.4
resolution: "path-case@npm:3.0.4"
@@ -31062,6 +31353,15 @@ __metadata:
languageName: node
linkType: hard
"path-exists@npm:^2.0.0":
version: 2.1.0
resolution: "path-exists@npm:2.1.0"
dependencies:
pinkie-promise: ^2.0.0
checksum: fdb734f1d00f225f7a0033ce6d73bff6a7f76ea08936abf0e5196fa6e54a645103538cd8aedcb90d6d8c3fa3705ded0c58a4da5948ae92aa8834892c1ab44a84
languageName: node
linkType: hard
"path-exists@npm:^3.0.0":
version: 3.0.0
resolution: "path-exists@npm:3.0.0"
@@ -31151,6 +31451,17 @@ __metadata:
languageName: node
linkType: hard
"path-type@npm:^1.0.0":
version: 1.1.0
resolution: "path-type@npm:1.1.0"
dependencies:
graceful-fs: ^4.1.2
pify: ^2.0.0
pinkie-promise: ^2.0.0
checksum: 59a4b2c0e566baf4db3021a1ed4ec09a8b36fca960a490b54a6bcefdb9987dafe772852982b6011cd09579478a96e57960a01f75fa78a794192853c9d468fc79
languageName: node
linkType: hard
"path-type@npm:^3.0.0":
version: 3.0.0
resolution: "path-type@npm:3.0.0"
@@ -31371,7 +31682,7 @@ __metadata:
languageName: node
linkType: hard
"pify@npm:^2.3.0":
"pify@npm:^2.0.0, pify@npm:^2.3.0":
version: 2.3.0
resolution: "pify@npm:2.3.0"
checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba
@@ -31401,6 +31712,15 @@ __metadata:
languageName: node
linkType: hard
"pinkie-promise@npm:^2.0.0":
version: 2.0.1
resolution: "pinkie-promise@npm:2.0.1"
dependencies:
pinkie: ^2.0.0
checksum: b53a4a2e73bf56b6f421eef711e7bdcb693d6abb474d57c5c413b809f654ba5ee750c6a96dd7225052d4b96c4d053cdcb34b708a86fceed4663303abee52fcca
languageName: node
linkType: hard
"pinkie@npm:^1.0.0":
version: 1.0.0
resolution: "pinkie@npm:1.0.0"
@@ -31408,6 +31728,13 @@ __metadata:
languageName: node
linkType: hard
"pinkie@npm:^2.0.0":
version: 2.0.4
resolution: "pinkie@npm:2.0.4"
checksum: b12b10afea1177595aab036fc220785488f67b4b0fc49e7a27979472592e971614fa1c728e63ad3e7eb748b4ec3c3dbd780819331dad6f7d635c77c10537b9db
languageName: node
linkType: hard
"pirates@npm:^4.0.1, pirates@npm:^4.0.5":
version: 4.0.5
resolution: "pirates@npm:4.0.5"
@@ -33554,6 +33881,27 @@ __metadata:
languageName: node
linkType: hard
"read-pkg-up@npm:^1.0.1":
version: 1.0.1
resolution: "read-pkg-up@npm:1.0.1"
dependencies:
find-up: ^1.0.0
read-pkg: ^1.0.0
checksum: d18399a0f46e2da32beb2f041edd0cda49d2f2cc30195a05c759ef3ed9b5e6e19ba1ad1bae2362bdec8c6a9f2c3d18f4d5e8c369e808b03d498d5781cb9122c7
languageName: node
linkType: hard
"read-pkg@npm:^1.0.0":
version: 1.1.0
resolution: "read-pkg@npm:1.1.0"
dependencies:
load-json-file: ^1.0.0
normalize-package-data: ^2.3.2
path-type: ^1.0.0
checksum: a0f5d5e32227ec8e6a028dd5c5134eab229768dcb7a5d9a41a284ed28ad4b9284fecc47383dc1593b5694f4de603a7ffaee84b738956b9b77e0999567485a366
languageName: node
linkType: hard
"read-pkg@npm:^3.0.0":
version: 3.0.0
resolution: "read-pkg@npm:3.0.0"
@@ -33841,6 +34189,13 @@ __metadata:
languageName: node
linkType: hard
"regenerator-runtime@npm:^0.11.0":
version: 0.11.1
resolution: "regenerator-runtime@npm:0.11.1"
checksum: 3c97bd2c7b2b3247e6f8e2147a002eb78c995323732dad5dc70fac8d8d0b758d0295e7015b90d3d444446ae77cbd24b9f9123ec3a77018e81d8999818301b4f4
languageName: node
linkType: hard
"regenerator-runtime@npm:^0.13.3":
version: 0.13.9
resolution: "regenerator-runtime@npm:0.13.9"
@@ -34172,6 +34527,13 @@ __metadata:
languageName: node
linkType: hard
"require-main-filename@npm:^1.0.1":
version: 1.0.1
resolution: "require-main-filename@npm:1.0.1"
checksum: 1fef30754da961f4e13c450c3eb60c7ae898a529c6ad6fa708a70bd2eed01564ceb299187b2899f5562804d797a059f39a5789884d0ac7b7ae1defc68fba4abf
languageName: node
linkType: hard
"require-main-filename@npm:^2.0.0":
version: 2.0.0
resolution: "require-main-filename@npm:2.0.0"
@@ -34972,6 +35334,16 @@ __metadata:
languageName: node
linkType: hard
"sentence-case@npm:^2.1.0":
version: 2.1.1
resolution: "sentence-case@npm:2.1.1"
dependencies:
no-case: ^2.2.0
upper-case-first: ^1.1.2
checksum: ce5ca48804051e056a6956ad75a1a7d833e5d8f5021a015d380a22d3cf04496d5238de2e5c876d9701a9218633052c3a65911ca1b6460d36a41ecad46e81d139
languageName: node
linkType: hard
"sentence-case@npm:^3.0.4":
version: 3.0.4
resolution: "sentence-case@npm:3.0.4"
@@ -35366,6 +35738,15 @@ __metadata:
languageName: node
linkType: hard
"snake-case@npm:^2.1.0":
version: 2.1.0
resolution: "snake-case@npm:2.1.0"
dependencies:
no-case: ^2.2.0
checksum: 7e42b4841103be4dd050b2f57f5cb423d5164524c1cb3d81efda9809265a82a2d02ddf44361beae37d75a239308e6414be85fe441dc48cd70c708cb975387d10
languageName: node
linkType: hard
"snake-case@npm:^3.0.4":
version: 3.0.4
resolution: "snake-case@npm:3.0.4"
@@ -35895,6 +36276,17 @@ __metadata:
languageName: node
linkType: hard
"string-width@npm:^1.0.1, string-width@npm:^1.0.2":
version: 1.0.2
resolution: "string-width@npm:1.0.2"
dependencies:
code-point-at: ^1.0.0
is-fullwidth-code-point: ^1.0.0
strip-ansi: ^3.0.0
checksum: 5c79439e95bc3bd7233a332c5f5926ab2ee90b23816ed4faa380ce3b2576d7800b0a5bb15ae88ed28737acc7ea06a518c2eef39142dd727adad0e45c776cd37e
languageName: node
linkType: hard
"string-width@npm:^5.0.0, string-width@npm:^5.0.1, string-width@npm:^5.1.2":
version: 5.1.2
resolution: "string-width@npm:5.1.2"
@@ -36086,7 +36478,7 @@ __metadata:
languageName: node
linkType: hard
"strip-ansi@npm:^3.0.0":
"strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1":
version: 3.0.1
resolution: "strip-ansi@npm:3.0.1"
dependencies:
@@ -36111,6 +36503,15 @@ __metadata:
languageName: node
linkType: hard
"strip-bom@npm:^2.0.0":
version: 2.0.0
resolution: "strip-bom@npm:2.0.0"
dependencies:
is-utf8: ^0.2.0
checksum: 08efb746bc67b10814cd03d79eb31bac633393a782e3f35efbc1b61b5165d3806d03332a97f362822cf0d4dd14ba2e12707fcff44fe1c870c48a063a0c9e4944
languageName: node
linkType: hard
"strip-bom@npm:^3.0.0":
version: 3.0.0
resolution: "strip-bom@npm:3.0.0"
@@ -37018,6 +37419,16 @@ __metadata:
languageName: node
linkType: hard
"title-case@npm:^2.1.0":
version: 2.1.1
resolution: "title-case@npm:2.1.1"
dependencies:
no-case: ^2.2.0
upper-case: ^1.0.3
checksum: e88ddfc4608a7fb18ed440139d9c42a5f8a50f916e07062be2aef5e2038720746ed51c4fdf9e7190d24a8cc10e6dec9773027fc44450b3a4a5e5c49b4a931fb1
languageName: node
linkType: hard
"title-case@npm:^3.0.3":
version: 3.0.3
resolution: "title-case@npm:3.0.3"
@@ -38379,7 +38790,7 @@ __metadata:
languageName: node
linkType: hard
"upper-case-first@npm:^1.1.0":
"upper-case-first@npm:^1.1.0, upper-case-first@npm:^1.1.2":
version: 1.1.2
resolution: "upper-case-first@npm:1.1.2"
dependencies:
@@ -38397,7 +38808,7 @@ __metadata:
languageName: node
linkType: hard
"upper-case@npm:^1.0.3, upper-case@npm:^1.1.0, upper-case@npm:^1.1.1":
"upper-case@npm:^1.0.3, upper-case@npm:^1.1.0, upper-case@npm:^1.1.1, upper-case@npm:^1.1.3":
version: 1.1.3
resolution: "upper-case@npm:1.1.3"
checksum: 991c845de75fa56e5ad983f15e58494dd77b77cadd79d273cc11e8da400067e9881ae1a52b312aed79b3d754496e2e0712e08d22eae799e35c7f9ba6f3d8a85d
@@ -38555,7 +38966,7 @@ __metadata:
languageName: node
linkType: hard
"use-sync-external-store@npm:1.2.0, use-sync-external-store@npm:^1.2.0":
"use-sync-external-store@npm:1.2.0":
version: 1.2.0
resolution: "use-sync-external-store@npm:1.2.0"
peerDependencies:
@@ -39299,6 +39710,13 @@ __metadata:
languageName: node
linkType: hard
"which-module@npm:^1.0.0":
version: 1.0.0
resolution: "which-module@npm:1.0.0"
checksum: 98434f7deb36350cb543c1f15612188541737e1f12d39b23b1c371dff5cf4aa4746210f2bdec202d5fe9da8682adaf8e3f7c44c520687d30948cfc59d5534edb
languageName: node
linkType: hard
"which-module@npm:^2.0.0":
version: 2.0.0
resolution: "which-module@npm:2.0.0"
@@ -39381,6 +39799,15 @@ __metadata:
languageName: node
linkType: hard
"window-size@npm:^0.2.0":
version: 0.2.0
resolution: "window-size@npm:0.2.0"
bin:
window-size: cli.js
checksum: a85e2acf156cfa194301294809867bdadd8a48ee5d972d9fa8e3e1b3420a1d0201b13ac8eb0348a0d14bbf2c3316565b6a749749c2384c5d286caf8a064c4f90
languageName: node
linkType: hard
"word-wrap@npm:^1.2.3":
version: 1.2.3
resolution: "word-wrap@npm:1.2.3"
@@ -39406,6 +39833,16 @@ __metadata:
languageName: node
linkType: hard
"wrap-ansi@npm:^2.0.0":
version: 2.1.0
resolution: "wrap-ansi@npm:2.1.0"
dependencies:
string-width: ^1.0.1
strip-ansi: ^3.0.1
checksum: 2dacd4b3636f7a53ee13d4d0fe7fa2ed9ad81e9967e17231924ea88a286ec4619a78288de8d41881ee483f4449ab2c0287cde8154ba1bd0126c10271101b2ee3
languageName: node
linkType: hard
"wrap-ansi@npm:^6.0.1, wrap-ansi@npm:^6.2.0":
version: 6.2.0
resolution: "wrap-ansi@npm:6.2.0"
@@ -39717,6 +40154,13 @@ __metadata:
languageName: node
linkType: hard
"y18n@npm:^3.2.1":
version: 3.2.2
resolution: "y18n@npm:3.2.2"
checksum: 6154fd7544f8bbf5b18cdf77692ed88d389be49c87238ecb4e0d6a5276446cd2a5c29cc4bdbdddfc7e4e498b08df9d7e38df4a1453cf75eecfead392246ea74a
languageName: node
linkType: hard
"y18n@npm:^4.0.0":
version: 4.0.3
resolution: "y18n@npm:4.0.3"
@@ -39825,6 +40269,16 @@ __metadata:
languageName: node
linkType: hard
"yargs-parser@npm:^3.2.0":
version: 3.2.0
resolution: "yargs-parser@npm:3.2.0"
dependencies:
camelcase: ^3.0.0
lodash.assign: ^4.1.0
checksum: d86fd69816a28a617f4cad21f7bfe7f7c931b16d063c73449cd914db409b8d5981a0f29f9e2a05014a7229164aa47336adf5a8856fa9870ab892346d29138e9a
languageName: node
linkType: hard
"yargs@npm:^15.1.0, yargs@npm:^15.3.1":
version: 15.4.1
resolution: "yargs@npm:15.4.1"
@@ -39904,6 +40358,28 @@ __metadata:
languageName: node
linkType: hard
"yargs@npm:^5.0.0":
version: 5.0.0
resolution: "yargs@npm:5.0.0"
dependencies:
cliui: ^3.2.0
decamelize: ^1.1.1
get-caller-file: ^1.0.1
lodash.assign: ^4.2.0
os-locale: ^1.4.0
read-pkg-up: ^1.0.1
require-directory: ^2.1.1
require-main-filename: ^1.0.1
set-blocking: ^2.0.0
string-width: ^1.0.2
which-module: ^1.0.0
window-size: ^0.2.0
y18n: ^3.2.1
yargs-parser: ^3.2.0
checksum: 478e9c8562c5cf5b9c2efc7c917e0b00c489cc50153d5d911ab68767c2cfddaf0b5bd4e04d6fefc00cb1d8f6263eab1d73df79a24d650ba95f5d45c819a1585a
languageName: node
linkType: hard
"yarn@npm:^1.22.18":
version: 1.22.19
resolution: "yarn@npm:1.22.19"
@@ -40040,13 +40516,6 @@ __metadata:
languageName: node
linkType: hard
"zod@npm:^3.22.2":
version: 3.22.2
resolution: "zod@npm:3.22.2"
checksum: 231e2180c8eabb56e88680d80baff5cf6cbe6d64df3c44c50ebe52f73081ecd0229b1c7215b9552537f537a36d9e36afac2737ddd86dc14e3519bdbc777e82b9
languageName: node
linkType: hard
"zod@npm:^3.22.3, zod@npm:^3.22.4":
version: 3.22.4
resolution: "zod@npm:3.22.4"