Skip to content
1 change: 1 addition & 0 deletions lib/internal/test_runner/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ function lazyBootstrapRoot() {
};
const globalOptions = parseCommandLine();
globalOptions.cwd = process.cwd();
globalOptions.timeout = Infinity;
Comment thread
jakecastelli marked this conversation as resolved.
Outdated
createTestTree(rootTestOptions, globalOptions);
globalRoot.reporter.on('test:summary', (data) => {
if (!data.success) {
Expand Down
14 changes: 13 additions & 1 deletion lib/internal/test_runner/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,8 @@ class Test extends AsyncResource {
}

createSubtest(Factory, name, options, fn, overrides) {
const timeoutPerTest = this.config.timeoutPerTest;
Comment thread
jakecastelli marked this conversation as resolved.
Outdated

if (typeof name === 'function') {
fn = name;
} else if (name !== null && typeof name === 'object') {
Expand All @@ -797,8 +799,18 @@ class Test extends AsyncResource {
fn = options;
}

if (options !== null && typeof options === 'object') {
if (this.childNumber > 0 && options.timeout === undefined && timeoutPerTest !== Infinity) {
options.timeout = timeoutPerTest;
}
}

if (options === null || typeof options !== 'object') {
options = kEmptyObject;
if (this.childNumber > 0 && timeoutPerTest !== Infinity) {
options = { __proto__: null, timeout: timeoutPerTest };
} else {
options = kEmptyObject;
}
}

let parent = this;
Expand Down
8 changes: 4 additions & 4 deletions lib/internal/test_runner/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ function parseCommandLine() {
let shard;
let testNamePatterns = mapPatternFlagToRegExArray('--test-name-pattern');
let testSkipPatterns = mapPatternFlagToRegExArray('--test-skip-pattern');
let timeout;
let timeoutPerTest;

if (isChildProcessV8) {
kBuiltinReporters.set('v8-serializer', 'internal/test_runner/reporter/v8-serializer');
Expand Down Expand Up @@ -242,7 +242,7 @@ function parseCommandLine() {

if (isTestRunner) {
isolation = getOptionValue('--test-isolation');
timeout = getOptionValue('--test-timeout') || Infinity;
timeoutPerTest = getOptionValue('--test-timeout') || Infinity;
Comment thread
jakecastelli marked this conversation as resolved.
Outdated

if (isolation === 'none') {
concurrency = 1;
Expand Down Expand Up @@ -271,7 +271,7 @@ function parseCommandLine() {
};
}
} else {
timeout = Infinity;
timeoutPerTest = getOptionValue('--test-timeout') || Infinity;
concurrency = 1;
const testNamePatternFlag = getOptionValue('--test-name-pattern');
only = getOptionValue('--test-only');
Expand Down Expand Up @@ -334,7 +334,7 @@ function parseCommandLine() {
sourceMaps,
testNamePatterns,
testSkipPatterns,
timeout,
timeoutPerTest,
updateSnapshots,
watch,
};
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/test-runner/output/test-timeout-flag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Flags: --test-timeout=20
'use strict';
const { describe, test } = require('node:test');
const { setTimeout } = require('node:timers/promises');

describe('--test-timeout is set to 20ms', () => {
Comment thread
jakecastelli marked this conversation as resolved.
test('should timeout after 20ms', async () => {
await setTimeout(100);
});
test('should timeout after 5ms', { timeout: 5 }, async () => {
await setTimeout(100);
});
test('should not timeout', { timeout: 50 }, async () => {
await setTimeout(25);
});
test('should pass', async () => {
await setTimeout(10);
});
});
55 changes: 55 additions & 0 deletions test/fixtures/test-runner/output/test-timeout-flag.snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
TAP version 13
# Subtest: --test-timeout is set to 20ms
# Subtest: should timeout after 20ms
not ok 1 - should timeout after 20ms
---
duration_ms: *
type: 'test'
location: '/test/fixtures/test-runner/output/test-timeout-flag.js:(LINE):3'
failureType: 'testTimeoutFailure'
error: 'test timed out after 20ms'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 0)
...
# Subtest: should timeout after 5ms
not ok 2 - should timeout after 5ms
---
duration_ms: *
type: 'test'
location: '/test/fixtures/test-runner/output/test-timeout-flag.js:(LINE):3'
failureType: 'testTimeoutFailure'
error: 'test timed out after 5ms'
code: 'ERR_TEST_FAILURE'
...
# Subtest: should not timeout
ok 3 - should not timeout
---
duration_ms: *
type: 'test'
...
# Subtest: should pass
ok 4 - should pass
---
duration_ms: *
type: 'test'
...
1..4
not ok 1 - --test-timeout is set to 20ms
---
duration_ms: *
type: 'suite'
location: '/test/fixtures/test-runner/output/test-timeout-flag.js:(LINE):1'
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
1..1
# tests 4
# suites 1
# pass 2
# fail 0
# cancelled 2
# skipped 0
# todo 0
Comment thread
pmarchini marked this conversation as resolved.
# duration_ms *
6 changes: 3 additions & 3 deletions test/parallel/test-runner-cli-timeout.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ const env = { ...process.env, 'NODE_DEBUG': 'test_runner' };
test('default timeout -- Infinity', async () => {
const args = ['--test'];
const cp = spawnSync(process.execPath, args, { cwd, env });
assert.match(cp.stderr.toString(), /timeout: Infinity,/);
assert.match(cp.stderr.toString(), /timeoutPerTest: Infinity/);
Comment thread
jakecastelli marked this conversation as resolved.
Outdated
});

test('timeout of 10ms', async () => {
const args = ['--test', '--test-timeout', 10];
const cp = spawnSync(process.execPath, args, { cwd, env });
assert.match(cp.stderr.toString(), /timeout: 10,/);
assert.match(cp.stderr.toString(), /timeoutPerTest: 10/);
});

test('isolation=none uses the --test-timeout flag', async () => {
const args = [
'--test', '--test-isolation=none', '--test-timeout=10',
];
const cp = spawnSync(process.execPath, args, { cwd, env });
assert.match(cp.stderr.toString(), /timeout: 10,/);
assert.match(cp.stderr.toString(), /timeoutPerTest: 10/);
});
9 changes: 9 additions & 0 deletions test/parallel/test-runner-output.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ const tests = [
name: 'test-runner/output/timeout_in_before_each_should_not_affect_further_tests.js',
flags: ['--test-reporter=tap'],
},
{
name: 'test-runner/output/test-timeout-flag.js',
flags: ['--test-reporter=tap'],
},
// --test-timeout should work with or without --test flag
{
name: 'test-runner/output/test-timeout-flag.js',
flags: ['--test-reporter=tap', '--test'],
Comment thread
pmarchini marked this conversation as resolved.
},
{
name: 'test-runner/output/hooks-with-no-global-test.js',
flags: ['--test-reporter=tap'],
Expand Down
Loading