* 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>
216 lines
5.7 KiB
TypeScript
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: {},
|
|
}));
|
|
}
|