Files
cal.diy/packages/lib/CalEventParser.test.ts
okxint 009f91d163 fix(CalEventParser): skip null/undefined values in getUserFieldsResponses (#29703)
When a booking question is optional and left unanswered, the field value
stored in customInputs can be null. The previous guard only checked
for empty strings, so null (and undefined) would coerce to the literal
strings "null"/"undefined" inside the template literal, polluting calendar
event descriptions.

Fixes #29688
2026-07-08 18:01:41 +00:00

117 lines
3.6 KiB
TypeScript

import type { TFunction } from "i18next";
import { describe, expect, it } from "vitest";
import type { CalendarEvent } from "@calcom/types/Calendar";
import { getRichDescription, getUserFieldsResponses } from "./CalEventParser";
describe("getRichDescription", () => {
const t = ((key: string, _args?: Record<string, unknown>) => key) as TFunction;
const mockCalEvent: CalendarEvent = {
type: "test",
title: "Test Event",
description: "Test description",
startTime: "2023-01-01T10:00:00Z",
endTime: "2023-01-01T11:00:00Z",
organizer: {
email: "test@example.com",
name: "Test Organizer",
timeZone: "America/New_York",
language: { translate: t, locale: "en" },
},
attendees: [
{
email: "attendee@example.com",
name: "Test Attendee",
timeZone: "America/New_York",
language: { translate: t, locale: "en" },
},
],
location: "https://zoom.us/j/123456brahhh",
additionalNotes: "Some additional notes",
};
it("should format description without extra whitespace", () => {
const description = getRichDescription(mockCalEvent, t);
console.log("Description:", description);
console.log(
"Lines:",
description.split("\n").map((line) => `"${line}"`)
);
// Description should not have more than 2 consecutive newlines
expect(description).not.toMatch(/\n{3,}/);
// Each non-empty line should have content
const lines = description.split("\n").filter((line) => line.trim());
lines.forEach((line) => {
expect(line.trim().length).toBeGreaterThan(0);
});
// Verify content is present and properly formatted
expect(description).toContain("what:");
expect(description).toContain("Test Event");
expect(description).toContain("where:");
expect(description).toContain("https://zoom.us/j/123456brahhh");
});
it("should include payment info when present", () => {
const eventWithPayment = {
...mockCalEvent,
paymentInfo: {
link: "https://payment.test",
},
};
const description = getRichDescription(eventWithPayment, t);
expect(description).toContain("pay_now:\nhttps://payment.test");
});
it("should handle empty optional fields", () => {
const eventWithoutOptionals = {
...mockCalEvent,
description: "",
additionalNotes: "",
location: "",
};
const description = getRichDescription(eventWithoutOptionals, t);
expect(description).not.toContain("description:");
expect(description).not.toContain("additional_notes:");
// Should still contain required fields
expect(description).toContain("what:");
expect(description).toContain("who:");
});
});
describe("getUserFieldsResponses", () => {
const t = ((key: string) => key) as TFunction;
it("does not render null values for unanswered optional booking questions", () => {
// When an optional booking question is left unanswered, customInputs may store null
// for that field. Before the fix, null coerced to the string "null" in the template
// literal, polluting the calendar event description.
const result = getUserFieldsResponses(
{ customInputs: { "Phone Number": null, Name: "Alice" } },
t
);
expect(result).not.toContain("null");
expect(result).toContain("Name");
expect(result).toContain("Alice");
});
it("does not render empty string values for booking questions", () => {
const result = getUserFieldsResponses(
{ customInputs: { "Phone Number": "", Name: "Bob" } },
t
);
expect(result).not.toContain("Phone Number");
expect(result).toContain("Name");
expect(result).toContain("Bob");
});
});