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. 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); +});