Skip to content

Commit 1f81b2f

Browse files
author
maxeljkin
committed
feat: node-arguments for passing arguments to child processes node instances
1 parent 20f86d6 commit 1f81b2f

13 files changed

+119
-5
lines changed

docs/05-command-line.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ $ npx ava --help
2121
--color Force color output
2222
--no-color Disable color output
2323
--reset-cache Reset AVA's compilation cache and exit
24+
--node-arguments Configure options to be passed to child node processes when running tests
2425
--config JavaScript file for AVA to read its config from, instead of using package.json
2526
or ava.config.js files
2627

docs/06-configuration.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ To ignore files, prefix the pattern with an `!` (exclamation mark).
4444
"testOptions": {
4545
"babelrc": false
4646
}
47-
}
47+
},
48+
"nodeArguments": [
49+
"--trace-deprecation",
50+
"--napi-modules"
51+
]
4852
}
4953
}
5054
```
@@ -70,6 +74,7 @@ Arguments passed to the CLI will always take precedence over the CLI options con
7074
- `babel`: test file specific Babel options. See our [Babel recipe](./recipes/babel.md#configuring-babel) for more details
7175
- `babel.extensions`: extensions of test files that will be precompiled using AVA's Babel presets. Setting this overrides the default `"js"` value, so make sure to include that extension in the list
7276
- `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.
77+
- `nodeArguments`: Configure options to be passed to child node processes when running tests
7378

7479
Note that providing files on the CLI overrides the `files` option.
7580

lib/api.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ class Api extends Emittery {
311311
}
312312

313313
async _computeForkExecArgv() {
314-
const execArgv = this.options.testOnlyExecArgv || process.execArgv;
314+
const execArgv = this.options.nodeArguments;
315315
if (execArgv.length === 0) {
316316
return Promise.resolve(execArgv);
317317
}

lib/cli.js

+7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ exports.run = async () => { // eslint-disable-line complexity
5151
--color Force color output
5252
--no-color Disable color output
5353
--reset-cache Reset AVA's compilation cache and exit
54+
--node-arguments Configure options to be passed to child node processes when running tests
5455
--config JavaScript file for AVA to read its config from, instead of using package.json
5556
or ava.config.js files
5657
@@ -115,6 +116,10 @@ exports.run = async () => { // eslint-disable-line complexity
115116
type: 'boolean',
116117
default: false
117118
},
119+
'node-arguments': {
120+
type: 'string',
121+
default: conf.nodeArguments
122+
},
118123
'--': {
119124
type: 'string'
120125
}
@@ -180,6 +185,7 @@ exports.run = async () => { // eslint-disable-line complexity
180185
const Watcher = require('./watcher');
181186
const babelManager = require('./babel-manager');
182187
const normalizeExtensions = require('./extensions');
188+
const normalizeNodeArguments = require('./node-arguments');
183189
const {normalizeGlobs} = require('./globs');
184190
const validateEnvironmentVariables = require('./environment-variables');
185191

@@ -265,6 +271,7 @@ exports.run = async () => { // eslint-disable-line complexity
265271
snapshotDir: conf.snapshotDir ? path.resolve(projectDir, conf.snapshotDir) : null,
266272
timeout: conf.timeout,
267273
updateSnapshots: conf.updateSnapshots,
274+
nodeArguments: normalizeNodeArguments(conf.nodeArguments || []),
268275
workerArgv: cli.flags['--']
269276
});
270277

lib/node-arguments.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
const trim = require('lodash/trim');
4+
5+
// Parse formats:
6+
// --param=--parameter --abc
7+
// |_this____|
8+
// --param=" --arg1 --arg2 param " --abc
9+
// |_this part___________|
10+
function parseCliParameter(str) {
11+
str = str[0].startsWith('"') && str.endsWith('"') ? str.slice(1, str.length - 1) : str;
12+
return trim(str)
13+
.split(' ');
14+
}
15+
16+
function normalizeNodeArguments(nodeArguments) {
17+
const parsed = Array.isArray(nodeArguments) ? nodeArguments : parseCliParameter(nodeArguments);
18+
19+
const detectedInspect = parsed.find(arg => /^--inspect(-brk)?(=|$)/.test(arg));
20+
21+
if (detectedInspect && detectedInspect.includes('=')) {
22+
throw new Error('The \'nodeArguments\' configuration must not contain inspect with port.');
23+
}
24+
25+
const mainProcessArgs = process.execArgv.filter(arg => !detectedInspect || !/^--inspect(-brk)?(=|$)/.test(arg));
26+
27+
return parsed.concat(mainProcessArgs);
28+
}
29+
30+
module.exports = normalizeNodeArguments;

test/api.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function apiCreator(options = {}) {
3737
options.experiments = {};
3838
options.globs = normalizeGlobs(options.files, options.helpers, options.sources, options.extensions.all);
3939
options.resolveTestsFrom = options.resolveTestsFrom || options.projectDir;
40+
options.nodeArguments = options.nodeArguments || [];
4041
const instance = new Api(options);
4142
if (!options.precompileHelpers) {
4243
instance._precompileHelpers = () => Promise.resolve();
@@ -1179,7 +1180,7 @@ test('using --match with matching tests will only report those passing tests', t
11791180

11801181
function generatePassDebugTests(execArgv) {
11811182
test(`pass ${execArgv.join(' ')} to fork`, t => {
1182-
const api = apiCreator({testOnlyExecArgv: execArgv});
1183+
const api = apiCreator({nodeArguments: execArgv});
11831184
return api._computeForkExecArgv()
11841185
.then(result => {
11851186
t.true(result.length === execArgv.length);
@@ -1190,7 +1191,7 @@ function generatePassDebugTests(execArgv) {
11901191

11911192
function generatePassInspectIntegrationTests(execArgv) {
11921193
test(`pass ${execArgv.join(' ')} to fork`, t => {
1193-
const api = apiCreator({testOnlyExecArgv: execArgv});
1194+
const api = apiCreator({nodeArguments: execArgv});
11941195
return api.run([path.join(__dirname, 'fixture/inspect-arg.js')])
11951196
.then(runStatus => {
11961197
t.is(runStatus.stats.passedTests, 1);

test/fixture/node-arguments.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import test from '../..';
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+
});
+8
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

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global.SETUP_CALLED = true;

test/fixture/node-arguments/test.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import test from '../../..';
2+
3+
test('works', t => {
4+
t.plan(2);
5+
t.truthy(global.SETUP_CALLED);
6+
t.truthy(process.execArgv.includes('--require'));
7+
});

test/helper/report.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ const run = (type, reporter, match = []) => {
9494
concurrency: 1,
9595
updateSnapshots: false,
9696
snapshotDir: false,
97-
color: true
97+
color: true,
98+
nodeArguments: []
9899
};
99100
let pattern = '*.js';
100101

test/integration/node-arguments.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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'], err => t.ifError(err));
8+
});
9+
10+
test('reads node arguments from config', t => {
11+
t.plan(1);
12+
execCli(['test.js'], {
13+
dirname: 'fixture/node-arguments'
14+
}, err => t.ifError(err));
15+
});

test/node-arguments.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
const {test} = require('tap');
4+
const normalizeNodeArguments = require('../lib/node-arguments');
5+
6+
test('normalizes multiple node arguments from cli', t => {
7+
t.deepEqual(normalizeNodeArguments('"--a --b --c"'), ['--a', '--b', '--c']);
8+
t.end();
9+
});
10+
11+
test('normalizes multiple node arguments from config', t => {
12+
t.deepEqual(normalizeNodeArguments(['--arg1', '--b']), ['--arg1', '--b']);
13+
t.end();
14+
});
15+
16+
test('normalizes single node arguments from cli', t => {
17+
t.deepEqual(normalizeNodeArguments('--test-flag'), ['--test-flag']);
18+
t.end();
19+
});
20+
21+
test('removes extra inspect', t => {
22+
process.execArgv = ['--inspect-brk=123'];
23+
t.deepEqual(normalizeNodeArguments('--inspect'), ['--inspect']);
24+
t.end();
25+
});
26+
27+
test('fails on inspect with port', t => {
28+
t.throws(() => normalizeNodeArguments('--inspect=9230'), 'The \'nodeArguments\' configuration must not contain inspect with port.');
29+
t.end();
30+
});
31+

0 commit comments

Comments
 (0)