From 511d8c36f6b26a41347b7e3a00fb363b802cb16c Mon Sep 17 00:00:00 2001 From: Michiel Degezelle Date: Tue, 25 Nov 2025 08:35:31 +0100 Subject: [PATCH 1/7] Run tests with status in parallel for multiple handles --- bin/cli.js | 16 +++++++---- lib/liquidTestRunner.js | 61 +++++++++++++++++++++++++++++------------ package.json | 2 +- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index d4e826c9..5700143f 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -451,15 +451,15 @@ program .command("run-test") .description("Run Liquid Tests for a reconciliation template from a YAML file") .requiredOption("-f, --firm ", "Specify the firm to be used", firmIdDefault) - .option("-h, --handle ", "Specify the reconciliation to be used (mandatory)") - .option("-at, --account-template ", "Specify the account template to be used (mandatory)") + .option("-h, --handle ", "Specify one or more reconciliations to be used (mandatory)") + .option("-at, --account-template ", "Specify one or more account templates to be used (mandatory)") .option("-t, --test ", "Specify the name of the test to be run (optional)", "") .option("--html-input", "Get a static html of the input-view of the template generated with the Liquid Test data (optional)", false) .option("--html-preview", "Get a static html of the export-view of the template generated with the Liquid Test data (optional)", false) .option("--preview-only", "Skip the checking of the results of the Liquid Test in case you only want to generate a preview template (optional)", false) .option("--status", "Only return the status of the test runs as PASSED/FAILED (optional)", false) - .action((options) => { + .action(async (options) => { if (!options.handle && !options.accountTemplate) { consola.error("You need to specify either a reconciliation handle or an account template"); process.exit(1); @@ -468,14 +468,20 @@ program const templateType = options.handle ? "reconciliationText" : "accountTemplate"; const templateName = options.handle ? options.handle : options.accountTemplate; + // Check if multiple handles are provided without --status flag + if (templateName.length > 1 && !options.status) { + consola.error("Multiple handles are only allowed when used with the --status flag"); + process.exit(1); + } + if (options.status) { - liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test); + await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test); } else { if (options.previewOnly && !options.htmlInput && !options.htmlPreview) { consola.info(`When using "--preview-only" you need to specify at least one of the following options: "--html-input", "--html-preview"`); process.exit(1); } - liquidTestRunner.runTestsWithOutput(options.firm, templateType, templateName, options.test, options.previewOnly, options.htmlInput, options.htmlPreview); + await liquidTestRunner.runTestsWithOutput(options.firm, templateType, templateName, options.test, options.previewOnly, options.htmlInput, options.htmlPreview); } }); diff --git a/lib/liquidTestRunner.js b/lib/liquidTestRunner.js index 9ade06da..74238ecf 100644 --- a/lib/liquidTestRunner.js +++ b/lib/liquidTestRunner.js @@ -427,27 +427,54 @@ async function runTestsStatusOnly(firmId, templateType, handle, testName = "") { process.exit(1); } - let status = "FAILED"; - const testResult = await runTests(firmId, templateType, handle, testName, false, "none"); + // handle can be an array (multiple handles/templates) or a string (single handle/template) + const handles = Array.isArray(handle) ? handle : [handle]; - if (!testResult) { - status = "PASSED"; - consola.success(status); - return status; - } - - const testRun = testResult?.testRun; + const runSingleHandle = async (singleHandle) => { + let status = "FAILED"; + const failedTestNames = []; + const testResult = await runTests(firmId, templateType, singleHandle, testName, false, "none"); - if (testRun && testRun?.status === "completed") { - const errorsPresent = checkAllTestsErrorsPresent(testRun.tests); - if (errorsPresent === false) { + if (!testResult) { status = "PASSED"; - consola.success(status); - return status; + } else { + const testRun = testResult?.testRun; + + if (testRun && testRun?.status === "completed") { + const errorsPresent = checkAllTestsErrorsPresent(testRun.tests); + if (errorsPresent === false) { + status = "PASSED"; + } else { + // Extract failed test names + const testNames = Object.keys(testRun.tests).sort(); + testNames.forEach((testName) => { + const testErrorsPresent = checkTestErrorsPresent(testName, testRun.tests); + if (testErrorsPresent) { + failedTestNames.push(testName); + } + }); + } + } } - } - consola.error(status); - return status; + + if (status === "PASSED") { + consola.success(`${singleHandle}: ${status}`); + } else { + consola.error(`${singleHandle}: ${status}`); + // Display failed test names + failedTestNames.forEach((testName) => { + consola.log(` ${testName}: FAILED`); + }); + } + + return { handle: singleHandle, status, failedTestNames }; + }; + + const results = await Promise.all(handles.map(runSingleHandle)); + + const overallStatus = results.every((result) => result.status === "PASSED") ? "PASSED" : "FAILED"; + + return overallStatus; } module.exports = { diff --git a/package.json b/package.json index c05da0ff..5e247514 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "silverfin-cli", - "version": "1.47.0", + "version": "1.50.0", "description": "Command line tool for Silverfin template development", "main": "index.js", "license": "MIT", From a5eff1e3332d9b090c5a45420be437ad89be56be Mon Sep 17 00:00:00 2001 From: Michiel Degezelle Date: Wed, 26 Nov 2025 16:32:26 +0100 Subject: [PATCH 2/7] Don't transform the handle string into an array in case of a non-status test run --- bin/cli.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 5700143f..78a29294 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -468,21 +468,30 @@ program const templateType = options.handle ? "reconciliationText" : "accountTemplate"; const templateName = options.handle ? options.handle : options.accountTemplate; - // Check if multiple handles are provided without --status flag - if (templateName.length > 1 && !options.status) { - consola.error("Multiple handles are only allowed when used with the --status flag"); + // Normalize to array for uniform handling (Commander returns array for variadic, string otherwise) + const templateNames = Array.isArray(templateName) ? templateName : [templateName]; + + // Block multiple handles/templates without --status + if (templateNames.length > 1 && !options.status) { + consola.error("Multiple handles/templates are only allowed when used with the --status flag"); process.exit(1); } if (options.status) { - await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test); - } else { - if (options.previewOnly && !options.htmlInput && !options.htmlPreview) { - consola.info(`When using "--preview-only" you need to specify at least one of the following options: "--html-input", "--html-preview"`); - process.exit(1); - } - await liquidTestRunner.runTestsWithOutput(options.firm, templateType, templateName, options.test, options.previewOnly, options.htmlInput, options.htmlPreview); + // Status mode: allow multiple, pass array of template names + await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateNames, options.test); + return; + } + + // Non-status mode: always run a single template, pass string handle/name + const singleTemplateName = templateNames[0]; + + if (options.previewOnly && !options.htmlInput && !options.htmlPreview) { + consola.info(`When using "--preview-only" you need to specify at least one of the following options: "--html-input", "--html-preview"`); + process.exit(1); } + + await liquidTestRunner.runTestsWithOutput(options.firm, templateType, singleTemplateName, options.test, options.previewOnly, options.htmlInput, options.htmlPreview); }); // Create Liquid Test From e9ee2790bb1e9b332ca81151f9db79b2c7bde8fe Mon Sep 17 00:00:00 2001 From: Michiel Degezelle Date: Wed, 3 Dec 2025 13:35:34 +0100 Subject: [PATCH 3/7] Bump version to correct number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e247514..6c39a3e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "silverfin-cli", - "version": "1.50.0", + "version": "1.48.0", "description": "Command line tool for Silverfin template development", "main": "index.js", "license": "MIT", From 0345934a0460a5849bb4765d29489da6037a53c7 Mon Sep 17 00:00:00 2001 From: Michiel Degezelle Date: Fri, 5 Dec 2025 13:56:39 +0100 Subject: [PATCH 4/7] Review comments --- bin/cli.js | 10 +++------- lib/liquidTestRunner.js | 9 +++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 78a29294..768a9200 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -467,24 +467,20 @@ program const templateType = options.handle ? "reconciliationText" : "accountTemplate"; const templateName = options.handle ? options.handle : options.accountTemplate; - - // Normalize to array for uniform handling (Commander returns array for variadic, string otherwise) - const templateNames = Array.isArray(templateName) ? templateName : [templateName]; - // Block multiple handles/templates without --status - if (templateNames.length > 1 && !options.status) { + if (templateName.length > 1 && !options.status) { consola.error("Multiple handles/templates are only allowed when used with the --status flag"); process.exit(1); } if (options.status) { // Status mode: allow multiple, pass array of template names - await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateNames, options.test); + await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test); return; } // Non-status mode: always run a single template, pass string handle/name - const singleTemplateName = templateNames[0]; + const singleTemplateName = templateName[0]; if (options.previewOnly && !options.htmlInput && !options.htmlPreview) { consola.info(`When using "--preview-only" you need to specify at least one of the following options: "--html-input", "--html-preview"`); diff --git a/lib/liquidTestRunner.js b/lib/liquidTestRunner.js index 74238ecf..f44bfd24 100644 --- a/lib/liquidTestRunner.js +++ b/lib/liquidTestRunner.js @@ -421,15 +421,12 @@ async function runTestsWithOutput(firmId, templateType, handle, testName = "", p // RETURN (AND LOG) ONLY PASSED OR FAILED // CAN BE USED BY GITHUB ACTIONS -async function runTestsStatusOnly(firmId, templateType, handle, testName = "") { +async function runTestsStatusOnly(firmId, templateType, handles, testName = "") { if (templateType !== "reconciliationText" && templateType !== "accountTemplate") { consola.error(`Template type is missing or invalid`); process.exit(1); } - // handle can be an array (multiple handles/templates) or a string (single handle/template) - const handles = Array.isArray(handle) ? handle : [handle]; - const runSingleHandle = async (singleHandle) => { let status = "FAILED"; const failedTestNames = []; @@ -458,9 +455,9 @@ async function runTestsStatusOnly(firmId, templateType, handle, testName = "") { } if (status === "PASSED") { - consola.success(`${singleHandle}: ${status}`); + consola.log(`${singleHandle}: ${status}`); } else { - consola.error(`${singleHandle}: ${status}`); + consola.log(`${singleHandle}: ${status}`); // Display failed test names failedTestNames.forEach((testName) => { consola.log(` ${testName}: FAILED`); From 744946d7b1a97c1a05e1f834f30931dc0ac5231a Mon Sep 17 00:00:00 2001 From: Michiel Degezelle Date: Fri, 5 Dec 2025 16:49:32 +0100 Subject: [PATCH 5/7] Add additional warning in case no handles are provided --- bin/cli.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/cli.js b/bin/cli.js index 768a9200..21511eb1 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -467,6 +467,12 @@ program const templateType = options.handle ? "reconciliationText" : "accountTemplate"; const templateName = options.handle ? options.handle : options.accountTemplate; + + if (!templateName || templateName.length === 0) { + consola.error("You need to provide at least one handle or account template name"); + process.exit(1); + } + // Block multiple handles/templates without --status if (templateName.length > 1 && !options.status) { consola.error("Multiple handles/templates are only allowed when used with the --status flag"); From 76583cbd5273e7644e91c80aa22338099791006e Mon Sep 17 00:00:00 2001 From: Michiel Degezelle Date: Mon, 12 Jan 2026 11:12:05 +0100 Subject: [PATCH 6/7] Add pipe separation logic --- bin/cli.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bin/cli.js b/bin/cli.js index 21511eb1..3fa71af7 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -466,7 +466,12 @@ program } const templateType = options.handle ? "reconciliationText" : "accountTemplate"; - const templateName = options.handle ? options.handle : options.accountTemplate; + let templateName = options.handle ? options.handle : options.accountTemplate; + + // Support pipe-separated values: if single string contains pipes, split it + if (templateName.length === 1 && typeof templateName[0] === 'string' && templateName[0].includes('|')) { + templateName = templateName[0].split('|').map(name => name.trim()).filter(name => name.length > 0); + } if (!templateName || templateName.length === 0) { consola.error("You need to provide at least one handle or account template name"); From 6e15195a445be9cf6a4a5361a9b2cf6570ceec23 Mon Sep 17 00:00:00 2001 From: Michiel Degezelle Date: Mon, 12 Jan 2026 11:30:34 +0100 Subject: [PATCH 7/7] Bump version and add changelog --- CHANGELOG.md | 4 ++++ package-lock.json | 22 +++++++++++----------- package.json | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41ed43c1..33d9e2de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [1.52.0] (12/01/2026) +This update improves test execution performance when running tests with status checks across multiple template handles. +Tests are now run in parallel for multiple handles when using the `--status` flag, significantly reducing the overall execution time. Previously, tests with status checks for multiple handles would run sequentially, but now they leverage parallel processing for better efficiency. This change only affects the `silverfin run-test` command when both multiple handles and the status flag are used together. + ## [1.51.0] (08/01/2026) This update should have no user impact whatsoever. diff --git a/package-lock.json b/package-lock.json index 446f8bb2..687d65ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "silverfin-cli", - "version": "1.51.0", + "version": "1.52.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "silverfin-cli", - "version": "1.51.0", + "version": "1.52.0", "license": "MIT", "dependencies": { "axios": "^1.6.2", @@ -1265,9 +1265,9 @@ } }, "node_modules/@types/node": { - "version": "25.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", - "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "version": "25.0.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.6.tgz", + "integrity": "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1561,9 +1561,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "version": "2.9.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", + "integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1690,9 +1690,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001762", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", - "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", + "version": "1.0.30001764", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", + "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index 875bce4b..2f115c60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "silverfin-cli", - "version": "1.51.0", + "version": "1.52.0", "description": "Command line tool for Silverfin template development", "main": "index.js", "license": "MIT",