| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent bf1525c commit f302286
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -123,7 +123,7 @@ class SpecReporter extends Transform { | |||
| 123 | 123 | case 'test:diagnostic': | |
| 124 | 124 | return `${colors[type]}${this.#indent(data.nesting)}${symbols[type]}${data.message}${white}\n`; | |
| 125 | 125 | case 'test:coverage': | |
| 126 | - return getCoverageReport(this.#indent(data.nesting), data.summary, symbols['test:coverage'], blue); | ||
| 126 | + return getCoverageReport(this.#indent(data.nesting), data.summary, symbols['test:coverage'], blue, true); | ||
| 127 | 127 | } | |
| 128 | 128 | } | |
| 129 | 129 | _transform({ type, data }, encoding, callback) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,21 +3,29 @@ const { | |||
| 3 | 3 | ArrayPrototypeJoin, | |
| 4 | 4 | ArrayPrototypeMap, | |
| 5 | 5 | ArrayPrototypePush, | |
| 6 | + ArrayPrototypeReduce, | ||
| 6 | 7 | ObjectCreate, | |
| 7 | 8 | ObjectGetOwnPropertyDescriptor, | |
| 9 | + MathFloor, | ||
| 10 | + MathMax, | ||
| 11 | + MathMin, | ||
| 8 | 12 | NumberPrototypeToFixed, | |
| 9 | 13 | SafePromiseAllReturnArrayLike, | |
| 10 | 14 | RegExp, | |
| 11 | 15 | RegExpPrototypeExec, | |
| 12 | 16 | SafeMap, | |
| 17 | + StringPrototypePadStart, | ||
| 18 | + StringPrototypePadEnd, | ||
| 19 | + StringPrototypeRepeat, | ||
| 20 | + StringPrototypeSlice, | ||
| 13 | 21 | } = primordials; | |
| 14 | 22 | ||
| 15 | 23 | const { basename, relative } = require('path'); | |
| 16 | 24 | const { createWriteStream } = require('fs'); | |
| 17 | 25 | const { pathToFileURL } = require('internal/url'); | |
| 18 | 26 | const { createDeferredPromise } = require('internal/util'); | |
| 19 | 27 | const { getOptionValue } = require('internal/options'); | |
| 20 | - const { green, red, white, shouldColorize } = require('internal/util/colors'); | ||
| 28 | + const { green, yellow, red, white, shouldColorize } = require('internal/util/colors'); | ||
| 21 | 29 | ||
| 22 | 30 | const { | |
| 23 | 31 | codes: { | |
@@ -28,6 +36,13 @@ const { | |||
| 28 | 36 | } = require('internal/errors'); | |
| 29 | 37 | const { compose } = require('stream'); | |
| 30 | 38 | ||
| 39 | + const coverageColors = { | ||
| 40 | + __proto__: null, | ||
| 41 | + high: green, | ||
| 42 | + medium: yellow, | ||
| 43 | + low: red, | ||
| 44 | + }; | ||
| 45 | + | ||
| 31 | 46 | const kMultipleCallbackInvocations = 'multipleCallbackInvocations'; | |
| 32 | 47 | const kRegExpPattern = /^\/(.*)\/([a-z]*)$/; | |
| 33 | 48 | const kSupportedFileExtensions = /\.[cm]?js$/; | |
@@ -257,45 +272,139 @@ function countCompletedTest(test, harness = test.root.harness) { | |||
| 257 | 272 | } | |
| 258 | 273 | ||
| 259 | 274 | ||
| 260 | - function coverageThreshold(coverage, color) { | ||
| 261 | - coverage = NumberPrototypeToFixed(coverage, 2); | ||
| 262 | - if (color) { | ||
| 263 | - if (coverage > 90) return `${green}${coverage}${color}`; | ||
| 264 | - if (coverage < 50) return `${red}${coverage}${color}`; | ||
| 275 | + const memo = new SafeMap(); | ||
| 276 | + function addTableLine(prefix, width) { | ||
| 277 | + const key = `${prefix}-${width}`; | ||
| 278 | + let value = memo.get(key); | ||
| 279 | + if (value === undefined) { | ||
| 280 | + value = `${prefix}${StringPrototypeRepeat('-', width)}\n`; | ||
| 281 | + memo.set(key, value); | ||
| 265 | 282 | } | |
| 266 | - return coverage; | ||
| 283 | + | ||
| 284 | + return value; | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + const kHorizontalEllipsis = '\u2026'; | ||
| 288 | + function truncateStart(string, width) { | ||
| 289 | + return string.length > width ? `${kHorizontalEllipsis}${StringPrototypeSlice(string, string.length - width + 1)}` : string; | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + function truncateEnd(string, width) { | ||
| 293 | + return string.length > width ? `${StringPrototypeSlice(string, 0, width - 1)}${kHorizontalEllipsis}` : string; | ||
| 294 | + } | ||
| 295 | + | ||
| 296 | + function formatLinesToRanges(values) { | ||
| 297 | + return ArrayPrototypeMap(ArrayPrototypeReduce(values, (prev, current, index, array) => { | ||
| 298 | + if ((index > 0) && ((current - array[index - 1]) === 1)) { | ||
| 299 | + prev[prev.length - 1][1] = current; | ||
| 300 | + } else { | ||
| 301 | + prev.push([current]); | ||
| 302 | + } | ||
| 303 | + return prev; | ||
| 304 | + }, []), (range) => ArrayPrototypeJoin(range, '-')); | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + function formatUncoveredLines(lines, table) { | ||
| 308 | + if (table) return ArrayPrototypeJoin(formatLinesToRanges(lines), ' '); | ||
| 309 | + return ArrayPrototypeJoin(lines, ', '); | ||
| 267 | 310 | } | |
| 268 | 311 | ||
| 269 | - function getCoverageReport(pad, summary, symbol, color) { | ||
| 270 | - let report = `${color}${pad}${symbol}start of coverage report\n`; | ||
| 312 | + const kColumns = ['line %', 'branch %', 'funcs %']; | ||
| 313 | + const kColumnsKeys = ['coveredLinePercent', 'coveredBranchPercent', 'coveredFunctionPercent']; | ||
| 314 | + const kSeparator = ' | '; | ||
| 315 | + | ||
| 316 | + function getCoverageReport(pad, summary, symbol, color, table) { | ||
| 317 | + const prefix = `${pad}${symbol}`; | ||
| 318 | + let report = `${color}${prefix}start of coverage report\n`; | ||
| 319 | + | ||
| 320 | + let filePadLength; | ||
| 321 | + let columnPadLengths = []; | ||
| 322 | + let uncoveredLinesPadLength; | ||
| 323 | + let tableWidth; | ||
| 324 | + | ||
| 325 | + if (table) { | ||
| 326 | + // Get expected column sizes | ||
| 327 | + filePadLength = table && ArrayPrototypeReduce(summary.files, (acc, file) => | ||
| 328 | + MathMax(acc, relative(summary.workingDirectory, file.path).length), 0); | ||
| 329 | + filePadLength = MathMax(filePadLength, 'file'.length); | ||
| 330 | + const fileWidth = filePadLength + 2; | ||
| 331 | + | ||
| 332 | + columnPadLengths = ArrayPrototypeMap(kColumns, (column) => (table ? MathMax(column.length, 6) : 0)); | ||
| 333 | + const columnsWidth = ArrayPrototypeReduce(columnPadLengths, (acc, columnPadLength) => acc + columnPadLength + 3, 0); | ||
| 334 | + | ||
| 335 | + uncoveredLinesPadLength = table && ArrayPrototypeReduce(summary.files, (acc, file) => | ||
| 336 | + MathMax(acc, formatUncoveredLines(file.uncoveredLineNumbers, table).length), 0); | ||
| 337 | + uncoveredLinesPadLength = MathMax(uncoveredLinesPadLength, 'uncovered lines'.length); | ||
| 338 | + const uncoveredLinesWidth = uncoveredLinesPadLength + 2; | ||
| 339 | + | ||
| 340 | + tableWidth = fileWidth + columnsWidth + uncoveredLinesWidth; | ||
| 341 | + | ||
| 342 | + // Fit with sensible defaults | ||
| 343 | + const availableWidth = (process.stdout.columns || Infinity) - prefix.length; | ||
| 344 | + const columnsExtras = tableWidth - availableWidth; | ||
| 345 | + if (table && columnsExtras > 0) { | ||
| 346 | + // Ensure file name is sufficiently visible | ||
| 347 | + const minFilePad = MathMin(8, filePadLength); | ||
| 348 | + filePadLength -= MathFloor(columnsExtras * 0.2); | ||
| 349 | + filePadLength = MathMax(filePadLength, minFilePad); | ||
| 350 | + | ||
| 351 | + // Get rest of available space, subtracting margins | ||
| 352 | + uncoveredLinesPadLength = MathMax(availableWidth - columnsWidth - (filePadLength + 2) - 2, 1); | ||
| 353 | + | ||
| 354 | + // Update table width | ||
| 355 | + tableWidth = availableWidth; | ||
| 356 | + } else { | ||
| 357 | + uncoveredLinesPadLength = Infinity; | ||
| 358 | + } | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + | ||
| 362 | + function getCell(string, width, pad, truncate, coverage) { | ||
| 363 | + if (!table) return string; | ||
| 364 | + | ||
| 365 | + let result = string; | ||
| 366 | + if (pad) result = pad(result, width); | ||
| 367 | + if (truncate) result = truncate(result, width); | ||
| 368 | + if (color && coverage !== undefined) { | ||
| 369 | + if (coverage > 90) return `${coverageColors.high}${result}${color}`; | ||
| 370 | + if (coverage > 50) return `${coverageColors.medium}${result}${color}`; | ||
| 371 | + return `${coverageColors.low}${result}${color}`; | ||
| 372 | + } | ||
| 373 | + return result; | ||
| 374 | + } | ||
| 271 | 375 | ||
| 272 | - report += `${pad}${symbol}file | line % | branch % | funcs % | uncovered lines\n`; | ||
| 376 | + // Head | ||
| 377 | + if (table) report += addTableLine(prefix, tableWidth); | ||
| 378 | + report += `${prefix}${getCell('file', filePadLength, StringPrototypePadEnd, truncateEnd)}${kSeparator}` + | ||
| 379 | + `${ArrayPrototypeJoin(ArrayPrototypeMap(kColumns, (column, i) => getCell(column, columnPadLengths[i], StringPrototypePadStart)), kSeparator)}${kSeparator}` + | ||
| 380 | + `${getCell('uncovered lines', uncoveredLinesPadLength, false, truncateEnd)}\n`; | ||
| 381 | + if (table) report += addTableLine(prefix, tableWidth); | ||
| 273 | 382 | ||
| 383 | + // Body | ||
| 274 | 384 | for (let i = 0; i < summary.files.length; ++i) { | |
| 275 | - const { | ||
| 276 | - path, | ||
| 277 | - coveredLinePercent, | ||
| 278 | - coveredBranchPercent, | ||
| 279 | - coveredFunctionPercent, | ||
| 280 | - uncoveredLineNumbers, | ||
| 281 | - } = summary.files[i]; | ||
| 282 | - const relativePath = relative(summary.workingDirectory, path); | ||
| 283 | - const lines = coverageThreshold(coveredLinePercent, color); | ||
| 284 | - const branches = coverageThreshold(coveredBranchPercent, color); | ||
| 285 | - const functions = coverageThreshold(coveredFunctionPercent, color); | ||
| 286 | - const uncovered = ArrayPrototypeJoin(uncoveredLineNumbers, ', '); | ||
| 287 | - | ||
| 288 | - report += `${pad}${symbol}${relativePath} | ${lines} | ${branches} | ` + | ||
| 289 | - `${functions} | ${uncovered}\n`; | ||
| 385 | + const file = summary.files[i]; | ||
| 386 | + const relativePath = relative(summary.workingDirectory, file.path); | ||
| 387 | + | ||
| 388 | + let fileCoverage = 0; | ||
| 389 | + const coverages = ArrayPrototypeMap(kColumnsKeys, (columnKey) => { | ||
| 390 | + const percent = file[columnKey]; | ||
| 391 | + fileCoverage += percent; | ||
| 392 | + return percent; | ||
| 393 | + }); | ||
| 394 | + fileCoverage /= kColumnsKeys.length; | ||
| 395 | + | ||
| 396 | + report += `${prefix}${getCell(relativePath, filePadLength, StringPrototypePadEnd, truncateStart, fileCoverage)}${kSeparator}` + | ||
| 397 | + `${ArrayPrototypeJoin(ArrayPrototypeMap(coverages, (coverage, j) => getCell(NumberPrototypeToFixed(coverage, 2), columnPadLengths[j], StringPrototypePadStart, false, coverage)), kSeparator)}${kSeparator}` + | ||
| 398 | + `${getCell(formatUncoveredLines(file.uncoveredLineNumbers, table), uncoveredLinesPadLength, false, truncateEnd)}\n`; | ||
| 290 | 399 | } | |
| 291 | 400 | ||
| 292 | - const { totals } = summary; | ||
| 293 | - report += `${pad}${symbol}all files | ` + | ||
| 294 | - `${coverageThreshold(totals.coveredLinePercent, color)} | ` + | ||
| 295 | - `${coverageThreshold(totals.coveredBranchPercent, color)} | ` + | ||
| 296 | - `${coverageThreshold(totals.coveredFunctionPercent, color)} |\n`; | ||
| 401 | + // Foot | ||
| 402 | + if (table) report += addTableLine(prefix, tableWidth); | ||
| 403 | + report += `${prefix}${getCell('all files', filePadLength, StringPrototypePadEnd, truncateEnd)}${kSeparator}` + | ||
| 404 | + `${ArrayPrototypeJoin(ArrayPrototypeMap(kColumnsKeys, (columnKey, j) => getCell(NumberPrototypeToFixed(summary.totals[columnKey], 2), columnPadLengths[j], StringPrototypePadStart, false, summary.totals[columnKey])), kSeparator)} |\n`; | ||
| 405 | + if (table) report += addTableLine(prefix, tableWidth); | ||
| 297 | 406 | ||
| 298 | - report += `${pad}${symbol}end of coverage report\n`; | ||
| 407 | + report += `${prefix}end of coverage report\n`; | ||
| 299 | 408 | if (color) { | |
| 300 | 409 | report += white; | |
| 301 | 410 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -28,6 +28,7 @@ module.exports = { | |||
| 28 | 28 | module.exports.blue = hasColors ? '\u001b[34m' : ''; | |
| 29 | 29 | module.exports.green = hasColors ? '\u001b[32m' : ''; | |
| 30 | 30 | module.exports.white = hasColors ? '\u001b[39m' : ''; | |
| 31 | + module.exports.yellow = hasColors ? '\u001b[33m' : ''; | ||
| 31 | 32 | module.exports.red = hasColors ? '\u001b[31m' : ''; | |
| 32 | 33 | module.exports.gray = hasColors ? '\u001b[90m' : ''; | |
| 33 | 34 | module.exports.clear = hasColors ? '\u001bc' : ''; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,16 +41,21 @@ function getTapCoverageFixtureReport() { | |||
| 41 | 41 | } | |
| 42 | 42 | ||
| 43 | 43 | function getSpecCoverageFixtureReport() { | |
| 44 | + /* eslint-disable max-len */ | ||
| 44 | 45 | const report = [ | |
| 45 | 46 | '\u2139 start of coverage report', | |
| 46 | - '\u2139 file | line % | branch % | funcs % | uncovered lines', | ||
| 47 | - '\u2139 test/fixtures/test-runner/coverage.js | 78.65 | 38.46 | 60.00 | 12, ' + | ||
| 48 | - '13, 16, 17, 18, 19, 20, 21, 22, 27, 39, 43, 44, 61, 62, 66, 67, 71, 72', | ||
| 49 | - '\u2139 test/fixtures/test-runner/invalid-tap.js | 100.00 | 100.00 | 100.00 | ', | ||
| 50 | - '\u2139 test/fixtures/v8-coverage/throw.js | 71.43 | 50.00 | 100.00 | 5, 6', | ||
| 51 | - '\u2139 all files | 78.35 | 43.75 | 60.00 |', | ||
| 47 | + '\u2139 -------------------------------------------------------------------------------------------------------------------', | ||
| 48 | + '\u2139 file | line % | branch % | funcs % | uncovered lines', | ||
| 49 | + '\u2139 -------------------------------------------------------------------------------------------------------------------', | ||
| 50 | + '\u2139 test/fixtures/test-runner/coverage.js | 78.65 | 38.46 | 60.00 | 12-13 16-22 27 39 43-44 61-62 66-67 71-72', | ||
| 51 | + '\u2139 test/fixtures/test-runner/invalid-tap.js | 100.00 | 100.00 | 100.00 | ', | ||
| 52 | + '\u2139 test/fixtures/v8-coverage/throw.js | 71.43 | 50.00 | 100.00 | 5-6', | ||
| 53 | + '\u2139 -------------------------------------------------------------------------------------------------------------------', | ||
| 54 | + '\u2139 all files | 78.35 | 43.75 | 60.00 |', | ||
| 55 | + '\u2139 -------------------------------------------------------------------------------------------------------------------', | ||
| 52 | 56 | '\u2139 end of coverage report', | |
| 53 | 57 | ].join('\n'); | |
| 58 | + /* eslint-enable max-len */ | ||
| 54 | 59 | ||
| 55 | 60 | if (common.isWindows) { | |
| 56 | 61 | return report.replaceAll('/', '\\'); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments