Skip to content

Commit 7b20f6c

Browse files
Allow Node arguments to be configured
Supports `nodeArguments` configuration and `--node-arguments` on the CLI. Fixes #2090. Co-authored-by: Mark Wubben <[email protected]>
1 parent ad27246 commit 7b20f6c

File tree

15 files changed

+126
-5
lines changed

15 files changed

+126
-5
lines changed

docs/05-command-line.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Options:
2929
--fail-fast Stop after first test failure [boolean]
3030
--match, -m Only run tests with matching title (can be repeated)
3131
[string]
32+
--node-arguments Additional Node.js arguments for launching worker
33+
processes (specify as a single string) [string]
3234
--serial, -s Run tests serially [boolean]
3335
--tap, -t Generate TAP output [boolean]
3436
--timeout, -T Set global timeout (milliseconds or human-readable,
@@ -178,3 +180,17 @@ $ npx ava --tap | npx tap-nyan
178180
<img src="../media/tap-reporter.png" width="420">
179181

180182
Please note that the TAP reporter is unavailable when using [watch mode](./recipes/watch-mode.md).
183+
184+
## Node arguments
185+
186+
The `--node-arguments` argument may be used to specify additional arguments for launching worker processes. These are combined with the `nodeArguments` configuration and any arguments passed to the `node` binary when starting AVA.
187+
188+
**Only pass trusted values.**
189+
190+
Specify the arguments as a single string:
191+
192+
```console
193+
npx ava --node-arguments="--throw-deprecation --zero-fill-buffers"
194+
```
195+
196+
**Only pass trusted values.**

docs/06-configuration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ To ignore files, prefix the pattern with an `!` (exclamation mark).
3131
"verbose": true,
3232
"require": [
3333
"./my-helper-module.js"
34+
],
35+
"nodeArguments": [
36+
"--trace-deprecation",
37+
"--napi-modules"
3438
]
3539
}
3640
}
@@ -53,6 +57,7 @@ Arguments passed to the CLI will always take precedence over the CLI options con
5357
- `extensions`: extensions of test files. Setting this overrides the default `["cjs", "mjs", "js"]` value, so make sure to include those extensions in the list
5458
- `require`: extra modules to require before tests are run. Modules are required in the [worker processes](./01-writing-tests.md#process-isolation)
5559
- `timeout`: Timeouts in AVA behave differently than in other test frameworks. AVA resets a timer after each test, forcing tests to quit if no new test results were received within the specified timeout. This can be used to handle stalled tests. See our [timeout documentation](./07-test-timeouts.md) for more options.
60+
- `nodeArguments`: Configure Node.js arguments used to launch worker processes.
5661

5762
Note that providing files on the CLI overrides the `files` option.
5863

@@ -219,4 +224,8 @@ export default {
219224
};
220225
```
221226

227+
## Node arguments
228+
229+
The `nodeArguments` configuration may be used to specify additional arguments for launching worker processes. These are combined with `--node-arguments` passed on the CLI and any arguments passed to the `node` binary when starting AVA.
230+
222231
[CLI]: ./05-command-line.md

lib/api.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ class Api extends Emittery {
219219
options.updateSnapshots = true;
220220
}
221221

222-
const worker = fork(file, options, process.execArgv);
222+
const worker = fork(file, options, apiOptions.nodeArguments);
223223
runStatus.observeWorker(worker, file);
224224

225225
pendingWorkers.add(worker);

lib/cli.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ const FLAGS = {
3535
description: 'Only run tests with matching title (can be repeated)',
3636
type: 'string'
3737
},
38+
'node-arguments': {
39+
coerce: coerceLastValue,
40+
description: 'Additional Node.js arguments for launching worker processes (specify as a single string)',
41+
type: 'string'
42+
},
3843
serial: {
3944
alias: 's',
4045
coerce: coerceLastValue,
@@ -161,7 +166,7 @@ exports.run = async () => { // eslint-disable-line complexity
161166
combined.failFast = argv[flag];
162167
} else if (flag === 'update-snapshots') {
163168
combined.updateSnapshots = argv[flag];
164-
} else {
169+
} else if (flag !== 'node-arguments') {
165170
combined[flag] = argv[flag];
166171
}
167172
}
@@ -254,6 +259,7 @@ exports.run = async () => { // eslint-disable-line complexity
254259
const babelManager = require('./babel-manager');
255260
const normalizeExtensions = require('./extensions');
256261
const {normalizeGlobs, normalizePatterns} = require('./globs');
262+
const normalizeNodeArguments = require('./node-arguments');
257263
const validateEnvironmentVariables = require('./environment-variables');
258264

259265
let pkg;
@@ -303,6 +309,13 @@ exports.run = async () => { // eslint-disable-line complexity
303309
exit(error.message);
304310
}
305311

312+
let nodeArguments;
313+
try {
314+
nodeArguments = normalizeNodeArguments(conf.nodeArguments, argv['node-arguments']);
315+
} catch (error) {
316+
exit(error.message);
317+
}
318+
306319
let parallelRuns = null;
307320
if (isCi && ciParallelVars) {
308321
const {index: currentIndex, total: totalRuns} = ciParallelVars;
@@ -328,6 +341,7 @@ exports.run = async () => { // eslint-disable-line complexity
328341
moduleTypes,
329342
environmentVariables,
330343
match,
344+
nodeArguments,
331345
parallelRuns,
332346
projectDir,
333347
ranFromCli: true,

lib/fork.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const AVA_PATH = path.resolve(__dirname, '..');
1414

1515
const workerPath = require.resolve('./worker/subprocess');
1616

17-
module.exports = (file, opts, execArgv) => {
17+
module.exports = (file, opts, execArgv = process.execArgv) => {
1818
let finished = false;
1919

2020
const emitter = new Emittery();
@@ -34,7 +34,7 @@ module.exports = (file, opts, execArgv) => {
3434
cwd: opts.projectDir,
3535
silent: true,
3636
env: {NODE_ENV: 'test', ...process.env, ...opts.environmentVariables, AVA_PATH},
37-
execArgv: execArgv || process.execArgv
37+
execArgv
3838
});
3939

4040
subprocess.stdout.on('data', chunk => {

lib/node-arguments.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
const arrgv = require('arrgv');
3+
4+
function normalizeNodeArguments(fromConf = [], fromArgv = '') {
5+
let parsedArgv = [];
6+
if (fromArgv !== '') {
7+
try {
8+
parsedArgv = arrgv(fromArgv);
9+
} catch {
10+
throw new Error('Could not parse `--node-arguments` value. Make sure all strings are closed and backslashes are used correctly.');
11+
}
12+
}
13+
14+
return [...process.execArgv, ...fromConf, ...parsedArgv];
15+
}
16+
17+
module.exports = normalizeNodeArguments;

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"dependencies": {
6060
"@concordance/react": "^2.0.0",
6161
"ansi-styles": "^4.2.1",
62+
"arrgv": "^1.0.2",
6263
"arrify": "^2.0.1",
6364
"chalk": "^3.0.0",
6465
"chokidar": "^3.3.1",

test/fixture/node-arguments.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const test = require('../..');
2+
3+
test('exec arguments includes --throw-deprecation and --zero-fill-buffers', t => {
4+
t.plan(2);
5+
t.truthy(process.execArgv.includes('--throw-deprecation'));
6+
t.truthy(process.execArgv.includes('--zero-fill-buffers'));
7+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"ava": {
3+
"nodeArguments": [
4+
"--require",
5+
"./setup.js"
6+
]
7+
}
8+
}

test/fixture/node-arguments/setup.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global.SETUP_CALLED = true;

test/fixture/node-arguments/test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const test = require('../../..');
2+
3+
test('works', t => {
4+
t.plan(2);
5+
t.truthy(global.SETUP_CALLED, 'setup variable set');
6+
t.truthy(process.execArgv.some(argv => argv.startsWith('--require')), 'require passed');
7+
});

test/helper/cli.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function execCli(args, opts, cb) {
2626
const processPromise = new Promise(resolve => {
2727
// Spawning a child with piped IO means that the CLI will never see a TTY.
2828
// Inserting a shim here allows us to fake a TTY.
29-
child = childProcess.spawn(process.execPath, ['-r', ttySimulator, cliPath].concat(args), {
29+
child = childProcess.spawn(process.execPath, ['--require', ttySimulator, cliPath].concat(args), {
3030
cwd: dirname,
3131
env: {CI: '1', ...env}, // Force CI to ensure the correct reporter is selected
3232
// env,

test/integration/node-arguments.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
const {test} = require('tap');
3+
const {execCli} = require('../helper/cli');
4+
5+
test('passes node arguments to workers', t => {
6+
t.plan(1);
7+
execCli(['--node-arguments="--throw-deprecation --zero-fill-buffers"', 'node-arguments.js'],
8+
(err, stdout, stderr) => t.ifError(err, null, {stdout, stderr}));
9+
});
10+
11+
test('reads node arguments from config', t => {
12+
t.plan(1);
13+
execCli(['test.js'], {
14+
dirname: 'fixture/node-arguments'
15+
}, (err, stdout, stderr) => t.ifError(err, null, {stdout, stderr}));
16+
});
17+
18+
test('detects incomplete --node-arguments', t => {
19+
t.plan(2);
20+
execCli(['--node-arguments="--foo=\'bar"', 'node-arguments.js'], (err, stdout, stderr) => {
21+
t.ok(err);
22+
t.match(stderr, /Could not parse `--node-arguments` value. Make sure all strings are closed and backslashes are used correctly./);
23+
});
24+
});

test/node-arguments.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
3+
const {test} = require('tap');
4+
const normalizeNodeArguments = require('../lib/node-arguments');
5+
6+
test('combines arguments', async t => {
7+
t.deepEqual(
8+
await normalizeNodeArguments(['--require setup.js'], '--throw-deprecation --zero-fill-buffers'),
9+
[...process.execArgv, '--require setup.js', '--throw-deprecation', '--zero-fill-buffers']
10+
);
11+
t.end();
12+
});

0 commit comments

Comments
 (0)