Files
calendar/packages/app-store/_utils/raqb/raqbUtils.test.ts
T
Benny JooGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
00e5770273 refactor: Move attribute and routing-forms code from app-store/lib to features (#26025)
* mv

* wip

* wip

* wip

* wip

* plural

* fix: resolve TypeScript type errors for attribute refactoring

- Add TransformedAttributeOption type to handle transformed contains field
- Update imports to use routing-forms Attribute type where needed
- Fix getValueOfAttributeOption to use TransformedAttributeOption type
- Update AttributeOptionValueWithType to use TransformedAttributeOption
- Fix pre-existing lint warnings (any -> unknown, proper type casting)

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

* fix

* cleanup

* cleanup

* fix

* fix

* fix

* fix

* fix

* fix: add explicit type annotation to reduce callback in getAttributes.ts

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

* fix: use RouterOutputs from @calcom/trpc/react for consistent type inference

- Update AppPage.tsx to use RouterOutputs from @calcom/trpc/react instead of inferRouterOutputs<AppRouter>
- Update MultiDisconnectIntegration.tsx to use the same RouterOutputs type source
- Add eslint-disable comments for pre-existing warnings (useEffect deps, img elements)

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

* fix: explicitly type attribute property in AttributeOptionAssignment

The attribute property from Pick<AttributeOption, 'attribute'> was typed as
'unknown' because Prisma relations don't have explicit types when picked.
This explicitly defines the attribute shape with id, type, and isLocked
properties that are used in assignValueToUser.ts.

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

* fix: remove unused assignedUsers from AttributeOptionAssignment Pick

The assignedUsers property was not being used in the code but was required
by the Pick type, causing a type mismatch when the actual data from the
database query didn't include it.

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

* fix: update import path for AttributeOptionAssignment and BulkAttributeAssigner types

The types.d.ts file was moved from packages/lib/service/attribute/ to
packages/app-store/routing-forms/types/. Updated the import path in utils.ts
to point to the new location.

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

* fix

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-12-29 21:51:06 -03:00

1775 lines
44 KiB
TypeScript

import { expect, describe, it, vi } from "vitest";
import type { Attribute } from "@calcom/app-store/routing-forms/types/types";
import type { AttributesQueryValue, RaqbChild } from "@calcom/lib/raqb/types";
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",
},
},
},
})
);
});
});