From d185d7f7d81ae0cada45b1e6fc5bc8745db71634 Mon Sep 17 00:00:00 2001
From: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Date: Mon, 4 Dec 2023 16:00:36 +0000
Subject: [PATCH 01/41] feat: invites bypass disabled signup (#12626)
* invites bypass disabled signup
* nit:remove new line
* add tests
* chore: spelling
---------
Co-authored-by: Udit Takkar
---
apps/web/pages/api/auth/signup.ts | 14 ++++++--
apps/web/pages/signup.tsx | 2 +-
apps/web/playwright/signup.e2e.ts | 57 +++++++++++++++++++++++++++++--
3 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/apps/web/pages/api/auth/signup.ts b/apps/web/pages/api/auth/signup.ts
index 4d90edf60e..2a35d20a16 100644
--- a/apps/web/pages/api/auth/signup.ts
+++ b/apps/web/pages/api/auth/signup.ts
@@ -6,8 +6,18 @@ import { type RequestWithUsernameStatus } from "@calcom/features/auth/signup/use
import { IS_PREMIUM_USERNAME_ENABLED } from "@calcom/lib/constants";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
+import { signupSchema } from "@calcom/prisma/zod-utils";
+
+function ensureSignupIsEnabled(req: RequestWithUsernameStatus) {
+ const { token } = signupSchema
+ .pick({
+ token: true,
+ })
+ .parse(req.body);
+
+ // Stil allow signups if there is a team invite
+ if (token) return;
-function ensureSignupIsEnabled() {
if (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true") {
throw new HttpError({
statusCode: 403,
@@ -29,7 +39,7 @@ export default async function handler(req: RequestWithUsernameStatus, res: NextA
// Use a try catch instead of returning res every time
try {
ensureReqIsPost(req);
- ensureSignupIsEnabled();
+ ensureSignupIsEnabled(req);
/**
* Im not sure its worth merging these two handlers. They are different enough to be separate.
diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx
index 0cae0e4ee2..278b039a15 100644
--- a/apps/web/pages/signup.tsx
+++ b/apps/web/pages/signup.tsx
@@ -525,7 +525,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
// username + email prepopulated from query params
const { username: preFillusername, email: prefilEmail } = querySchema.parse(ctx.query);
- if (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" || flags["disable-signup"]) {
+ if ((process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" && !token) || flags["disable-signup"]) {
return {
notFound: true,
};
diff --git a/apps/web/playwright/signup.e2e.ts b/apps/web/playwright/signup.e2e.ts
index 60775a2143..65da61ebe2 100644
--- a/apps/web/playwright/signup.e2e.ts
+++ b/apps/web/playwright/signup.e2e.ts
@@ -4,7 +4,8 @@ import { randomBytes } from "crypto";
import { APP_NAME, IS_PREMIUM_USERNAME_ENABLED, IS_MAILHOG_ENABLED } from "@calcom/lib/constants";
import { test } from "./lib/fixtures";
-import { getEmailsReceivedByUser } from "./lib/testUtils";
+import { getEmailsReceivedByUser, localize } from "./lib/testUtils";
+import { expectInvitationEmailToBeReceived } from "./team/expects";
test.describe.configure({ mode: "parallel" });
@@ -12,8 +13,9 @@ test.describe("Signup Flow Test", async () => {
test.beforeEach(async ({ features }) => {
features.reset(); // This resets to the inital state not an empt yarray
});
- test.afterAll(async ({ users }) => {
+ test.afterAll(async ({ users, emails }) => {
await users.deleteAll();
+ emails?.deleteAll();
});
test("Username is taken", async ({ page, users }) => {
// log in trail user
@@ -228,4 +230,55 @@ test.describe("Signup Flow Test", async () => {
const verifyEmail = receivedEmails?.items[0];
expect(verifyEmail?.subject).toBe(`${APP_NAME}: Verify your account`);
});
+ test("If signup is disabled allow team invites", async ({ browser, page, users, emails }) => {
+ // eslint-disable-next-line playwright/no-skipped-test
+ test.skip(process.env.NEXT_PUBLIC_DISABLE_SIGNUP !== "true", "Skipping due to signup being enabled");
+
+ const t = await localize("en");
+ const teamOwner = await users.create(undefined, { hasTeam: true });
+ const { team } = await teamOwner.getFirstTeam();
+ await teamOwner.apiLogin();
+ await page.goto(`/settings/teams/${team.id}/members`);
+ await page.waitForLoadState("networkidle");
+
+ await test.step("Invite User to team", async () => {
+ // TODO: This invite logic should live in a fixture - its used in team and orgs invites (Duplicated from team/org invites)
+ const invitedUserEmail = `rick_${Date.now()}@domain-${Date.now()}.com`;
+ await page.locator(`button:text("${t("add")}")`).click();
+ await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
+ await page.locator(`button:text("${t("send_invite")}")`).click();
+ await page.waitForLoadState("networkidle");
+ const inviteLink = await expectInvitationEmailToBeReceived(
+ page,
+ emails,
+ invitedUserEmail,
+ `${team.name}'s admin invited you to join the team ${team.name} on Cal.com`,
+ "signup?token"
+ );
+
+ //Check newly invited member exists and is pending
+ await expect(
+ page.locator(`[data-testid="email-${invitedUserEmail.replace("@", "")}-pending"]`)
+ ).toHaveCount(1);
+
+ // eslint-disable-next-line playwright/no-conditional-in-test
+ if (!inviteLink) return;
+
+ // Follow invite link to new window
+ const context = await browser.newContext();
+ const newPage = await context.newPage();
+ await newPage.goto(inviteLink);
+ await newPage.waitForLoadState("networkidle");
+
+ const url = new URL(newPage.url());
+ expect(url.pathname).toBe("/signup");
+
+ // Check required fields
+ await newPage.locator("input[name=password]").fill(`P4ssw0rd!`);
+ await newPage.locator("button[type=submit]").click();
+ await newPage.waitForURL("/getting-started?from=signup");
+ await newPage.close();
+ await context.close();
+ });
+ });
});
From bb7057ea04368f0b5414ea930841b20f9a7d1ee9 Mon Sep 17 00:00:00 2001
From: Benny Joo
Date: Mon, 4 Dec 2023 23:31:36 +0000
Subject: [PATCH 02/41] fix: type error in `settings/admin` in app router
(#12638)
---
.../settings/admin/organizations/page.tsx | 8 +++-----
.../settings/admin/users/[id]/edit/page.tsx | 7 ++-----
.../(settings-layout)/settings/admin/users/add/page.tsx | 8 +++-----
.../(settings-layout)/settings/admin/users/page.tsx | 8 +++-----
apps/web/pages/settings/admin/organizations/index.tsx | 2 --
apps/web/pages/settings/admin/users/[id]/edit.tsx | 2 --
apps/web/pages/settings/admin/users/add.tsx | 2 --
apps/web/pages/settings/admin/users/index.tsx | 2 --
.../organizations/pages/settings/admin/AdminOrgPage.tsx | 2 ++
packages/features/ee/users/pages/users-add-view.tsx | 2 ++
packages/features/ee/users/pages/users-edit-view.tsx | 2 ++
packages/features/ee/users/pages/users-listing-view.tsx | 2 ++
12 files changed, 19 insertions(+), 28 deletions(-)
diff --git a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/organizations/page.tsx b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/organizations/page.tsx
index 2be58eaf21..7ec2b7fa6b 100644
--- a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/organizations/page.tsx
+++ b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/organizations/page.tsx
@@ -1,13 +1,11 @@
-import Page from "@pages/settings/admin/organizations/index";
import { _generateMetadata } from "app/_utils";
+import Page from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgPage";
+
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("organizations"),
(t) => t("orgs_page_description")
);
-export default function AppPage() {
- // @ts-expect-error FIXME Property 'Component' is incompatible with index signature
- return ;
-}
+export default Page;
diff --git a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/[id]/edit/page.tsx b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/[id]/edit/page.tsx
index dc07519796..ae354725f6 100644
--- a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/[id]/edit/page.tsx
+++ b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/[id]/edit/page.tsx
@@ -1,10 +1,10 @@
-import Page from "@pages/settings/admin/users/[id]/edit";
import { getServerCaller } from "app/_trpc/serverClient";
import { type Params } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { cookies, headers } from "next/headers";
import { z } from "zod";
+import Page from "@calcom/features/ee/users/pages/users-edit-view";
import prisma from "@calcom/prisma";
const userIdSchema = z.object({ id: z.coerce.number() });
@@ -33,7 +33,4 @@ export const generateMetadata = async ({ params }: { params: Params }) => {
);
};
-export default function AppPage() {
- // @ts-expect-error FIXME AppProps | undefined' does not satisfy the constraint 'PageProps'
- return ;
-}
+export default Page;
diff --git a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/add/page.tsx b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/add/page.tsx
index f291a5e22d..511ad02a7d 100644
--- a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/add/page.tsx
+++ b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/add/page.tsx
@@ -1,13 +1,11 @@
-import Page from "@pages/settings/admin/users/add";
import { _generateMetadata } from "app/_utils";
+import Page from "@calcom/features/ee/users/pages/users-add-view";
+
export const generateMetadata = async () =>
await _generateMetadata(
() => "Add new user",
() => "Here you can add a new user."
);
-export default function AppPage() {
- // @ts-expect-error FIXME AppProps | undefined' does not satisfy the constraint 'PageProps'
- return ;
-}
+export default Page;
diff --git a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/page.tsx b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/page.tsx
index fb25352449..0bd414f599 100644
--- a/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/page.tsx
+++ b/apps/web/app/future/(shared-page-wrapper)/(settings-layout)/settings/admin/users/page.tsx
@@ -1,13 +1,11 @@
-import Page from "@pages/settings/admin/users/index";
import { _generateMetadata } from "app/_utils";
+import Page from "@calcom/features/ee/users/pages/users-listing-view";
+
export const generateMetadata = async () =>
await _generateMetadata(
() => "Users",
() => "A list of all the users in your account including their name, title, email and role."
);
-export default function AppPage() {
- // @ts-expect-error FIXME Property 'Component' is incompatible with index signature
- return ;
-}
+export default Page;
diff --git a/apps/web/pages/settings/admin/organizations/index.tsx b/apps/web/pages/settings/admin/organizations/index.tsx
index 4937f158c2..9c2d7cc1de 100644
--- a/apps/web/pages/settings/admin/organizations/index.tsx
+++ b/apps/web/pages/settings/admin/organizations/index.tsx
@@ -1,5 +1,3 @@
-"use client";
-
import AdminOrgsPage from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgPage";
import type { CalPageWrapper } from "@components/PageWrapper";
diff --git a/apps/web/pages/settings/admin/users/[id]/edit.tsx b/apps/web/pages/settings/admin/users/[id]/edit.tsx
index 3969862d83..0ee1269cca 100644
--- a/apps/web/pages/settings/admin/users/[id]/edit.tsx
+++ b/apps/web/pages/settings/admin/users/[id]/edit.tsx
@@ -1,5 +1,3 @@
-"use client";
-
import UsersEditView from "@calcom/features/ee/users/pages/users-edit-view";
import type { CalPageWrapper } from "@components/PageWrapper";
diff --git a/apps/web/pages/settings/admin/users/add.tsx b/apps/web/pages/settings/admin/users/add.tsx
index b4afda4e4b..293a90214e 100644
--- a/apps/web/pages/settings/admin/users/add.tsx
+++ b/apps/web/pages/settings/admin/users/add.tsx
@@ -1,5 +1,3 @@
-"use client";
-
import UsersAddView from "@calcom/features/ee/users/pages/users-add-view";
import type { CalPageWrapper } from "@components/PageWrapper";
diff --git a/apps/web/pages/settings/admin/users/index.tsx b/apps/web/pages/settings/admin/users/index.tsx
index d822ee7dab..194c966682 100644
--- a/apps/web/pages/settings/admin/users/index.tsx
+++ b/apps/web/pages/settings/admin/users/index.tsx
@@ -1,5 +1,3 @@
-"use client";
-
import UsersListingView from "@calcom/features/ee/users/pages/users-listing-view";
import type { CalPageWrapper } from "@components/PageWrapper";
diff --git a/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx b/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx
index c111ba3040..0042acd986 100644
--- a/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx
+++ b/packages/features/ee/organizations/pages/settings/admin/AdminOrgPage.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import NoSSR from "@calcom/core/components/NoSSR";
import LicenseRequired from "@calcom/ee/common/components/LicenseRequired";
import { extractDomainFromWebsiteUrl } from "@calcom/ee/organizations/lib/utils";
diff --git a/packages/features/ee/users/pages/users-add-view.tsx b/packages/features/ee/users/pages/users-add-view.tsx
index 30ffc93ae0..44f3d8d1d0 100644
--- a/packages/features/ee/users/pages/users-add-view.tsx
+++ b/packages/features/ee/users/pages/users-add-view.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { usePathname, useRouter } from "next/navigation";
import { getParserWithGeneric } from "@calcom/prisma/zod-utils";
diff --git a/packages/features/ee/users/pages/users-edit-view.tsx b/packages/features/ee/users/pages/users-edit-view.tsx
index 2da6d2126e..c4d6169f53 100644
--- a/packages/features/ee/users/pages/users-edit-view.tsx
+++ b/packages/features/ee/users/pages/users-edit-view.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { usePathname, useRouter } from "next/navigation";
import { z } from "zod";
diff --git a/packages/features/ee/users/pages/users-listing-view.tsx b/packages/features/ee/users/pages/users-listing-view.tsx
index 5ddbc9d8e9..915530b4bf 100644
--- a/packages/features/ee/users/pages/users-listing-view.tsx
+++ b/packages/features/ee/users/pages/users-listing-view.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import NoSSR from "@calcom/core/components/NoSSR";
import { Button, Meta } from "@calcom/ui";
From 325f250d396470676ea755df49b90abfb0bb24de Mon Sep 17 00:00:00 2001
From: Nafees Nazik <84864519+G3root@users.noreply.github.com>
Date: Tue, 5 Dec 2023 05:01:55 +0530
Subject: [PATCH 03/41] feat: add resend email transport (#12645)
Co-authored-by: Erik
---
.env.example | 4 ++++
packages/lib/serverConfig.ts | 14 ++++++++++++++
turbo.json | 3 ++-
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/.env.example b/.env.example
index c82423c34e..95eddc71ac 100644
--- a/.env.example
+++ b/.env.example
@@ -196,6 +196,10 @@ EMAIL_SERVER_PORT=1025
# Make sure to run mailhog container manually or with `yarn dx`
E2E_TEST_MAILHOG_ENABLED=
+# Resend
+# Send transactional email using resend
+# RESEND_API_KEY=
+
# **********************************************************************************************************
# Set the following value to true if you wish to enable Team Impersonation
diff --git a/packages/lib/serverConfig.ts b/packages/lib/serverConfig.ts
index 09feb75661..8a13b31ff2 100644
--- a/packages/lib/serverConfig.ts
+++ b/packages/lib/serverConfig.ts
@@ -6,6 +6,20 @@ import { isENVDev } from "@calcom/lib/env";
import { getAdditionalEmailHeaders } from "./getAdditionalEmailHeaders";
function detectTransport(): SendmailTransport.Options | SMTPConnection.Options | string {
+ if (process.env.RESEND_API_KEY) {
+ const transport = {
+ host: "smtp.resend.com",
+ secure: true,
+ port: 465,
+ auth: {
+ user: "resend",
+ pass: process.env.RESEND_API_KEY,
+ },
+ };
+
+ return transport;
+ }
+
if (process.env.EMAIL_SERVER) {
return process.env.EMAIL_SERVER;
}
diff --git a/turbo.json b/turbo.json
index ab654d12ba..f33f3941f7 100644
--- a/turbo.json
+++ b/turbo.json
@@ -337,6 +337,7 @@
"ZOHOCRM_CLIENT_ID",
"ZOHOCRM_CLIENT_SECRET",
"ZOOM_CLIENT_ID",
- "ZOOM_CLIENT_SECRET"
+ "ZOOM_CLIENT_SECRET",
+ "RESEND_API_KEY"
]
}
From f2a59fe4e8405ab3ee4669dc2e54a13c41cf5a07 Mon Sep 17 00:00:00 2001
From: Hariom Balhara
Date: Tue, 5 Dec 2023 21:46:54 +0530
Subject: [PATCH 04/41] Fix booking error in case of no calendar credential but
stray destinationCalendar (#12680)
---
packages/core/EventManager.ts | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/packages/core/EventManager.ts b/packages/core/EventManager.ts
index 4aa5e30beb..edfd96d3a3 100644
--- a/packages/core/EventManager.ts
+++ b/packages/core/EventManager.ts
@@ -574,24 +574,25 @@ export default class EventManager {
(c) => c.type === destination.integration
);
// It might not be the first connected calendar as it seems that the order is not guaranteed to be ascending of credentialId.
- const firstCalendarCredential = destinationCalendarCredentials[0];
+ const firstCalendarCredential = destinationCalendarCredentials[0] as
+ | (typeof destinationCalendarCredentials)[number]
+ | undefined;
if (!firstCalendarCredential) {
log.warn(
"No other credentials found of the same type as the destination calendar. Falling back to first connected calendar"
);
await fallbackToFirstConnectedCalendar();
+ } else {
+ log.warn(
+ "No credentialId found for destination calendar, falling back to first found calendar of same type as destination calendar",
+ safeStringify({
+ destination: getPiiFreeDestinationCalendar(destination),
+ firstConnectedCalendar: getPiiFreeCredential(firstCalendarCredential),
+ })
+ );
+ createdEvents.push(await createEvent(firstCalendarCredential, event));
}
-
- log.warn(
- "No credentialId found for destination calendar, falling back to first found calendar",
- safeStringify({
- destination: getPiiFreeDestinationCalendar(destination),
- firstConnectedCalendar: getPiiFreeCredential(firstCalendarCredential),
- })
- );
-
- createdEvents.push(await createEvent(firstCalendarCredential, event));
}
}
} else {
From 9f509419040fd2db9fd8f2141ca35faaff16020b Mon Sep 17 00:00:00 2001
From: Hariom Balhara
Date: Wed, 6 Dec 2023 00:20:55 +0530
Subject: [PATCH 05/41] test: Booking creation failure in case of stray
destinationCalendar with no matching calendar credential (#12682)
---
.../utils/bookingScenario/bookingScenario.ts | 10 ++
.../test/fresh-booking.test.ts | 146 ++++++++++++++++++
2 files changed, 156 insertions(+)
diff --git a/apps/web/test/utils/bookingScenario/bookingScenario.ts b/apps/web/test/utils/bookingScenario/bookingScenario.ts
index 65efac03e9..af7717219e 100644
--- a/apps/web/test/utils/bookingScenario/bookingScenario.ts
+++ b/apps/web/test/utils/bookingScenario/bookingScenario.ts
@@ -613,6 +613,16 @@ export function getGoogleCalendarCredential() {
});
}
+export function getAppleCalendarCredential() {
+ return getMockedCredential({
+ metadataLookupKey: "applecalendar",
+ key: {
+ scope:
+ "https://www.applecalendar.example/auth/calendar.events https://www.applecalendar.example/auth/calendar.readonly",
+ },
+ });
+}
+
export function getZoomAppCredential() {
return getMockedCredential({
metadataLookupKey: "zoomvideo",
diff --git a/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts b/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts
index c893929b9a..bb8189d8d9 100644
--- a/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts
+++ b/packages/features/bookings/lib/handleNewBooking/test/fresh-booking.test.ts
@@ -21,6 +21,7 @@ import {
createBookingScenario,
getDate,
getGoogleCalendarCredential,
+ getAppleCalendarCredential,
TestData,
getOrganizer,
getBooker,
@@ -784,6 +785,151 @@ describe("handleNewBooking", () => {
},
timeout
);
+
+ test(
+ "If destination calendar is there for Google Calendar but there are no Google Calendar credentials but there is an Apple Calendar credential connected, it should create the event in Apple Calendar",
+ async ({ emails }) => {
+ const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
+ const booker = getBooker({
+ email: "booker@example.com",
+ name: "Booker",
+ });
+
+ const organizer = getOrganizer({
+ name: "Organizer",
+ email: "organizer@example.com",
+ id: 101,
+ schedules: [TestData.schedules.IstWorkHours],
+ credentials: [getAppleCalendarCredential()],
+ selectedCalendars: [TestData.selectedCalendars.google],
+ destinationCalendar: {
+ integration: "google_calendar",
+ externalId: "organizer@google-calendar.com",
+ },
+ });
+
+ await createBookingScenario(
+ getScenarioData({
+ webhooks: [
+ {
+ userId: organizer.id,
+ eventTriggers: ["BOOKING_CREATED"],
+ subscriberUrl: "http://my-webhook.example.com",
+ active: true,
+ eventTypeId: 1,
+ appId: null,
+ },
+ ],
+ eventTypes: [
+ {
+ id: 1,
+ slotInterval: 45,
+ length: 45,
+ users: [
+ {
+ id: 101,
+ },
+ ],
+ },
+ ],
+ organizer,
+ apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
+ })
+ );
+
+ mockSuccessfulVideoMeetingCreation({
+ metadataLookupKey: "dailyvideo",
+ videoMeetingData: {
+ id: "MOCK_ID",
+ password: "MOCK_PASS",
+ url: `http://mock-dailyvideo.example.com/meeting-1`,
+ },
+ });
+
+ const calendarMock = mockCalendarToHaveNoBusySlots("applecalendar", {
+ create: {
+ uid: "MOCK_ID",
+ id: "MOCKED_APPLE_CALENDAR_EVENT_ID",
+ iCalUID: "MOCKED_APPLE_CALENDAR_ICS_ID",
+ },
+ });
+
+ const mockBookingData = getMockRequestDataForBooking({
+ data: {
+ eventTypeId: 1,
+ responses: {
+ email: booker.email,
+ name: booker.name,
+ location: { optionValue: "", value: BookingLocations.CalVideo },
+ },
+ },
+ });
+
+ const { req } = createMockNextJsRequest({
+ method: "POST",
+ body: mockBookingData,
+ });
+
+ const createdBooking = await handleNewBooking(req);
+ expect(createdBooking.responses).toContain({
+ email: booker.email,
+ name: booker.name,
+ });
+
+ expect(createdBooking).toContain({
+ location: BookingLocations.CalVideo,
+ });
+
+ await expectBookingToBeInDatabase({
+ description: "",
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ uid: createdBooking.uid!,
+ eventTypeId: mockBookingData.eventTypeId,
+ status: BookingStatus.ACCEPTED,
+ references: [
+ {
+ type: appStoreMetadata.dailyvideo.type,
+ uid: "MOCK_ID",
+ meetingId: "MOCK_ID",
+ meetingPassword: "MOCK_PASS",
+ meetingUrl: "http://mock-dailyvideo.example.com/meeting-1",
+ },
+ {
+ type: appStoreMetadata.applecalendar.type,
+ uid: "MOCKED_APPLE_CALENDAR_EVENT_ID",
+ meetingId: "MOCKED_APPLE_CALENDAR_EVENT_ID",
+ meetingPassword: "MOCK_PASSWORD",
+ meetingUrl: "https://UNUSED_URL",
+ },
+ ],
+ });
+
+ expectWorkflowToBeTriggered();
+ expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
+ calendarId: "organizer@google-calendar.com",
+ videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
+ });
+
+ expectSuccessfulBookingCreationEmails({
+ booking: {
+ uid: createdBooking.uid!,
+ },
+ booker,
+ organizer,
+ emails,
+ iCalUID: "MOCKED_APPLE_CALENDAR_ICS_ID",
+ });
+
+ expectBookingCreatedWebhookToHaveBeenFired({
+ booker,
+ organizer,
+ location: BookingLocations.CalVideo,
+ subscriberUrl: "http://my-webhook.example.com",
+ videoCallUrl: `${WEBAPP_URL}/video/${createdBooking.uid}`,
+ });
+ },
+ timeout
+ );
});
describe("Video Meeting Creation", () => {
From ca0d23529af0be39c048381728d16c0906fd3f31 Mon Sep 17 00:00:00 2001
From: Henrik Klee <89942958+stabildev@users.noreply.github.com>
Date: Wed, 6 Dec 2023 09:41:35 +0100
Subject: [PATCH 06/41] updates storybook config and fixes and updates stories
(#12683)
add packages
---
apps/storybook/.gitignore | 4 +-
.../storybook/.storybook/{main.js => main.ts} | 32 +++--
apps/storybook/.storybook/preview.jsx | 44 -------
apps/storybook/.storybook/preview.tsx | 73 +++++++++++
apps/storybook/components/CustomArgsTable.tsx | 4 +-
apps/storybook/package.json | 4 +-
packages/atoms/booker/Booker.docs.mdx | 12 ++
packages/atoms/booker/Booker.stories.tsx | 16 +++
packages/atoms/booker/booker.stories.mdx | 16 ---
.../components/VerifyCodeDialog.docs.mdx | 24 ++++
.../components/VerifyCodeDialog.stories.tsx | 48 +++++++
.../components/event-meta/EventMeta.docs.mdx | 13 ++
.../event-meta/EventMeta.stories.mdx | 59 ---------
.../event-meta/EventMeta.stories.tsx | 62 +++++++++
.../components/event-meta/event.mock.ts | 11 +-
.../components/verifyCodeDialog.stories.mdx | 60 ---------
.../ui/components/badge/badge.stories.mdx | 2 +-
.../breadcrumb/breadcrumb.stories.mdx | 2 +-
packages/ui/components/dialog/Dialog.docs.mdx | 52 ++++++++
.../ui/components/dialog/Dialog.stories.tsx | 87 +++++++++++++
.../ui/components/dialog/dialog.stories.mdx | 119 ------------------
.../form/color-picker/colorpicker.stories.mdx | 25 ++--
.../form/color-picker/colorpicker.test.tsx | 2 +-
.../components/form/wizard/wizard.stories.mdx | 1 -
.../tabs/__stories__/verticalTabs.stories.mdx | 4 +-
.../ui/components/tooltip/Tooltip.docs.mdx | 20 +++
.../ui/components/tooltip/Tooltip.stories.tsx | 37 ++++++
.../ui/components/tooltip/tooltip.stories.mdx | 69 ----------
packages/ui/index.tsx | 2 +
packages/ui/package.json | 2 +
30 files changed, 497 insertions(+), 409 deletions(-)
rename apps/storybook/.storybook/{main.js => main.ts} (66%)
delete mode 100644 apps/storybook/.storybook/preview.jsx
create mode 100644 apps/storybook/.storybook/preview.tsx
create mode 100644 packages/atoms/booker/Booker.docs.mdx
create mode 100644 packages/atoms/booker/Booker.stories.tsx
delete mode 100644 packages/atoms/booker/booker.stories.mdx
create mode 100644 packages/features/bookings/components/VerifyCodeDialog.docs.mdx
create mode 100644 packages/features/bookings/components/VerifyCodeDialog.stories.tsx
create mode 100644 packages/features/bookings/components/event-meta/EventMeta.docs.mdx
delete mode 100644 packages/features/bookings/components/event-meta/EventMeta.stories.mdx
create mode 100644 packages/features/bookings/components/event-meta/EventMeta.stories.tsx
delete mode 100644 packages/features/bookings/components/verifyCodeDialog.stories.mdx
create mode 100644 packages/ui/components/dialog/Dialog.docs.mdx
create mode 100644 packages/ui/components/dialog/Dialog.stories.tsx
delete mode 100644 packages/ui/components/dialog/dialog.stories.mdx
create mode 100644 packages/ui/components/tooltip/Tooltip.docs.mdx
create mode 100644 packages/ui/components/tooltip/Tooltip.stories.tsx
delete mode 100644 packages/ui/components/tooltip/tooltip.stories.mdx
diff --git a/apps/storybook/.gitignore b/apps/storybook/.gitignore
index 8c444bd2f4..5689902ae1 100644
--- a/apps/storybook/.gitignore
+++ b/apps/storybook/.gitignore
@@ -8,7 +8,9 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
-storybook-static
+storybook-static/*
+!storybook-static/favicon.ico
+!storybook-static/sb-cover.jpg
dist
dist-ssr
*.local
diff --git a/apps/storybook/.storybook/main.js b/apps/storybook/.storybook/main.ts
similarity index 66%
rename from apps/storybook/.storybook/main.js
rename to apps/storybook/.storybook/main.ts
index 5fa1ab07ce..bc68cb15c0 100644
--- a/apps/storybook/.storybook/main.js
+++ b/apps/storybook/.storybook/main.ts
@@ -1,14 +1,16 @@
-import { dirname, join } from "path";
+import type { StorybookConfig } from "@storybook/nextjs";
+import path, { dirname, join } from "path";
-const path = require("path");
-
-module.exports = {
+const config: StorybookConfig = {
stories: [
"../intro.stories.mdx",
- "../../../packages/ui/components/**/*.stories.mdx",
- "../../../packages/atoms/**/*.stories.mdx",
- "../../../packages/features/**/*.stories.mdx",
+ "../../../packages/ui/components/**/*.stories.mdx", // legacy SB6 stories
"../../../packages/ui/components/**/*.stories.@(js|jsx|ts|tsx)",
+ "../../../packages/ui/components/**/*.docs.mdx",
+ "../../../packages/features/**/*.stories.@(js|jsx|ts|tsx)",
+ "../../../packages/features/**/*.docs.mdx",
+ "../../../packages/atoms/**/*.stories.@(js|jsx|ts|tsx)",
+ "../../../packages/atoms/**/*.docs.mdx",
],
addons: [
@@ -17,23 +19,23 @@ module.exports = {
getAbsolutePath("@storybook/addon-interactions"),
getAbsolutePath("storybook-addon-rtl-direction"),
getAbsolutePath("storybook-react-i18next"),
- getAbsolutePath("@storybook/addon-mdx-gfm"),
],
framework: {
- name: getAbsolutePath("@storybook/nextjs"),
+ name: getAbsolutePath("@storybook/nextjs") as "@storybook/nextjs",
options: {
- builder: {
- fsCache: true,
- lazyCompilation: true,
- },
+ // builder: {
+ // fsCache: true,
+ // lazyCompilation: true,
+ // },
},
},
staticDirs: ["../public"],
webpackFinal: async (config, { configType }) => {
+ config.resolve = config.resolve || {};
config.resolve.fallback = {
fs: false,
assert: false,
@@ -61,6 +63,8 @@ module.exports = {
zlib: false,
};
+ config.module = config.module || {};
+ config.module.rules = config.module.rules || [];
config.module.rules.push({
test: /\.css$/,
use: [
@@ -85,6 +89,8 @@ module.exports = {
},
};
+export default config;
+
function getAbsolutePath(value) {
return dirname(require.resolve(join(value, "package.json")));
}
diff --git a/apps/storybook/.storybook/preview.jsx b/apps/storybook/.storybook/preview.jsx
deleted file mode 100644
index 0ff662d639..0000000000
--- a/apps/storybook/.storybook/preview.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { I18nextProvider } from "react-i18next";
-
-import "../styles/globals.css";
-import "../styles/storybook-styles.css";
-import i18n from "./i18next";
-
-export const parameters = {
- actions: { argTypesRegex: "^on[A-Z].*" },
- controls: {
- matchers: {
- color: /(background|color)$/i,
- date: /Date$/,
- },
- },
-
- globals: {
- locale: "en",
- locales: {
- en: "English",
- fr: "Français",
- },
- },
- i18n,
-};
-
-const withI18next = (Story) => (
-
-
-
-
-
-);
-
-export const decorators = [withI18next];
-
-window.getEmbedNamespace = () => {
- const url = new URL(document.URL);
- const namespace = url.searchParams.get("embed");
- return namespace;
-};
-
-window.getEmbedTheme = () => {
- return "auto";
-};
diff --git a/apps/storybook/.storybook/preview.tsx b/apps/storybook/.storybook/preview.tsx
new file mode 100644
index 0000000000..620f7a9ff6
--- /dev/null
+++ b/apps/storybook/.storybook/preview.tsx
@@ -0,0 +1,73 @@
+// adds tooltip context to all stories
+import { TooltipProvider } from "@radix-ui/react-tooltip";
+import type { Preview } from "@storybook/react";
+import React from "react";
+import { I18nextProvider } from "react-i18next";
+
+import type { EmbedThemeConfig } from "@calcom/embed-core/src/types";
+// adds trpc context to all stories (esp. booker)
+import { StorybookTrpcProvider } from "@calcom/ui";
+
+import "../styles/globals.css";
+import "../styles/storybook-styles.css";
+import i18n from "./i18next";
+
+const preview: Preview = {
+ parameters: {
+ actions: { argTypesRegex: "^on[A-Z].*" },
+
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/,
+ },
+ },
+
+ globals: {
+ locale: "en",
+ locales: {
+ en: "English",
+ fr: "Français",
+ },
+ },
+
+ i18n,
+
+ nextjs: {
+ appDirectory: true,
+ },
+ },
+
+ decorators: [
+ (Story) => (
+
+
+
+
+
+
+
+
+
+ ),
+ ],
+};
+
+export default preview;
+
+declare global {
+ interface Window {
+ getEmbedNamespace: () => string | null;
+ getEmbedTheme: () => EmbedThemeConfig | null;
+ }
+}
+
+window.getEmbedNamespace = () => {
+ const url = new URL(document.URL);
+ const namespace = url.searchParams.get("embed");
+ return namespace;
+};
+
+window.getEmbedTheme = () => {
+ return "auto";
+};
diff --git a/apps/storybook/components/CustomArgsTable.tsx b/apps/storybook/components/CustomArgsTable.tsx
index e8feabf577..1bb51bbdd9 100644
--- a/apps/storybook/components/CustomArgsTable.tsx
+++ b/apps/storybook/components/CustomArgsTable.tsx
@@ -1,6 +1,6 @@
import { ArgsTable } from "@storybook/addon-docs";
-import { SortType } from "@storybook/components";
-import { PropDescriptor } from "@storybook/store";
+import type { SortType } from "@storybook/blocks";
+import type { PropDescriptor } from "@storybook/preview-api";
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore storybook addon types component as any so we have to do
type Component = any;
diff --git a/apps/storybook/package.json b/apps/storybook/package.json
index 1b16a80ca3..0cc7d6cb30 100644
--- a/apps/storybook/package.json
+++ b/apps/storybook/package.json
@@ -20,7 +20,10 @@
"@radix-ui/react-slider": "^1.0.0",
"@radix-ui/react-switch": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
+ "@storybook/addon-docs": "^7.6.3",
+ "@storybook/blocks": "^7.6.3",
"@storybook/nextjs": "^7.6.3",
+ "@storybook/preview-api": "^7.6.3",
"next": "^13.4.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -33,7 +36,6 @@
"@storybook/addon-essentials": "^7.6.3",
"@storybook/addon-interactions": "^7.6.3",
"@storybook/addon-links": "^7.6.3",
- "@storybook/addon-mdx-gfm": "^7.6.3",
"@storybook/nextjs": "^7.6.3",
"@storybook/react": "^7.6.3",
"@storybook/testing-library": "^0.2.2",
diff --git a/packages/atoms/booker/Booker.docs.mdx b/packages/atoms/booker/Booker.docs.mdx
new file mode 100644
index 0000000000..f92c09c954
--- /dev/null
+++ b/packages/atoms/booker/Booker.docs.mdx
@@ -0,0 +1,12 @@
+import { Meta, Canvas, ArgsTable } from "@storybook/blocks";
+import { Title } from "@calcom/storybook/components";
+import { Booker } from "./Booker";
+import * as BookerStories from "./Booker.stories";
+
+
+
+
+
+
+
+
-