From b7cd7030c2cf768f352984ee3be3dc2f986db141 Mon Sep 17 00:00:00 2001 From: madkate42 Date: Thu, 19 Feb 2026 15:29:09 -0500 Subject: [PATCH 1/2] test_runner: print coverage and diagnostics with dot reporter --- lib/internal/test_runner/reporter/dot.js | 22 ++++++++++++++++++- .../test-runner-coverage-thresholds.js | 17 ++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/internal/test_runner/reporter/dot.js b/lib/internal/test_runner/reporter/dot.js index 45ff047bc4e5a0..71b04f1decbdca 100644 --- a/lib/internal/test_runner/reporter/dot.js +++ b/lib/internal/test_runner/reporter/dot.js @@ -4,12 +4,19 @@ const { MathMax, } = primordials; const colors = require('internal/util/colors'); -const { formatTestReport } = require('internal/test_runner/reporter/utils'); +const { getCoverageReport } = require('internal/test_runner/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 diagnostics = []; + let coverage; for await (const { type, data } of source) { if (type === 'test:pass') { yield `${colors.green}.${colors.reset}`; @@ -25,6 +32,12 @@ module.exports = async function* dot(source) { columns = getLineLength(); count = 0; } + if (type === 'test:diagnostic') { + ArrayPrototypePush(diagnostics, data); + } + if (type === 'test:coverage') { + coverage = data; + } } yield '\n'; if (failedTests.length > 0) { @@ -33,6 +46,13 @@ module.exports = async function* dot(source) { yield formatTestReport('test:fail', test); } } + for (const diagnostic of diagnostics) { + const color = reporterColorMap[diagnostic.level] || reporterColorMap['test:diagnostic']; + yield `${color}${reporterUnicodeSymbolMap['test:diagnostic']}${diagnostic.message}${colors.white}\n`; + } + if (coverage) { + yield getCoverageReport('', coverage.summary, reporterUnicodeSymbolMap['test:coverage'], colors.blue, true); + } }; function getLineLength() { diff --git a/test/parallel/test-runner-coverage-thresholds.js b/test/parallel/test-runner-coverage-thresholds.js index e45e1191299ca7..22f052792433cf 100644 --- a/test/parallel/test-runner-coverage-thresholds.js +++ b/test/parallel/test-runner-coverage-thresholds.js @@ -143,6 +143,23 @@ for (const coverage of coverages) { assert(!findCoverageFileForPid(result.pid)); }); + test(`test failing ${coverage.flag} with dot reporter`, () => { + const result = spawnSync(process.execPath, [ + '--test', + '--experimental-test-coverage', + '--test-coverage-exclude=!test/**', + `${coverage.flag}=99`, + '--test-reporter', 'dot', + fixture, + ]); + + const stdout = result.stdout.toString(); + assert.match(stdout, RegExp(`Error: ${coverage.actual.toFixed(2)}% ${coverage.name} coverage does not meet threshold of 99%`)); + assert.match(stdout, /start of coverage report/); + assert.strictEqual(result.status, 1); + assert(!findCoverageFileForPid(result.pid)); + }); + test(`test out-of-range ${coverage.flag} (too high)`, () => { const result = spawnSync(process.execPath, [ '--test', From 3be8badb91e53189118601cb429c52dda99bfab6 Mon Sep 17 00:00:00 2001 From: madkate42 Date: Thu, 19 Feb 2026 18:27:05 -0500 Subject: [PATCH 2/2] adds a snapshot test --- .../output/dot_reporter_coverage.mjs | 13 +++++++++++++ .../output/dot_reporter_coverage.snapshot | 19 +++++++++++++++++++ .../test-output-dot-reporter-coverage.mjs | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 test/fixtures/test-runner/output/dot_reporter_coverage.mjs create mode 100644 test/fixtures/test-runner/output/dot_reporter_coverage.snapshot create mode 100644 test/test-runner/test-output-dot-reporter-coverage.mjs diff --git a/test/fixtures/test-runner/output/dot_reporter_coverage.mjs b/test/fixtures/test-runner/output/dot_reporter_coverage.mjs new file mode 100644 index 00000000000000..6662d9be0b2e21 --- /dev/null +++ b/test/fixtures/test-runner/output/dot_reporter_coverage.mjs @@ -0,0 +1,13 @@ +// Flags: --experimental-test-coverage --test-coverage-lines=99 +// here we can't import common module as the coverage will be different based on the system +// Unused imports are here in order to populate the coverage report +// eslint-disable-next-line no-unused-vars +import * as a from '../coverage-snap/b.js'; +// eslint-disable-next-line no-unused-vars +import * as b from '../coverage-snap/a.js'; + +import { test } from 'node:test'; + +process.stdout.columns = 80; + +test('passing test for dot coverage report'); diff --git a/test/fixtures/test-runner/output/dot_reporter_coverage.snapshot b/test/fixtures/test-runner/output/dot_reporter_coverage.snapshot new file mode 100644 index 00000000000000..99b7ee0321cc3e --- /dev/null +++ b/test/fixtures/test-runner/output/dot_reporter_coverage.snapshot @@ -0,0 +1,19 @@ +. + +ℹ Error: *% line coverage does not meet threshold of 99%. +ℹ start of coverage report +ℹ ------------------------------------------------------------------------------ +ℹ file | line % | branch % | funcs % | uncovered lines +ℹ ------------------------------------------------------------------------------ +ℹ test | | | | +ℹ fixtures | | | | +ℹ test-runner | | | | +ℹ coverage-snap | | | | +ℹ a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-… +ℹ b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11 +ℹ output | | | | +ℹ dot_reporter_cove….mjs | 100.00 | 100.00 | 100.00 | +ℹ ------------------------------------------------------------------------------ +ℹ all files | 61.84 | 100.00 | 0.00 | +ℹ ------------------------------------------------------------------------------ +ℹ end of coverage report diff --git a/test/test-runner/test-output-dot-reporter-coverage.mjs b/test/test-runner/test-output-dot-reporter-coverage.mjs new file mode 100644 index 00000000000000..993efcf99add3d --- /dev/null +++ b/test/test-runner/test-output-dot-reporter-coverage.mjs @@ -0,0 +1,16 @@ +// Test that the output of test-runner/output/dot_reporter_coverage.mjs matches +// test-runner/output/dot_reporter_coverage.snapshot +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/dot_reporter_coverage.mjs'), + defaultTransform, + { flags: ['--test-reporter=dot', '--test-coverage-exclude=!test/**'] }, +);