Files
calendar/packages/app-store/routing-forms/lib/substituteVariables.test.ts
T
Abir RoyandGitHub e4a6b97017 fix(routing-forms): correct variable encoding and URL construction in Routing Forms Event Redirect Custom URLs (#25499)
* fix: change slugification to uri encoding

* fix: add conditional joiner symbol between event type redirect url and all other search params

* fix: update integration test to expect encoded variable

* test: add tests for query parameters joiner logic
2025-12-04 08:59:22 +00:00

230 lines
8.3 KiB
TypeScript

import { describe, it, expect } from "vitest";
import type { FormResponse, Field } from "../types/types";
import { substituteVariables } from "./substituteVariables";
describe("substituteVariables", () => {
const createSelectField = (
id: string,
identifier: string,
label: string,
options?: Array<{ id: string; label: string }>
): Field => ({
id,
identifier,
label,
type: "select",
options,
});
const createTextField = (id: string, identifier: string, label: string): Field => ({
id,
identifier,
label,
type: "text",
});
const createMultiselectField = (
id: string,
identifier: string,
label: string,
options?: Array<{ id: string; label: string }>
): Field => ({
id,
identifier,
label,
type: "multiselect",
options,
});
const createFormResponse = (
fieldId: string,
value: string | string[] | number,
label: string
): FormResponse => ({
[fieldId]: {
value,
label,
},
});
it("should substitute variables with option labels (not field labels or values)", () => {
const fields = [
createSelectField("field1", "department", "Department", [
{ id: "sales-123", label: "Sales Team" },
{ id: "eng-456", label: "Engineering" },
]),
];
const routeValue = "/team/{department}/meeting";
const response = createFormResponse("field1", "sales-123", "Department");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/team/Sales%20Team/meeting");
expect(result).not.toBe("/team/sales-123/meeting");
expect(result).not.toBe("/team/department/meeting");
});
it("should handle multiple variable substitutions", () => {
const fields = [
createSelectField("field1", "department", "Department", [{ id: "eng-456", label: "Engineering" }]),
createTextField("field2", "teamName", "Team Name"),
];
const routeValue = "/{department}/{teamName}/book";
const response: FormResponse = {
...createFormResponse("field1", "eng-456", "Department"),
...createFormResponse("field2", "Backend Team", "Team Name"),
};
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/Engineering/Backend%20Team/book");
});
it("should handle special characters in labels by encoding them", () => {
const fields = [
createSelectField("field1", "department", "Department", [{ id: "hr_dept", label: "HR & Recruitment" }]),
];
const routeValue = "/meeting/{department}";
const response = createFormResponse("field1", "hr_dept", "Department");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/meeting/HR%20%26%20Recruitment");
});
it("should handle case-insensitive variable matching", () => {
const fields = [
createSelectField("field1", "department", "Department", [
{ id: "support-001", label: "Customer Support" },
]),
];
const routeValue = "/team/{DEPARTMENT}/schedule";
const response = createFormResponse("field1", "support-001", "Department");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/team/Customer%20Support/schedule");
});
it("should not substitute variables that don't have matching fields", () => {
const fields = [createTextField("field1", "knownField", "Known Field")];
const routeValue = "/team/{unknownField}/meeting";
const response = createFormResponse("field1", "sales-123", "Sales Team");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/team/{unknownField}/meeting");
});
it("should handle array values in response labels", () => {
const fields = [
createMultiselectField("field3", "priority", "Priority Level", [
{ id: "high", label: "High" },
{ id: "urgent", label: "Urgent" },
]),
];
const routeValue = "/priorities/{priority}";
const response = createFormResponse("field3", ["high", "urgent"], "Priority Level");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/priorities/High%2CUrgent");
});
it("should handle numeric labels", () => {
const fields = [
createSelectField("field1", "department", "Department", [{ id: "room-id-123", label: "Room 404" }]),
];
const routeValue = "/room/{department}";
const response = createFormResponse("field1", "room-id-123", "Department");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/room/Room%20404");
});
it("should not modify the URL if no variables are present", () => {
const fields = [createTextField("field1", "someField", "Some Field")];
const routeValue = "/team/fixed/meeting";
const response = createFormResponse("field1", "sales-123", "Sales Team");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/team/fixed/meeting");
});
it("should handle complex URLs with query parameters", () => {
const fields = [
createSelectField("field1", "department", "Department", [
{ id: "marketing-789", label: "Marketing & PR" },
]),
];
const routeValue = "/event/{department}?type=meeting&priority=high";
const response = createFormResponse("field1", "marketing-789", "Department");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/event/Marketing%20%26%20PR?type=meeting&priority=high");
});
it("should substitute text field values directly", () => {
const fields = [createTextField("field1", "username", "Username")];
const routeValue = "/user/{username}/profile";
const response = createFormResponse("field1", "John Doe", "Username");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/user/John%20Doe/profile");
});
it("should handle number field values", () => {
const fields = [
{ id: "field1", identifier: "employeeId", label: "Employee ID", type: "number" as const },
];
const routeValue = "/employee/{employeeId}/details";
const response = createFormResponse("field1", 12345, "Employee ID");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/employee/12345/details");
});
it("should handle multiple text and number fields in one URL", () => {
const fields = [
createTextField("field1", "department", "Department Name"),
{ id: "field2", identifier: "roomNumber", label: "Room Number", type: "number" as const },
createTextField("field3", "building", "Building"),
];
const routeValue = "/{building}/floor/{roomNumber}/{department}";
const response: FormResponse = {
...createFormResponse("field1", "Engineering", "Department Name"),
...createFormResponse("field2", 404, "Room Number"),
...createFormResponse("field3", "Tower A", "Building"),
};
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/Tower%20A/floor/404/Engineering");
});
it("should handle text fields with special characters", () => {
const fields = [createTextField("field1", "projectName", "Project Name")];
const routeValue = "/project/{projectName}/board";
const response = createFormResponse("field1", "Cal.com Platform & API", "Project Name");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/project/Cal.com%20Platform%20%26%20API/board");
});
it("should handle empty text field values", () => {
const fields = [createTextField("field1", "optional", "Optional Field")];
const routeValue = "/form/{optional}/submit";
const response = createFormResponse("field1", "", "Optional Field");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/form//submit");
});
it("should handle textarea fields like text fields", () => {
const fields = [
{ id: "field1", identifier: "description", label: "Description", type: "textarea" as const },
];
const routeValue = "/ticket/{description}";
const response = createFormResponse("field1", "Bug Report Summary", "Description");
const result = substituteVariables(routeValue, response, fields);
expect(result).toBe("/ticket/Bug%20Report%20Summary");
});
});