Files
calendar/packages/lib/raqb/raqbUtils.test.ts
T
Hariom BalharaandGitHub 016a966057 feat: Support an array response for a field when used as Value of Field (#22740)
* Passing tests and fixed

* self review addressed adn more tests
2025-07-30 15:07:16 +00:00

1775 lines
44 KiB
TypeScript

import { expect, describe, it, vi } from "vitest";
import type { AttributesQueryValue, RaqbChild } from "@calcom/lib/raqb/types";
import type { Attribute } from "@calcom/lib/service/attribute/server/getAttributes";
import { AttributeType } from "@calcom/prisma/enums";
import { RoutingFormFieldType } from "@calcom/routing-forms/lib/FieldTypes";
import { getValueOfAttributeOption, acrossQueryValueCompatiblity } from "./raqbUtils";
const { resolveQueryValue } = acrossQueryValueCompatiblity;
// Mock the getFieldResponseValueAsLabel
vi.mock("@calcom/app-store/routing-forms/lib/getFieldResponseValueAsLabel", () => ({
getFieldResponseValueAsLabel: ({ fieldResponseValue }: { field: any; fieldResponseValue: any }) => {
// For testing, just return the value as-is
return fieldResponseValue;
},
}));
// Test Data Builders for AttributesQueryValue
const createQueryValueRule = (overrides?: Partial<RaqbChild>): RaqbChild => ({
type: "rule",
properties: {
field: "default-field",
operator: "select_equals",
value: ["default-value"],
valueSrc: ["value"],
valueError: [null],
valueType: ["select"],
},
...overrides,
});
const createAttributesQueryValue = (overrides?: {
id?: string;
type?: "group" | "switch_group";
children1?: Record<string, RaqbChild>;
properties?: any;
}): AttributesQueryValue => ({
id: overrides?.id || "test-id",
type: overrides?.type || "group",
children1: overrides?.children1 || {},
properties: overrides?.properties,
});
const createQueryValueWithRule = ({
ruleId,
field,
operator,
value,
valueSrc = ["value"],
valueError = [null],
valueType = ["select"],
}: {
ruleId: string;
field: string;
operator: string;
value: any[];
valueSrc?: string[];
valueError?: (string | null)[];
valueType?: string[];
}): AttributesQueryValue => {
return createAttributesQueryValue({
children1: {
[ruleId]: createQueryValueRule({
type: "rule",
properties: {
field,
operator,
value,
valueSrc,
valueError,
valueType,
},
}),
},
});
};
const createComplexQueryValue = ({
id,
rules,
}: {
id?: string;
rules: Array<{
ruleId: string;
field: string;
operator: string;
value: any[];
valueSrc?: string[];
valueError?: (string | null)[];
valueType?: string[];
}>;
}): AttributesQueryValue => {
const children1: Record<string, RaqbChild> = {};
rules.forEach((rule) => {
children1[rule.ruleId] = createQueryValueRule({
type: "rule",
properties: {
field: rule.field,
operator: rule.operator,
value: rule.value,
valueSrc: rule.valueSrc || ["value"],
valueError: rule.valueError || [null],
valueType: rule.valueType || ["select"],
},
});
});
return createAttributesQueryValue({
id,
children1,
});
};
const createNestedGroupQueryValue = ({
groups,
}: {
groups: Array<{
groupId: string;
rules: Array<{
ruleId: string;
field: string;
operator?: string;
value: any[];
valueSrc?: string[];
valueError?: (string | null)[];
valueType?: string[];
}>;
}>;
}): AttributesQueryValue => {
const children1: Record<string, RaqbChild> = {};
groups.forEach((group) => {
const groupChildren: Record<string, RaqbChild> = {};
group.rules.forEach((rule) => {
groupChildren[rule.ruleId] = createQueryValueRule({
type: "rule",
properties: {
field: rule.field,
operator: rule.operator || "select_equals",
value: rule.value,
valueSrc: rule.valueSrc || ["value"],
valueError: rule.valueError || [null],
valueType: rule.valueType || ["select"],
},
});
});
children1[group.groupId] = {
type: "group",
children1: groupChildren,
} as RaqbChild;
});
return createAttributesQueryValue({
type: "group",
children1,
});
};
describe("getValueOfAttributeOption", () => {
it("should return non-array value for non-array input - It is a requirement for RAQB to not unnecessarily transform single value to an array of one item", () => {
const input = { value: "option1", isGroup: false, contains: [] };
const result = getValueOfAttributeOption(input);
expect(result).toEqual("option1");
});
it("should return flat array of values for simple options", () => {
const input = [
{ value: "option1", isGroup: false, contains: [] },
{ value: "option2", isGroup: false, contains: [] },
];
const result = getValueOfAttributeOption(input);
expect(result).toEqual(["option1", "option2"]);
});
it("should flatten nested options from contains array", () => {
const input = [
{
value: "group1",
isGroup: true,
contains: [
{ value: "suboption1", id: "opt-1", slug: "option-1" },
{ value: "suboption2", id: "opt-2", slug: "option-2" },
],
},
];
const result = getValueOfAttributeOption(input);
expect(result).toEqual(["suboption1", "suboption2"]);
});
it("should handle mix of simple and nested options", () => {
const input = [
{ value: "option1", isGroup: false, contains: [] },
{
value: "group1",
isGroup: true,
contains: [
{ value: "suboption1", id: "opt-1", slug: "option-1" },
{ value: "suboption2", id: "opt-2", slug: "option-2" },
],
},
{ value: "option2", isGroup: false, contains: [] },
];
const result = getValueOfAttributeOption(input);
expect(result).toEqual(["option1", "suboption1", "suboption2", "option2"]);
});
it("should remove duplicate values", () => {
const input = [
{ value: "option1", isGroup: false, contains: [] },
{
value: "group1",
isGroup: true,
contains: [
{ value: "option1", id: "opt-1", slug: "option-1" },
{ value: "suboption2", id: "opt-2", slug: "option-2" },
],
},
];
const result = getValueOfAttributeOption(input);
expect(result).toEqual(["option1", "suboption2"]);
});
it("should handle empty input array", () => {
const input: {
value: string;
isGroup: boolean;
contains: { value: string; id: string; slug: string }[];
}[] = [];
const result = getValueOfAttributeOption(input);
expect(result).toEqual([]);
});
it("should still use contains if contains is empty and isGroup=true", () => {
const input = [{ value: "group1", isGroup: true, contains: [] }];
const result = getValueOfAttributeOption(input);
expect(result).toEqual([]);
});
});
describe("resolveQueryValue", () => {
const mockFields = [
{ id: "location", type: RoutingFormFieldType.MULTI_SELECT, label: "Location", options: [] },
{ id: "city", type: RoutingFormFieldType.SINGLE_SELECT, label: "City", options: [] },
];
// Mock attributes for testing
const mockAttributes: Attribute[] = [
{
id: "attr-1",
name: "Location",
slug: "location",
type: AttributeType.MULTI_SELECT,
options: [
{ id: "opt-1", value: "New York", slug: "new-york" },
{ id: "opt-2", value: "London", slug: "london" },
],
},
{
id: "attr-2",
name: "City",
slug: "city",
type: AttributeType.SINGLE_SELECT,
options: [
{ id: "opt-3", value: "Mumbai", slug: "mumbai" },
{ id: "opt-4", value: "Delhi", slug: "delhi" },
],
},
];
describe("attribute option ID replacement", () => {
it("should replace attribute option IDs with lowercase values", () => {
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-1",
operator: "select_equals",
value: ["opt-1"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
field: "attr-1",
operator: "select_equals",
value: ["new york"], // Should be lowercase
}),
},
},
})
);
});
it("should replace multiple attribute option IDs in the same value array", () => {
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-1",
operator: "multiselect_some_in",
value: [["opt-1", "opt-2", "opt-3", "opt-4"]],
valueType: ["multiselect"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
field: "attr-1",
operator: "multiselect_some_in",
value: [["new york", "london", "mumbai", "delhi"]],
}),
},
},
})
);
});
it("should handle attribute option IDs in nested rules", () => {
const queryValue = createComplexQueryValue({
rules: [
{
ruleId: "rule1",
field: "attr-1",
operator: "select_equals",
value: ["opt-1"],
},
{
ruleId: "rule2",
field: "attr-2",
operator: "select_equals",
value: ["opt-3"],
},
],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: ["new york"],
}),
},
rule2: {
type: "rule",
properties: expect.objectContaining({
value: ["mumbai"],
}),
},
},
})
);
});
it("should replace attribute option IDs before processing field templates", () => {
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-1",
operator: "multiselect_some_in",
value: [["opt-1", "{field:location}"]],
valueType: ["multiselect"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Paris"], label: "Paris" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: [["new york", "paris"]], // opt-1 replaced with "new york", field template with "paris"
}),
},
},
})
);
});
it("should handle attribute option IDs with special characters", () => {
const specialAttributes: Attribute[] = [
{
id: "attr-special",
name: "Special",
slug: "special",
type: AttributeType.SINGLE_SELECT,
options: [
{ id: "opt-special-1", value: "São Paulo", slug: "sao-paulo" },
{ id: "opt-special-2", value: "Zürich", slug: "zurich" },
],
},
];
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-special",
operator: "select_equals",
value: ["opt-special-1"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: specialAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: ["são paulo"], // Should preserve special characters but be lowercase
}),
},
},
})
);
});
it("should not replace IDs that don't match any attribute options", () => {
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-1",
operator: "select_equals",
value: ["non-existent-id", "opt-1"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: ["non-existent-id", "new york"], // Only opt-1 should be replaced
}),
},
},
})
);
});
it("should handle empty attributes array", () => {
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-1",
operator: "select_equals",
value: ["opt-1"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: [], // No attributes
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: ["opt-1"], // Should remain unchanged
}),
},
},
})
);
});
it("should handle attributes with no options", () => {
const attributesWithoutOptions: Attribute[] = [
{
id: "attr-no-options",
name: "No Options",
slug: "no-options",
type: AttributeType.TEXT,
options: [], // Empty options array
},
];
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-no-options",
operator: "text_equals",
value: ["some-value"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: attributesWithoutOptions,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: ["some-value"], // Should remain unchanged
}),
},
},
})
);
});
it("should replace option IDs in complex nested group structures", () => {
const queryValue = createNestedGroupQueryValue({
groups: [
{
groupId: "group1",
rules: [
{
ruleId: "rule1",
field: "attr-1",
operator: "multiselect_some_in",
value: [["opt-1", "opt-2"]],
valueType: ["multiselect"],
},
{
ruleId: "rule2",
field: "attr-2",
operator: "select_equals",
value: ["opt-3"],
},
],
},
],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
group1: {
type: "group",
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: [["new york", "london"]],
}),
},
rule2: {
type: "rule",
properties: expect.objectContaining({
value: ["mumbai"],
}),
},
},
},
},
})
);
});
it("should handle option IDs mixed with field templates in double-nested arrays", () => {
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "attr-1",
operator: "multiselect_some_in",
value: [["opt-1", "{field:city}", "opt-2", "{field:location}"]],
valueType: ["multiselect"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
city: { value: "Tokyo", label: "Tokyo" },
location: { value: ["Berlin", "Paris"], label: "Berlin, Paris" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
value: [["new york", "tokyo", "london", "berlin", "paris"]],
}),
},
},
})
);
});
});
it("should handle simple field template replacement with single value", () => {
const queryValue = createQueryValueWithRule({
ruleId: "a8a89bba",
field: "city",
operator: "select_equals",
value: ["{field:city}"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
city: { label: "Mumbai", value: "Mumbai" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
a8a89bba: {
type: "rule",
properties: expect.objectContaining({
field: "city",
operator: "select_equals",
value: ["mumbai"],
}),
},
},
})
);
});
it("should handle field template in double-nested array for multiselect", () => {
const queryValue = createQueryValueWithRule({
ruleId: "rule1",
field: "location",
operator: "multiselect_some_in",
value: [["{field:location}"]],
valueType: ["multiselect"],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { label: "Delhi", value: "Delhi" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
field: "location",
operator: "multiselect_some_in",
value: [["delhi"]],
}),
},
},
})
);
});
it("should handle mixed array with field template and regular values", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "multiselect_some_in",
value: [["{field:location}", "Chennai"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: "Delhi", label: "Delhi" }, // Single value, not array
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "multiselect_some_in",
value: [["delhi", "Chennai"]], // Single value replaced
},
},
},
})
);
});
it("should handle multiple field templates in the same array", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "multifield",
operator: "multiselect_some_in",
value: [["{field:location}", "{field:city}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi"], label: "Delhi" },
city: { value: "Mumbai", label: "Mumbai" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "multifield",
operator: "multiselect_some_in",
value: [["delhi", "mumbai"]],
},
},
},
})
);
});
it("should handle field template with no response value", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "select_equals",
value: ["{field:location}"],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {}, // No response for location field
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "select_equals",
value: ["{field:location}"], // Should remain unchanged
},
},
},
})
);
});
it("should handle field template with unknown field", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "unknown",
operator: "select_equals",
value: ["{field:unknown}"],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
unknown: { value: "test", label: "test" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "unknown",
operator: "select_equals",
value: ["{field:unknown}"], // Should remain unchanged
},
},
},
})
);
});
it("should handle complex nested structure", () => {
const queryValue = createAttributesQueryValue({
id: "test",
type: "group",
children1: {
rule1: createQueryValueRule({
type: "rule",
properties: {
field: "attr1",
operator: "multiselect_some_in",
value: [["{field:location}", "Fixed-Value"]],
valueType: ["multiselect"],
},
}),
},
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi", "Haryana"], label: "Delhi, Haryana" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
id: "test",
type: "group",
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
field: "attr1",
value: [["delhi", "haryana", "Fixed-Value"]],
operator: "multiselect_some_in",
}),
},
},
})
);
});
it("should handle empty array values", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "multiselect_some_in",
value: [["{field:location}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: [], label: "" }, // Empty array
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "multiselect_some_in",
value: [[]], // Should be empty array
},
},
},
})
);
});
it("should handle no dynamicFieldValueOperands", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: ["{field:location}"],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: undefined,
attributes: mockAttributes,
});
expect(result).toEqual(queryValue); // Should return unchanged
});
it("should preserve case for non-template values in mixed arrays", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "multiselect_some_in",
value: [["{field:location}", "Chennai", "MUMBAI"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi"], label: "Delhi" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
operator: "multiselect_some_in",
value: [["delhi", "Chennai", "MUMBAI"]], // Only field template value is lowercased
},
},
},
})
);
});
it("should handle deeply nested field templates", () => {
const queryValue = createNestedGroupQueryValue({
groups: [
{
groupId: "group1",
rules: [
{
ruleId: "rule1",
field: "location",
value: [["{field:location}", "Fixed1"]],
valueType: ["multiselect"],
},
{
ruleId: "rule2",
field: "city",
value: [["{field:city}", "Fixed2"]],
valueType: ["multiselect"],
},
],
},
],
});
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi", "Mumbai"], label: "Delhi, Mumbai" },
city: { value: "Chennai", label: "Chennai" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
type: "group",
children1: {
group1: {
type: "group",
children1: {
rule1: {
type: "rule",
properties: expect.objectContaining({
field: "location",
value: [["delhi", "mumbai", "Fixed1"]],
}),
},
rule2: {
type: "rule",
properties: expect.objectContaining({
field: "city",
value: [["chennai", "Fixed2"]],
}),
},
},
},
},
})
);
});
it("should handle field template with array containing special characters", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["New York", "São Paulo", "Zürich"], label: "New York, São Paulo, Zürich" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["new york", "são paulo", "zürich"]],
},
},
},
})
);
});
it("should handle field template in arrays at different nesting levels", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "city",
value: ["{field:city}"], // Single nested
},
},
rule2: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}"]], // Double nested
},
},
rule3: {
type: "rule",
properties: {
field: "city",
value: [[["{field:city}"]]], // Triple nested
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi"], label: "Delhi" },
city: { value: "Mumbai", label: "Mumbai" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "city",
value: ["mumbai"],
},
},
rule2: {
type: "rule",
properties: {
field: "location",
value: [["delhi"]],
},
},
rule3: {
type: "rule",
properties: {
field: "city",
value: [[["mumbai"]]],
},
},
},
})
);
});
it("should handle null and undefined values gracefully", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "mixed",
value: [["{field:location}", null, "{field:city}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi"], label: "Delhi" },
city: { value: null as any, label: "" }, // null value
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "mixed",
value: [["delhi", null, "{field:city}"]], // city remains as template due to null value
},
},
},
})
);
});
it("should handle numeric values from fields", () => {
const numericFields = [
{ id: "age", type: RoutingFormFieldType.NUMBER, label: "Age", options: [] },
{ id: "count", type: RoutingFormFieldType.NUMBER, label: "Count", options: [] },
];
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "numbers",
value: [["{field:age}", "{field:count}", 42]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: numericFields,
response: {
age: { value: 25, label: "25" },
count: { value: 100, label: "100" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "numbers",
value: [["25", "100", 42]],
},
},
},
})
);
});
it("should handle very large arrays efficiently", () => {
const largeArray = new Array(100).fill("item");
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}", ...largeArray]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi", "Mumbai"], label: "Delhi, Mumbai" },
},
},
attributes: mockAttributes,
});
expect(result.children1?.rule1.properties?.value[0]).toHaveLength(102); // 2 from field + 100 items
expect(result.children1?.rule1.properties?.value[0].slice(0, 2)).toEqual(["delhi", "mumbai"]);
});
it("should handle edge case with empty string field values", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["", " ", "Delhi"], label: ", , Delhi" }, // Empty and whitespace strings
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["", " ", "delhi"]],
},
},
},
})
);
});
it("should handle field templates with hyphens and underscores in field names", () => {
const specialFields = [
{ id: "field-with-dashes", type: RoutingFormFieldType.TEXT, label: "Dashed Field", options: [] },
{
id: "field_with_underscores",
type: RoutingFormFieldType.TEXT,
label: "Underscored Field",
options: [],
},
];
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "special",
value: ["{field:field-with-dashes}", "{field:field_with_underscores}"],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: specialFields,
response: {
"field-with-dashes": { value: "Value2", label: "Value2" },
field_with_underscores: { value: "Value3", label: "Value3" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "special",
value: ["value2", "value3"],
},
},
},
})
);
});
it("should handle mixed field templates and fixed values in complex scenarios", () => {
const queryValue = {
children1: {
group1: {
type: "rule",
properties: {
field: "mixed1",
value: [["{field:location}", "Fixed1", "{field:city}"]],
},
},
group2: {
type: "rule",
properties: {
field: "mixed2",
value: [["Fixed2", "{field:location}"]],
},
},
group3: {
type: "rule",
properties: {
field: "mixed3",
value: [["{field:city}", "{field:location}", "Fixed3", "{field:city}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi", "Mumbai"], label: "Delhi, Mumbai" },
city: { value: "Chennai", label: "Chennai" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
group1: {
type: "rule",
properties: {
field: "mixed1",
value: [["delhi", "mumbai", "Fixed1", "chennai"]],
},
},
group2: {
type: "rule",
properties: {
field: "mixed2",
value: [["Fixed2", "delhi", "mumbai"]],
},
},
group3: {
type: "rule",
properties: {
field: "mixed3",
value: [["chennai", "delhi", "mumbai", "Fixed3", "chennai"]],
},
},
},
})
);
});
it("should handle field templates in different JSON value types", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "city",
value: "{field:city}",
},
},
rule2: {
type: "rule",
properties: {
field: "city",
value: ["{field:city}"],
},
},
rule3: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}"]],
},
},
rule4: {
type: "rule",
properties: {
field: "mixed",
customProp: "{field:city}",
nested: {
value: ["{field:location}"],
},
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: { value: ["Delhi", "Mumbai"], label: "Delhi, Mumbai" },
city: { value: "Chennai", label: "Chennai" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "city",
value: "chennai",
},
},
rule2: {
type: "rule",
properties: {
field: "city",
value: ["chennai"],
},
},
rule3: {
type: "rule",
properties: {
field: "location",
value: [["delhi", "mumbai"]],
},
},
rule4: {
type: "rule",
properties: {
field: "mixed",
customProp: "chennai",
nested: {
value: ["delhi", "mumbai"],
},
},
},
},
})
);
});
it("should handle empty response object", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}", "Fixed"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {}, // Empty response
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}", "Fixed"]], // Template unchanged
},
},
},
})
);
});
it("should handle response with empty field value object", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
location: {} as any, // Empty object, no value property
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "location",
value: [["{field:location}"]], // Template unchanged
},
},
},
})
);
});
it("should handle multiple occurrences of the same field template", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "city",
value: ["{field:city}"],
},
},
rule2: {
type: "rule",
properties: {
field: "city",
value: ["{field:city}"],
},
},
rule3: {
type: "rule",
properties: {
field: "city",
value: [["{field:city}", "{field:city}"]],
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
city: { value: "Mumbai", label: "Mumbai" },
},
},
attributes: mockAttributes,
});
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "city",
value: ["mumbai"],
},
},
rule2: {
type: "rule",
properties: {
field: "city",
value: ["mumbai"],
},
},
rule3: {
type: "rule",
properties: {
field: "city",
value: [["mumbai", "mumbai"]],
},
},
},
})
);
});
it("should handle invalid query structure gracefully", () => {
// Test with an invalid query structure
const invalidQueryValue = null as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue: invalidQueryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
city: { value: "Mumbai", label: "Mumbai" },
},
},
attributes: mockAttributes,
});
expect(result).toBeNull();
});
it("should handle valid JSON that causes processing errors gracefully", () => {
const queryValue = {
children1: {
rule1: {
type: "rule",
properties: {
field: "nonexistent",
value: ["{field:nonexistent}"],
},
},
rule2: {
type: "rule",
properties: {
field: "city",
customField: "{field:city}",
},
},
},
} as unknown as AttributesQueryValue;
const result = resolveQueryValue({
queryValue,
dynamicFieldValueOperands: {
fields: mockFields,
response: {
city: { value: "Mumbai", label: "Mumbai" },
},
},
attributes: mockAttributes,
});
// Non-existent field should remain as-is, existing field should be resolved
expect(result).toEqual(
expect.objectContaining({
children1: {
rule1: {
type: "rule",
properties: {
field: "nonexistent",
value: ["{field:nonexistent}"],
},
},
rule2: {
type: "rule",
properties: {
field: "city",
customField: "mumbai",
},
},
},
})
);
});
});