Files
cal.diy/packages/features/bookings/Booker/hooks/useInitialFormValues.test.ts
T
73f51920c5 refactor: move Booker hooks from packages/features to apps/web/modules (#27343)
* refactor: move Booker hooks from packages/features to apps/web/modules

Migrate the following Booker hooks:
- useOverlayCalendar
- useSkipConfirmStep
- useInitializeWeekStart
- useIsQuickAvailabilityCheckFeatureEnabled
- useDecoyBooking

These hooks are only imported by apps/web files, making them safe to move
without creating circular dependencies.

Part of the tRPC-driven UI migration from packages/features to apps/web/modules.

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

* fix: revert useIsQuickAvailabilityCheckFeatureEnabled move and fix import paths

- Reverted useIsQuickAvailabilityCheckFeatureEnabled.ts back to packages/features since it's imported by useSlots.ts in packages/features (would create circular dependency)
- Fixed import paths in useOverlayCalendar.ts to use @calcom/features paths
- Fixed import paths in useSkipConfirmStep.ts to use @calcom/features paths
- Updated Booker.tsx to import useIsQuickAvailabilityCheckFeatureEnabled from original location

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

* fix: use @calcom/web alias for hook imports to fix vitest resolution

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

* refactor

* migrate more hooks

* fix

* mv types

* fix

* fix

* fix

* fix

* revert: remove formatting-only changes to make PR easier to review

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

* fix

* fix

* fix

* revert: restore original import ordering in moved hooks

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

* fix imports

* fix

* fix

* wip

* wip

* wip

* wip

* wip

* wip

* fix: add isBrowser guard to useSlotsViewOnSmallScreen hook

Address Cubic AI review feedback (confidence 9/10) by adding the isBrowser
guard to the useSlotsViewOnSmallScreen hook to follow the file's client-only
contract and avoid executing during SSR/prerendering.

Co-Authored-By: unknown <>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-31 07:33:33 -03:00

294 lines
7.7 KiB
TypeScript

/**
* @vitest-environment jsdom
*/
import { cleanup, renderHook, waitFor } from "@testing-library/react";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import type { BookerEvent } from "@calcom/features/bookings/types";
import { useInitialFormValues } from "./useInitialFormValues";
vi.mock("@calcom/features/bookings/Booker/store", () => ({
useBookerStore: vi.fn((selector) => {
const state = {
bookingData: null,
formValues: {},
};
return selector(state);
}),
}));
vi.mock("@calcom/features/bookings/lib/getBookingResponsesSchema", () => ({
getBookingResponsesPartialSchema: vi.fn(() => ({
parseAsync: vi.fn((data) => Promise.resolve(data)),
})),
}));
describe("useInitialFormValues - Autofill Disable Feature", () => {
const mockBookingFields = [
{ name: "name", type: "text" as const, required: true },
{ name: "email", type: "email" as const, required: true },
{ name: "phone", type: "phone" as const, required: false },
] as unknown as BookerEvent["bookingFields"];
const baseProps = {
rescheduleUid: null,
isRescheduling: false,
email: null,
name: null,
username: null,
hasSession: false,
extraOptions: {},
prefillFormParams: {
guests: [],
name: null,
},
};
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
cleanup();
});
describe("when organization has NOT disabled autofill", () => {
it("should use URL parameters to prefill form fields", async () => {
const eventType: Pick<BookerEvent, "bookingFields" | "team" | "owner"> = {
bookingFields: mockBookingFields,
team: {
organizationSettings: {
disableAutofillOnBookingPage: false,
},
},
owner: null,
};
const extraOptions = {
phone: "+1234567890",
customField: "custom value",
};
const prefillFormParams = {
guests: ["guest1@example.com", "guest2@example.com"],
name: "John Doe",
};
const { result } = renderHook(() =>
useInitialFormValues({
...baseProps,
eventType,
extraOptions,
prefillFormParams,
})
);
await waitFor(() => {
expect(result.current.values.responses).toBeDefined();
});
expect(result.current.values.responses).toMatchObject({
name: "John Doe",
phone: "+1234567890",
});
});
it("should use URL parameters when organizationSettings is undefined", async () => {
const eventType: Pick<BookerEvent, "bookingFields" | "team" | "owner"> = {
bookingFields: mockBookingFields,
team: null,
owner: null,
};
const extraOptions = {
phone: "+1234567890",
};
const prefillFormParams = {
guests: [],
name: "Jane Smith",
};
const { result } = renderHook(() =>
useInitialFormValues({
...baseProps,
eventType,
extraOptions,
prefillFormParams,
})
);
await waitFor(() => {
expect(result.current.values.responses).toBeDefined();
});
expect(result.current.values.responses).toMatchObject({
name: "Jane Smith",
phone: "+1234567890",
});
});
});
describe("when organization HAS disabled autofill", () => {
it("should NOT use URL parameters when team.organizationSettings.disableAutofillOnBookingPage is true", async () => {
const eventType: Pick<BookerEvent, "bookingFields" | "team" | "owner"> = {
bookingFields: mockBookingFields,
team: {
parent: {
organizationSettings: {
disableAutofillOnBookingPage: true,
},
},
},
owner: null,
};
const extraOptions = {
phone: "+1234567890",
customField: "custom value",
};
const prefillFormParams = {
guests: ["guest1@example.com"],
name: "John Doe",
};
const { result } = renderHook(() =>
useInitialFormValues({
...baseProps,
eventType,
extraOptions,
prefillFormParams,
})
);
await waitFor(() => {
expect(result.current.values.responses).toBeDefined();
});
expect(result.current.values.responses?.phone).toBeUndefined();
expect(result.current.values.responses?.name).toBe("");
});
it("should NOT use URL parameters when team.parent.organizationSettings.disableAutofillOnBookingPage is true", async () => {
const eventType: Pick<BookerEvent, "bookingFields" | "team" | "owner"> = {
bookingFields: mockBookingFields,
team: {
organizationSettings: null,
parent: {
organizationSettings: {
disableAutofillOnBookingPage: true,
},
},
},
owner: null,
};
const extraOptions = {
phone: "+9876543210",
};
const prefillFormParams = {
guests: [],
name: "Parent Team User",
};
const { result } = renderHook(() =>
useInitialFormValues({
...baseProps,
eventType,
extraOptions,
prefillFormParams,
})
);
await waitFor(() => {
expect(result.current.values.responses).toBeDefined();
});
expect(result.current.values.responses?.phone).toBeUndefined();
expect(result.current.values.responses?.name).toBe("");
});
it("should NOT use URL parameters when owner.organization.organizationSettings.disableAutofillOnBookingPage is true", async () => {
const eventType: Pick<BookerEvent, "bookingFields" | "team" | "owner"> = {
bookingFields: mockBookingFields,
team: null,
owner: {
profile: {
organization: {
organizationSettings: {
disableAutofillOnBookingPage: true,
},
},
},
},
};
const extraOptions = {
phone: "+5555555555",
};
const prefillFormParams = {
guests: [],
name: "Personal Event User",
};
const { result } = renderHook(() =>
useInitialFormValues({
...baseProps,
eventType,
extraOptions,
prefillFormParams,
})
);
await waitFor(() => {
expect(result.current.values.responses).toBeDefined();
});
expect(result.current.values.responses?.phone).toBeUndefined();
expect(result.current.values.responses?.name).toBe("");
});
});
describe("session data handling with autofill disabled", () => {
it("should not use session email/name when autofill is disabled", async () => {
const eventType: Pick<BookerEvent, "bookingFields" | "team" | "owner"> = {
bookingFields: mockBookingFields,
team: {
parent: {
organizationSettings: {
disableAutofillOnBookingPage: true,
},
},
},
owner: null,
};
const extraOptions = {
phone: "+1234567890", // This should be blocked
};
const { result } = renderHook(() =>
useInitialFormValues({
...baseProps,
eventType,
extraOptions,
email: "session@example.com", // Session data
name: "Session User", // Session data
hasSession: true,
})
);
await waitFor(() => {
expect(result.current.values.responses).toBeDefined();
});
expect(result.current.values.responses?.email).toBe("");
expect(result.current.values.responses?.name).toBe("");
expect(result.current.values.responses?.phone).toBeUndefined();
});
});
});