Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -1384,6 +1384,9 @@ added:
- v18.9.0
- v16.19.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/61367
description: Add the `env` option.
- version: v24.7.0
pr-url: https://github.com/nodejs/node/pull/59443
description: Added a rerunFailuresFilePath option.
Expand Down Expand Up @@ -1504,6 +1507,9 @@ changes:
* `functionCoverage` {number} Require a minimum percent of covered functions. If code
coverage does not reach the threshold specified, the process will exit with code `1`.
**Default:** `0`.
* `env` {Object} Specify environment variables to be passed along to the test process.
This options is not compatible with `isolation='none'`. These variables will override
those from the main process, and are not merged with `process.env`.
* Returns: {TestsStream}

**Note:** `shard` is used to horizontally parallelize test running across
Expand Down
12 changes: 11 additions & 1 deletion lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ function runTestFile(path, filesWatcher, opts) {
const subtest = opts.root.createSubtest(FileTest, testPath, testOpts, async (t) => {
const args = getRunArgs(path, opts);
const stdio = ['pipe', 'pipe', 'pipe'];
const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
const env = { __proto__: null, NODE_TEST_CONTEXT: 'child-v8', ...(opts.env || process.env) };
if (watchMode) {
stdio.push('ipc');
env.WATCH_REPORT_DEPENDENCIES = '1';
Expand Down Expand Up @@ -610,6 +610,7 @@ function run(options = kEmptyObject) {
argv = [],
cwd = process.cwd(),
rerunFailuresFilePath,
env,
} = options;

if (files != null) {
Expand Down Expand Up @@ -718,6 +719,14 @@ function run(options = kEmptyObject) {
validatePath(globalSetupPath, 'options.globalSetupPath');
}

if (env != null) {
validateObject(env);

if (isolation === 'none') {
throw new ERR_INVALID_ARG_VALUE('options.env', env, 'is not supported with isolation=\'none\'');
}
}

const rootTestOptions = { __proto__: null, concurrency, timeout, signal };
const globalOptions = {
__proto__: null,
Expand Down Expand Up @@ -763,6 +772,7 @@ function run(options = kEmptyObject) {
argv,
execArgv,
rerunFailuresFilePath,
env,
};

if (isolation === 'process') {
Expand Down
7 changes: 7 additions & 0 deletions test/fixtures/test-runner/process-env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { test } = require('node:test');

test('process.env is correct', (t) => {
t.assert.strictEqual(process.env.ABC, undefined, 'main process env var should be undefined');
t.assert.strictEqual(process.env.NODE_TEST_CONTEXT, 'child-v8', 'NODE_TEST_CONTEXT should be set by run()');
t.assert.strictEqual(process.env.FOOBAR, 'FUZZBUZZ', 'specified env var should be defined');
});
20 changes: 20 additions & 0 deletions test/parallel/test-runner-run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,26 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
});
});

describe('env', () => {
it('should allow env variables to be configured', async () => {
// Set a variable on main process env and test it does not exist within test env.
process.env.ABC = 'XYZ';
const stream = run({ files: [join(testFixtures, 'process-env.js')], env: { FOOBAR: 'FUZZBUZZ' } });
stream.on('test:fail', common.mustNotCall());
stream.on('test:pass', common.mustCall(1));
// eslint-disable-next-line no-unused-vars
for await (const _ of stream);
delete process.env.ABC;
});

it('should throw error when env is specified with isolation=none', async () => {
assert.throws(() => run({ env: { foo: 'bar' }, isolation: 'none' }), {
code: 'ERR_INVALID_ARG_VALUE',
message: /The property 'options\.env' is not supported with isolation='none'\. Received { foo: 'bar' }/
});
});
});

describe('forceExit', () => {
it('throws for non-boolean values', () => {
[Symbol(), {}, 0, 1, '1', Promise.resolve([])].forEach((forceExit) => {
Expand Down
Loading