diff --git a/lib/internal/test_runner/reporter/spec.js b/lib/internal/test_runner/reporter/spec.js index 0c5a3e95c7c75a..75b4d9773c4161 100644 --- a/lib/internal/test_runner/reporter/spec.js +++ b/lib/internal/test_runner/reporter/spec.js @@ -78,11 +78,12 @@ class SpecReporter extends Transform { } else if (todo !== undefined) { title += ` # ${typeof todo === 'string' && todo.length ? todo : 'TODO'}`; } + const error = this.#formatError(data.details?.error, indent); if (hasChildren) { // If this test has had children - it was already reported, so slightly modify the output - return `${prefix}${indent}${color}${symbols['arrow:right']}${white}${title}\n`; + const err = data.details?.error?.failureType === 'subtestsFailed' ? '' : error; + return `${prefix}${indent}${color}${symbols['arrow:right']}${white}${title}\n${err}`; } - const error = this.#formatError(data.details?.error, indent); if (skip !== undefined) { color = gray; symbol = symbols['hyphen:minus']; diff --git a/test/fixtures/test-runner/output/hooks_spec_reporter.js b/test/fixtures/test-runner/output/hooks_spec_reporter.js new file mode 100644 index 00000000000000..75bb4b6be1e908 --- /dev/null +++ b/test/fixtures/test-runner/output/hooks_spec_reporter.js @@ -0,0 +1,11 @@ +'use strict'; +require('../../../common'); +const fixtures = require('../../../common/fixtures'); +const spawn = require('node:child_process').spawn; + +const child = spawn(process.execPath, + ['--no-warnings', '--test-reporter', 'spec', fixtures.path('test-runner/output/hooks.js')], + { stdio: 'pipe' }); +// eslint-disable-next-line no-control-regex +child.stdout.on('data', (d) => process.stdout.write(d.toString().replace(/[^\x00-\x7F]/g, '').replace(/\u001b\[\d+m/g, ''))); +child.stderr.pipe(process.stderr); diff --git a/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot b/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot new file mode 100644 index 00000000000000..82c4704df4e63f --- /dev/null +++ b/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot @@ -0,0 +1,569 @@ + describe hooks + 1 (*ms) + 2 (*ms) + nested + nested 1 (*ms) + nested 2 (*ms) + nested (*ms) + + describe hooks (*ms) + + before throws + 1 + 'test did not finish before its parent and was cancelled' + + 2 + 'test did not finish before its parent and was cancelled' + + before throws (*ms) + + Error: before + * + * + * + * + * + * + * + * + * + + after throws + 1 (*ms) + 2 (*ms) + after throws (*ms) + + Error: after + * + * + * + * + * + * + * + * + * + + beforeEach throws + 1 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + at async Promise.all (index 0) + * + * + + 2 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + * + + beforeEach throws (*ms) + + afterEach throws + 1 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + at async Promise.all (index 0) + * + + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + + afterEach throws (*ms) + + afterEach when test fails + 1 (*ms) + Error: test + * + * + * + * + * + * + at new Promise () + * + * + at Array.map () + + 2 (*ms) + afterEach when test fails (*ms) + + afterEach throws and test fails + 1 (*ms) + Error: test + * + * + * + * + * + * + at new Promise () + * + * + at Array.map () + + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + + afterEach throws and test fails (*ms) + + test hooks + 1 (*ms) + 2 (*ms) + nested + nested 1 (*ms) + nested 2 (*ms) + nested (*ms) + + test hooks (*ms) + + t.before throws + 1 (*ms) + Error: before + * + * + * + * + * + * + * + * + * + * + + 2 (*ms) + Error: before + * + * + * + * + * + * + * + * + * + * + + t.before throws (*ms) + + t.beforeEach throws + 1 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + * + * + * + + 2 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + * + * + * + + t.beforeEach throws (*ms) + + t.afterEach throws + 1 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + * + + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + * + + t.afterEach throws (*ms) + + afterEach when test fails + 1 (*ms) + Error: test + * + * + * + * + * + * + * + * + * + + 2 (*ms) + afterEach when test fails (*ms) + + afterEach throws and test fails + 1 (*ms) + Error: test + * + * + * + * + * + * + * + * + * + + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + * + + afterEach throws and test fails (*ms) + + t.after() is called if test body throws (*ms) + Error: bye + * + * + * + * + + - after() called + before 1 called + before 2 called + after 1 called + after 2 called + tests 38 + suites 8 + pass 14 + fail 22 + cancelled 2 + skipped 0 + todo 0 + duration_ms * + + failing tests: + +* + 1 + 'test did not finish before its parent and was cancelled' + +* + 2 + 'test did not finish before its parent and was cancelled' + +* + before throws (*ms) + Error: before + * + * + * + * + * + * + * + * + * + +* + after throws (*ms) + Error: after + * + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + at async Promise.all (index 0) + * + * + +* + 2 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + at async Promise.all (index 0) + * + +* + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: test + * + * + * + * + * + * + at new Promise () + * + * + at Array.map () + +* + 1 (*ms) + Error: test + * + * + * + * + * + * + at new Promise () + * + * + at Array.map () + +* + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: before + * + * + * + * + * + * + * + * + * + * + +* + 2 (*ms) + Error: before + * + * + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + * + * + * + +* + 2 (*ms) + Error: beforeEach + * + * + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + * + +* + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: test + * + * + * + * + * + * + * + * + * + +* + 1 (*ms) + Error: test + * + * + * + * + * + * + * + * + * + +* + 2 (*ms) + Error: afterEach + * + * + * + * + * + * + * + * + * + * + +* + t.after() is called if test body throws (*ms) + Error: bye + * + * + * + * diff --git a/test/parallel/test-runner-output.mjs b/test/parallel/test-runner-output.mjs index 372ca8f3bae0ff..a1fd1bba09ef2f 100644 --- a/test/parallel/test-runner-output.mjs +++ b/test/parallel/test-runner-output.mjs @@ -67,6 +67,7 @@ const tests = [ { name: 'test-runner/output/describe_it.js' }, { name: 'test-runner/output/describe_nested.js' }, { name: 'test-runner/output/hooks.js' }, + { name: 'test-runner/output/hooks_spec_reporter.js', transform: specTransform }, { name: 'test-runner/output/timeout_in_before_each_should_not_affect_further_tests.js' }, { name: 'test-runner/output/hooks-with-no-global-test.js' }, { name: 'test-runner/output/before-and-after-each-too-many-listeners.js' },