diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.test.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.test.tsx new file mode 100644 index 000000000000..789ba078133c --- /dev/null +++ b/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.test.tsx @@ -0,0 +1,89 @@ +// Tests for AccordionPromptComponent functionality +// Testing the actual generateUniqueVariableName function from the component + +import { generateUniqueVariableName } from "./index"; + +describe("AccordionPromptComponent", () => { + describe("generateUniqueVariableName", () => { + it("should return 'variable_name' when template is empty", () => { + const result = generateUniqueVariableName(""); + expect(result).toBe("variable_name"); + }); + + it("should return 'variable_name' when no variables exist", () => { + const result = generateUniqueVariableName("Hello world!"); + expect(result).toBe("variable_name"); + }); + + it("should return 'variable_name' when only other variables exist", () => { + const result = generateUniqueVariableName( + "Hello {name}, your {id} is ready", + ); + expect(result).toBe("variable_name"); + }); + + it("should return 'variable_name_1' when 'variable_name' already exists", () => { + const result = generateUniqueVariableName("Hello {variable_name}!"); + expect(result).toBe("variable_name_1"); + }); + + it("should return 'variable_name_2' when 'variable_name' and 'variable_name_1' exist", () => { + const result = generateUniqueVariableName( + "Hello {variable_name} and {variable_name_1}!", + ); + expect(result).toBe("variable_name_2"); + }); + + it("should return 'variable_name_3' when first three exist", () => { + const result = generateUniqueVariableName( + "{variable_name}{variable_name_1}{variable_name_2}", + ); + expect(result).toBe("variable_name_3"); + }); + + it("should fill gaps - return 'variable_name_1' when only 'variable_name' and 'variable_name_2' exist", () => { + const result = generateUniqueVariableName( + "Hello {variable_name} and {variable_name_2}!", + ); + expect(result).toBe("variable_name_1"); + }); + + it("should handle mixed variables with 'variable_name' present", () => { + const result = generateUniqueVariableName( + "User: {name}, Template: {variable_name}, ID: {id}", + ); + expect(result).toBe("variable_name_1"); + }); + + it("should handle variables with similar names but not exact match", () => { + const result = generateUniqueVariableName("Hello {variable_name_extra}!"); + expect(result).toBe("variable_name"); + }); + + it("should handle multiple occurrences of the same variable", () => { + const result = generateUniqueVariableName( + "{variable_name} is {variable_name} and {variable_name}", + ); + expect(result).toBe("variable_name_1"); + }); + + it("should work with newlines in template", () => { + const result = generateUniqueVariableName( + "Line 1: {variable_name}\nLine 2: {variable_name_1}", + ); + expect(result).toBe("variable_name_2"); + }); + + it("should handle complex template with many variable types", () => { + const template = ` + Name: {name} + Age: {age} + Default: {variable_name} + Second: {variable_name_1} + City: {city} + `; + const result = generateUniqueVariableName(template); + expect(result).toBe("variable_name_2"); + }); + }); +}); diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx index eb60f2df54bc..d516e815b634 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx @@ -14,6 +14,31 @@ import type { InputProps, PromptAreaComponentType } from "../../types"; import useAlertStore from "@/stores/alertStore"; import { usePostValidatePrompt } from "@/controllers/API/queries/nodes/use-post-validate-prompt"; +/** + * Generates a unique variable name for the prompt template. + * If "variable_name" doesn't exist, returns it. + * Otherwise, returns "variable_name_1", "variable_name_2", etc. + */ +export const generateUniqueVariableName = (templateValue: string): string => { + const variableRegex = /\{([^{}]+)\}/g; + const existingVariables = new Set(); + let match: RegExpExecArray | null; + while ((match = variableRegex.exec(templateValue)) !== null) { + existingVariables.add(match[1]); + } + + let variableName = "variable_name"; + if (existingVariables.has(variableName)) { + let counter = 1; + while (existingVariables.has(`variable_name_${counter}`)) { + counter++; + } + variableName = `variable_name_${counter}`; + } + + return variableName; +}; + export default function AccordionPromptComponent({ field_name, nodeClass, @@ -369,7 +394,9 @@ export default function AccordionPromptComponent({ if (disabled || readonly || !contentEditableRef.current) return; isTypingRef.current = true; - const variableText = "{variable_name}"; + + const variableName = generateUniqueVariableName(internalValue); + const variableText = `{${variableName}}`; // Get current cursor position or end of text let insertPosition = internalValue.length;