From 86f7cb802dbe153b3ab0bd8c85b34b9f781ab3e4 Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Sat, 28 Sep 2024 09:52:04 -0400 Subject: [PATCH] test_runner: support custom arguments in `run()` PR-URL: https://github.com/nodejs/node/pull/55126 Reviewed-By: Matteo Collina Reviewed-By: Antoine du Hamel --- doc/api/test.md | 6 ++++++ lib/internal/test_runner/runner.js | 20 +++++++++++++++++++- test/fixtures/test-runner/print-arguments.js | 5 +++++ test/parallel/test-runner-run.mjs | 14 ++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/test-runner/print-arguments.js diff --git a/doc/api/test.md b/doc/api/test.md index 691b76549f9a85..b43b3b82e65e60 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -1299,6 +1299,12 @@ changes: * `setup` {Function} A function that accepts the `TestsStream` instance and can be used to setup listeners before any tests are run. **Default:** `undefined`. + * `execArgv` {Array} An array of CLI flags to pass to the `node` executable when + spawning the subprocesses. This option has no effect when `isolation` is `'none`'. + **Default:** `[]` + * `argv` {Array} An array of CLI flags to pass to each test file when spawning the + subprocesses. This option has no effect when `isolation` is `'none'`. + **Default:** `[]`. * `signal` {AbortSignal} Allows aborting an in-progress test execution. * `testNamePatterns` {string|RegExp|Array} A String, RegExp or a RegExp Array, that can be used to only run tests whose name matches the provided pattern. diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index 4a19e13d9a029c..b8d7740614cee5 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -10,6 +10,7 @@ const { ArrayPrototypeJoin, ArrayPrototypeMap, ArrayPrototypePush, + ArrayPrototypePushApply, ArrayPrototypeShift, ArrayPrototypeSlice, ArrayPrototypeSome, @@ -130,7 +131,13 @@ function filterExecArgv(arg, i, arr) { !ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`)); } -function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, testSkipPatterns, only }) { +function getRunArgs(path, { forceExit, + inspectPort, + testNamePatterns, + testSkipPatterns, + only, + argv: suppliedArgs, + execArgv }) { const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv); if (forceExit === true) { ArrayPrototypePush(argv, '--test-force-exit'); @@ -148,12 +155,16 @@ function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, testSkipPa ArrayPrototypePush(argv, '--test-only'); } + ArrayPrototypePushApply(argv, execArgv); + if (path === kIsolatedProcessName) { ArrayPrototypePush(argv, '--test', ...ArrayPrototypeSlice(process.argv, 1)); } else { ArrayPrototypePush(argv, path); } + ArrayPrototypePushApply(argv, suppliedArgs); + return argv; } @@ -548,6 +559,8 @@ function run(options = kEmptyObject) { lineCoverage = 0, branchCoverage = 0, functionCoverage = 0, + execArgv = [], + argv = [], } = options; if (files != null) { @@ -643,6 +656,9 @@ function run(options = kEmptyObject) { validateInteger(branchCoverage, 'options.branchCoverage', 0, 100); validateInteger(functionCoverage, 'options.functionCoverage', 0, 100); + validateStringArray(argv, 'options.argv'); + validateStringArray(execArgv, 'options.execArgv'); + const rootTestOptions = { __proto__: null, concurrency, timeout, signal }; const globalOptions = { __proto__: null, @@ -685,6 +701,8 @@ function run(options = kEmptyObject) { forceExit, cwd, isolation, + argv, + execArgv, }; if (isolation === 'process') { diff --git a/test/fixtures/test-runner/print-arguments.js b/test/fixtures/test-runner/print-arguments.js new file mode 100644 index 00000000000000..6140b3b8d26060 --- /dev/null +++ b/test/fixtures/test-runner/print-arguments.js @@ -0,0 +1,5 @@ +const { test } = require('node:test'); + +test('process.argv is setup', (t) => { + t.assert.deepStrictEqual(process.argv.slice(2), ['--a-custom-argument']); +}); diff --git a/test/parallel/test-runner-run.mjs b/test/parallel/test-runner-run.mjs index c423465ab849ae..1460ef15ed8dea 100644 --- a/test/parallel/test-runner-run.mjs +++ b/test/parallel/test-runner-run.mjs @@ -33,6 +33,20 @@ describe('require(\'node:test\').run', { concurrency: true }, () => { for await (const _ of stream); }); + const argPrintingFile = join(testFixtures, 'print-arguments.js'); + it('should allow custom arguments via execArgv', async () => { + const result = await run({ files: [argPrintingFile], execArgv: ['-p', '"Printed"'] }).compose(spec).toArray(); + assert.strictEqual(result[0].toString(), 'Printed\n'); + }); + + it('should allow custom arguments via argv', async () => { + const stream = run({ files: [argPrintingFile], argv: ['--a-custom-argument'] }); + stream.on('test:fail', common.mustNotCall()); + stream.on('test:pass', common.mustCall()); + // eslint-disable-next-line no-unused-vars + for await (const _ of stream); + }); + it('should run same file twice', async () => { const stream = run({ files: [