Files
cal.diy/packages/testing/src/setupVitest.ts
T
8c39210a12 fix: scroll issues in Embed (#26583)
* chore: add new view for two step slot selection for embed

* fix: make sure two step slot selection is false by default

* chore: update embed playground to showcase two step slot selection

* chore: add tests

* chore: update embed snippet generator code to include two step slot selection

* chore: update docs

* fix: back button styling

* chore: implement feedback from cubic

* fix: add missing isEnableTwoStepSlotSelectionVisible properties to test mock store

Co-Authored-By: unknown <>

* chore: implement PR feedback

* chore: update slot selection modal

* chore: add prop to hide available times header

* fix: scope two-step slot selection visibility to mobile only

Restores the isMobile check in the timeslot visibility guard to ensure
desktop embeds continue to show the booking UI when two-step slot
selection is enabled. The feature should only hide the timeslot list
on mobile devices.

Addresses Cubic AI review feedback (confidence 9/10).

Co-Authored-By: unknown <>

* fixup: use translations for loading instead of actual word

* fix: type check

* fix: add window.matchMedia mock for jsdom test environment

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: add window.matchMedia mock to packages/testing/src/setupVitest.ts

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: unit tests failing

* add a width style to the two-step slot selection embed container.

* refactor: rename enableTwoStepSlotSelection to useSlotsViewOnSmallScreen

- Rename external-facing embed config option from enableTwoStepSlotSelection to useSlotsViewOnSmallScreen
- Update URL parameter parsing in embed-iframe.ts
- Update type definitions in types.ts and index.d.ts
- Update internal variable names to slotsViewOnSmallScreen for consistency
- Update documentation, playground examples, and tests

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* Add config to prefill all booking fields so that slot has confirm button along with it

* chore: implement PR feedback

* fix: move twoStepSlotSelection embed outside misc-embeds div to fix e2e test

The e2e test was failing because the misc-embeds div content was
intercepting pointer events on mobile viewport. This follows the same
pattern used for skeletonDemo - the embed is now outside misc-embeds
and the div is hidden when testing this namespace.

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: prevent pointer event interception in twoStepSlotSelection embed

Hide heading and note elements and set pointer-events: none on container
elements while keeping pointer-events: auto on the iframe container.
This prevents the container from intercepting clicks on the iframe
during mobile viewport e2e tests.

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: hide all non-essential content for twoStepSlotSelection e2e test

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: disable pointer-events on body for twoStepSlotSelection e2e test

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: remove pointer-events: auto on container to let clicks pass through to iframe

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: explicitly set pointer-events: none on container and inline-embed-container

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: also set pointer-events: none on html element to prevent interception

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: make .place div fill viewport for twoStepSlotSelection e2e test

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: failing embed tests

* fixup

* fixup fixup

* fix: failing tests

* fix: update checks for verifying prefilled date

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2026-01-21 15:44:33 -03:00

216 lines
5.7 KiB
TypeScript

import matchers from "@testing-library/jest-dom/matchers";
import ResizeObserver from "resize-observer-polyfill";
import { vi, expect } from "vitest";
import createFetchMock from "vitest-fetch-mock";
import type { CalendarService } from "@calcom/types/Calendar";
global.ResizeObserver = ResizeObserver;
if (typeof window !== "undefined") {
Object.defineProperty(window, "matchMedia", {
writable: true,
configurable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
}
const fetchMocker = createFetchMock(vi);
// sets globalThis.fetch and globalThis.fetchMock to our mocked version
fetchMocker.enableMocks();
expect.extend(matchers);
class MockExchangeCalendarService implements CalendarService {
constructor() {}
async createEvent() {
return {
uid: "mock",
id: "mock",
password: "",
type: "",
url: "",
additionalInfo: {},
};
}
async updateEvent() {
return {
uid: "mock",
id: "mock",
password: "",
type: "",
url: "",
additionalInfo: {},
};
}
async deleteEvent() {}
async getAvailability() {
return [];
}
async listCalendars() {
return [];
}
}
vi.mock("@calcom/exchangecalendar/lib/CalendarService", () => ({
default: MockExchangeCalendarService,
}));
vi.mock("@calcom/exchange2013calendar/lib/CalendarService", () => ({
default: MockExchangeCalendarService,
}));
vi.mock("@calcom/exchange2016calendar/lib/CalendarService", () => ({
default: MockExchangeCalendarService,
}));
const MOCK_PAYMENT_UID = "MOCK_PAYMENT_UID";
function createMockPaymentService(_credentials?: unknown) {
return {
async create(
payment: { amount: number; currency: string },
bookingId: number,
_userId: number,
_username: string | null,
_bookerName: string | null,
paymentOption: string,
_bookerEmail: string,
_bookerPhoneNumber?: string | null,
_selectedEventTypeTitle?: string,
_eventTitle?: string
) {
const { default: prismaMock } = await import("@calcom/testing/lib/__mocks__/prisma");
const externalId = "mock_payment_external_id";
const paymentCreateData = {
uid: MOCK_PAYMENT_UID,
appId: null,
bookingId,
fee: 10,
success: false,
refunded: false,
data: {},
externalId,
paymentOption,
amount: payment.amount,
currency: payment.currency,
};
const createdPayment = await prismaMock.payment.create({
data: paymentCreateData,
});
return createdPayment;
},
async collectCard() {
return { success: true };
},
async chargeCard() {
return { success: true };
},
async refund() {
return { success: true };
},
async deletePayment() {
return { success: true };
},
async afterPayment(
event: Record<string, unknown>,
_booking: Record<string, unknown>,
paymentData: { paymentOption?: string; amount: number; currency: string }
) {
const { sendAwaitingPaymentEmailAndSMS } = await import("@calcom/emails/email-manager");
await sendAwaitingPaymentEmailAndSMS({
...event,
paymentInfo: {
link: "http://mock-payment.example.com/",
paymentOption: paymentData.paymentOption || "ON_BOOKING",
amount: paymentData.amount,
currency: paymentData.currency,
},
});
return { success: true };
},
};
}
vi.mock("@calcom/app-store/stripepayment/index", () => ({
BuildPaymentService: createMockPaymentService,
}));
vi.mock("@calcom/app-store/paypal/index", () => ({
BuildPaymentService: createMockPaymentService,
}));
vi.mock("@calcom/app-store/alby/index", () => ({
BuildPaymentService: createMockPaymentService,
}));
vi.mock("@calcom/app-store/hitpay/index", () => ({
BuildPaymentService: createMockPaymentService,
}));
vi.mock("@calcom/app-store/btcpayserver/index", () => ({
BuildPaymentService: createMockPaymentService,
}));
vi.mock("@calcom/app-store/mock-payment-app/index", () => ({
BuildPaymentService: createMockPaymentService,
}));
vi.mock("@calcom/app-store/payment.services.generated", () => ({
PaymentServiceMap: {
stripepayment: Promise.resolve({ BuildPaymentService: createMockPaymentService }),
paypal: Promise.resolve({ BuildPaymentService: createMockPaymentService }),
alby: Promise.resolve({ BuildPaymentService: createMockPaymentService }),
hitpay: Promise.resolve({ BuildPaymentService: createMockPaymentService }),
btcpayserver: Promise.resolve({ BuildPaymentService: createMockPaymentService }),
"mock-payment-app": Promise.resolve({ BuildPaymentService: createMockPaymentService }),
},
}));
class MockCrmService {
constructor() {}
async createEvent() {
return [];
}
async updateEvent() {
return [];
}
async deleteEvent() {}
async getContacts() {
return [];
}
async createContacts() {
return [];
}
}
vi.mock("@calcom/app-store/crm.apps.generated", () => ({
CrmServiceMap: {
closecom: Promise.resolve({ default: MockCrmService }),
hubspot: Promise.resolve({ default: MockCrmService }),
"pipedrive-crm": Promise.resolve({ default: MockCrmService }),
salesforce: Promise.resolve({ default: MockCrmService }),
"zoho-bigin": Promise.resolve({ default: MockCrmService }),
zohocrm: Promise.resolve({ default: MockCrmService }),
},
}));
if (!process.env.INTEGRATION_TESTS) {
vi.mock("@calcom/app-store/salesforce/lib/graphql/documents/queries", () => ({
GetAccountRecordsForRRSkip: {},
}));
}