Skip to content

Commit

Permalink
test_runner: add code coverage support to spec reporter
Browse files Browse the repository at this point in the history
  • Loading branch information
pulkit-30 committed Mar 12, 2023
1 parent 7f2ab4e commit 17cf7f6
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 0 deletions.
38 changes: 38 additions & 0 deletions lib/internal/test_runner/reporter/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
ArrayPrototypeShift,
ArrayPrototypeUnshift,
hardenRegExp,
NumberPrototypeToFixed,
RegExpPrototypeSymbolSplit,
SafeMap,
StringPrototypeRepeat,
Expand All @@ -14,6 +15,7 @@ const assert = require('assert');
const Transform = require('internal/streams/transform');
const { inspectWithNoCustomRetry } = require('internal/errors');
const { green, blue, red, white, gray } = require('internal/util/colors');
const { relative } = require('path');


const inspectOptions = { __proto__: null, colors: true, breakLength: Infinity };
Expand All @@ -29,6 +31,7 @@ const symbols = {
'test:fail': '\u2716 ',
'test:pass': '\u2714 ',
'test:diagnostic': '\u2139 ',
'test:coverage': '\u2139 ',
'arrow:right': '\u25B6 ',
'hyphen:minus': '\uFE63 ',
};
Expand Down Expand Up @@ -60,6 +63,39 @@ class SpecReporter extends Transform {
), `\n${indent} `);
return `\n${indent} ${message}\n`;
}
#coverageThresholdColor(coverage, color = blue) {
coverage = NumberPrototypeToFixed(coverage, 2);
if (coverage > 90) return `${green}${coverage}${color}`;
if (coverage < 50) return `${red}${coverage}${color}`;
return coverage;
}
#reportCoverage(nesting, symbol, color, summary) {
const indent = this.#indent(nesting);
let report = `${color}${indent}${symbol}========= coverage report =========\n`;
report += `${indent}${symbol}file | line % | branch % | funcs % | uncovered lines\n`;
for (let i = 0; i < summary.files.length; ++i) {
const {
path,
coveredLinePercent,
coveredBranchPercent,
coveredFunctionPercent,
uncoveredLineNumbers,
} = summary.files[i];
const relativePath = relative(summary.workingDirectory, path);
const uncovered = ArrayPrototypeJoin(uncoveredLineNumbers, ', ');
report += `${indent}${symbol}${relativePath} | ${this.#coverageThresholdColor(coveredLinePercent)} | ${this.#coverageThresholdColor(coveredBranchPercent)} | ` +
`${this.#coverageThresholdColor(coveredFunctionPercent)} | ${gray}${uncovered}${color}\n`;
}
const {
coveredLinePercent,
coveredBranchPercent,
coveredFunctionPercent,
} = summary.totals;
report += `${indent}${symbol}all files | ${this.#coverageThresholdColor(coveredLinePercent)} | ${this.#coverageThresholdColor(coveredBranchPercent)} | ` +
`${this.#coverageThresholdColor(coveredFunctionPercent)} |\n`;
report += `${symbol}${indent}==================================== ${white}`
return report;
}
#handleEvent({ type, data }) {
let color = colors[type] ?? white;
let symbol = symbols[type] ?? ' ';
Expand Down Expand Up @@ -103,6 +139,8 @@ class SpecReporter extends Transform {
break;
case 'test:diagnostic':
return `${color}${this.#indent(data.nesting)}${symbol}${data.message}${white}\n`;
case 'test:coverage':
return this.#reportCoverage(data.nesting, symbols['test:coverage'], blue, data.summary);
}
}
_transform({ type, data }, encoding, callback) {
Expand Down
70 changes: 70 additions & 0 deletions test/parallel/test-runner-spec-coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use strict';
const common = require('../common');
const assert = require('node:assert');
const { spawnSync } = require('node:child_process');
const { readdirSync } = require('node:fs');
const { test } = require('node:test');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');

tmpdir.refresh();

function findCoverageFileForPid(pid) {
const pattern = `^coverage\\-${pid}\\-(\\d{13})\\-(\\d+)\\.json$`;
const regex = new RegExp(pattern);

return readdirSync(tmpdir.path).find((file) => {
return regex.test(file);
});
}

function getCoverageFixtureReport() {
const report = [
'\u2139 ========= coverage report =========',
'\u2139 file | line % | branch % | funcs % | uncovered lines',
'\u2139 test/fixtures/test-runner/coverage.js | 78.65 | 38.46 | 60.00 | 12, 13, 16, 17, 18, 19, 20, 21, 22, 27, 39, 43, 44, 61, 62, 66, 67, 71, 72',
'\u2139 test/fixtures/test-runner/invalid-tap.js | 100.00 | 100.00 | 100.00 | ',
'\u2139 test/fixtures/v8-coverage/throw.js | 71.43 | 50.00 | 100.00 | 5, 6',
'\u2139 all files | 78.35 | 43.75 | 60.00 |',
'\u2139 ===================================='
].join('\n');

if (common.isWindows) {
return report.replaceAll('/', '\\');
}

return report;
}

test('coverage is reported and dumped to NODE_V8_COVERAGE if present', (t) => {
if (!process.features.inspector) {
return;
}

const fixture = fixtures.path('test-runner', 'coverage.js');
const args = ['--experimental-test-coverage', '--test-reporter', 'spec', fixture];
const options = { env: { ...process.env, NODE_V8_COVERAGE: tmpdir.path } };
const result = spawnSync(process.execPath, args, options);
const report = getCoverageFixtureReport();

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

test('coverage is reported without NODE_V8_COVERAGE present', (t) => {
if (!process.features.inspector) {
return;
}

const fixture = fixtures.path('test-runner', 'coverage.js');
const args = ['--experimental-test-coverage', '--test-reporter', 'spec', fixture];
const result = spawnSync(process.execPath, args);
const report = getCoverageFixtureReport();

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

0 comments on commit 17cf7f6

Please sign in to comment.