| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,7 @@ const assert = require('assert'); | |||
| 15 | 15 | const Transform = require('internal/streams/transform'); | |
| 16 | 16 | const { inspectWithNoCustomRetry } = require('internal/errors'); | |
| 17 | 17 | const { green, blue, red, white, gray } = require('internal/util/colors'); | |
| 18 | - | ||
| 18 | + const { getCoverageReport } = require('internal/test_runner/utils'); | ||
| 19 | 19 | ||
| 20 | 20 | const inspectOptions = { __proto__: null, colors: true, breakLength: Infinity }; | |
| 21 | 21 | ||
@@ -30,6 +30,7 @@ const symbols = { | |||
| 30 | 30 | 'test:fail': '\u2716 ', | |
| 31 | 31 | 'test:pass': '\u2714 ', | |
| 32 | 32 | 'test:diagnostic': '\u2139 ', | |
| 33 | + 'test:coverage': '\u2139 ', | ||
| 33 | 34 | 'arrow:right': '\u25B6 ', | |
| 34 | 35 | 'hyphen:minus': '\uFE63 ', | |
| 35 | 36 | }; | |
@@ -115,6 +116,8 @@ class SpecReporter extends Transform { | |||
| 115 | 116 | break; | |
| 116 | 117 | case 'test:diagnostic': | |
| 117 | 118 | return `${colors[type]}${this.#indent(data.nesting)}${symbols[type]}${data.message}${white}\n`; | |
| 119 | + case 'test:coverage': | ||
| 120 | + return getCoverageReport(this.#indent(data.nesting), data.summary, symbols['test:coverage'], blue); | ||
| 118 | 121 | } | |
| 119 | 122 | } | |
| 120 | 123 | _transform({ type, data }, encoding, callback) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,6 @@ const { | |||
| 3 | 3 | ArrayPrototypeForEach, | |
| 4 | 4 | ArrayPrototypeJoin, | |
| 5 | 5 | ArrayPrototypePush, | |
| 6 | - NumberPrototypeToFixed, | ||
| 7 | 6 | ObjectEntries, | |
| 8 | 7 | RegExpPrototypeSymbolReplace, | |
| 9 | 8 | SafeMap, | |
@@ -13,7 +12,7 @@ const { | |||
| 13 | 12 | } = primordials; | |
| 14 | 13 | const { inspectWithNoCustomRetry } = require('internal/errors'); | |
| 15 | 14 | const { isError, kEmptyObject } = require('internal/util'); | |
| 16 | - const { relative } = require('path'); | ||
| 15 | + const { getCoverageReport } = require('internal/test_runner/utils'); | ||
| 17 | 16 | const kDefaultIndent = ' '; // 4 spaces | |
| 18 | 17 | const kFrameStartRegExp = /^ {4}at /; | |
| 19 | 18 | const kLineBreakRegExp = /\n|\r\n/; | |
@@ -49,7 +48,7 @@ async function * tapReporter(source) { | |||
| 49 | 48 | yield `${indent(data.nesting)}# ${tapEscape(data.message)}\n`; | |
| 50 | 49 | break; | |
| 51 | 50 | case 'test:coverage': | |
| 52 | - yield reportCoverage(data.nesting, data.summary); | ||
| 51 | + yield getCoverageReport(indent(data.nesting), data.summary, '# ', ''); | ||
| 53 | 52 | break; | |
| 54 | 53 | } | |
| 55 | 54 | } | |
@@ -73,40 +72,6 @@ function reportTest(nesting, testNumber, status, name, skip, todo) { | |||
| 73 | 72 | return line; | |
| 74 | 73 | } | |
| 75 | 74 | ||
| 76 | - function reportCoverage(nesting, summary) { | ||
| 77 | - const pad = indent(nesting); | ||
| 78 | - let report = `${pad}# start of coverage report\n`; | ||
| 79 | - | ||
| 80 | - report += `${pad}# file | line % | branch % | funcs % | uncovered lines\n`; | ||
| 81 | - | ||
| 82 | - for (let i = 0; i < summary.files.length; ++i) { | ||
| 83 | - const { | ||
| 84 | - path, | ||
| 85 | - coveredLinePercent, | ||
| 86 | - coveredBranchPercent, | ||
| 87 | - coveredFunctionPercent, | ||
| 88 | - uncoveredLineNumbers, | ||
| 89 | - } = summary.files[i]; | ||
| 90 | - const relativePath = relative(summary.workingDirectory, path); | ||
| 91 | - const lines = NumberPrototypeToFixed(coveredLinePercent, 2); | ||
| 92 | - const branches = NumberPrototypeToFixed(coveredBranchPercent, 2); | ||
| 93 | - const functions = NumberPrototypeToFixed(coveredFunctionPercent, 2); | ||
| 94 | - const uncovered = ArrayPrototypeJoin(uncoveredLineNumbers, ', '); | ||
| 95 | - | ||
| 96 | - report += `${pad}# ${relativePath} | ${lines} | ${branches} | ` + | ||
| 97 | - `${functions} | ${uncovered}\n`; | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - const { totals } = summary; | ||
| 101 | - report += `${pad}# all files | ` + | ||
| 102 | - `${NumberPrototypeToFixed(totals.coveredLinePercent, 2)} | ` + | ||
| 103 | - `${NumberPrototypeToFixed(totals.coveredBranchPercent, 2)} | ` + | ||
| 104 | - `${NumberPrototypeToFixed(totals.coveredFunctionPercent, 2)} |\n`; | ||
| 105 | - | ||
| 106 | - report += `${pad}# end of coverage report\n`; | ||
| 107 | - return report; | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | 75 | function reportDetails(nesting, data = kEmptyObject) { | |
| 111 | 76 | const { error, duration_ms } = data; | |
| 112 | 77 | const _indent = indent(nesting); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,19 +1,22 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | const { | |
| 3 | + ArrayPrototypeJoin, | ||
| 3 | 4 | ArrayPrototypeMap, | |
| 4 | 5 | ArrayPrototypePush, | |
| 5 | 6 | ObjectGetOwnPropertyDescriptor, | |
| 7 | + NumberPrototypeToFixed, | ||
| 6 | 8 | SafePromiseAllReturnArrayLike, | |
| 7 | 9 | RegExp, | |
| 8 | 10 | RegExpPrototypeExec, | |
| 9 | 11 | SafeMap, | |
| 10 | 12 | } = primordials; | |
| 11 | 13 | ||
| 12 | - const { basename } = require('path'); | ||
| 14 | + const { basename, relative } = require('path'); | ||
| 13 | 15 | const { createWriteStream } = require('fs'); | |
| 14 | 16 | const { pathToFileURL } = require('internal/url'); | |
| 15 | 17 | const { createDeferredPromise } = require('internal/util'); | |
| 16 | 18 | const { getOptionValue } = require('internal/options'); | |
| 19 | + const { green, red, white } = require('internal/util/colors'); | ||
| 17 | 20 | ||
| 18 | 21 | const { | |
| 19 | 22 | codes: { | |
@@ -246,6 +249,52 @@ function countCompletedTest(test, harness = test.root.harness) { | |||
| 246 | 249 | harness.counters.all++; | |
| 247 | 250 | } | |
| 248 | 251 | ||
| 252 | + | ||
| 253 | + function coverageThreshold(coverage, color) { | ||
| 254 | + coverage = NumberPrototypeToFixed(coverage, 2); | ||
| 255 | + if (color) { | ||
| 256 | + if (coverage > 90) return `${green}${coverage}${color}`; | ||
| 257 | + if (coverage < 50) return `${red}${coverage}${color}`; | ||
| 258 | + } | ||
| 259 | + return coverage; | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + function getCoverageReport(pad, summary, symbol, color) { | ||
| 263 | + let report = `${color}${pad}${symbol}start of coverage report\n`; | ||
| 264 | + | ||
| 265 | + report += `${pad}${symbol}file | line % | branch % | funcs % | uncovered lines\n`; | ||
| 266 | + | ||
| 267 | + for (let i = 0; i < summary.files.length; ++i) { | ||
| 268 | + const { | ||
| 269 | + path, | ||
| 270 | + coveredLinePercent, | ||
| 271 | + coveredBranchPercent, | ||
| 272 | + coveredFunctionPercent, | ||
| 273 | + uncoveredLineNumbers, | ||
| 274 | + } = summary.files[i]; | ||
| 275 | + const relativePath = relative(summary.workingDirectory, path); | ||
| 276 | + const lines = coverageThreshold(coveredLinePercent, color); | ||
| 277 | + const branches = coverageThreshold(coveredBranchPercent, color); | ||
| 278 | + const functions = coverageThreshold(coveredFunctionPercent, color); | ||
| 279 | + const uncovered = ArrayPrototypeJoin(uncoveredLineNumbers, ', '); | ||
| 280 | + | ||
| 281 | + report += `${pad}${symbol}${relativePath} | ${lines} | ${branches} | ` + | ||
| 282 | + `${functions} | ${uncovered}\n`; | ||
| 283 | + } | ||
| 284 | + | ||
| 285 | + const { totals } = summary; | ||
| 286 | + report += `${pad}${symbol}all files | ` + | ||
| 287 | + `${coverageThreshold(totals.coveredLinePercent, color)} | ` + | ||
| 288 | + `${coverageThreshold(totals.coveredBranchPercent, color)} | ` + | ||
| 289 | + `${coverageThreshold(totals.coveredFunctionPercent, color)} |\n`; | ||
| 290 | + | ||
| 291 | + report += `${pad}${symbol}end of coverage report\n`; | ||
| 292 | + if (color) { | ||
| 293 | + report += white; | ||
| 294 | + } | ||
| 295 | + return report; | ||
| 296 | + } | ||
| 297 | + | ||
| 249 | 298 | module.exports = { | |
| 250 | 299 | convertStringToRegExp, | |
| 251 | 300 | countCompletedTest, | |
@@ -255,4 +304,5 @@ module.exports = { | |||
| 255 | 304 | isTestFailureError, | |
| 256 | 305 | parseCommandLine, | |
| 257 | 306 | setupTestReporters, | |
| 307 | + getCoverageReport, | ||
| 258 | 308 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,7 +18,7 @@ function findCoverageFileForPid(pid) { | |||
| 18 | 18 | }); | |
| 19 | 19 | } | |
| 20 | 20 | ||
| 21 | - function getCoverageFixtureReport() { | ||
| 21 | + function getTapCoverageFixtureReport() { | ||
| 22 | 22 | const report = [ | |
| 23 | 23 | '# start of coverage report', | |
| 24 | 24 | '# file | line % | branch % | funcs % | uncovered lines', | |
@@ -37,64 +37,120 @@ function getCoverageFixtureReport() { | |||
| 37 | 37 | return report; | |
| 38 | 38 | } | |
| 39 | 39 | ||
| 40 | - test('--experimental-test-coverage and --test cannot be combined', () => { | ||
| 41 | - // TODO(cjihrig): This test can be removed once multi-process code coverage | ||
| 42 | - // is supported. | ||
| 43 | - const args = ['--test', '--experimental-test-coverage']; | ||
| 44 | - const result = spawnSync(process.execPath, args); | ||
| 45 | - | ||
| 46 | - // 9 is the documented exit code for an invalid CLI argument. | ||
| 47 | - assert.strictEqual(result.status, 9); | ||
| 48 | - assert.match( | ||
| 49 | - result.stderr.toString(), | ||
| 50 | - /--experimental-test-coverage cannot be used with --test/ | ||
| 51 | - ); | ||
| 52 | - }); | ||
| 40 | + function getSpecCoverageFixtureReport() { | ||
| 41 | + const report = [ | ||
| 42 | + '\u2139 start of coverage report', | ||
| 43 | + '\u2139 file | line % | branch % | funcs % | uncovered lines', | ||
| 44 | + '\u2139 test/fixtures/test-runner/coverage.js | 78.65 | 38.46 | 60.00 | 12, ' + | ||
| 45 | + '13, 16, 17, 18, 19, 20, 21, 22, 27, 39, 43, 44, 61, 62, 66, 67, 71, 72', | ||
| 46 | + '\u2139 test/fixtures/test-runner/invalid-tap.js | 100.00 | 100.00 | 100.00 | ', | ||
| 47 | + '\u2139 test/fixtures/v8-coverage/throw.js | 71.43 | 50.00 | 100.00 | 5, 6', | ||
| 48 | + '\u2139 all files | 78.35 | 43.75 | 60.00 |', | ||
| 49 | + '\u2139 end of coverage report', | ||
| 50 | + ].join('\n'); | ||
| 53 | 51 | ||
| 54 | - test('handles the inspector not being available', (t) => { | ||
| 55 | - if (process.features.inspector) { | ||
| 56 | - return; | ||
| 52 | + if (common.isWindows) { | ||
| 53 | + return report.replaceAll('/', '\\'); | ||
| 57 | 54 | } | |
| 58 | 55 | ||
| 59 | - const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 60 | - const args = ['--experimental-test-coverage', fixture]; | ||
| 61 | - const result = spawnSync(process.execPath, args); | ||
| 56 | + return report; | ||
| 57 | + } | ||
| 62 | 58 | ||
| 63 | - assert(!result.stdout.toString().includes('# start of coverage report')); | ||
| 64 | - assert(result.stderr.toString().includes('coverage could not be collected')); | ||
| 65 | - assert.strictEqual(result.status, 0); | ||
| 66 | - assert(!findCoverageFileForPid(result.pid)); | ||
| 67 | - }); | ||
| 59 | + test('test coverage report', async (t) => { | ||
| 60 | + await t.test('--experimental-test-coverage and --test cannot be combined', () => { | ||
| 61 | + // TODO(cjihrig): This test can be removed once multi-process code coverage | ||
| 62 | + // is supported. | ||
| 63 | + const args = ['--test', '--experimental-test-coverage']; | ||
| 64 | + const result = spawnSync(process.execPath, args); | ||
| 65 | + | ||
| 66 | + // 9 is the documented exit code for an invalid CLI argument. | ||
| 67 | + assert.strictEqual(result.status, 9); | ||
| 68 | + assert.match( | ||
| 69 | + result.stderr.toString(), | ||
| 70 | + /--experimental-test-coverage cannot be used with --test/ | ||
| 71 | + ); | ||
| 72 | + }); | ||
| 68 | 73 | ||
| 69 | - test('coverage is reported and dumped to NODE_V8_COVERAGE if present', (t) => { | ||
| 70 | - if (!process.features.inspector) { | ||
| 71 | - return; | ||
| 72 | - } | ||
| 74 | + await t.test('handles the inspector not being available', (t) => { | ||
| 75 | + if (process.features.inspector) { | ||
| 76 | + return; | ||
| 77 | + } | ||
| 73 | 78 | ||
| 74 | - const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 75 | - const args = ['--experimental-test-coverage', fixture]; | ||
| 76 | - const options = { env: { ...process.env, NODE_V8_COVERAGE: tmpdir.path } }; | ||
| 77 | - const result = spawnSync(process.execPath, args, options); | ||
| 78 | - const report = getCoverageFixtureReport(); | ||
| 79 | + const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 80 | + const args = ['--experimental-test-coverage', fixture]; | ||
| 81 | + const result = spawnSync(process.execPath, args); | ||
| 79 | 82 | ||
| 80 | - assert(result.stdout.toString().includes(report)); | ||
| 81 | - assert.strictEqual(result.stderr.toString(), ''); | ||
| 82 | - assert.strictEqual(result.status, 0); | ||
| 83 | - assert(findCoverageFileForPid(result.pid)); | ||
| 83 | + assert(!result.stdout.toString().includes('# start of coverage report')); | ||
| 84 | + assert(result.stderr.toString().includes('coverage could not be collected')); | ||
| 85 | + assert.strictEqual(result.status, 0); | ||
| 86 | + assert(!findCoverageFileForPid(result.pid)); | ||
| 87 | + }); | ||
| 84 | 88 | }); | |
| 85 | 89 | ||
| 86 | - test('coverage is reported without NODE_V8_COVERAGE present', (t) => { | ||
| 87 | - if (!process.features.inspector) { | ||
| 88 | - return; | ||
| 89 | - } | ||
| 90 | + test('test tap coverage reporter', async (t) => { | ||
| 91 | + await t.test('coverage is reported and dumped to NODE_V8_COVERAGE if present', (t) => { | ||
| 92 | + if (!process.features.inspector) { | ||
| 93 | + return; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 97 | + const args = ['--experimental-test-coverage', '--test-reporter', 'tap', fixture]; | ||
| 98 | + const options = { env: { ...process.env, NODE_V8_COVERAGE: tmpdir.path } }; | ||
| 99 | + const result = spawnSync(process.execPath, args, options); | ||
| 100 | + const report = getTapCoverageFixtureReport(); | ||
| 101 | + | ||
| 102 | + assert(result.stdout.toString().includes(report)); | ||
| 103 | + assert.strictEqual(result.stderr.toString(), ''); | ||
| 104 | + assert.strictEqual(result.status, 0); | ||
| 105 | + assert(findCoverageFileForPid(result.pid)); | ||
| 106 | + }); | ||
| 107 | + | ||
| 108 | + await t.test('coverage is reported without NODE_V8_COVERAGE present', (t) => { | ||
| 109 | + if (!process.features.inspector) { | ||
| 110 | + return; | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 114 | + const args = ['--experimental-test-coverage', '--test-reporter', 'tap', fixture]; | ||
| 115 | + const result = spawnSync(process.execPath, args); | ||
| 116 | + const report = getTapCoverageFixtureReport(); | ||
| 90 | 117 | ||
| 91 | - const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 92 | - const args = ['--experimental-test-coverage', fixture]; | ||
| 93 | - const result = spawnSync(process.execPath, args); | ||
| 94 | - const report = getCoverageFixtureReport(); | ||
| 118 | + assert(result.stdout.toString().includes(report)); | ||
| 119 | + assert.strictEqual(result.stderr.toString(), ''); | ||
| 120 | + assert.strictEqual(result.status, 0); | ||
| 121 | + assert(!findCoverageFileForPid(result.pid)); | ||
| 122 | + }); | ||
| 123 | + }); | ||
| 95 | 124 | ||
| 96 | - assert(result.stdout.toString().includes(report)); | ||
| 97 | - assert.strictEqual(result.stderr.toString(), ''); | ||
| 98 | - assert.strictEqual(result.status, 0); | ||
| 99 | - assert(!findCoverageFileForPid(result.pid)); | ||
| 125 | + test('test spec coverage reporter', async (t) => { | ||
| 126 | + await t.test('coverage is reported and dumped to NODE_V8_COVERAGE if present', (t) => { | ||
| 127 | + if (!process.features.inspector) { | ||
| 128 | + return; | ||
| 129 | + } | ||
| 130 | + const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 131 | + const args = ['--experimental-test-coverage', '--test-reporter', 'spec', fixture]; | ||
| 132 | + const options = { env: { ...process.env, NODE_V8_COVERAGE: tmpdir.path } }; | ||
| 133 | + const result = spawnSync(process.execPath, args, options); | ||
| 134 | + const report = getSpecCoverageFixtureReport(); | ||
| 135 | + | ||
| 136 | + assert(result.stdout.toString().includes(report)); | ||
| 137 | + assert.strictEqual(result.stderr.toString(), ''); | ||
| 138 | + assert.strictEqual(result.status, 0); | ||
| 139 | + assert(findCoverageFileForPid(result.pid)); | ||
| 140 | + }); | ||
| 141 | + | ||
| 142 | + await t.test('coverage is reported without NODE_V8_COVERAGE present', (t) => { | ||
| 143 | + if (!process.features.inspector) { | ||
| 144 | + return; | ||
| 145 | + } | ||
| 146 | + const fixture = fixtures.path('test-runner', 'coverage.js'); | ||
| 147 | + const args = ['--experimental-test-coverage', '--test-reporter', 'spec', fixture]; | ||
| 148 | + const result = spawnSync(process.execPath, args); | ||
| 149 | + const report = getSpecCoverageFixtureReport(); | ||
| 150 | + | ||
| 151 | + assert(result.stdout.toString().includes(report)); | ||
| 152 | + assert.strictEqual(result.stderr.toString(), ''); | ||
| 153 | + assert.strictEqual(result.status, 0); | ||
| 154 | + assert(!findCoverageFileForPid(result.pid)); | ||
| 155 | + }); | ||
| 100 | 156 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments