From 551fb8e32b44c0731823005c220b500efa66f2ce Mon Sep 17 00:00:00 2001 From: ! Maisto Date: Sat, 18 Jul 2026 15:06:12 +0200 Subject: [PATCH 1/5] test_runner: mark coverage failures as warn-level diagnostics Threads a level ('warn') through rootTest.diagnostic() for the coverage-related warnings, so reporters can distinguish them from routine info diagnostics. Signed-off-by: Maisto --- lib/internal/test_runner/harness.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/test_runner/harness.js b/lib/internal/test_runner/harness.js index 16f9392bf7763b..7e9310de19f117 100644 --- a/lib/internal/test_runner/harness.js +++ b/lib/internal/test_runner/harness.js @@ -178,7 +178,7 @@ function configureCoverage(rootTest, globalOptions) { } catch (err) { const msg = `Warning: Code coverage could not be enabled. ${err}`; - rootTest.diagnostic(msg); + rootTest.diagnostic(msg, 'warn'); rootTest.harness.success = false; process.exitCode = kGenericUserError; } @@ -194,7 +194,7 @@ function collectCoverage(rootTest, coverage) { try { summary = coverage.summary(); } catch (err) { - rootTest.diagnostic(`Warning: Could not report code coverage. ${err}`); + rootTest.diagnostic(`Warning: Could not report code coverage. ${err}`, 'warn'); rootTest.harness.success = false; process.exitCode = kGenericUserError; } @@ -202,7 +202,7 @@ function collectCoverage(rootTest, coverage) { try { coverage.cleanup(); } catch (err) { - rootTest.diagnostic(`Warning: Could not clean up code coverage. ${err}`); + rootTest.diagnostic(`Warning: Could not clean up code coverage. ${err}`, 'warn'); rootTest.harness.success = false; process.exitCode = kGenericUserError; } From 50a84d70530aaf1034e79314d07edec374a22799 Mon Sep 17 00:00:00 2001 From: ! Maisto Date: Sat, 18 Jul 2026 15:32:45 +0200 Subject: [PATCH 2/5] test_runner: thread diagnostic level through Test/TestContext/SuiteContext Test.prototype.diagnostic() now stores { message, level } instead of a bare string, and TestContext/SuiteContext forward an optional level argument (default 'info'). This is consumed by the dot reporter to surface warn/error-level diagnostics, such as coverage failures. Signed-off-by: Maisto --- lib/internal/test_runner/test.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index 1b9faad58a68b5..a56a224e126df7 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -312,8 +312,8 @@ class TestContext { return Number(envWorkerId) || undefined; } - diagnostic(message) { - this.#test.diagnostic(message); + diagnostic(message, level = 'info') { + this.#test.diagnostic(message, level); } log(message, data) { @@ -525,8 +525,8 @@ class SuiteContext { return this.#suite.attempt ?? 0; } - diagnostic(message) { - this.#suite.diagnostic(message); + diagnostic(message, level = 'info') { + this.#suite.diagnostic(message, level); } log(message, data) { @@ -1203,8 +1203,8 @@ class Test extends AsyncResource { this.message = message; } - diagnostic(message) { - ArrayPrototypePush(this.diagnostics, message); + diagnostic(message, level = 'info') { + ArrayPrototypePush(this.diagnostics, { __proto__: null, message, level }); } log(message, data) { @@ -1551,7 +1551,7 @@ class Test extends AsyncResource { const coverage = harness.coverage(); harness.snapshotManager?.writeSnapshotFiles(); for (let i = 0; i < diagnostics.length; i++) { - reporter.diagnostic(nesting, loc, diagnostics[i]); + reporter.diagnostic(nesting, loc, diagnostics[i].message, diagnostics[i].level); } const duration = this.duration(); @@ -1693,7 +1693,8 @@ class Test extends AsyncResource { } for (let i = 0; i < this.diagnostics.length; i++) { - this.reporter.diagnostic(this.nesting, this.loc, this.diagnostics[i]); + const { message, level } = this.diagnostics[i]; + this.reporter.diagnostic(this.nesting, this.loc, message, level); } } From 8b052257e2d00b75dfc68a48ea6272306427552c Mon Sep 17 00:00:00 2001 From: ! Maisto Date: Sat, 18 Jul 2026 15:47:46 +0200 Subject: [PATCH 3/5] test_runner: display warn/error diagnostics with the dot reporter Previously the dot reporter only handled test:pass/test:fail events, so a warn/error-level diagnostic (e.g. a coverage collection failure) produced no visible output at all, only a non-zero exit code. This mirrors the coloring/symbol already used by the spec reporter. Fixes: https://github.com/nodejs/node/issues/60884 Signed-off-by: Maisto --- lib/internal/test_runner/reporter/dot.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/internal/test_runner/reporter/dot.js b/lib/internal/test_runner/reporter/dot.js index 45ff047bc4e5a0..683b2784475010 100644 --- a/lib/internal/test_runner/reporter/dot.js +++ b/lib/internal/test_runner/reporter/dot.js @@ -4,12 +4,17 @@ const { MathMax, } = primordials; const colors = require('internal/util/colors'); -const { formatTestReport } = require('internal/test_runner/reporter/utils'); +const { + formatTestReport, + reporterColorMap, + reporterUnicodeSymbolMap, +} = require('internal/test_runner/reporter/utils'); module.exports = async function* dot(source) { let count = 0; let columns = getLineLength(); const failedTests = []; + const diagnosticWarnings = []; for await (const { type, data } of source) { if (type === 'test:pass') { yield `${colors.green}.${colors.reset}`; @@ -18,6 +23,9 @@ module.exports = async function* dot(source) { yield `${colors.red}X${colors.reset}`; ArrayPrototypePush(failedTests, data); } + if (type === 'test:diagnostic' && (data.level === 'warn' || data.level === 'error')) { + ArrayPrototypePush(diagnosticWarnings, data); + } if ((type === 'test:fail' || type === 'test:pass') && ++count === columns) { yield '\n'; @@ -27,6 +35,12 @@ module.exports = async function* dot(source) { } } yield '\n'; + if (diagnosticWarnings.length > 0) { + for (const diagnostic of diagnosticWarnings) { + const diagnosticColor = reporterColorMap[diagnostic.level] || reporterColorMap['test:diagnostic']; + yield `${diagnosticColor}${reporterUnicodeSymbolMap['test:diagnostic']}${diagnostic.message}${colors.white}\n`; + } + } if (failedTests.length > 0) { yield `\n${colors.red}Failed tests:${colors.white}\n\n`; for (const test of failedTests) { From 8919274bb4538a92af884bb52ef952ff77fe4896 Mon Sep 17 00:00:00 2001 From: ! Maisto Date: Sat, 18 Jul 2026 16:14:59 +0200 Subject: [PATCH 4/5] test: add dot reporter coverage-failure snapshot test Reuses the existing coverage_failure.js fixture, which mocks TestCoverage.prototype.summary() to throw, and asserts the dot reporter's output against a new snapshot. Signed-off-by: Maisto --- .../test-output-dot-coverage-failure.mjs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/test-runner/test-output-dot-coverage-failure.mjs diff --git a/test/test-runner/test-output-dot-coverage-failure.mjs b/test/test-runner/test-output-dot-coverage-failure.mjs new file mode 100644 index 00000000000000..a754ccc1222a64 --- /dev/null +++ b/test/test-runner/test-output-dot-coverage-failure.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/coverage_failure.js matches +// test-runner/output/dot_coverage_failure.snapshot when using the dot reporter +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { spawnAndAssert, defaultTransform, ensureCwdIsProjectRoot } from '../common/assertSnapshot.js'; + +if (!process.features.inspector) { + common.skip('inspector support required'); +} + +ensureCwdIsProjectRoot(); +await spawnAndAssert( + fixtures.path('test-runner/output/coverage_failure.js'), + defaultTransform, + { flags: ['--test-reporter=dot', '--test-coverage-exclude=!test/**'] }, +); From 83608dc3f34ae27da9823ba881d8414375e3c706 Mon Sep 17 00:00:00 2001 From: ! Maisto Date: Sat, 18 Jul 2026 16:19:42 +0200 Subject: [PATCH 5/5] test: add dot reporter coverage-failure snapshot fixture Signed-off-by: Maisto --- test/fixtures/test-runner/output/dot_coverage_failure.snapshot | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 test/fixtures/test-runner/output/dot_coverage_failure.snapshot diff --git a/test/fixtures/test-runner/output/dot_coverage_failure.snapshot b/test/fixtures/test-runner/output/dot_coverage_failure.snapshot new file mode 100644 index 00000000000000..6135648132e446 --- /dev/null +++ b/test/fixtures/test-runner/output/dot_coverage_failure.snapshot @@ -0,0 +1,2 @@ +. +ℹ Warning: Could not report code coverage. Error: Failed to collect coverage