monocart-coverage-reports
Advanced tools
Comparing version 2.5.9 to 2.6.0
@@ -66,3 +66,3 @@ #!/usr/bin/env node | ||
if (!configOptions) { | ||
Util.logError(err.message); | ||
Util.logError(`ERROR: failed to import "${configPath}" ${err.message} `); | ||
return; | ||
@@ -69,0 +69,0 @@ } |
const Util = require('../utils/util.js'); | ||
const BranchTypes = { | ||
ConditionalExpression: 'ConditionalExpression', | ||
LogicalExpression: 'LogicalExpression', | ||
IfStatement: 'IfStatement', | ||
SwitchStatement: 'SwitchStatement', | ||
AssignmentPattern: 'AssignmentPattern' | ||
}; | ||
const getParentFunctionState = (reverseParents) => { | ||
@@ -115,3 +123,3 @@ const parentFunction = reverseParents.find((it) => it._state && it._state.isFunction); | ||
if (type === 'LogicalExpression') { | ||
if (type === BranchTypes.LogicalExpression) { | ||
// && or || | ||
@@ -278,4 +286,4 @@ group.operator = node.operator; | ||
ConditionalExpression, | ||
LogicalExpression, | ||
IfStatement, | ||
LogicalExpression, | ||
SwitchStatement, | ||
@@ -462,3 +470,3 @@ AssignmentPattern | ||
AssignmentPattern: (node, parents) => { | ||
const group = createBranchGroup('AssignmentPattern', node, parents, branchMap); | ||
const group = createBranchGroup(BranchTypes.AssignmentPattern, node, parents, branchMap); | ||
addBranch(group, node, locationMap); | ||
@@ -472,3 +480,3 @@ }, | ||
const { consequent, alternate } = node; | ||
const group = createBranchGroup('ConditionalExpression', node, parents, branchMap); | ||
const group = createBranchGroup(BranchTypes.ConditionalExpression, node, parents, branchMap); | ||
addBranch(group, consequent, locationMap); | ||
@@ -483,3 +491,3 @@ addBranch(group, alternate, locationMap); | ||
const { consequent, alternate } = node; | ||
const group = createBranchGroup('IfStatement', node, parents, branchMap); | ||
const group = createBranchGroup(BranchTypes.IfStatement, node, parents, branchMap); | ||
addBranch(group, consequent, locationMap); | ||
@@ -515,3 +523,3 @@ | ||
} else { | ||
group = createBranchGroup('LogicalExpression', node, parents, branchMap); | ||
group = createBranchGroup(BranchTypes.LogicalExpression, node, parents, branchMap); | ||
addBranch(group, left, locationMap); | ||
@@ -543,3 +551,3 @@ } | ||
SwitchStatement: (node, parents) => { | ||
const group = createBranchGroup('SwitchStatement', node, parents, branchMap); | ||
const group = createBranchGroup(BranchTypes.SwitchStatement, node, parents, branchMap); | ||
const cases = node.cases; | ||
@@ -672,3 +680,4 @@ cases.forEach((switchCase) => { | ||
module.exports = { | ||
BranchTypes, | ||
collectAstInfo | ||
}; |
@@ -0,0 +0,0 @@ const { |
@@ -0,0 +0,0 @@ const fs = require('fs'); |
@@ -397,2 +397,48 @@ /** | ||
const checkOriginalRangeCode = (range, state, startEndMap, type) => { | ||
// only for original | ||
if (!state.original) { | ||
return true; | ||
} | ||
const { start, end } = range; | ||
if (start >= end) { | ||
// console.log('invalid branch', state.sourcePath, range); | ||
return false; | ||
} | ||
// could be vue code | ||
const text = state.locator.getSlice(start, end).trim(); | ||
if (!text) { | ||
return false; | ||
} | ||
// invalid original code | ||
// Matches any character that is not a word character from the basic Latin alphabet. | ||
// Equivalent to [^A-Za-z0-9_] | ||
if (text.length === 1 && (/\W/).test(text)) { | ||
// ; } = | ||
// console.log(text, type, state.sourcePath, range); | ||
return false; | ||
} | ||
// type: bytes, functions, branches, statements | ||
// do not check repeated range for `bytes` | ||
// instead, use `dedupeCountRanges` to accumulate count | ||
if (type === 'bytes') { | ||
return true; | ||
} | ||
// check repeated range | ||
const key = `${start}_${end}`; | ||
if (startEndMap.has(key)) { | ||
return false; | ||
} | ||
startEndMap.set(key, range); | ||
return true; | ||
}; | ||
const handleFunctionsCoverage = (state) => { | ||
@@ -405,8 +451,7 @@ | ||
const startEndMap = new Map(); | ||
const { functions, astInfo } = state; | ||
astInfo.functions.forEach((it) => { | ||
const { start, end } = it; | ||
if (start >= end) { | ||
// console.log('invalid function', start, end); | ||
if (!checkOriginalRangeCode(it, state, startEndMap, 'functions')) { | ||
return; | ||
@@ -427,8 +472,7 @@ } | ||
const startEndMap = new Map(); | ||
const { branches, astInfo } = state; | ||
astInfo.branches.forEach((it) => { | ||
const { start, end } = it; | ||
if (start >= end) { | ||
// console.log('invalid branch', state.sourcePath, it); | ||
if (!checkOriginalRangeCode(it, state, startEndMap, 'branches')) { | ||
return; | ||
@@ -449,8 +493,7 @@ } | ||
const startEndMap = new Map(); | ||
const { statements, astInfo } = state; | ||
astInfo.statements.forEach((it) => { | ||
const { start, end } = it; | ||
if (start >= end) { | ||
// console.log('invalid statement', start, end); | ||
if (!checkOriginalRangeCode(it, state, startEndMap, 'statements')) { | ||
return; | ||
@@ -464,4 +507,11 @@ } | ||
const handleBytesCoverage = (state) => { | ||
const handleOriginalBytesCoverage = (state) => { | ||
const startEndMap = new Map(); | ||
state.bytes = state.bytes.filter((it) => { | ||
return checkOriginalRangeCode(it, state, startEndMap, 'bytes'); | ||
}); | ||
}; | ||
const handleGeneratedBytesCoverage = (state) => { | ||
const { js, coverageList } = state; | ||
@@ -660,48 +710,5 @@ | ||
const handleOriginalBytesCoverage = (state, originalStateMap) => { | ||
// ======================================================================================================== | ||
const { js, coverageList } = state; | ||
// const time_start_mapping = Date.now(); | ||
if (js) { | ||
// v8 coverage | ||
coverageList.forEach((block) => { | ||
block.ranges.forEach((range, index) => { | ||
// remove wrap functions for original files | ||
if (range.wrap) { | ||
// console.log(range); | ||
return; | ||
} | ||
const { | ||
startOffset, endOffset, count | ||
} = range; | ||
const result = findOriginalRange(startOffset, endOffset, state, originalStateMap); | ||
if (result.error) { | ||
logMappingErrors('byte', result); | ||
return; | ||
} | ||
addJsBytesCoverage(result.originalState, { | ||
generatedStart: startOffset, | ||
generatedEnd: endOffset, | ||
startOffset: result.start, | ||
endOffset: result.end, | ||
count | ||
}); | ||
}); | ||
}); | ||
} else { | ||
// support css later | ||
// current css no sourceMap, so never come in | ||
// coverageList.forEach((range) => { | ||
// }); | ||
} | ||
const handleOriginalEmptyBytesCoverage = (state, originalStateMap) => { | ||
const checkList = []; | ||
@@ -730,5 +737,9 @@ | ||
const line = state.locator.getLine(endMapping.generatedLine + 1); | ||
// last column | ||
const toEndLength = line.length - endMapping.generatedColumn; | ||
endMapping.generatedEndOffset = endMapping.generatedOffset + toEndLength; | ||
// could be no line found | ||
if (line) { | ||
// last column | ||
endMapping.generatedEndOffset = line.end; | ||
} else { | ||
endMapping.generatedEndOffset = endMapping.generatedOffset; | ||
} | ||
} | ||
@@ -748,2 +759,3 @@ const endOffset = endMapping.generatedEndOffset; | ||
// no file to handle | ||
if (!checkList.length) { | ||
@@ -755,3 +767,3 @@ return; | ||
// should using coverageList to generate bytes first | ||
handleBytesCoverage(state); | ||
handleGeneratedBytesCoverage(state); | ||
@@ -785,4 +797,46 @@ checkList.forEach((it) => { | ||
}); | ||
}; | ||
const handleAllOriginalBytesCoverage = (state, originalStateMap) => { | ||
const { js, coverageList } = state; | ||
// only for js, no sourcemap for css for now | ||
if (!js) { | ||
return; | ||
} | ||
// v8 coverage | ||
coverageList.forEach((block) => { | ||
block.ranges.forEach((range, index) => { | ||
// remove wrap functions for original files | ||
if (range.wrap) { | ||
// console.log(range); | ||
return; | ||
} | ||
const { | ||
startOffset, endOffset, count | ||
} = range; | ||
const result = findOriginalRange(startOffset, endOffset, state, originalStateMap); | ||
if (result.error) { | ||
logMappingErrors('byte', result); | ||
return; | ||
} | ||
addJsBytesCoverage(result.originalState, { | ||
generatedStart: startOffset, | ||
generatedEnd: endOffset, | ||
startOffset: result.start, | ||
endOffset: result.end, | ||
count | ||
}); | ||
}); | ||
}); | ||
handleOriginalEmptyBytesCoverage(state, originalStateMap); | ||
}; | ||
@@ -1019,3 +1073,3 @@ | ||
handleStatementsCoverage(state); | ||
handleBytesCoverage(state); | ||
handleGeneratedBytesCoverage(state); | ||
@@ -1067,4 +1121,8 @@ }; | ||
// handle bytes ranges | ||
handleOriginalBytesCoverage(state, originalStateMap); | ||
handleAllOriginalBytesCoverage(state, originalStateMap); | ||
originalStateMap.forEach((originalState) => { | ||
handleOriginalBytesCoverage(originalState); | ||
}); | ||
// collect coverage for original list | ||
@@ -1071,0 +1129,0 @@ collectOriginalList(state, originalStateMap); |
@@ -0,0 +0,0 @@ const Util = require('../utils/util.js'); |
@@ -0,0 +0,0 @@ const Util = require('../utils/util.js'); |
@@ -0,0 +0,0 @@ const Util = require('../utils/util.js'); |
@@ -0,0 +0,0 @@ const Util = require('../utils/util.js'); |
@@ -0,0 +0,0 @@ const Util = require('../utils/util.js'); |
@@ -0,0 +0,0 @@ module.exports = { |
@@ -5,2 +5,4 @@ const fs = require('fs'); | ||
const EC = require('eight-colors'); | ||
const { pathToFileURL } = require('url'); | ||
const Util = require('./utils/util.js'); | ||
@@ -16,3 +18,3 @@ const { | ||
const { minimatch } = require('./packages/monocart-coverage-vendor.js'); | ||
const { pathToFileURL } = require('url'); | ||
const { getGroupedRows } = require('./utils/snapshot.js'); | ||
@@ -79,4 +81,2 @@ // ======================================================================================================== | ||
'v8-json': 'v8', | ||
'codecov': 'v8', | ||
'console-details': 'v8', | ||
@@ -99,2 +99,4 @@ // istanbul | ||
// both | ||
'codecov': 'both', | ||
'console-details': 'both', | ||
'console-summary': 'both', | ||
@@ -151,2 +153,186 @@ 'raw': 'both' | ||
const getRowData = (rowName, summary, metrics) => { | ||
const summaryRow = {}; | ||
let lowest = { | ||
pct: 100, | ||
status: 'high' | ||
}; | ||
metrics.map((k) => { | ||
const s = summary[k]; | ||
if (!s) { | ||
return; | ||
} | ||
const percent = s.pct; | ||
if (typeof percent !== 'number') { | ||
return; | ||
} | ||
summaryRow[k] = Util.getColorStrByStatus(Util.PSF(percent, 100, 2), s.status); | ||
if (percent < lowest.pct) { | ||
lowest = s; | ||
} | ||
}); | ||
summaryRow.nameStatus = lowest.status; | ||
summaryRow.name = rowName; | ||
return summaryRow; | ||
}; | ||
const getUncoveredLines = (file) => { | ||
const lines = []; | ||
const dataLines = file.data.lines; | ||
let startLine; | ||
let endLine; | ||
const addLines = () => { | ||
if (!startLine) { | ||
return; | ||
} | ||
if (endLine) { | ||
// range | ||
const link = startLine.color === 'yellow' && endLine.color === 'yellow' ? EC.yellow('-') : EC.red('-'); | ||
lines.push(EC[startLine.color](startLine.line) + link + EC[endLine.color](endLine.line)); | ||
startLine = null; | ||
endLine = null; | ||
} else { | ||
// only start | ||
lines.push(EC[startLine.color](startLine.line)); | ||
startLine = null; | ||
} | ||
}; | ||
const setLines = (line, color) => { | ||
if (startLine) { | ||
endLine = { | ||
line, | ||
color | ||
}; | ||
return; | ||
} | ||
startLine = { | ||
line, | ||
color | ||
}; | ||
}; | ||
Object.keys(dataLines).forEach((line) => { | ||
const count = dataLines[line]; | ||
if (count === 0) { | ||
setLines(line, 'red'); | ||
return; | ||
} | ||
// 0 < count < 1 | ||
if (typeof count === 'string') { | ||
setLines(line, 'yellow'); | ||
return; | ||
} | ||
// count >= 1 | ||
addLines(); | ||
}); | ||
addLines(); | ||
return lines.join(','); | ||
}; | ||
const getDetailsRows = (files, metrics, cdOptions) => { | ||
const skipPercent = cdOptions.skipPercent; | ||
if (typeof skipPercent === 'number' && skipPercent > 0) { | ||
files = files.filter((file) => { | ||
const { summary } = file; | ||
for (const k of metrics) { | ||
const percent = summary[k].pct; | ||
if (typeof percent === 'number' && percent < skipPercent) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}); | ||
} | ||
const flatRows = []; | ||
files.forEach((file) => { | ||
const { sourcePath, summary } = file; | ||
const fileRow = getRowData(sourcePath, summary, metrics); | ||
fileRow.uncoveredLines = getUncoveredLines(file); | ||
flatRows.push(fileRow); | ||
}); | ||
return getGroupedRows(flatRows); | ||
}; | ||
const handleCodecovReport = async (reportData, reportOptions, options) => { | ||
const codecovOptions = { | ||
outputFile: 'codecov.json', | ||
... reportOptions | ||
}; | ||
const jsonPath = path.resolve(options.outputDir, codecovOptions.outputFile); | ||
// https://docs.codecov.com/docs/codecov-custom-coverage-format | ||
const coverage = {}; | ||
reportData.files.forEach((item) => { | ||
const { sourcePath, data } = item; | ||
coverage[sourcePath] = data.lines; | ||
}); | ||
const codecovData = { | ||
coverage | ||
}; | ||
await Util.writeFile(jsonPath, JSON.stringify(codecovData)); | ||
return Util.relativePath(jsonPath); | ||
}; | ||
const handleConsoleDetailsReport = (reportData, reportOptions, options) => { | ||
const cdOptions = { | ||
maxCols: 50, | ||
skipPercent: 0, | ||
metrics: [], | ||
... reportOptions | ||
}; | ||
const { | ||
type, name, summary, files | ||
} = reportData; | ||
if (name) { | ||
EC.logCyan(name); | ||
} | ||
const metrics = Util.getMetrics(cdOptions.metrics, type); | ||
const rows = getDetailsRows(files, metrics, cdOptions); | ||
const summaryRow = getRowData('Summary', summary, metrics); | ||
// no rows if skipped all by skipPercent | ||
if (rows.length) { | ||
rows.push({ | ||
innerBorder: true | ||
}); | ||
} | ||
rows.push(summaryRow); | ||
const columns = [{ | ||
id: 'name', | ||
name: 'Name' | ||
}, ... metrics.map((m) => { | ||
return { | ||
id: m, | ||
name: Util.capitalizeFirstLetter(m), | ||
align: 'right' | ||
}; | ||
}), { | ||
id: 'uncoveredLines', | ||
name: 'Uncovered Lines' | ||
}]; | ||
return CG({ | ||
options: { | ||
silent: cdOptions.silent, | ||
nullPlaceholder: '', | ||
defaultMaxWidth: cdOptions.maxCols | ||
}, | ||
columns, | ||
rows | ||
}); | ||
}; | ||
const handleConsoleSummaryReport = (reportData, reportOptions, options) => { | ||
@@ -167,3 +353,3 @@ | ||
const metrics = Util.getMetrics(type, csOptions.metrics); | ||
const metrics = Util.getMetrics(csOptions.metrics, type); | ||
@@ -277,2 +463,4 @@ const rows = metrics.map((k) => { | ||
const buildInBothReports = { | ||
'codecov': handleCodecovReport, | ||
'console-details': handleConsoleDetailsReport, | ||
'console-summary': handleConsoleSummaryReport, | ||
@@ -279,0 +467,0 @@ 'raw': handleRawReport |
@@ -20,3 +20,3 @@ declare namespace MCR { | ||
bytes?: [number, number]; | ||
statements?: [number, number]; | ||
statements: [number, number]; | ||
branches?: [number, number]; | ||
@@ -37,10 +37,2 @@ functions?: [number, number]; | ||
}] | | ||
['codecov'] | ["codecov", { | ||
outputFile?: string; | ||
}] | | ||
['console-details'] | ['console-details', { | ||
maxCols?: number; | ||
skipPercent?: number; | ||
metrics?: Array<"bytes" | "statements" | "branches" | "functions" | "lines">; | ||
}] | | ||
['clover'] | ['clover', { | ||
@@ -98,5 +90,13 @@ file?: string; | ||
}] | | ||
['codecov'] | ["codecov", { | ||
outputFile?: string; | ||
}] | | ||
['console-summary'] | ['console-summary', { | ||
metrics?: Array<"bytes" | "statements" | "branches" | "functions" | "lines">; | ||
}] | | ||
['console-details'] | ['console-details', { | ||
maxCols?: number; | ||
skipPercent?: number; | ||
metrics?: Array<"bytes" | "statements" | "branches" | "functions" | "lines">; | ||
}] | | ||
['raw'] | ['raw', { | ||
@@ -133,3 +133,3 @@ outputDir?: string; | ||
bytes?: MetricsSummary; | ||
statements?: MetricsSummary; | ||
statements: MetricsSummary; | ||
branches: MetricsSummary; | ||
@@ -298,5 +298,10 @@ functions: MetricsSummary; | ||
/** {function} onEnd hook */ | ||
onEnd?: (reportData: CoverageResults) => Promise<void>; | ||
onEnd?: (coverageResults: CoverageResults) => Promise<void>; | ||
} | ||
export interface McrCliOptions extends CoverageReportOptions { | ||
/** {function} onStart hook */ | ||
onStart?: (coverageReport: CoverageReport) => Promise<void>; | ||
} | ||
export class CoverageReport { | ||
@@ -319,7 +324,38 @@ | ||
export interface McrCliOptions extends CoverageReportOptions { | ||
/** {function} onStart hook */ | ||
onStart?: (coverageReport: CoverageReport) => Promise<void>; | ||
export interface CoverageSnapshot { | ||
type: "v8" | "istanbul"; | ||
summary: { | ||
bytes?: string; | ||
statements: string; | ||
branches: string; | ||
functions: string; | ||
lines: string; | ||
}, | ||
files: { | ||
[sourcePath: string]: { | ||
bytes?: string; | ||
statements: string; | ||
branches: string; | ||
functions: string; | ||
lines: string; | ||
uncoveredLines: string; | ||
} | ||
} | ||
} | ||
/** get snapshot from coverage report data */ | ||
export function getSnapshot(coverageResults: CoverageResults): CoverageSnapshot; | ||
/** diff two snapshots */ | ||
export function diffSnapshot(oldData: CoverageSnapshot, newData: CoverageSnapshot, diffOptions: { | ||
skipEqual?: boolean; | ||
showSummary?: boolean; | ||
maxCols?: number; | ||
metrics?: Array<"bytes" | "statements" | "branches" | "functions" | "lines">; | ||
}): { | ||
change: boolean; | ||
results: any[]; | ||
message: string; | ||
}; | ||
} | ||
@@ -326,0 +362,0 @@ |
@@ -8,2 +8,3 @@ const fs = require('fs'); | ||
const { getInputData, generateCoverageReports } = require('./generate.js'); | ||
const { getSnapshot, diffSnapshot } = require('./utils/snapshot.js'); | ||
@@ -151,3 +152,3 @@ class CoverageReport { | ||
if (typeof onEnd === 'function') { | ||
await onEnd(coverageResults); | ||
await onEnd.call(this, coverageResults); | ||
} | ||
@@ -181,3 +182,5 @@ | ||
MCR.CoverageReport = CoverageReport; | ||
MCR.getSnapshot = getSnapshot; | ||
MCR.diffSnapshot = diffSnapshot; | ||
module.exports = MCR; |
@@ -32,5 +32,12 @@ const istanbulLibReport = require('istanbul-lib-report'); | ||
const sourcePath = node.getQualifiedName(); | ||
const fileCoverage = node.getFileCoverage(); | ||
const lines = fileCoverage.getLineCoverage(); | ||
this.files.push({ | ||
sourcePath, | ||
summary: fileSummary | ||
summary: fileSummary, | ||
data: { | ||
lines | ||
} | ||
}); | ||
@@ -37,0 +44,0 @@ } |
@@ -0,0 +0,0 @@ const fs = require('fs'); |
@@ -0,0 +0,0 @@ class Concurrency { |
@@ -134,6 +134,6 @@ const Util = { | ||
getMetrics: (type, metrics) => { | ||
getMetrics: (metrics, type) => { | ||
const istanbulMetrics = ['statements', 'branches', 'functions', 'lines']; | ||
const v8Metrics = ['bytes'].concat(istanbulMetrics); | ||
const allMetrics = type === 'v8' ? v8Metrics : istanbulMetrics; | ||
const allMetrics = type === 'istanbul' ? istanbulMetrics : v8Metrics; | ||
let list = allMetrics; | ||
@@ -140,0 +140,0 @@ if (Util.isList(metrics)) { |
@@ -0,0 +0,0 @@ |
@@ -0,0 +0,0 @@ const http = require('http'); |
@@ -0,0 +0,0 @@ const path = require('path'); |
@@ -0,0 +0,0 @@ const fs = require('fs'); |
@@ -0,0 +0,0 @@ const Util = require('../utils/util.js'); |
233
lib/v8/v8.js
const path = require('path'); | ||
const CG = require('console-grid'); | ||
const EC = require('eight-colors'); | ||
const Util = require('../utils/util.js'); | ||
@@ -278,229 +276,2 @@ const { getV8Summary } = require('./v8-summary.js'); | ||
const mergeSingleSubGroups = (item) => { | ||
if (!item.subs) { | ||
return; | ||
} | ||
if (item.subs.length === 1) { | ||
const sub = item.subs[0]; | ||
if (!sub.subs) { | ||
return; | ||
} | ||
item.name = [item.name, sub.name].filter((it) => it).join('/'); | ||
item.subs = sub.subs; | ||
mergeSingleSubGroups(item); | ||
return; | ||
} | ||
item.subs.forEach((sub) => { | ||
mergeSingleSubGroups(sub); | ||
}); | ||
}; | ||
const getRowData = (rowName, summary, metrics) => { | ||
const summaryRow = {}; | ||
let lowest = { | ||
pct: 100, | ||
status: 'high' | ||
}; | ||
metrics.map((k) => { | ||
const s = summary[k]; | ||
const percent = s.pct; | ||
if (typeof percent !== 'number') { | ||
return; | ||
} | ||
summaryRow[k] = Util.getColorStrByStatus(Util.PSF(percent, 100, 2), s.status); | ||
if (percent < lowest.pct) { | ||
lowest = s; | ||
} | ||
}); | ||
summaryRow.name = Util.getColorStrByStatus(rowName, lowest.status); | ||
return summaryRow; | ||
}; | ||
const getUncoveredLines = (file) => { | ||
const lines = []; | ||
const dataLines = file.data.lines; | ||
let startLine; | ||
let endLine; | ||
const addLines = () => { | ||
if (!startLine) { | ||
return; | ||
} | ||
if (endLine) { | ||
// range | ||
const link = startLine.color === 'yellow' && endLine.color === 'yellow' ? EC.yellow('-') : EC.red('-'); | ||
lines.push(EC[startLine.color](startLine.line) + link + EC[endLine.color](endLine.line)); | ||
startLine = null; | ||
endLine = null; | ||
} else { | ||
// only start | ||
lines.push(EC[startLine.color](startLine.line)); | ||
startLine = null; | ||
} | ||
}; | ||
const setLines = (line, color) => { | ||
if (startLine) { | ||
endLine = { | ||
line, | ||
color | ||
}; | ||
return; | ||
} | ||
startLine = { | ||
line, | ||
color | ||
}; | ||
}; | ||
Object.keys(dataLines).forEach((line) => { | ||
const count = dataLines[line]; | ||
if (count === 0) { | ||
setLines(line, 'red'); | ||
return; | ||
} | ||
// 0 < count < 1 | ||
if (typeof count === 'string') { | ||
setLines(line, 'yellow'); | ||
return; | ||
} | ||
// count >= 1 | ||
addLines(); | ||
}); | ||
addLines(); | ||
return lines.join(','); | ||
}; | ||
const getGroupedRows = (files, metrics, cdOptions) => { | ||
const skipPercent = cdOptions.skipPercent; | ||
if (typeof skipPercent === 'number' && skipPercent > 0) { | ||
files = files.filter((file) => { | ||
const { summary } = file; | ||
for (const k of metrics) { | ||
const percent = summary[k].pct; | ||
if (typeof percent === 'number' && percent < skipPercent) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}); | ||
} | ||
let groups = []; | ||
files.forEach((file) => { | ||
const { sourcePath, summary } = file; | ||
const pathList = sourcePath.split('/'); | ||
const lastName = pathList.pop(); | ||
let subs = groups; | ||
pathList.forEach((key) => { | ||
const item = subs.find((it) => it.name === key && it.subs); | ||
if (item) { | ||
subs = item.subs; | ||
return; | ||
} | ||
const sub = { | ||
name: key, | ||
subs: [] | ||
}; | ||
subs.push(sub); | ||
subs = sub.subs; | ||
}); | ||
const fileRow = getRowData(lastName, summary, metrics); | ||
fileRow.uncoveredLines = getUncoveredLines(file); | ||
subs.push(fileRow); | ||
}); | ||
const group = { | ||
subs: groups | ||
}; | ||
mergeSingleSubGroups(group); | ||
if (group.name) { | ||
groups = [group]; | ||
} | ||
return groups; | ||
}; | ||
const handleConsoleDetailsReport = (reportData, reportOptions, options) => { | ||
const cdOptions = { | ||
maxCols: 50, | ||
skipPercent: 0, | ||
metrics: [], | ||
... reportOptions | ||
}; | ||
const { | ||
name, summary, files | ||
} = reportData; | ||
if (name) { | ||
EC.logCyan(name); | ||
} | ||
const metrics = Util.getMetrics('v8', cdOptions.metrics); | ||
const rows = getGroupedRows(files, metrics, cdOptions); | ||
const summaryRow = getRowData('Summary', summary, metrics); | ||
// no rows if skipped all by skipPercent | ||
if (rows.length) { | ||
rows.push({ | ||
innerBorder: true | ||
}); | ||
} | ||
rows.push(summaryRow); | ||
const columns = [{ | ||
id: 'name', | ||
name: 'Name' | ||
}, ... metrics.map((m) => { | ||
return { | ||
id: m, | ||
name: Util.capitalizeFirstLetter(m), | ||
align: 'right' | ||
}; | ||
}), { | ||
id: 'uncoveredLines', | ||
name: 'Uncovered Lines' | ||
}]; | ||
CG({ | ||
options: { | ||
nullPlaceholder: '', | ||
defaultMaxWidth: cdOptions.maxCols | ||
}, | ||
columns, | ||
rows | ||
}); | ||
}; | ||
const handleCodecovReport = async (reportData, reportOptions, options) => { | ||
const codecovOptions = { | ||
outputFile: 'codecov.json', | ||
... reportOptions | ||
}; | ||
const jsonPath = path.resolve(options.outputDir, codecovOptions.outputFile); | ||
// https://docs.codecov.com/docs/codecov-custom-coverage-format | ||
const coverage = {}; | ||
reportData.files.forEach((item) => { | ||
const { sourcePath, data } = item; | ||
coverage[sourcePath] = data.lines; | ||
}); | ||
const codecovData = { | ||
coverage | ||
}; | ||
await Util.writeFile(jsonPath, JSON.stringify(codecovData)); | ||
return Util.relativePath(jsonPath); | ||
}; | ||
const handleV8JsonReport = async (reportData, reportOptions, options) => { | ||
@@ -592,5 +363,3 @@ const v8JsonOptions = { | ||
'v8': handleV8HtmlReport, | ||
'v8-json': handleV8JsonReport, | ||
'codecov': handleCodecovReport, | ||
'console-details': handleConsoleDetailsReport | ||
'v8-json': handleV8JsonReport | ||
}; | ||
@@ -597,0 +366,0 @@ |
{ | ||
"name": "monocart-coverage-reports", | ||
"version": "2.5.9", | ||
"version": "2.6.0", | ||
"description": "Monocart coverage reports", | ||
@@ -42,2 +42,3 @@ "main": "./lib/index.js", | ||
"test": "npx mcr \"npm run test-all\" -c test/mcr-options.js", | ||
"test:snap": "cross-env TEST_SNAPSHOT=true npm run test", | ||
"dev": "sf d v8", | ||
@@ -60,3 +61,3 @@ "open": "node ./scripts/open.js", | ||
"dependencies": { | ||
"console-grid": "~2.2.1", | ||
"console-grid": "~2.2.2", | ||
"eight-colors": "~1.3.0", | ||
@@ -63,0 +64,0 @@ "istanbul-lib-coverage": "~3.2.2", |
@@ -75,3 +75,5 @@ # Monocart Coverage Reports | ||
## Available Reports | ||
> V8 build-in reports (V8 data only): | ||
- `v8` | ||
@@ -85,9 +87,5 @@ - Browser: Build with webpack [V8](https://cenfun.github.io/monocart-coverage-reports/v8) and [Minify](https://cenfun.github.io/monocart-coverage-reports/minify); Build with [Rollup](https://cenfun.github.io/monocart-coverage-reports/rollup) and [Esbuild](https://cenfun.github.io/monocart-coverage-reports/esbuild); Collect with [puppeteer](https://cenfun.github.io/monocart-coverage-reports/puppeteer/); [anonymous](https://cenfun.github.io/monocart-coverage-reports/anonymous/) and [css](https://cenfun.github.io/monocart-coverage-reports/css/) | ||
- [V8 coverage-report.json](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json) | ||
- `codecov` | ||
- coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports) | ||
- `console-details` Show file coverage and uncovered lines in the console. Like `text`, but for V8. For Github actions, we can enforce color with env: `FORCE_COLOR: true`. | ||
 | ||
> Istanbul build-in reports (both V8 and istanbul data): | ||
> Istanbul build-in reports (both V8 and istanbul data): | ||
- `clover` | ||
@@ -112,3 +110,7 @@ - `cobertura` | ||
> Other reports: | ||
> Other build-in reports (both V8 and istanbul data): | ||
- `codecov` | ||
- coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports) | ||
- `console-summary` shows coverage summary in the console | ||
@@ -118,2 +120,6 @@ | ||
- `console-details` Show file coverage and uncovered lines in the console. Like `text`, but for V8. For Github actions, we can enforce color with env: `FORCE_COLOR: true`. | ||
 | ||
- `raw` only keep all original data, which can be used for other reports input with `inputDir` | ||
@@ -782,3 +788,6 @@ - see [Merge Coverage Reports](#merge-coverage-reports) | ||
### VSCode Extension | ||
- [Coverage Gutters](https://github.com/ryanluker/vscode-coverage-gutters) - Display test coverage generated by lcov or xml in VSCode editor. | ||
## Thanks | ||
- Special thanks to [@edumserrano](https://github.com/edumserrano) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
755090
32
7738
788
Updatedconsole-grid@~2.2.2