diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 37ff2473b68011..7c7996fa319ca9 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -263,7 +263,7 @@ class TestCoverage { ArrayPrototypePush(branchReports, { __proto__: null, line: range.lines[0]?.line, - count: range.count, + count: range.ignoredLines === range.lines.length ? 1 : range.count, }); if (range.count !== 0 || diff --git a/test/fixtures/test-runner/coverage-ignore-branch.js b/test/fixtures/test-runner/coverage-ignore-branch.js new file mode 100644 index 00000000000000..ef92b397dbf0a0 --- /dev/null +++ b/test/fixtures/test-runner/coverage-ignore-branch.js @@ -0,0 +1,11 @@ +'use strict'; + +function getValue(condition) { + if (condition) { + return 'truthy'; + } + /* node:coverage ignore next */ + return 'falsy'; +} + +module.exports = { getValue }; diff --git a/test/fixtures/test-runner/coverage-ignore-branch.test.js b/test/fixtures/test-runner/coverage-ignore-branch.test.js new file mode 100644 index 00000000000000..a6a177151fc738 --- /dev/null +++ b/test/fixtures/test-runner/coverage-ignore-branch.test.js @@ -0,0 +1,10 @@ +'use strict'; +const { describe, it } = require('node:test'); +const assert = require('node:assert'); +const { getValue } = require('./coverage-ignore-branch.js'); + +describe('getValue', () => { + it('should return truthy when condition is true', () => { + assert.strictEqual(getValue(true), 'truthy'); + }); +}); diff --git a/test/parallel/test-runner-coverage.js b/test/parallel/test-runner-coverage.js index 5a8f3d743538cb..cdbfab49199d68 100644 --- a/test/parallel/test-runner-coverage.js +++ b/test/parallel/test-runner-coverage.js @@ -566,3 +566,28 @@ test('coverage with directory and file named "file"', skipIfNoInspector, () => { assert.strictEqual(result.status, 0); assert(result.stdout.toString().includes('start of coverage report')); }); + +// Regression test for https://github.com/nodejs/node/issues/61586 +// When a branch leads to ignored code, it should be marked as covered. +test('coverage ignores branches leading to ignored code', skipIfNoInspector, async (t) => { + const fixture = fixtures.path('test-runner', 'coverage-ignore-branch.test.js'); + const child = spawnSync(process.execPath, + [ + '--test', + '--experimental-test-coverage', + '--test-coverage-exclude=!test/**', + '--test-reporter', + fixtures.fileURL('test-runner/custom_reporters/coverage.mjs'), + fixture, + ]); + assert.strictEqual(child.stderr.toString(), ''); + const stdout = child.stdout.toString(); + const coverage = JSON.parse(stdout); + + const file = coverage.summary.files.find((f) => f.path.endsWith('coverage-ignore-branch.js')); + assert(file, 'Expected coverage-ignore-branch.js in coverage report'); + + // All branches should be covered because the untaken falsy branch + // leads to code that is ignored via /* node:coverage ignore next */. + assert.strictEqual(file.coveredBranchCount, file.totalBranchCount); +});