From bd19b1a44ebf6ac0bd641e305a374cd1daacbd64 Mon Sep 17 00:00:00 2001 From: developers-universe-1 Date: Thu, 28 May 2026 16:17:37 -0400 Subject: [PATCH 1/2] test_runner: mark ignored branches as covered in coverage report When a branch leads to code that is fully ignored via /* node:coverage ignore next */, the branch should be marked as covered (count = 1) instead of uncovered (count = 0). This aligns with the existing logic that already counts fully-ignored branches as covered for summary statistics (BRF/BRH). Fixes: https://github.com/nodejs/node/issues/61586 --- lib/internal/test_runner/coverage.js | 2 +- .../test-runner/coverage-ignore-branch.js | 11 ++++++++ .../coverage-ignore-branch.test.js | 10 ++++++++ test/parallel/test-runner-coverage.js | 25 +++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/test-runner/coverage-ignore-branch.js create mode 100644 test/fixtures/test-runner/coverage-ignore-branch.test.js 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); +}); From e08e22a904e20c10d3215968c59ee9de46c59ed3 Mon Sep 17 00:00:00 2001 From: developers-universe-1 Date: Thu, 28 May 2026 16:28:35 -0400 Subject: [PATCH 2/2] doc: document that fs.copyFile() always dereferences symlinks Add a note to fs.copyFile(), fs.copyFileSync(), and fsPromises.copyFile() documenting that they always dereference symlinks, matching the behavior already documented for fs.cp(). Fixes: https://github.com/nodejs/node/issues/61518 --- doc/api/fs.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/api/fs.md b/doc/api/fs.md index 350555f2f8ad99..2a38e5b9774fa4 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1277,6 +1277,8 @@ No guarantees are made about the atomicity of the copy operation. If an error occurs after the destination file has been opened for writing, an attempt will be made to remove the destination. +`fsPromises.copyFile()` always dereferences symlinks. + ```mjs import { copyFile, constants } from 'node:fs/promises'; @@ -2754,6 +2756,8 @@ callback function. Node.js makes no guarantees about the atomicity of the copy operation. If an error occurs after the destination file has been opened for writing, Node.js will attempt to remove the destination. +`fs.copyFile()` always dereferences symlinks. + `mode` is an optional integer that specifies the behavior of the copy operation. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. @@ -5857,6 +5861,8 @@ already exists. Returns `undefined`. Node.js makes no guarantees about the atomicity of the copy operation. If an error occurs after the destination file has been opened for writing, Node.js will attempt to remove the destination. +`fs.copyFileSync()` always dereferences symlinks. + `mode` is an optional integer that specifies the behavior of the copy operation. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g.