Files
calendar/apps/web/components/booking/__tests__/CancelBooking.cancellationFee.test.tsx
T
Benny JooGitHubbenny@cal.com <sldisek783@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
3aab13b4da refactor: circular deps between app store and lib [3] (#23742)
* getEnabledAppsFromCredentials

* wip

* wip

* mv more

* fix: update test mocking for PrismockClient in processPaymentRefund.test.ts

- Replace prismaMock with prismock for proper PrismockClient usage
- Update mock data structure to include required fields (id, userId, teamId, etc.)
- Create app and credential records separately to handle PrismockClient limitations
- Replace 'as any' type casting with vi.mocked() for proper type safety
- Adjust test expectations to match PrismockClient's actual return structure
- Add proper cleanup in beforeEach hook for both credential and app records

Fixes failing unit tests after migration from packages/lib to packages/features/bookings/lib/payment

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* publish platform libraries

* fix import

* bump version

* fix apiv2

* publish platform-libraries

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-09-14 11:24:16 -03:00

258 lines
7.1 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import * as React from "react";
import { describe, expect, it, vi, beforeAll } from "vitest";
import * as shouldChargeModule from "@calcom/features/bookings/lib/payment/shouldChargeNoShowCancellationFee";
import CancelBooking from "../CancelBooking";
beforeAll(() => {
Element.prototype.scrollIntoView = vi.fn();
});
vi.mock("@calcom/trpc", () => ({
trpc: {
viewer: {
bookings: {
requestReschedule: {
useMutation: () => ({
mutate: vi.fn(),
isLoading: false,
}),
},
},
},
},
}));
vi.mock("next-i18next", () => ({
useTranslation: () => ({
t: (key: string, options?: Record<string, unknown>) => {
if (key === "cancellation_fee_warning_cancel") {
const opts = options as {
time?: string;
unit?: string;
amount?: number;
formatParams?: { amount?: { currency?: string } };
};
return `Cancelling within ${opts?.time} ${opts?.unit} will result in a ${opts?.amount} ${opts?.formatParams?.amount?.currency} cancellation fee being charged to your card.`;
}
return key;
},
}),
}));
vi.mock("@calcom/lib/hooks/useLocale", () => ({
useLocale: () => ({
t: (key: string, options?: Record<string, unknown>) => {
if (key === "cancel_booking_acknowledge_no_show_fee") {
const opts = options as {
timeValue?: number;
timeUnit?: string;
amount?: number;
formatParams?: { amount?: { currency?: string } };
};
return `I acknowledge that cancelling within ${opts?.timeValue} ${opts?.timeUnit} will result in a ${opts?.amount} ${opts?.formatParams?.amount?.currency} cancellation fee being charged to my card.`;
}
return key;
},
}),
}));
vi.mock("@calcom/lib/hooks/useTelemetry", () => ({
useTelemetry: () => ({
event: vi.fn(),
}),
}));
vi.mock("@calcom/lib/hooks/useRefreshData", () => ({
useRefreshData: () => vi.fn(),
}));
vi.mock("next/router", () => ({
useRouter: () => ({
push: vi.fn(),
query: {},
}),
}));
vi.mock("@calcom/features/bookings/lib/payment/shouldChargeNoShowCancellationFee", () => ({
shouldChargeNoShowCancellationFee: vi.fn(),
}));
const mockBookingWithCancellationFee = {
uid: "test-booking-uid",
title: "Test Meeting",
id: 123,
startTime: new Date(Date.now() + 30 * 60 * 1000),
payment: {
amount: 1000,
currency: "usd",
appId: "stripe",
},
};
const mockEventTypeMetadataWithFee = {
apps: {
stripe: {
autoChargeNoShowFeeIfCancelled: true,
autoChargeNoShowFeeTimeValue: 1,
autoChargeNoShowFeeTimeUnit: "hours" as const,
paymentOption: "HOLD" as const,
},
},
};
const mockEventTypeMetadataWithoutFee = {
apps: {
stripe: {
autoChargeNoShowFeeIfCancelled: true,
autoChargeNoShowFeeTimeValue: 1,
autoChargeNoShowFeeTimeUnit: "hours" as const,
paymentOption: "HOLD" as const,
},
},
};
const mockProps = {
recurringEvent: null,
setIsCancellationMode: vi.fn(),
theme: "light",
allRemainingBookings: false,
seatReferenceUid: undefined,
currentUserEmail: "test@example.com",
bookingCancelledEventProps: {
booking: {},
organizer: {
name: "Test Organizer",
email: "organizer@example.com",
timeZone: "UTC",
},
eventType: {},
},
internalNotePresets: [],
};
const mockBookingWithoutCancellationFee = {
uid: "test-booking-uid-2",
title: "Test Meeting 2",
id: 124,
startTime: new Date(Date.now() + 2 * 60 * 60 * 1000),
payment: {
amount: 1000,
currency: "usd",
appId: "stripe",
},
};
describe("CancelBooking Cancellation Fee Warning", () => {
it("should show cancellation fee warning when booking is within time threshold", () => {
vi.mocked(shouldChargeModule.shouldChargeNoShowCancellationFee).mockReturnValue(true);
render(
<CancelBooking
booking={mockBookingWithCancellationFee}
profile={{ name: "Test User", slug: "test-user" }}
team={null}
isHost={false}
eventTypeMetadata={mockEventTypeMetadataWithFee}
{...mockProps}
/>
);
expect(
screen.getByText(/I acknowledge that cancelling within 1 hours will result in a/)
).toBeInTheDocument();
});
it("should not show cancellation fee warning when booking is outside time threshold", () => {
vi.mocked(shouldChargeModule.shouldChargeNoShowCancellationFee).mockReturnValue(false);
render(
<CancelBooking
booking={mockBookingWithoutCancellationFee}
profile={{ name: "Test User", slug: "test-user" }}
team={null}
isHost={false}
eventTypeMetadata={mockEventTypeMetadataWithoutFee}
{...mockProps}
/>
);
expect(screen.queryByText(/I acknowledge that cancelling within/)).not.toBeInTheDocument();
});
it("should not show cancellation fee warning when user is host", () => {
vi.mocked(shouldChargeModule.shouldChargeNoShowCancellationFee).mockReturnValue(false);
render(
<CancelBooking
booking={mockBookingWithCancellationFee}
profile={{ name: "Test User", slug: "test-user" }}
team={null}
isHost={true}
eventTypeMetadata={mockEventTypeMetadataWithFee}
{...mockProps}
/>
);
expect(screen.queryByText(/I acknowledge that cancelling within/)).not.toBeInTheDocument();
});
it("should not show cancellation fee warning when cancellation fee is disabled", () => {
const eventTypeMetadataWithDisabledFee = {
apps: {
stripe: {
autoChargeNoShowFeeIfCancelled: false,
autoChargeNoShowFeeTimeValue: 1,
autoChargeNoShowFeeTimeUnit: "hours" as const,
paymentOption: "HOLD" as const,
},
},
};
vi.mocked(shouldChargeModule.shouldChargeNoShowCancellationFee).mockReturnValue(false);
render(
<CancelBooking
booking={mockBookingWithCancellationFee}
profile={{ name: "Test User", slug: "test-user" }}
team={null}
isHost={false}
eventTypeMetadata={eventTypeMetadataWithDisabledFee}
{...mockProps}
/>
);
expect(screen.queryByText(/I acknowledge that cancelling within/)).not.toBeInTheDocument();
});
it("should not show cancellation fee warning when payment option is not HOLD", () => {
const eventTypeMetadataWithOnBookingPayment = {
apps: {
stripe: {
autoChargeNoShowFeeIfCancelled: true,
autoChargeNoShowFeeTimeValue: 1,
autoChargeNoShowFeeTimeUnit: "hours" as const,
paymentOption: "ON_BOOKING" as const,
},
},
};
vi.mocked(shouldChargeModule.shouldChargeNoShowCancellationFee).mockReturnValue(false);
render(
<CancelBooking
booking={mockBookingWithCancellationFee}
profile={{ name: "Test User", slug: "test-user" }}
team={null}
isHost={false}
eventTypeMetadata={eventTypeMetadataWithOnBookingPayment}
{...mockProps}
/>
);
expect(screen.queryByText(/I acknowledge that cancelling within/)).not.toBeInTheDocument();
});
});