From 8e19b7e40206e953e763e16eb66a7e43f6fd8822 Mon Sep 17 00:00:00 2001 From: MAN$I VERMA Date: Sat, 25 Jul 2026 12:47:08 +0530 Subject: [PATCH] feat: Extend automated ingredient validation to include argSelector ingredients (#2641) (#2643) --- src/core/Ingredient.mjs | 24 +++++++++++++- .../operations/AutomatedValidationTestOp.mjs | 17 ++++++++++ src/node/api.mjs | 4 +++ .../operations/tests/AutomatedValidation.mjs | 33 +++++++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/core/Ingredient.mjs b/src/core/Ingredient.mjs index 1c0f0cc3..57c3efa6 100644 --- a/src/core/Ingredient.mjs +++ b/src/core/Ingredient.mjs @@ -86,6 +86,9 @@ class Ingredient { if (this.type === "option" && Array.isArray(checkVal)) { checkVal = checkVal[this.defaultIndex ?? 0]; } + if (this.type === "argSelector" && Array.isArray(checkVal)) { + checkVal = checkVal[this.defaultIndex ?? 0]?.name || ""; + } // 1. check if empty let isEmpty = false; @@ -99,8 +102,10 @@ class Ingredient { let isAllowedOptionEmpty = false; if (this.type === "option" && Array.isArray(this.defaultValue)) { isAllowedOptionEmpty = this.defaultValue.includes(""); + } else if (this.type === "argSelector" && Array.isArray(this.defaultValue)) { + isAllowedOptionEmpty = this.defaultValue.some(opt => opt.name === ""); } - if (this.allowEmpty === false || (this.type === "option" && !isAllowedOptionEmpty)) { + if (this.allowEmpty === false || ((this.type === "option" || this.type === "argSelector") && !isAllowedOptionEmpty)) { throw new OperationError(`${this.name} cannot be empty.`); } return true; @@ -150,6 +155,23 @@ class Ingredient { } } + // 5. argSelector checks + if (this.type === "argSelector") { + if (Array.isArray(this.defaultValue)) { + const permittedOptions = this.defaultValue + .map(opt => opt.name) + .filter(optName => { + if (typeof optName !== "string") return false; + return !optName.match(/^\[\/?[a-z0-9 -()^]+\]$/i); + }); + const valStr = (checkVal !== null && checkVal !== undefined) ? String(checkVal).toLowerCase() : ""; + const matchedOption = permittedOptions.find(opt => opt.toLowerCase() === valStr); + if (!matchedOption) { + throw new OperationError(`${this.name} must be one of the following: ${permittedOptions.join(", ")}.`); + } + } + } + return true; } diff --git a/src/core/operations/AutomatedValidationTestOp.mjs b/src/core/operations/AutomatedValidationTestOp.mjs index 92f803ae..52af20c2 100644 --- a/src/core/operations/AutomatedValidationTestOp.mjs +++ b/src/core/operations/AutomatedValidationTestOp.mjs @@ -66,6 +66,23 @@ class AutomatedValidationTestOp extends Operation { "type": "option", "value": ["[Group 1]", "Option 1", "Option 2", "[/Group 1]", "[Group 2]", "Option 3", "[/Group 2]"], "allowEmpty": false + }, + { + "name": "Arg Selector Ingredient", + "type": "argSelector", + "value": [ + { + name: "Option 1", + on: [0], + off: [1] + }, + { + name: "Option 2", + on: [1], + off: [0] + } + ], + "allowEmpty": false } ]; } diff --git a/src/node/api.mjs b/src/node/api.mjs index 83163d37..a48ed3a9 100644 --- a/src/node/api.mjs +++ b/src/node/api.mjs @@ -77,6 +77,10 @@ function transformArgs(opArgsList, newArgs) { return !Array.isArray(arg.value) ? arg.value : arg.value[arg.defaultIndex ?? 0]; } + if (arg.type === "argSelector") { + return !Array.isArray(arg.value) ? arg.value : (arg.value[arg.defaultIndex ?? 0]?.name || ""); + } + if (arg.type === "editableOption") { return typeof arg.value === "string" ? arg.value : arg.value[arg.defaultIndex ?? 0].value; } diff --git a/tests/operations/tests/AutomatedValidation.mjs b/tests/operations/tests/AutomatedValidation.mjs index f55efa01..6190e2d2 100644 --- a/tests/operations/tests/AutomatedValidation.mjs +++ b/tests/operations/tests/AutomatedValidation.mjs @@ -150,5 +150,38 @@ TestRegister.addTests([ args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, ""] } ] + }, + { + name: "Automated Validation: Valid Arg Selector value", + input: "test", + expectedOutput: "Success", + recipeConfig: [ + { + op: "Automated Validation Test Op", + args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, "Option 1", "Option 2"] + } + ] + }, + { + name: "Automated Validation: Invalid Arg Selector value", + input: "test", + expectedOutput: "Arg Selector Ingredient must be one of the following: Option 1, Option 2.", + recipeConfig: [ + { + op: "Automated Validation Test Op", + args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, "Option 1", "Option 3"] + } + ] + }, + { + name: "Automated Validation: Arg Selector value empty (invalid)", + input: "test", + expectedOutput: "Arg Selector Ingredient cannot be empty.", + recipeConfig: [ + { + op: "Automated Validation Test Op", + args: [5, 1.5, "hello", "", { "option": "Option A", "string": "test" }, "Option 1", ""] + } + ] } ]);