Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
11 changes: 11 additions & 0 deletions lib/internal/test_runner/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -586,10 +586,21 @@ class Test extends AsyncResource {
return;
}

// Do not run for hooks and root test as hooks instance are shared between tests suite so aborting them will
// abort cause them to not run for further tests.
if (this.parent !== null) {
this.#abortController.abort();
}

await afterEach();
await after();
this.pass();
} catch (err) {
// Do not run for hooks and root test as hooks instance are shared between tests suite so aborting them will
// abort cause them to not run for further tests.
if (this.parent !== null) {
this.#abortController.abort();
}
try { await afterEach(); } catch { /* test is already failing, let's ignore the error */ }
try { await after(); } catch { /* Ignore error. */ }
if (isTestFailureError(err)) {
Expand Down
25 changes: 25 additions & 0 deletions test/fixtures/test-runner/aborts/failed-test-still-call-abort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const {test, afterEach} = require('node:test');
const assert = require('node:assert');

let testCount = 0;
let signal;

afterEach(() => {
testCount++;
assert.equal(signal.aborted, true);

console.log(`abort called for test ${testCount}`)
});

test("sync", (t) => {
signal = t.signal;
assert.equal(signal.aborted, false);
throw new Error('failing the sync test');
});

test("async", async (t) => {
await null;
signal = t.signal;
assert.equal(signal.aborted, false);
throw new Error('failing the async test');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const {test, afterEach} = require('node:test');
const assert = require('node:assert');

let testCount = 0;
let signal;

afterEach(() => {
testCount++;
assert.equal(signal.aborted, true);

console.log(`abort called for test ${testCount}`)
});

test("sync", (t) => {
signal = t.signal;
assert.equal(signal.aborted, false);
});

test("async", async (t) => {
await null;
signal = t.signal;
assert.equal(signal.aborted, false);
});
98 changes: 94 additions & 4 deletions test/parallel/test-runner-run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,102 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {

it('should emit "test:watch:drained" event on watch mode', async () => {
const controller = new AbortController();
await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
.on('data', function({ type }) {
if (type === 'test:watch:drained') {
controller.abort();
await run({
files: [join(testFixtures, 'test/random.cjs')],
watch: true,
signal: controller.signal,
}).on('data', function({ type }) {
if (type === 'test:watch:drained') {
controller.abort();
}
});
});

describe('AbortSignal', () => {
it('should stop watch mode when abortSignal aborts', async () => {
const controller = new AbortController();
const result = await run({
files: [join(testFixtures, 'test/random.cjs')],
watch: true,
signal: controller.signal,
})
.compose(async function* (source) {
for await (const chunk of source) {
if (chunk.type === 'test:pass') {
controller.abort();
yield chunk.data.name;
}
}
})
.toArray();
assert.deepStrictEqual(result, ['this should pass']);
});

it('should abort when test succeeded', async () => {
const stream = run({
files: [
fixtures.path(
'test-runner',
'aborts',
'successful-test-still-call-abort.js'
),
],
});

let passedTestCount = 0;
let failedTestCount = 0;

let output = '';
for await (const data of stream) {
if (data.type === 'test:stdout') {
output += data.data.message.toString();
}
if (data.type === 'test:fail') {
failedTestCount++;
}
if (data.type === 'test:pass') {
passedTestCount++;
}
}

assert.match(output, /abort called for test 1/);
assert.match(output, /abort called for test 2/);
assert.strictEqual(failedTestCount, 0, new Error('no tests should fail'));
assert.strictEqual(passedTestCount, 2);
});

it('should abort when test failed', async () => {
const stream = run({
files: [
fixtures.path(
'test-runner',
'aborts',
'failed-test-still-call-abort.js'
),
],
});

let passedTestCount = 0;
let failedTestCount = 0;

let output = '';
for await (const data of stream) {
if (data.type === 'test:stdout') {
output += data.data.message.toString();
}
if (data.type === 'test:fail') {
failedTestCount++;
}
if (data.type === 'test:pass') {
passedTestCount++;
}
}

assert.match(output, /abort called for test 1/);
assert.match(output, /abort called for test 2/);
assert.strictEqual(passedTestCount, 0, new Error('no tests should pass'));
assert.strictEqual(failedTestCount, 2);
});
});

describe('sharding', () => {
Expand Down