Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/internal/fs/glob.js
Original file line number Diff line number Diff line change
Expand Up @@ -927,13 +927,15 @@ class Glob {
* @param {string} path the path to check
* @param {string} pattern the glob pattern to match
* @param {boolean} windows whether the path is on a Windows system, defaults to `isWindows`
* @param {object} [options] the options for the minimatch module
* @returns {boolean}
*/
function matchGlobPattern(path, pattern, windows = isWindows) {
function matchGlobPattern(path, pattern, windows = isWindows, options = kEmptyObject) {
validateString(path, 'path');
validateString(pattern, 'pattern');
return lazyMinimatch().minimatch(path, pattern, {
kEmptyObject,
...options,
nocase: isMacOS || isWindows,
windowsPathsNoEscape: true,
nonegate: true,
Expand Down
12 changes: 10 additions & 2 deletions lib/internal/test_runner/coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ const kIgnoreRegex = /\/\* node:coverage ignore next (?<count>\d+ )?\*\//;
const kLineEndingRegex = /\r?\n$/u;
const kLineSplitRegex = /(?<=\r?\n)/u;
const kStatusRegex = /\/\* node:coverage (?<status>enable|disable) \*\//;
const kMatchGlobPatternOptions = { __proto__: null, dot: true };

function matchCoverageGlob(path, pattern) {
return matchGlobPattern(path, pattern, undefined, kMatchGlobPatternOptions);
}

class CoverageLine {
constructor(line, startOffset, src, length = src?.length) {
Expand Down Expand Up @@ -478,10 +483,13 @@ class TestCoverage {
} = this.options;

// This check filters out files that match the exclude globs.
// dot:true is only applied to relativePath so user-supplied globs match
// dotfiles within the project, without misinterpreting dot segments in the
// absolute filesystem path (e.g. test tmp dirs like `test/.tmp.0`).
if (excludeGlobs?.length > 0) {
for (let i = 0; i < excludeGlobs.length; ++i) {
if (
matchGlobPattern(relativePath, excludeGlobs[i]) ||
matchCoverageGlob(relativePath, excludeGlobs[i]) ||
matchGlobPattern(absolutePath, excludeGlobs[i])
) return true;
}
Expand All @@ -491,7 +499,7 @@ class TestCoverage {
if (includeGlobs?.length > 0) {
for (let i = 0; i < includeGlobs.length; ++i) {
if (
matchGlobPattern(relativePath, includeGlobs[i]) ||
matchCoverageGlob(relativePath, includeGlobs[i]) ||
matchGlobPattern(absolutePath, includeGlobs[i])
) return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const test = require('node:test');
const assert = require('node:assert');
const { foo } = require('../logic-file.js');

test('foo returns 1 from a dotfile test', () => {
assert.strictEqual(foo(), 1);
});
57 changes: 31 additions & 26 deletions test/parallel/test-runner-coverage-default-exclusion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ async function setupFixtures() {
await cp(fixtureDir, tmpdir.path, { recursive: true });
}

function assertDefaultExclusions(stdout) {
assert.match(stdout, /# start of coverage report/);
assert.doesNotMatch(stdout, /# file-test\.js\s+\|/);
assert.doesNotMatch(stdout, /# file\.test\.mjs\s+\|/);
assert.doesNotMatch(stdout, /# file\.test\.ts\s+\|/);
assert.doesNotMatch(stdout, /# test\.cjs\s+\|/);
assert.doesNotMatch(stdout, /#\s+not-matching-test-name\.js\s+\|/);
assert.match(stdout, /# end of coverage report/);
}

describe('test runner coverage default exclusion', skipIfNoInspector, () => {
before(async () => {
await setupFixtures();
Expand Down Expand Up @@ -58,18 +68,6 @@ describe('test runner coverage default exclusion', skipIfNoInspector, () => {
});

it('should exclude test files from coverage by default', async () => {
const report = [
'# start of coverage report',
'# --------------------------------------------------------------',
'# file | line % | branch % | funcs % | uncovered lines',
'# --------------------------------------------------------------',
'# logic-file.js | 66.67 | 100.00 | 50.00 | 5-7',
'# --------------------------------------------------------------',
'# all files | 66.67 | 100.00 | 50.00 | ',
'# --------------------------------------------------------------',
'# end of coverage report',
].join('\n');

const args = [
'--no-experimental-strip-types',
'--test',
Expand All @@ -82,23 +80,11 @@ describe('test runner coverage default exclusion', skipIfNoInspector, () => {
});

assert.strictEqual(result.stderr.toString(), '');
assert(result.stdout.toString().includes(report));
assertDefaultExclusions(result.stdout.toString());
assert.strictEqual(result.status, 0);
});

it('should exclude ts test files', async () => {
const report = [
'# start of coverage report',
'# --------------------------------------------------------------',
'# file | line % | branch % | funcs % | uncovered lines',
'# --------------------------------------------------------------',
'# logic-file.js | 66.67 | 100.00 | 50.00 | 5-7',
'# --------------------------------------------------------------',
'# all files | 66.67 | 100.00 | 50.00 | ',
'# --------------------------------------------------------------',
'# end of coverage report',
].join('\n');

const args = [
'--test',
'--experimental-test-coverage',
Expand All @@ -111,7 +97,26 @@ describe('test runner coverage default exclusion', skipIfNoInspector, () => {
});

assert.strictEqual(result.stderr.toString(), '');
assert(result.stdout.toString().includes(report));
assertDefaultExclusions(result.stdout.toString());
assert.strictEqual(result.status, 0);
});

it('should exclude dotfile test files from coverage by default', async () => {
const args = [
'--no-experimental-strip-types',
'--test',
'--experimental-test-coverage',
'--test-reporter=tap',
'test/.dotfile.cjs',
];
const result = spawnSync(process.execPath, args, {
env: { ...process.env, NODE_TEST_TMPDIR: tmpdir.path },
cwd: tmpdir.path
});

assert.strictEqual(result.stderr.toString(), '');
assertDefaultExclusions(result.stdout.toString());
assert.doesNotMatch(result.stdout.toString(), /#\s+\.dotfile\.cjs\s+\|/);
assert.strictEqual(result.status, 0);
});
});
Loading