diff --git a/CHANGELOG.md b/CHANGELOG.md index 41ed43c..33d9e2d 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/bin/cli.js b/bin/cli.js index 5dd5385..4586a4b 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -453,8 +453,8 @@ 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) @@ -462,7 +462,7 @@ program .option("--status", "Only return the status of the test runs as PASSED/FAILED (optional)", false) .option("-p, --pattern ", "Run all tests that match this pattern (optional)", "") - .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); @@ -476,15 +476,41 @@ 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"); + process.exit(1); + } + if (options.status) { - liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test, options.pattern); - } 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, options.pattern); + // Status mode: allow multiple, pass array of template names + await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test, options.pattern); + return; + } + + // Non-status mode: always run a single template, pass string handle/name + 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"`); + process.exit(1); } + + await liquidTestRunner.runTestsWithOutput( + options.firm, + templateType, + singleTemplateName, + options.test, + options.previewOnly, + options.htmlInput, + options.htmlPreview, + options.pattern + ); }); // Create Liquid Test diff --git a/lib/liquidTestRunner.js b/lib/liquidTestRunner.js index b8716a2..25f84df 100644 --- a/lib/liquidTestRunner.js +++ b/lib/liquidTestRunner.js @@ -469,33 +469,58 @@ 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 = "", pattern = "") { +async function runTestsStatusOnly(firmId, templateType, handles, testName = "", pattern = "") { if (templateType !== "reconciliationText" && templateType !== "accountTemplate") { consola.error(`Template type is missing or invalid`); process.exit(1); } - let status = "FAILED"; - const testResult = await runTests(firmId, templateType, handle, testName, false, "none", pattern); + const runSingleHandle = async (singleHandle) => { + let status = "FAILED"; + const failedTestNames = []; + const testResult = await runTests(firmId, templateType, singleHandle, testName, false, "none", pattern); + + if (!testResult) { + status = "FAILED"; + consola.error(`Error running tests for ${singleHandle}`); + } 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); + } + }); + } + } + } - if (!testResult) { - status = "PASSED"; - consola.success(status); - return status; - } + if (status === "PASSED") { + consola.log(`${singleHandle}: ${status}`); + } else { + consola.log(`${singleHandle}: ${status}`); + // Display failed test names + failedTestNames.forEach((testName) => { + consola.log(` ${testName}: FAILED`); + }); + } - const testRun = testResult?.testRun; + return { handle: singleHandle, status, failedTestNames }; + }; - if (testRun && testRun?.status === "completed") { - const errorsPresent = checkAllTestsErrorsPresent(testRun.tests); - if (errorsPresent === false) { - status = "PASSED"; - consola.success(status); - return status; - } - } - consola.error(status); - return status; + 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-lock.json b/package-lock.json index 446f8bb..687d65f 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 875bce4..2f115c6 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",