monocart-coverage-reports
Advanced tools
Comparing version 2.0.7 to 2.0.8
@@ -6,5 +6,4 @@ const { | ||
const { | ||
createBranches, collectBranches, findInRanges | ||
} = require('./branches.js'); | ||
const { createBranches, collectBranches } = require('./branches.js'); | ||
const findInRanges = require('./find-in-ranges.js'); | ||
@@ -11,0 +10,0 @@ const getFunctionRange = (start, end, state) => { |
const Util = require('../utils/util.js'); | ||
const findInRanges = require('./find-in-ranges.js'); | ||
const quickFindRange = (position, ranges) => { | ||
let start = 0; | ||
let end = ranges.length - 1; | ||
while (end - start > 1) { | ||
const i = Math.floor((start + end) * 0.5); | ||
const item = ranges[i]; | ||
if (position < item.startOffset) { | ||
end = i; | ||
continue; | ||
} | ||
if (position > item.endOffset) { | ||
start = i; | ||
continue; | ||
} | ||
return ranges[i]; | ||
} | ||
// last two items, less is start | ||
const endItem = ranges[end]; | ||
if (position < endItem.startOffset) { | ||
return ranges[start]; | ||
} | ||
return ranges[end]; | ||
}; | ||
const findInRanges = (start, end, ranges) => { | ||
if (!Util.isList(ranges)) { | ||
return; | ||
} | ||
const range = quickFindRange(start, ranges); | ||
if (start >= range.startOffset && end <= range.endOffset) { | ||
return range; | ||
} | ||
}; | ||
const findBranchBlock = (start, end, functionInfo) => { | ||
@@ -435,5 +400,4 @@ const { range } = functionInfo; | ||
module.exports = { | ||
findInRanges, | ||
createBranches, | ||
collectBranches | ||
}; |
@@ -0,1 +1,13 @@ | ||
// https://github.com/demurgos/v8-coverage | ||
/** | ||
* @ranges is always non-empty. The first range is called the "root range". | ||
* @isBlockCoverage indicates if the function has block coverage information | ||
* @false means that there is a single range and its count is the number of times the function was called. | ||
* @true means that the ranges form a tree of blocks representing how many times each statement or expression inside was executed. | ||
* It detects skipped or repeated statements. The root range counts the number of function calls. | ||
* | ||
* @functionName can be an empty string. This is common for the FunctionCov representing the whole module. | ||
*/ | ||
// if you have a line of code that says `var x= 10; console.log(x);` that's one line and 2 statements. | ||
const path = require('path'); | ||
@@ -10,2 +22,3 @@ | ||
const { getJsAstInfo, getCssAstInfo } = require('./ast.js'); | ||
const { getIgnoredRanges } = require('./ignore.js'); | ||
@@ -18,5 +31,120 @@ const { dedupeCountRanges } = require('../utils/dedupe.js'); | ||
const InfoFunction = require('./info-function.js'); | ||
const findInRanges = require('./find-in-ranges.js'); | ||
// ======================================================================================================== | ||
const handleIgnoredRanges = (list, ignoredRanges) => { | ||
list.forEach((item) => { | ||
if (item.count > 0) { | ||
return; | ||
} | ||
const range = findInRanges(item.start, item.end, ignoredRanges); | ||
if (range) { | ||
// console.log(item, range); | ||
item.ignored = true; | ||
} | ||
}); | ||
}; | ||
// ======================================================================================================== | ||
const updateLinesCount = (bytes, locator, lineMap) => { | ||
bytes.forEach((range) => { | ||
const { | ||
start, end, count, ignored | ||
} = range; | ||
const sLoc = locator.offsetToLocation(start); | ||
const eLoc = locator.offsetToLocation(end); | ||
// update lines coverage | ||
const lines = Util.getRangeLines(sLoc, eLoc); | ||
lines.forEach((it) => { | ||
const line = lineMap.get(it.line); | ||
if (!line) { | ||
return; | ||
} | ||
// from outside into inside, uncovered is certain | ||
// default is covered | ||
if (it.entire) { | ||
line.covered = count > 0; | ||
line.count = count; | ||
if (ignored) { | ||
if (!line.covered) { | ||
line.ignored = true; | ||
} | ||
} | ||
} else { | ||
if (!ignored) { | ||
if (count > 0) { | ||
line.count = count; | ||
} else { | ||
// not covered if any count = 0 | ||
line.covered = false; | ||
} | ||
} | ||
} | ||
// if (!line.history) { | ||
// line.history = []; | ||
// } | ||
// line.history.push(`${it.entire}-${count}`); | ||
}); | ||
}); | ||
// if (state.sourcePath.endsWith('component.js')) { | ||
// console.log('===========================================', state.sourcePath); | ||
// console.log(coverageInfo.lines); | ||
// } | ||
}; | ||
const handleLinesCoverage = (bytes, locator) => { | ||
const lines = []; | ||
// line 1 based | ||
const lineMap = new Map(); | ||
// init lines | ||
let blankCount = 0; | ||
let commentCount = 0; | ||
locator.lines.forEach((it) => { | ||
// exclude blank and comment | ||
if (it.blank) { | ||
blankCount += 1; | ||
return; | ||
} | ||
if (it.comment) { | ||
commentCount += 1; | ||
return; | ||
} | ||
// line 1-base | ||
const line = it.line + 1; | ||
// default count to 1, both js and css | ||
const lineInfo = new InfoLine(line, it.length, 1); | ||
lineMap.set(line, lineInfo); | ||
lines.push(lineInfo); | ||
}); | ||
updateLinesCount(bytes, locator, lineMap); | ||
return { | ||
lines, | ||
blankCount, | ||
commentCount | ||
}; | ||
}; | ||
// ======================================================================================================== | ||
const calculateV8Functions = (functions) => { | ||
@@ -30,2 +158,6 @@ | ||
functions.forEach((fn) => { | ||
if (fn.ignored) { | ||
return; | ||
} | ||
v8Functions.total += 1; | ||
@@ -47,8 +179,9 @@ if (fn.count > 0) { | ||
branches.forEach((branch) => { | ||
branch.locations.forEach((location) => { | ||
v8Branches.total += 1; | ||
if (location.count > 0) { | ||
v8Branches.covered += 1; | ||
} | ||
}); | ||
if (branch.ignored) { | ||
return; | ||
} | ||
v8Branches.total += 1; | ||
if (branch.count > 0) { | ||
v8Branches.covered += 1; | ||
} | ||
}); | ||
@@ -68,2 +201,6 @@ | ||
lines.forEach((ln) => { | ||
if (ln.ignored) { | ||
// console.log(ln); | ||
return; | ||
} | ||
v8Lines.total += 1; | ||
@@ -79,38 +216,16 @@ // full line covered | ||
// ======================================================================================================== | ||
const createEmptyCoverageInfo = () => { | ||
// data for v8 UI | ||
const data = { | ||
bytes: [], | ||
return { | ||
// v8 UI | ||
data: { | ||
bytes: [], | ||
functions: [], | ||
branches: [] | ||
}, | ||
// istanbul | ||
functions: [], | ||
branches: [] | ||
}; | ||
const branches = []; | ||
const functions = []; | ||
const lines = []; | ||
// line 1 based | ||
const lineMap = new Map(); | ||
const blankCount = 0; | ||
const commentCount = 0; | ||
return { | ||
// v8 | ||
data, | ||
// istanbul | ||
branches, | ||
functions, | ||
lines, | ||
lineMap, | ||
blankCount, | ||
commentCount | ||
}; | ||
}; | ||
@@ -128,19 +243,11 @@ | ||
*/ | ||
const collectFileCoverage = (item, coverageInfo, state) => { | ||
const collectFileCoverage = (item, state, coverageData, options) => { | ||
const { sourcePath } = item; | ||
const { coverageInfo, locator } = state; | ||
const { | ||
// v8 | ||
data, | ||
// istanbul | ||
branches, | ||
functions, | ||
lines, | ||
blankCount, | ||
commentCount | ||
branches | ||
} = coverageInfo; | ||
@@ -151,6 +258,23 @@ | ||
data.bytes = dedupeCountRanges(data.bytes); | ||
const ignoredRanges = getIgnoredRanges(locator, options); | ||
if (ignoredRanges) { | ||
handleIgnoredRanges(data.bytes, ignoredRanges); | ||
handleIgnoredRanges(data.functions, ignoredRanges); | ||
handleIgnoredRanges(data.branches, ignoredRanges); | ||
// console.log(ignoredRanges); | ||
} | ||
// after bytes with ignored, before calculateV8Lines | ||
const { | ||
lines, blankCount, commentCount | ||
} = handleLinesCoverage(data.bytes, locator); | ||
item.data = data; | ||
item.summary = { | ||
functions: calculateV8Functions(functions), | ||
branches: calculateV8Branches(branches), | ||
functions: calculateV8Functions(data.functions), | ||
branches: calculateV8Branches(data.branches), | ||
lines: calculateV8Lines(lines, blankCount, commentCount) | ||
@@ -189,3 +313,3 @@ }; | ||
// append to dist file state | ||
state.coverageData[sourcePath] = istanbulCoverage; | ||
coverageData[sourcePath] = istanbulCoverage; | ||
@@ -196,67 +320,10 @@ }; | ||
const updateLineCoverage = (start, end, count, lineMap, locator) => { | ||
const sLoc = locator.offsetToLocation(start); | ||
const eLoc = locator.offsetToLocation(end); | ||
// update lines coverage | ||
const lines = Util.getRangeLines(sLoc, eLoc); | ||
lines.forEach((it) => { | ||
const line = lineMap.get(it.line); | ||
if (!line) { | ||
return; | ||
} | ||
// from outside into inside, uncovered is certain | ||
// default is covered | ||
if (it.entire) { | ||
line.covered = count > 0; | ||
line.count = count; | ||
} else { | ||
if (count > 0) { | ||
line.count = count; | ||
} else { | ||
// not covered if any count = 0 | ||
line.covered = false; | ||
} | ||
} | ||
// if (!line.history) { | ||
// line.history = []; | ||
// } | ||
// line.history.push(`${it.entire}-${count}`); | ||
}); | ||
// if (state.sourcePath.endsWith('component.js')) { | ||
// console.log('===========================================', state.sourcePath); | ||
// console.log(coverageInfo.lines); | ||
// } | ||
}; | ||
// https://github.com/demurgos/v8-coverage | ||
/** | ||
* @ranges is always non-empty. The first range is called the "root range". | ||
* @isBlockCoverage indicates if the function has block coverage information | ||
* @false means that there is a single range and its count is the number of times the function was called. | ||
* @true means that the ranges form a tree of blocks representing how many times each statement or expression inside was executed. | ||
* It detects skipped or repeated statements. The root range counts the number of function calls. | ||
* | ||
* @functionName can be an empty string. This is common for the FunctionCov representing the whole module. | ||
*/ | ||
// if you have a line of code that says `var x= 10; console.log(x);` that's one line and 2 statements. | ||
const addJsLineCoverage = (state, range) => { | ||
const { locator, coverageInfo } = state; | ||
const { data, lineMap } = coverageInfo; | ||
const addJsBytesCoverage = (state, range) => { | ||
const { coverageInfo } = state; | ||
const { data } = coverageInfo; | ||
const { | ||
startOffset, endOffset, count | ||
} = range; | ||
// add bytes range | ||
data.bytes.push({ | ||
// index, | ||
start: startOffset, | ||
@@ -266,26 +333,18 @@ end: endOffset, | ||
}); | ||
updateLineCoverage(startOffset, endOffset, count, lineMap, locator); | ||
}; | ||
const addCssLineCoverage = (state, range) => { | ||
const { coverageInfo, locator } = state; | ||
const { lineMap, data } = coverageInfo; | ||
const addCssBytesCoverage = (state, range) => { | ||
const { coverageInfo } = state; | ||
const { data } = coverageInfo; | ||
const { | ||
start, end, count | ||
} = range; | ||
// add bytes range | ||
data.bytes.push(range); | ||
updateLineCoverage(start, end, count, lineMap, locator); | ||
// add css bytes range, already start, end | ||
data.bytes.push({ | ||
start, | ||
end, | ||
count | ||
}); | ||
}; | ||
// ======================================================================================================== | ||
const updateOffsetToLocation = (locator, loc) => { | ||
@@ -304,2 +363,4 @@ const sLoc = locator.offsetToLocation(loc.start); | ||
// ======================================================================================================== | ||
const handleFunctionsCoverage = (state) => { | ||
@@ -403,46 +464,190 @@ | ||
}; | ||
const handleBytesCoverage = (state) => { | ||
const { js, coverageList } = state; | ||
if (js) { | ||
coverageList.forEach((block) => { | ||
block.ranges.forEach((range) => { | ||
addJsBytesCoverage(state, range); | ||
}); | ||
}); | ||
} else { | ||
coverageList.forEach((range) => { | ||
addCssBytesCoverage(state, range); | ||
}); | ||
} | ||
}; | ||
const handleLinesCoverage = (state) => { | ||
// ======================================================================================================== | ||
const { locator, coverageInfo } = state; | ||
const handleOriginalFunctionsCoverage = (state, originalMap) => { | ||
const lines = coverageInfo.lines; | ||
const lineMap = coverageInfo.lineMap; | ||
// functions only for js | ||
if (!state.js) { | ||
return; | ||
} | ||
// init lines | ||
let blankCount = 0; | ||
let commentCount = 0; | ||
// console.log(state.astInfo.functions); | ||
locator.lines.forEach((it) => { | ||
// exclude blank and comment | ||
if (it.blank) { | ||
blankCount += 1; | ||
// function count | ||
state.astInfo.functions.forEach((it) => { | ||
const { | ||
start, end, wrap | ||
} = it; | ||
// remove webpack wrap functions for functions count, not for ranges here | ||
if (wrap) { | ||
return; | ||
} | ||
if (it.comment) { | ||
commentCount += 1; | ||
// rename to startOffset and endOffset | ||
const range = { | ||
startOffset: start, | ||
endOffset: end | ||
}; | ||
const result = findOriginalRange(range, state, originalMap); | ||
if (result.error) { | ||
return; | ||
} | ||
// line 1-base | ||
const line = it.line + 1; | ||
// default count to 1, both js and css | ||
const lineInfo = new InfoLine(line, it.length, 1); | ||
lineMap.set(line, lineInfo); | ||
lines.push(lineInfo); | ||
const { originalRange, originalState } = result; | ||
// add back to original ast | ||
originalState.astInfo.functions.push({ | ||
... it, | ||
generatedStart: start, | ||
generatedEnd: end, | ||
start: originalRange.startOffset, | ||
end: originalRange.endOffset | ||
}); | ||
// if (originalState.sourcePath.endsWith('store.js')) { | ||
// console.log('=========================================================='); | ||
// console.log(it, result.startMapping, result.endMapping); | ||
// } | ||
}); | ||
coverageInfo.blankCount = blankCount; | ||
coverageInfo.commentCount = commentCount; | ||
}; | ||
const handleRangesCoverage = (state) => { | ||
const handleOriginalBranchesCoverage = (state, originalMap) => { | ||
// branches only for js | ||
if (!state.js) { | ||
return; | ||
} | ||
// console.log(state.astInfo.branches); | ||
// function count | ||
state.astInfo.branches.forEach((it) => { | ||
const { | ||
type, loc, locations | ||
} = it; | ||
// rename to startOffset and endOffset | ||
const bRange = { | ||
startOffset: loc.start, | ||
endOffset: loc.end | ||
}; | ||
// start | ||
const result = findOriginalRange(bRange, state, originalMap); | ||
if (result.error) { | ||
// not in the original files | ||
return; | ||
} | ||
const { originalRange, originalState } = result; | ||
const newBranchLoc = { | ||
start: originalRange.startOffset, | ||
end: originalRange.endOffset | ||
}; | ||
let hasError; | ||
const newLocations = locations.map((oLoc) => { | ||
const newLoc = { | ||
... oLoc | ||
}; | ||
if (newLoc.none) { | ||
return newLoc; | ||
} | ||
const { start, end } = newLoc; | ||
const lRange = { | ||
startOffset: start, | ||
endOffset: end | ||
}; | ||
const res = findOriginalRange(lRange, state, originalMap); | ||
if (res.error) { | ||
// It should not happen unless it is minify files, the SourceMap has some order problems | ||
hasError = true; | ||
// console.log('====================================', state.sourcePath, loc); | ||
// console.log(res.errors); | ||
// Util.logError(`Not found original branch. start: ${start}, end: ${end}`); | ||
return newLoc; | ||
} | ||
const oRage = res.originalRange; | ||
newLoc.start = oRage.startOffset; | ||
newLoc.end = oRage.endOffset; | ||
return newLoc; | ||
}); | ||
if (hasError) { | ||
return; | ||
} | ||
// add back to original ast | ||
originalState.astInfo.branches.push({ | ||
type, | ||
loc: newBranchLoc, | ||
locations: newLocations | ||
}); | ||
}); | ||
}; | ||
const handleOriginalBytesCoverage = (state, originalMap) => { | ||
const { js, coverageList } = state; | ||
// const time_start_mapping = Date.now(); | ||
if (js) { | ||
// v8 coverage | ||
coverageList.forEach((block) => { | ||
block.ranges.forEach((range) => { | ||
addJsLineCoverage(state, range); | ||
block.ranges.forEach((range, index) => { | ||
// remove wrap functions for original files | ||
if (range.wrap) { | ||
// console.log(range); | ||
return; | ||
} | ||
const result = findOriginalRange(range, state, originalMap); | ||
if (result.error) { | ||
return; | ||
} | ||
const { originalRange, originalState } = result; | ||
addJsBytesCoverage(originalState, originalRange); | ||
// if (originalRange.startOffset === 110 && originalRange.endOffset === 271) { | ||
// console.log('===============================', originalState.sourcePath); | ||
// console.log(result.startMapping, result.endMapping); | ||
// } | ||
}); | ||
@@ -452,5 +657,7 @@ }); | ||
} else { | ||
coverageList.forEach((range) => { | ||
addCssLineCoverage(state, range); | ||
}); | ||
// support css later | ||
// current css no sourceMap, so never come in | ||
// coverageList.forEach((range) => { | ||
// }); | ||
} | ||
@@ -460,14 +667,2 @@ | ||
const generateCoverageForDist = (item, state) => { | ||
// handle coverage | ||
handleFunctionsCoverage(state); | ||
handleBranchesCoverage(state); | ||
handleLinesCoverage(state); | ||
handleRangesCoverage(state); | ||
collectFileCoverage(item, state.coverageInfo, state); | ||
}; | ||
// ======================================================================================================== | ||
@@ -566,2 +761,4 @@ | ||
// ======================================================================================================== | ||
const initOriginalList = (state, originalDecodedMap, options) => { | ||
@@ -629,3 +826,3 @@ | ||
const collectOriginalList = (item, state, originalMap) => { | ||
const collectOriginalList = (item, state, originalMap, options) => { | ||
@@ -640,3 +837,3 @@ const { fileUrls, sourceMap } = state; | ||
const { | ||
js, type, sourcePath, source, coverageInfo | ||
js, type, sourcePath, source | ||
} = originalState; | ||
@@ -659,3 +856,3 @@ | ||
// generate coverage, coverageInfo for current file, state for dist file | ||
collectFileCoverage(sourceItem, coverageInfo, state); | ||
collectFileCoverage(sourceItem, originalState, state.coverageData, options); | ||
@@ -668,183 +865,14 @@ sourceList.push(sourceItem); | ||
// ======================================================================================= | ||
// ======================================================================================================== | ||
const handleOriginalFunctionsCoverage = (state, originalMap) => { | ||
const generateCoverageForDist = (item, state, options) => { | ||
// functions only for js | ||
if (!state.js) { | ||
return; | ||
} | ||
handleFunctionsCoverage(state); | ||
handleBranchesCoverage(state); | ||
handleBytesCoverage(state); | ||
// console.log(state.astInfo.functions); | ||
collectFileCoverage(item, state, state.coverageData, options); | ||
// function count | ||
state.astInfo.functions.forEach((it) => { | ||
const { | ||
start, end, wrap | ||
} = it; | ||
// remove webpack wrap functions for functions count, not for ranges here | ||
if (wrap) { | ||
return; | ||
} | ||
// rename to startOffset and endOffset | ||
const range = { | ||
startOffset: start, | ||
endOffset: end | ||
}; | ||
const result = findOriginalRange(range, state, originalMap); | ||
if (result.error) { | ||
return; | ||
} | ||
const { originalRange, originalState } = result; | ||
// add back to original ast | ||
originalState.astInfo.functions.push({ | ||
... it, | ||
generatedStart: start, | ||
generatedEnd: end, | ||
start: originalRange.startOffset, | ||
end: originalRange.endOffset | ||
}); | ||
// if (originalState.sourcePath.endsWith('store.js')) { | ||
// console.log('=========================================================='); | ||
// console.log(it, result.startMapping, result.endMapping); | ||
// } | ||
}); | ||
}; | ||
const handleOriginalBranchesCoverage = (state, originalMap) => { | ||
// branches only for js | ||
if (!state.js) { | ||
return; | ||
} | ||
// console.log(state.astInfo.branches); | ||
// function count | ||
state.astInfo.branches.forEach((it) => { | ||
const { | ||
type, loc, locations | ||
} = it; | ||
// rename to startOffset and endOffset | ||
const bRange = { | ||
startOffset: loc.start, | ||
endOffset: loc.end | ||
}; | ||
// start | ||
const result = findOriginalRange(bRange, state, originalMap); | ||
if (result.error) { | ||
// not in the original files | ||
return; | ||
} | ||
const { originalRange, originalState } = result; | ||
const newBranchLoc = { | ||
start: originalRange.startOffset, | ||
end: originalRange.endOffset | ||
}; | ||
let hasError; | ||
const newLocations = locations.map((oLoc) => { | ||
const newLoc = { | ||
... oLoc | ||
}; | ||
if (newLoc.none) { | ||
return newLoc; | ||
} | ||
const { start, end } = newLoc; | ||
const lRange = { | ||
startOffset: start, | ||
endOffset: end | ||
}; | ||
const res = findOriginalRange(lRange, state, originalMap); | ||
if (res.error) { | ||
// It should not happen unless it is minify files, the SourceMap has some order problems | ||
hasError = true; | ||
// console.log('====================================', state.sourcePath, loc); | ||
// console.log(res.errors); | ||
// Util.logError(`Not found original branch. start: ${start}, end: ${end}`); | ||
return newLoc; | ||
} | ||
const oRage = res.originalRange; | ||
newLoc.start = oRage.startOffset; | ||
newLoc.end = oRage.endOffset; | ||
return newLoc; | ||
}); | ||
if (hasError) { | ||
return; | ||
} | ||
// add back to original ast | ||
originalState.astInfo.branches.push({ | ||
type, | ||
loc: newBranchLoc, | ||
locations: newLocations | ||
}); | ||
}); | ||
}; | ||
const handleOriginalRangesCoverage = (state, originalMap) => { | ||
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 result = findOriginalRange(range, state, originalMap); | ||
if (result.error) { | ||
return; | ||
} | ||
const { originalRange, originalState } = result; | ||
addJsLineCoverage(originalState, originalRange); | ||
// if (originalRange.startOffset === 110 && originalRange.endOffset === 271) { | ||
// console.log('===============================', originalState.sourcePath); | ||
// console.log(result.startMapping, result.endMapping); | ||
// } | ||
}); | ||
}); | ||
} else { | ||
// support css later | ||
// current css no sourceMap, so never come in | ||
// coverageList.forEach((range) => { | ||
// }); | ||
} | ||
}; | ||
const unpackSourceMap = async (item, state, options) => { | ||
@@ -883,11 +911,6 @@ | ||
originalMap.forEach((originalState) => { | ||
handleFunctionsCoverage(originalState); | ||
handleBranchesCoverage(originalState); | ||
handleLinesCoverage(originalState); | ||
// console.log(originalState.sourcePath, '================================'); | ||
// console.log(originalState.astInfo.functions); | ||
// console.log(originalState.source.length); | ||
// console.log(originalState.coverageInfo.functions); | ||
// if (originalState.sourcePath.endsWith('demo.js')) { | ||
@@ -901,11 +924,9 @@ // console.log('=================================', originalState.sourcePath); | ||
// handle ranges after lines ready | ||
handleOriginalRangesCoverage(state, originalMap); | ||
handleOriginalBytesCoverage(state, originalMap); | ||
// collect coverage for original list | ||
state.sourceList = collectOriginalList(item, state, originalMap); | ||
state.sourceList = collectOriginalList(item, state, originalMap, options); | ||
}; | ||
// ======================================================================================================== | ||
const unpackDistFile = async (item, state, options) => { | ||
@@ -916,3 +937,4 @@ | ||
// js self | ||
generateCoverageForDist(item, state); | ||
item.debug = true; | ||
generateCoverageForDist(item, state, options); | ||
} else { | ||
@@ -928,3 +950,3 @@ item.dedupe = true; | ||
// css/js self | ||
generateCoverageForDist(item, state); | ||
generateCoverageForDist(item, state, options); | ||
@@ -931,0 +953,0 @@ } |
@@ -46,2 +46,5 @@ module.exports = { | ||
// [V8 only](Boolean) Enable/Disable ignoring uncovered codes with the special comments: /* v8 ignore next/next N/start/stop */ | ||
v8Ignore: true, | ||
// [Istanbul only] defaultSummarizer, sourceFinder | ||
@@ -48,0 +51,0 @@ |
@@ -130,56 +130,2 @@ const fs = require('fs'); | ||
const generateCoverageReports = async (dataList, options) => { | ||
// get first and check v8list or istanbul data | ||
const firstData = dataList[0]; | ||
const dataType = firstData.type; | ||
// console.log('data type', dataType); | ||
// init reports | ||
options.reportGroup = getReportGroup(options.reports, options.lcov, dataType); | ||
// console.log('reportGroup', options.reportGroup); | ||
// v8list | ||
if (dataType === 'v8') { | ||
// merge v8list first | ||
const v8list = await mergeV8Coverage(dataList, options); | ||
// console.log('after merge', v8list.map((it) => it.url)); | ||
const { coverageData, fileSources } = await convertV8List(v8list, options); | ||
return generateV8ListReports(v8list, coverageData, fileSources, options); | ||
} | ||
// istanbul data | ||
const istanbulData = mergeIstanbulCoverage(dataList, options); | ||
const { coverageData, fileSources } = initIstanbulData(istanbulData, options); | ||
return saveIstanbulReports(coverageData, fileSources, options); | ||
}; | ||
// ======================================================================================================== | ||
const getCoverageDataList = async (cacheDir) => { | ||
const files = fs.readdirSync(cacheDir).filter((f) => f.startsWith('coverage-')); | ||
if (!files.length) { | ||
return; | ||
} | ||
const dataList = []; | ||
for (const item of files) { | ||
const content = await Util.readFile(path.resolve(cacheDir, item)); | ||
if (content) { | ||
dataList.push(JSON.parse(content)); | ||
} | ||
} | ||
if (dataList.length) { | ||
return dataList; | ||
} | ||
}; | ||
// ======================================================================================================== | ||
const capitalizeFirstLetter = (string) => { | ||
@@ -274,11 +220,2 @@ return string.charAt(0).toUpperCase() + string.slice(1); | ||
if (type === 'istanbul') { | ||
columns.splice(4, 0, { | ||
id: 'skipped', | ||
name: 'Skipped', | ||
align: 'right', | ||
formatter: nFormatter | ||
}); | ||
} | ||
CG({ | ||
@@ -290,6 +227,68 @@ columns, | ||
// ======================================================================================================== | ||
const getCoverageResults = async (dataList, options) => { | ||
// get first and check v8list or istanbul data | ||
const firstData = dataList[0]; | ||
const dataType = firstData.type; | ||
// console.log('data type', dataType); | ||
// init reports | ||
options.reportGroup = getReportGroup(options.reports, options.lcov, dataType); | ||
// console.log('reportGroup', options.reportGroup); | ||
// v8list | ||
if (dataType === 'v8') { | ||
// merge v8list first | ||
const v8list = await mergeV8Coverage(dataList, options); | ||
// console.log('after merge', v8list.map((it) => it.url)); | ||
const { coverageData, fileSources } = await convertV8List(v8list, options); | ||
return generateV8ListReports(v8list, coverageData, fileSources, options); | ||
} | ||
// istanbul data | ||
const istanbulData = mergeIstanbulCoverage(dataList, options); | ||
const { coverageData, fileSources } = initIstanbulData(istanbulData, options); | ||
return saveIstanbulReports(coverageData, fileSources, options); | ||
}; | ||
const generateCoverageReports = async (dataList, options) => { | ||
const coverageResults = await getCoverageResults(dataList, options); | ||
// [ 'type', 'reportPath', 'name', 'watermarks', 'summary', 'files' ] | ||
// console.log(Object.keys(coverageResults)); | ||
showConsoleSummary(coverageResults, options); | ||
return coverageResults; | ||
}; | ||
// ======================================================================================================== | ||
const getCoverageDataList = async (cacheDir) => { | ||
const files = fs.readdirSync(cacheDir).filter((f) => f.startsWith('coverage-')); | ||
if (!files.length) { | ||
return; | ||
} | ||
const dataList = []; | ||
for (const item of files) { | ||
const content = await Util.readFile(path.resolve(cacheDir, item)); | ||
if (content) { | ||
dataList.push(JSON.parse(content)); | ||
} | ||
} | ||
if (dataList.length) { | ||
return dataList; | ||
} | ||
}; | ||
module.exports = { | ||
getCoverageDataList, | ||
generateCoverageReports, | ||
showConsoleSummary | ||
generateCoverageReports | ||
}; |
@@ -126,2 +126,5 @@ | ||
// [V8 only](Boolean) Enable/Disable ignoring uncovered codes with the special comments: /* v8 ignore next/next N/start/stop */ | ||
v8Ignore?: boolean, | ||
// [Istanbul only] defaultSummarizer, sourceFinder | ||
@@ -128,0 +131,0 @@ |
@@ -7,5 +7,3 @@ const fs = require('fs'); | ||
const { initV8ListAndSourcemap } = require('./v8/v8.js'); | ||
const { | ||
getCoverageDataList, generateCoverageReports, showConsoleSummary | ||
} = require('./generate.js'); | ||
const { getCoverageDataList, generateCoverageReports } = require('./generate.js'); | ||
@@ -113,4 +111,2 @@ class CoverageReport { | ||
showConsoleSummary(coverageResults, this.options); | ||
const onEnd = this.options.onEnd; | ||
@@ -117,0 +113,0 @@ if (typeof onEnd === 'function') { |
@@ -47,4 +47,3 @@ const istanbulLibReport = require('istanbul-lib-report'); | ||
const item = this.summary[id]; | ||
// NOTE: skipped | ||
item.uncovered = item.total - item.covered - item.skipped; | ||
item.uncovered = item.total - item.covered; | ||
}); | ||
@@ -51,0 +50,0 @@ |
@@ -106,3 +106,3 @@ const fs = require('fs'); | ||
const report = { | ||
const coverageResults = { | ||
type: 'istanbul', | ||
@@ -116,3 +116,3 @@ reportPath, | ||
return report; | ||
return coverageResults; | ||
}; | ||
@@ -119,0 +119,0 @@ |
@@ -65,7 +65,2 @@ | ||
// v8 ranges format | ||
// { startOffset: 0, endOffset: 6, count: 0 }, | ||
// { startOffset: 0, endOffset: 6, count: 1 }, | ||
// { startOffset: 0, endOffset: 297, count: 1 } | ||
// count ranges format | ||
@@ -72,0 +67,0 @@ // { start: 0, end: 6, count: 0 } |
@@ -1,33 +0,8 @@ | ||
// https://playwright.dev/docs/api/class-coverage | ||
const Util = require('../utils/util.js'); | ||
// url, text, ranges: [ {start, end} ] | ||
const getCssSummaryBytes = (item) => { | ||
// both css and js | ||
const getV8SummaryBytes = (item) => { | ||
const source = item.source; | ||
const total = source.length; | ||
let covered = 0; | ||
const bytes = item.data.bytes; | ||
if (bytes) { | ||
bytes.forEach((range) => { | ||
if (range.count > 0) { | ||
covered += range.end - range.start; | ||
} | ||
}); | ||
} | ||
// no functions for css | ||
return { | ||
total, | ||
covered | ||
}; | ||
}; | ||
// url, source, ranges:[{start,end, count}] | ||
const getJsSummaryBytes = (item) => { | ||
const source = item.source; | ||
const total = source.length; | ||
const uncoveredBytes = item.data.bytes.filter((range) => range.count === 0); | ||
@@ -39,2 +14,6 @@ | ||
uncoveredBytes.forEach((range) => { | ||
if (range.ignored) { | ||
return; | ||
} | ||
const { start, end } = range; | ||
@@ -67,11 +46,83 @@ | ||
const getV8SummaryBytes = (item) => { | ||
if (item.type === 'css') { | ||
return getCssSummaryBytes(item); | ||
} | ||
return getJsSummaryBytes(item); | ||
// calculate uncovered, pct, status | ||
const calculatePctAndStatus = (item, watermarks) => { | ||
Object.keys(item).forEach((k) => { | ||
const indicateData = item[k]; | ||
indicateData.uncovered = indicateData.total - indicateData.covered; | ||
let pct = ''; | ||
let status = 'unknown'; | ||
if (indicateData.total) { | ||
pct = Util.PNF(indicateData.covered, indicateData.total, 2); | ||
status = Util.getStatus(pct, watermarks[k]); | ||
} | ||
indicateData.pct = pct; | ||
indicateData.status = status; | ||
}); | ||
}; | ||
const getV8Summary = (v8list, watermarks) => { | ||
// get bytes summary | ||
v8list.forEach((entry) => { | ||
entry.summary.bytes = getV8SummaryBytes(entry); | ||
}); | ||
// overall summary | ||
const summary = { | ||
bytes: { | ||
total: 0, | ||
covered: 0 | ||
}, | ||
functions: { | ||
total: 0, | ||
covered: 0 | ||
}, | ||
branches: { | ||
total: 0, | ||
covered: 0 | ||
}, | ||
lines: { | ||
total: 0, | ||
covered: 0, | ||
blank: 0, | ||
comment: 0 | ||
} | ||
}; | ||
v8list.forEach((entry) => { | ||
const entrySummary = entry.summary; | ||
calculatePctAndStatus(entrySummary, watermarks); | ||
// do NOT add debug file | ||
if (entry.debug) { | ||
return; | ||
} | ||
Object.keys(entrySummary).forEach((k) => { | ||
const indicateData = entrySummary[k]; | ||
if (!indicateData) { | ||
return; | ||
} | ||
summary[k].total += indicateData.total; | ||
summary[k].covered += indicateData.covered; | ||
if (k === 'lines') { | ||
summary[k].blank += indicateData.blank; | ||
summary[k].comment += indicateData.comment; | ||
} | ||
}); | ||
}); | ||
// calculate overall summary | ||
calculatePctAndStatus(summary, watermarks); | ||
return summary; | ||
}; | ||
module.exports = { | ||
getV8SummaryBytes | ||
getV8Summary | ||
}; |
const path = require('path'); | ||
const Util = require('../utils/util.js'); | ||
const { getV8SummaryBytes } = require('./v8-summary.js'); | ||
const { getV8Summary } = require('./v8-summary.js'); | ||
const { dedupeRanges } = require('../utils/dedupe.js'); | ||
@@ -224,23 +224,4 @@ const { getSourcePath } = require('../utils/source-path.js'); | ||
// calculate uncovered, pct, status | ||
const calculatePctAndStatus = (item, watermarks) => { | ||
Object.keys(item).forEach((k) => { | ||
const indicateData = item[k]; | ||
indicateData.uncovered = indicateData.total - indicateData.covered; | ||
const saveV8Report = async (v8list, options) => { | ||
let pct = ''; | ||
let status = 'unknown'; | ||
if (indicateData.total) { | ||
pct = Util.PNF(indicateData.covered, indicateData.total, 2); | ||
status = Util.getStatus(pct, watermarks[k]); | ||
} | ||
indicateData.pct = pct; | ||
indicateData.status = status; | ||
}); | ||
}; | ||
const saveV8Report = async (v8list, options) => { | ||
const defaultWatermarks = { | ||
@@ -254,48 +235,4 @@ bytes: [50, 80], | ||
// init summary | ||
v8list.forEach((entry) => { | ||
entry.summary.bytes = getV8SummaryBytes(entry); | ||
}); | ||
const summary = getV8Summary(v8list, watermarks); | ||
// overall summary | ||
const summaryList = v8list.map((entry) => entry.summary); | ||
const summary = { | ||
bytes: { | ||
total: 0, | ||
covered: 0 | ||
}, | ||
functions: { | ||
total: 0, | ||
covered: 0 | ||
}, | ||
branches: { | ||
total: 0, | ||
covered: 0 | ||
}, | ||
lines: { | ||
total: 0, | ||
covered: 0, | ||
blank: 0, | ||
comment: 0 | ||
} | ||
}; | ||
summaryList.forEach((item) => { | ||
calculatePctAndStatus(item, watermarks); | ||
Object.keys(item).forEach((k) => { | ||
const indicateData = item[k]; | ||
if (!indicateData) { | ||
return; | ||
} | ||
summary[k].total += indicateData.total; | ||
summary[k].covered += indicateData.covered; | ||
if (k === 'lines') { | ||
summary[k].blank += indicateData.blank; | ||
summary[k].comment += indicateData.comment; | ||
} | ||
}); | ||
}); | ||
calculatePctAndStatus(summary, watermarks); | ||
const reportData = { | ||
@@ -343,3 +280,5 @@ name: options.name, | ||
const report = { | ||
const summaryList = v8list.map((entry) => entry.summary); | ||
const coverageResults = { | ||
type: 'v8', | ||
@@ -353,3 +292,3 @@ reportPath, | ||
return report; | ||
return coverageResults; | ||
}; | ||
@@ -356,0 +295,0 @@ |
{ | ||
"name": "monocart-coverage-reports", | ||
"version": "2.0.7", | ||
"version": "2.0.8", | ||
"description": "Monocart coverage reports", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -22,2 +22,3 @@ # Monocart Coverage Reports | ||
- [How Monocart Works](#how-monocart-works) | ||
* [Ignoring Uncovered Lines](#ignoring-uncovered-lines) | ||
* [Chromium Coverage API](#chromium-coverage-api) | ||
@@ -170,4 +171,5 @@ * [Istanbul Introduction](#istanbul-introduction) | ||
- For source code: enable `sourcemap` and do not compress/minify: | ||
- Webpack: with `source-map` [devtool](https://webpack.js.org/configuration/devtool/) and `development` [mode](https://webpack.js.org/configuration/mode/), example [webpack.config-v8.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-v8.js) | ||
- Rollup: [options](https://rollupjs.org/configuration-options/) `sourcemap: true` | ||
- Webpack: build with `source-map` [devtool](https://webpack.js.org/configuration/devtool/) and `development` [mode](https://webpack.js.org/configuration/mode/), example [webpack.config-v8.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-v8.js) | ||
- Rollup: build with [options](https://rollupjs.org/configuration-options/) `sourcemap: true` | ||
- Vite: build with [options](https://vitejs.dev/config/build-options.html) `sourcemap: true` and `minify: false` | ||
- Browser (Chromium Only) | ||
@@ -252,7 +254,5 @@ > Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api), see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js) | ||
// v8-to-istanbul: count range as function here. | ||
// Problem: there is no function name if the function is anonymous or expression. | ||
} | ||
} else if (block.functionName) { | ||
// v8-to-istanbul: count range as function here. | ||
// Problem: same as above function. | ||
} | ||
@@ -278,2 +278,24 @@ } | ||
## Ignoring Uncovered Codes | ||
To ignore codes, use the special comment which starts with `v8 ignore `: | ||
- ignoring all until told | ||
```js | ||
/* v8 ignore start */ | ||
function uncovered() { | ||
} | ||
/* v8 ignore stop */ | ||
``` | ||
- ignoring the next line or next N lines | ||
```js | ||
/* v8 ignore next */ | ||
const os = platform === 'wind32' ? 'Windows' : 'Other'; | ||
const os = platform === 'wind32' ? 'Windows' /* v8 ignore next */ : 'Other'; | ||
/* v8 ignore next 3 */ | ||
if (platform === 'linux') { | ||
console.info('hello linux'); | ||
} | ||
``` | ||
## Chromium Coverage API | ||
@@ -280,0 +302,0 @@ - [V8 coverage report](https://v8.dev/blog/javascript-code-coverage) - Native support for JavaScript code coverage to V8. (Chromium only) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
804718
32
6053
310