From bf98d61ea6fbab5f9f8a51bf1cc4b930abd79259 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 11 Sep 2024 11:35:46 +0100 Subject: [PATCH] add tests for new binding update code --- .../SetupPanel/AutomationBlockSetup.svelte | 3 - .../src/helpers/automations/nameHelpers.js | 59 ++++-- .../src/helpers/tests/nameHelpers.spec.js | 177 ++++++++++++++++++ .../builder/src/stores/builder/automations.js | 6 +- 4 files changed, 224 insertions(+), 21 deletions(-) create mode 100644 packages/builder/src/helpers/tests/nameHelpers.spec.js diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 1bd65c82f8..1e2e409243 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -49,7 +49,6 @@ import { getSchemaForDatasourcePlus, getEnvironmentBindings, - runtimeToReadableBinding, } from "dataBinding" import { TriggerStepID, ActionStepID } from "constants/backend/automations" import { onMount } from "svelte" @@ -677,7 +676,6 @@ const field = Object.values(FIELDS).find( field => field.type === value.type && field.subtype === value.subtype ) - console.log(bindingName) return { readableBinding: bindingName && !isLoopBlock @@ -710,7 +708,6 @@ !isLoopBlock ? allSteps[idx]?.name : allSteps[idx - 1]?.name - console.log(idx == 4 && bindingName) if (isLoopBlock) { schema = { diff --git a/packages/builder/src/helpers/automations/nameHelpers.js b/packages/builder/src/helpers/automations/nameHelpers.js index da5c999b2f..b863796a54 100644 --- a/packages/builder/src/helpers/automations/nameHelpers.js +++ b/packages/builder/src/helpers/automations/nameHelpers.js @@ -1,40 +1,71 @@ import { AutomationActionStepId } from "@budibase/types" -export const updateBindingsInInputs = (inputs, oldName, newName) => { +export const updateBindingsInInputs = (inputs, oldName, newName, stepIndex) => { if (typeof inputs === "string") { - return inputs.replace( - new RegExp(`stepsByName\\.${oldName}\\.`, "g"), - `stepsByName.${newName}.` - ) + return inputs + .replace( + new RegExp(`stepsByName\\.${oldName}\\.`, "g"), + `stepsByName.${newName}.` + ) + .replace( + new RegExp(`steps\\.${stepIndex}\\.`, "g"), + `stepsByName.${newName}.` + ) } if (Array.isArray(inputs)) { - return inputs.map(item => updateBindingsInInputs(item, oldName, newName)) + return inputs.map(item => + updateBindingsInInputs(item, oldName, newName, stepIndex) + ) } if (typeof inputs === "object" && inputs !== null) { const updatedInputs = {} for (const [key, value] of Object.entries(inputs)) { - updatedInputs[key] = updateBindingsInInputs(value, oldName, newName) + const updatedKey = updateBindingsInInputs( + key, + oldName, + newName, + stepIndex + ) + updatedInputs[updatedKey] = updateBindingsInInputs( + value, + oldName, + newName, + stepIndex + ) } return updatedInputs } - return inputs } -export const updateBindingsInSteps = (steps, oldName, newName) => { +export const updateBindingsInSteps = ( + steps, + oldName, + newName, + changedStepIndex +) => { return steps.map(step => { const updatedStep = { ...step, - inputs: updateBindingsInInputs(step.inputs, oldName, newName), + inputs: updateBindingsInInputs( + step.inputs, + oldName, + newName, + changedStepIndex + ), } - // Handle branch steps if ("branches" in updatedStep.inputs) { updatedStep.inputs.branches = updatedStep.inputs.branches.map(branch => ({ ...branch, - condition: updateBindingsInInputs(branch.condition, oldName, newName), + condition: updateBindingsInInputs( + branch.condition, + oldName, + newName, + changedStepIndex + ), })) if (updatedStep.inputs.children) { @@ -44,7 +75,8 @@ export const updateBindingsInSteps = (steps, oldName, newName) => { updatedStep.inputs.children[key] = updateBindingsInSteps( childSteps, oldName, - newName + newName, + changedStepIndex ) } } @@ -53,7 +85,6 @@ export const updateBindingsInSteps = (steps, oldName, newName) => { return updatedStep }) } - export const getNewStepName = (automation, step) => { const baseName = step.name diff --git a/packages/builder/src/helpers/tests/nameHelpers.spec.js b/packages/builder/src/helpers/tests/nameHelpers.spec.js new file mode 100644 index 0000000000..1ce2d1987a --- /dev/null +++ b/packages/builder/src/helpers/tests/nameHelpers.spec.js @@ -0,0 +1,177 @@ +import { cloneDeep } from "lodash" +import { + updateBindingsInInputs, + updateBindingsInSteps, +} from "../automations/nameHelpers" +describe("Automation Binding Update Functions", () => { + const sampleAutomation = { + definition: { + steps: [ + { + name: "First Step", + inputs: { + text: "Starting automation", + }, + id: "step1", + }, + { + name: "Second Step", + inputs: { + text: "{{ steps.0.success }} and {{ stepsByName.First Step.message }}", + }, + id: "step2", + }, + { + name: "Branch", + inputs: { + branches: [ + { + name: "branch1", + condition: { + equal: { + "steps.1.success": true, + }, + }, + }, + ], + children: { + branch1: [ + { + name: "Nested Step", + inputs: { + text: "{{ stepsByName.Second Step.message }} and {{ steps.1.success }}", + }, + id: "nestedStep", + }, + ], + }, + }, + id: "branchStep", + }, + ], + stepNames: { + step1: "First Step", + step2: "Second Step", + branchStep: "Branch", + }, + }, + } + + it("updateBindingsInInputs updates string bindings correctly", () => { + const input = "{{ stepsByName.oldName.success }} and {{ steps.1.message }}" + const result = updateBindingsInInputs(input, "oldName", "newName", 1) + expect(result).toBe( + "{{ stepsByName.newName.success }} and {{ stepsByName.newName.message }}" + ) + }) + + it("updateBindingsInInputs handles nested objects", () => { + const input = { + text: "{{ stepsByName.oldName.success }}", + nested: { + value: "{{ steps.1.message }}", + }, + } + const result = updateBindingsInInputs(input, "oldName", "newName", 1) + expect(result).toEqual({ + text: "{{ stepsByName.newName.success }}", + nested: { + value: "{{ stepsByName.newName.message }}", + }, + }) + }) + + it("updateBindingsInSteps updates bindings in all steps", () => { + const steps = cloneDeep(sampleAutomation.definition.steps) + const result = updateBindingsInSteps( + steps, + "Second Step", + "Renamed Step", + 1 + ) + + expect(result[1].name).toBe("Second Step") + + expect(result[2].inputs.branches[0].condition.equal).toEqual({ + "stepsByName.Renamed Step.success": true, + }) + + const nestedStepText = result[2].inputs.children.branch1[0].inputs.text + expect(nestedStepText).toBe( + "{{ stepsByName.Renamed Step.message }} and {{ stepsByName.Renamed Step.success }}" + ) + }) + + it("updateBindingsInSteps handles steps with no bindings", () => { + const steps = [ + { + name: "No Binding Step", + inputs: { + text: "Plain text", + }, + id: "noBindingStep", + }, + ] + const result = updateBindingsInSteps(steps, "Old Name", "New Name", 0) + expect(result).toEqual(steps) + }) + + it("updateBindingsInSteps updates bindings in deeply nested branches", () => { + const deeplyNestedStep = { + name: "Deep Branch", + inputs: { + branches: [ + { + name: "deepBranch", + condition: { + equal: { + "stepsByName.Second Step.success": true, + }, + }, + }, + ], + children: { + deepBranch: [ + { + name: "Deep Log", + inputs: { + text: "{{ steps.1.message }}", + }, + }, + ], + }, + }, + } + + const steps = [...sampleAutomation.definition.steps, deeplyNestedStep] + const result = updateBindingsInSteps( + steps, + "Second Step", + "Renamed Step", + 1 + ) + + expect( + result[3].inputs.branches[0].condition.equal[ + "stepsByName.Renamed Step.success" + ] + ).toBe(true) + expect(result[3].inputs.children.deepBranch[0].inputs.text).toBe( + "{{ stepsByName.Renamed Step.message }}" + ) + }) + + it("updateBindingsInSteps does not affect unrelated bindings", () => { + const steps = cloneDeep(sampleAutomation.definition.steps) + const result = updateBindingsInSteps( + steps, + "Second Step", + "Renamed Step", + 1 + ) + + expect(result[1].inputs.text).toBe( + "{{ steps.0.success }} and {{ stepsByName.First Step.message }}" + ) + }) +}) diff --git a/packages/builder/src/stores/builder/automations.js b/packages/builder/src/stores/builder/automations.js index eaf565c4f2..6627c67080 100644 --- a/packages/builder/src/stores/builder/automations.js +++ b/packages/builder/src/stores/builder/automations.js @@ -321,20 +321,18 @@ const automationActions = store => ({ const oldName = newAutomation.definition.steps[stepIndex].name const newName = name.trim() - // Update stepNames newAutomation.definition.stepNames = { ...newAutomation.definition.stepNames, [blockId]: newName, } - // Update the name in the step itself newAutomation.definition.steps[stepIndex].name = newName - // Update bindings in all steps newAutomation.definition.steps = updateBindingsInSteps( newAutomation.definition.steps, oldName, - newName + newName, + stepIndex ) await store.actions.save(newAutomation)