Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test_runner: fix afterEach not running on test failures #45204

Merged
merged 5 commits into from Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 18 additions & 10 deletions lib/internal/test_runner/test.js
Expand Up @@ -41,6 +41,7 @@ const {
const {
createDeferredPromise,
kEmptyObject,
once: runOnce,
} = require('internal/util');
const { isPromise } = require('internal/util/types');
const {
Expand Down Expand Up @@ -482,8 +483,14 @@ class Test extends AsyncResource {
return;
}

const { args, ctx } = this.getRunArgs();
const afterEach = runOnce(async () => {
if (this.parent?.hooks.afterEach.length > 0) {
await this.parent[kRunHook]('afterEach', { args, ctx });
}
});

try {
const { args, ctx } = this.getRunArgs();
if (this.parent?.hooks.beforeEach.length > 0) {
await this.parent[kRunHook]('beforeEach', { args, ctx });
}
Expand Down Expand Up @@ -518,12 +525,10 @@ class Test extends AsyncResource {
return;
}

if (this.parent?.hooks.afterEach.length > 0) {
await this.parent[kRunHook]('afterEach', { args, ctx });
}

await afterEach();
this.pass();
} catch (err) {
try { await afterEach(); } catch { /* test is already failing, let's the error */ }
if (isTestFailureError(err)) {
if (err.failureType === kTestTimeoutFailure) {
this.cancel(err);
Expand Down Expand Up @@ -728,6 +733,12 @@ class Suite extends Test {
}

async run() {
const hookArgs = this.getRunArgs();
const afterEach = runOnce(async () => {
if (this.parent?.hooks.afterEach.length > 0) {
await this.parent[kRunHook]('afterEach', hookArgs);
}
});
try {
this.parent.activeSubtests++;
await this.buildSuite;
Expand All @@ -739,7 +750,6 @@ class Suite extends Test {
return;
}

const hookArgs = this.getRunArgs();

if (this.parent?.hooks.beforeEach.length > 0) {
await this.parent[kRunHook]('beforeEach', hookArgs);
Expand All @@ -753,13 +763,11 @@ class Suite extends Test {

await SafePromiseRace([promise, stopPromise]);
await this[kRunHook]('after', hookArgs);

if (this.parent?.hooks.afterEach.length > 0) {
await this.parent[kRunHook]('afterEach', hookArgs);
}
await afterEach();

this.pass();
} catch (err) {
try { await afterEach(); } catch { /* test is already failing, let's the error */ }
if (isTestFailureError(err)) {
this.fail(err);
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/util.js
Expand Up @@ -453,7 +453,7 @@ function once(callback) {
return function(...args) {
if (called) return;
called = true;
ReflectApply(callback, this, args);
return ReflectApply(callback, this, args);
};
}

Expand Down
2 changes: 0 additions & 2 deletions test/message/test_runner_describe_it.out
Expand Up @@ -42,8 +42,6 @@ not ok 4 - sync fail todo with message # TODO this is a failing todo
*
*
*
*
*
...
# Subtest: sync skip pass
ok 5 - sync skip pass # SKIP
Expand Down
27 changes: 26 additions & 1 deletion test/message/test_runner_hooks.js
@@ -1,6 +1,6 @@
// Flags: --no-warnings
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const { test, describe, it, before, after, beforeEach, afterEach } = require('node:test');

Expand Down Expand Up @@ -76,6 +76,18 @@ describe('afterEach throws', () => {
it('2', () => {});
});

describe('afterEach when test fails', () => {
afterEach(common.mustCall(2));
it('1', () => { throw new Error('test'); });
it('2', () => {});
});

describe('afterEach throws and test fails', () => {
afterEach(() => { throw new Error('afterEach'); });
it('1', () => { throw new Error('test'); });
it('2', () => {});
});

test('test hooks', async (t) => {
const testArr = [];
t.beforeEach((t) => testArr.push('beforeEach ' + t.name));
Expand Down Expand Up @@ -111,3 +123,16 @@ test('t.afterEach throws', async (t) => {
await t.test('1', () => {});
await t.test('2', () => {});
});


test('afterEach when test fails', async (t) => {
t.afterEach(common.mustCall(2));
await t.test('1', () => { throw new Error('test'); });
await t.test('2', () => {});
});

test('afterEach throws and test fails', async (t) => {
afterEach(() => { throw new Error('afterEach'); });
await t.test('1', () => { throw new Error('test'); });
await t.test('2', () => {});
});
172 changes: 166 additions & 6 deletions test/message/test_runner_hooks.out
Expand Up @@ -189,6 +189,86 @@ not ok 5 - afterEach throws
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: afterEach when test fails
# Subtest: 1
not ok 1 - 1
---
duration_ms: *
failureType: 'testCodeFailure'
error: 'test'
code: 'ERR_TEST_FAILURE'
stack: |-
*
*
*
*
*
*
*
*
*
*
...
# Subtest: 2
ok 2 - 2
---
duration_ms: *
...
1..2
not ok 6 - afterEach when test fails
---
duration_ms: *
failureType: 'subtestsFailed'
error: '1 subtest failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: afterEach throws and test fails
# Subtest: 1
not ok 1 - 1
---
duration_ms: *
failureType: 'testCodeFailure'
error: 'test'
code: 'ERR_TEST_FAILURE'
stack: |-
*
*
*
*
*
*
*
*
*
*
...
# Subtest: 2
not ok 2 - 2
---
duration_ms: *
failureType: 'hookFailed'
error: 'failed running afterEach hook'
code: 'ERR_TEST_FAILURE'
stack: |-
*
*
*
*
*
*
*
*
*
*
...
1..2
not ok 7 - afterEach throws and test fails
---
duration_ms: *
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: test hooks
# Subtest: 1
ok 1 - 1
Expand Down Expand Up @@ -217,7 +297,7 @@ not ok 5 - afterEach throws
duration_ms: *
...
1..3
ok 6 - test hooks
ok 8 - test hooks
---
duration_ms: *
...
Expand Down Expand Up @@ -261,7 +341,7 @@ ok 6 - test hooks
*
...
1..2
not ok 7 - t.beforeEach throws
not ok 9 - t.beforeEach throws
---
duration_ms: *
failureType: 'subtestsFailed'
Expand Down Expand Up @@ -308,17 +388,97 @@ not ok 7 - t.beforeEach throws
*
...
1..2
not ok 8 - t.afterEach throws
not ok 10 - t.afterEach throws
---
duration_ms: *
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: afterEach when test fails
# Subtest: 1
not ok 1 - 1
---
duration_ms: *
failureType: 'testCodeFailure'
error: 'test'
code: 'ERR_TEST_FAILURE'
stack: |-
*
*
*
*
*
*
*
*
*
*
...
# Subtest: 2
ok 2 - 2
---
duration_ms: *
...
1..2
not ok 11 - afterEach when test fails
---
duration_ms: *
failureType: 'subtestsFailed'
error: '1 subtest failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: afterEach throws and test fails
# Subtest: 1
not ok 1 - 1
---
duration_ms: *
failureType: 'testCodeFailure'
error: 'test'
code: 'ERR_TEST_FAILURE'
stack: |-
*
*
*
*
*
*
*
*
*
*
...
# Subtest: 2
not ok 2 - 2
---
duration_ms: *
failureType: 'hookFailed'
error: 'failed running afterEach hook'
code: 'ERR_TEST_FAILURE'
stack: |-
*
*
*
*
*
*
*
*
*
*
...
1..2
not ok 12 - afterEach throws and test fails
---
duration_ms: *
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
1..8
# tests 8
1..12
# tests 12
# pass 2
# fail 6
# fail 10
# cancelled 0
# skipped 0
# todo 0
Expand Down
2 changes: 0 additions & 2 deletions test/message/test_runner_output.out
Expand Up @@ -42,8 +42,6 @@ not ok 4 - sync fail todo with message # TODO this is a failing todo
*
*
*
*
*
...
# Subtest: sync skip pass
ok 5 - sync skip pass # SKIP
Expand Down