diff --git a/apps/web/playwright/booking/recurringBooking.e2e.ts b/apps/web/playwright/booking/recurringBooking.e2e.ts new file mode 100644 index 0000000000..5f89ef9849 --- /dev/null +++ b/apps/web/playwright/booking/recurringBooking.e2e.ts @@ -0,0 +1,28 @@ +/* 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); + }); +}); diff --git a/apps/web/playwright/fixtures/regularBookings.ts b/apps/web/playwright/fixtures/regularBookings.ts index 93671b0a59..18f2f0a5d5 100644 --- a/apps/web/playwright/fixtures/regularBookings.ts +++ b/apps/web/playwright/fixtures/regularBookings.ts @@ -2,6 +2,7 @@ import { expect, type Page } from "@playwright/test"; import dayjs from "@calcom/dayjs"; +import { localize } from "../lib/testUtils"; import type { createUsersFixture } from "./users"; const reschedulePlaceholderText = "Let others know why you need to reschedule"; @@ -220,6 +221,23 @@ export function createBookingPageFixture(page: Page) { } 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(); }, @@ -246,6 +264,14 @@ export function createBookingPageFixture(page: Page) { 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"); @@ -279,6 +305,10 @@ export function createBookingPageFixture(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"); diff --git a/packages/features/bookings/components/event-meta/Occurences.tsx b/packages/features/bookings/components/event-meta/Occurences.tsx index fbed0add18..38cbdadcdd 100644 --- a/packages/features/bookings/components/event-meta/Occurences.tsx +++ b/packages/features/bookings/components/event-meta/Occurences.tsx @@ -47,7 +47,7 @@ export const EventOccurences = ({ event }: { event: PublicEvent }) => { i18n.language ); return ( - <> +
{recurringStrings.slice(0, 5).map((timeFormatted, key) => (

{timeFormatted}

))} @@ -59,7 +59,7 @@ export const EventOccurences = ({ event }: { event: PublicEvent }) => {

+ {t("plus_more", { count: recurringStrings.length - 5 })}

)} - +
); } @@ -73,6 +73,7 @@ export const EventOccurences = ({ event }: { event: PublicEvent }) => { min="1" max={event.recurringEvent.count} defaultValue={occurenceCount || event.recurringEvent.count} + data-testid="occurrence-input" onChange={(event) => { const pattern = /^(?=.*[0-9])\S+$/; const inputValue = parseInt(event.target.value); diff --git a/packages/features/eventtypes/components/EventTypeDescription.tsx b/packages/features/eventtypes/components/EventTypeDescription.tsx index ec8f15cd4c..437e425f1d 100644 --- a/packages/features/eventtypes/components/EventTypeDescription.tsx +++ b/packages/features/eventtypes/components/EventTypeDescription.tsx @@ -86,7 +86,7 @@ export const EventTypeDescription = ({ )} {recurringEvent?.count && recurringEvent.count > 0 && ( -
  • +
  • {t("repeats_up_to", { count: recurringEvent.count,