monocart-coverage-reports 2.3.1 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -7
- package/lib/cli.js +2 -6
- package/lib/converter/ast.js +3 -3
- package/lib/converter/branches.js +25 -8
- package/lib/converter/collect-source-maps.js +6 -2
- package/lib/converter/find-in-ranges.js +7 -7
- package/lib/converter/ignore.js +32 -10
- package/lib/istanbul/istanbul.js +1 -1
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/packages/monocart-coverage-vendor.js +4 -4
- package/lib/platform/share.js +5 -2
- package/lib/utils/source-path.js +2 -2
- package/lib/utils/util.js +8 -2
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
* [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
|
|
24
24
|
* [Multiprocessing Support](#multiprocessing-support)
|
|
25
25
|
* [Merge Coverage Reports](#merge-coverage-reports)
|
|
26
|
-
* [Integration](#integration)
|
|
27
26
|
* [Ignoring Uncovered Codes](#ignoring-uncovered-codes)
|
|
28
27
|
* [Chromium Coverage API](#chromium-coverage-api)
|
|
29
28
|
* [V8 Coverage Data Format](#v8-coverage-data-format)
|
|
30
29
|
* [How to convert V8 to Istanbul](#how-to-convert-v8-to-istanbul)
|
|
31
30
|
- [Using `v8-to-istanbul`](#using-v8-to-istanbul)
|
|
32
31
|
- [How Monocart Works](#how-monocart-works)
|
|
32
|
+
* [Integration](#integration) - Playwright, Jest, Vitest, Codecov, Coveralls, Sonar Cloud
|
|
33
33
|
* [Istanbul Introduction](#istanbul-introduction)
|
|
34
34
|
* [Thanks](#thanks)
|
|
35
35
|
|
|
@@ -342,7 +342,7 @@ Possible solutions:
|
|
|
342
342
|
- Sets Node.js env `NODE_V8_COVERAGE`=`dir` before the program running, the coverage data will be saved to the `dir` after the program exits gracefully.
|
|
343
343
|
- Read the JSON file(s) from the `dir` and generate coverage report.
|
|
344
344
|
- Example:
|
|
345
|
-
> cross-env NODE_V8_COVERAGE=`.temp/v8-coverage-env` node [./test/test-node-env.js](./test/test-node-env.js) && node [./test/generate-
|
|
345
|
+
> cross-env NODE_V8_COVERAGE=`.temp/v8-coverage-env` node [./test/test-node-env.js](./test/test-node-env.js) && node [./test/generate-report.js](./test/generate-report.js)
|
|
346
346
|
|
|
347
347
|
- [V8](https://nodejs.org/docs/latest/api/v8.html#v8takecoverage) API + NODE_V8_COVERAGE
|
|
348
348
|
- Writing the coverage started by NODE_V8_COVERAGE to disk on demand with `v8.takeCoverage()`, it does not require waiting until the program exits gracefully.
|
|
@@ -460,11 +460,6 @@ const coverageOptions = {
|
|
|
460
460
|
```
|
|
461
461
|
see example: [./test/test-merge.js](./test/test-merge.js)
|
|
462
462
|
|
|
463
|
-
## Integration
|
|
464
|
-
- [monocart-reporter](https://cenfun.github.io/monocart-reporter/) - Test reporter for [Playwright](https://github.com/microsoft/playwright)
|
|
465
|
-
- [jest-monocart-coverage](https://github.com/cenfun/jest-monocart-coverage) - Integration with [Jest](https://github.com/jestjs/jest/) for coverage reports
|
|
466
|
-
- [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - Integration with [Vitest](https://github.com/vitest-dev/vitest) for coverage reports
|
|
467
|
-
|
|
468
463
|
## Ignoring Uncovered Codes
|
|
469
464
|
To ignore codes, use the special comment which starts with `v8 ignore `:
|
|
470
465
|
- Ignoring all until stop
|
|
@@ -582,6 +577,58 @@ We implemented new converter instead of v8-to-istanbul:
|
|
|
582
577
|
| LogicalExpression | ✔ Not Perfect |
|
|
583
578
|
| SwitchStatement | ✔ Not Perfect |
|
|
584
579
|
|
|
580
|
+
## Integration
|
|
581
|
+
- [monocart-reporter](https://github.com/cenfun/monocart-reporter) - A [Playwright](https://github.com/microsoft/playwright) custom reporter, supports generating [Code Coverage Report](https://github.com/cenfun/monocart-reporter?#code-coverage-report)
|
|
582
|
+
- [jest-monocart-coverage](https://github.com/cenfun/jest-monocart-coverage) - A [Jest](https://github.com/jestjs/jest/) custom reporter for coverage reports
|
|
583
|
+
- [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - A [Vitest](https://github.com/vitest-dev/vitest) custom provider module for coverage reports
|
|
584
|
+
- [Codecov](https://codecov.com/)
|
|
585
|
+
- [](https://codecov.io/gh/cenfun/monocart-coverage-reports)
|
|
586
|
+
- Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
|
|
587
|
+
```js
|
|
588
|
+
const coverageOptions = {
|
|
589
|
+
outputDir: "./coverage-reports",
|
|
590
|
+
reports: [
|
|
591
|
+
['codecov']
|
|
592
|
+
]
|
|
593
|
+
};
|
|
594
|
+
```
|
|
595
|
+
- Github Actions example:
|
|
596
|
+
```yml
|
|
597
|
+
- name: Codecov
|
|
598
|
+
uses: codecov/codecov-action@v3
|
|
599
|
+
with:
|
|
600
|
+
files: ./coverage-reports/codecov.json
|
|
601
|
+
```
|
|
602
|
+
- [Coveralls](https://coveralls.io/)
|
|
603
|
+
- [](https://coveralls.io/github/cenfun/monocart-coverage-reports?branch=main)
|
|
604
|
+
- Github Actions example:
|
|
605
|
+
```yml
|
|
606
|
+
- name: Coveralls
|
|
607
|
+
uses: coverallsapp/github-action@v2
|
|
608
|
+
with:
|
|
609
|
+
files: ./coverage-reports/lcov.info
|
|
610
|
+
```
|
|
611
|
+
- [Sonar Cloud](https://sonarcloud.io/)
|
|
612
|
+
- [](https://sonarcloud.io/summary/new_code?id=monocart-coverage-reports)
|
|
613
|
+
- Github Actions example:
|
|
614
|
+
```yml
|
|
615
|
+
- name: Analyze with SonarCloud
|
|
616
|
+
uses: sonarsource/sonarcloud-github-action@master
|
|
617
|
+
env:
|
|
618
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
619
|
+
with:
|
|
620
|
+
projectBaseDir: ./
|
|
621
|
+
args: >
|
|
622
|
+
-Dsonar.organization=cenfun
|
|
623
|
+
-Dsonar.projectKey=monocart-coverage-reports
|
|
624
|
+
-Dsonar.projectName=monocart-coverage-reports
|
|
625
|
+
-Dsonar.javascript.lcov.reportPaths=docs/mcr/lcov.info
|
|
626
|
+
-Dsonar.sources=lib
|
|
627
|
+
-Dsonar.tests=test
|
|
628
|
+
-Dsonar.exclusions=dist/*,packages/*
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
|
|
585
632
|
## Istanbul Introduction
|
|
586
633
|
- [Istanbul coverage report](https://istanbul.js.org/) - Instrumenting source codes and generating coverage reports
|
|
587
634
|
- [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul)
|
package/lib/cli.js
CHANGED
|
@@ -16,12 +16,7 @@ const defaultOptions = require('./default/options.js');
|
|
|
16
16
|
const version = require('../package.json').version;
|
|
17
17
|
|
|
18
18
|
const cleanDir = (dir) => {
|
|
19
|
-
|
|
20
|
-
fs.rmSync(dir, {
|
|
21
|
-
recursive: true,
|
|
22
|
-
force: true
|
|
23
|
-
});
|
|
24
|
-
}
|
|
19
|
+
Util.rmSync(dir);
|
|
25
20
|
};
|
|
26
21
|
|
|
27
22
|
const initOptions = (cliOptions) => {
|
|
@@ -127,6 +122,7 @@ program
|
|
|
127
122
|
.option('-o, --outputDir <dir>', 'output dir for reports')
|
|
128
123
|
.option('-r, --reports <name[,name]>', 'coverage reports to use')
|
|
129
124
|
.option('-n, --name <name>', 'report name for title')
|
|
125
|
+
.option('-i, --inputDir <dir>', 'input dir for merging raw files')
|
|
130
126
|
|
|
131
127
|
.option('--outputFile <path>', 'output file for v8 report')
|
|
132
128
|
.option('--inline', 'inline html for v8 report')
|
package/lib/converter/ast.js
CHANGED
|
@@ -43,7 +43,7 @@ const collectAstInfo = (ast, state) => {
|
|
|
43
43
|
count: functionRoot.count,
|
|
44
44
|
range: functionRoot
|
|
45
45
|
};
|
|
46
|
-
createBranches(
|
|
46
|
+
createBranches(rootFunctionInfo, branchMap, ast);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// functions branches
|
|
@@ -63,7 +63,7 @@ const collectAstInfo = (ast, state) => {
|
|
|
63
63
|
},
|
|
64
64
|
|
|
65
65
|
// Function include FunctionDeclaration, ArrowFunctionExpression, FunctionExpression
|
|
66
|
-
Function(node) {
|
|
66
|
+
Function(node, parents) {
|
|
67
67
|
|
|
68
68
|
const {
|
|
69
69
|
start, end, id
|
|
@@ -94,7 +94,7 @@ const collectAstInfo = (ast, state) => {
|
|
|
94
94
|
|
|
95
95
|
functions.push(functionInfo);
|
|
96
96
|
|
|
97
|
-
createBranches(node.body,
|
|
97
|
+
createBranches(functionInfo, branchMap, node.body, node.params, parents);
|
|
98
98
|
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -146,12 +146,30 @@ const addNodeCount = (node, parents, functionInfo) => {
|
|
|
146
146
|
|
|
147
147
|
// =======================================================================================
|
|
148
148
|
|
|
149
|
-
const
|
|
149
|
+
const addAssignmentPattern = (params, parents, functionInfo, branchMap, locationMap) => {
|
|
150
|
+
if (!Util.isList(params)) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
params.forEach((param) => {
|
|
155
|
+
if (param.type !== 'AssignmentPattern') {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const start = param.start;
|
|
159
|
+
const group = createBranchGroup('AssignmentPattern', start, parents, branchMap, functionInfo);
|
|
160
|
+
addBranch(group, param, locationMap, functionInfo);
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const createBranches = (functionInfo, branchMap, body, params, functionParents = []) => {
|
|
150
165
|
|
|
151
166
|
// locationMap for chain LogicalExpression locations
|
|
152
167
|
const locationMap = new Map();
|
|
153
168
|
|
|
154
|
-
|
|
169
|
+
// function's arguments
|
|
170
|
+
addAssignmentPattern(params, functionParents, functionInfo, branchMap, locationMap);
|
|
171
|
+
|
|
172
|
+
Util.visitAst(body, {
|
|
155
173
|
|
|
156
174
|
Function() {
|
|
157
175
|
return 'break';
|
|
@@ -195,7 +213,7 @@ const createBranches = (ast, functionInfo, branchMap) => {
|
|
|
195
213
|
|
|
196
214
|
// binary-expr
|
|
197
215
|
// var b = a || b || c;
|
|
198
|
-
// do not
|
|
216
|
+
// do not use BinaryExpression
|
|
199
217
|
LogicalExpression: (node, parents) => {
|
|
200
218
|
addNodeCount(node, parents, functionInfo);
|
|
201
219
|
|
|
@@ -245,10 +263,6 @@ const createBranches = (ast, functionInfo, branchMap) => {
|
|
|
245
263
|
});
|
|
246
264
|
}
|
|
247
265
|
|
|
248
|
-
// default-arg - seems that V8 coverage do NOT supported
|
|
249
|
-
// (a = 0) => {}
|
|
250
|
-
// AssignmentPattern: (node) => {}
|
|
251
|
-
|
|
252
266
|
});
|
|
253
267
|
|
|
254
268
|
};
|
|
@@ -367,7 +381,10 @@ const updateBranchCount = (group) => {
|
|
|
367
381
|
SwitchStatement
|
|
368
382
|
};
|
|
369
383
|
|
|
370
|
-
handlers[type]
|
|
384
|
+
const handler = handlers[type];
|
|
385
|
+
if (handler) {
|
|
386
|
+
handler(group, parentCount, noBlockList, blockCount);
|
|
387
|
+
}
|
|
371
388
|
|
|
372
389
|
};
|
|
373
390
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const EC = require('eight-colors');
|
|
3
|
-
const { fileURLToPath } = require('url');
|
|
3
|
+
const { fileURLToPath, pathToFileURL } = require('url');
|
|
4
4
|
const Concurrency = require('../platform/concurrency.js');
|
|
5
5
|
const { convertSourceMap, axios } = require('../packages/monocart-coverage-vendor.js');
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ const request = async (options) => {
|
|
|
21
21
|
|
|
22
22
|
const loadSourceMap = async (url = '') => {
|
|
23
23
|
|
|
24
|
-
if (url.startsWith('file
|
|
24
|
+
if (url.startsWith('file:')) {
|
|
25
25
|
const p = fileURLToPath(url);
|
|
26
26
|
const content = Util.readFileSync(p);
|
|
27
27
|
if (!content) {
|
|
@@ -55,6 +55,10 @@ const getSourceMapUrl = (content, url) => {
|
|
|
55
55
|
// for some odd reason //# .. captures in 1 and /* .. */ in 2
|
|
56
56
|
const filename = r[1] || r[2];
|
|
57
57
|
|
|
58
|
+
if (!URL.canParse(filename, url)) {
|
|
59
|
+
url = pathToFileURL(url).toString();
|
|
60
|
+
}
|
|
61
|
+
|
|
58
62
|
let mapUrl;
|
|
59
63
|
|
|
60
64
|
try {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
const Util = require('../utils/util.js');
|
|
2
2
|
|
|
3
|
-
const quickFindRange = (position, ranges) => {
|
|
3
|
+
const quickFindRange = (position, ranges, startKey, endKey) => {
|
|
4
4
|
let start = 0;
|
|
5
5
|
let end = ranges.length - 1;
|
|
6
6
|
while (end - start > 1) {
|
|
7
7
|
const i = Math.floor((start + end) * 0.5);
|
|
8
8
|
const item = ranges[i];
|
|
9
|
-
if (position < item
|
|
9
|
+
if (position < item[startKey]) {
|
|
10
10
|
end = i;
|
|
11
11
|
continue;
|
|
12
12
|
}
|
|
13
|
-
if (position > item
|
|
13
|
+
if (position > item[endKey]) {
|
|
14
14
|
start = i;
|
|
15
15
|
continue;
|
|
16
16
|
}
|
|
@@ -18,19 +18,19 @@ const quickFindRange = (position, ranges) => {
|
|
|
18
18
|
}
|
|
19
19
|
// last two items, less is start
|
|
20
20
|
const endItem = ranges[end];
|
|
21
|
-
if (position < endItem
|
|
21
|
+
if (position < endItem[startKey]) {
|
|
22
22
|
return ranges[start];
|
|
23
23
|
}
|
|
24
24
|
return ranges[end];
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
const findInRanges = (start, end, ranges) => {
|
|
28
|
+
const findInRanges = (start, end, ranges, startKey = 'startOffset', endKey = 'endOffset') => {
|
|
29
29
|
if (!Util.isList(ranges)) {
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
const range = quickFindRange(start, ranges);
|
|
33
|
-
if (start >= range
|
|
32
|
+
const range = quickFindRange(start, ranges, startKey, endKey);
|
|
33
|
+
if (start >= range[startKey] && end <= range[endKey]) {
|
|
34
34
|
return range;
|
|
35
35
|
}
|
|
36
36
|
};
|
package/lib/converter/ignore.js
CHANGED
|
@@ -26,7 +26,13 @@ const getNextN = (content, currentLine, item, locator) => {
|
|
|
26
26
|
|
|
27
27
|
const addNextIgnore = (list, content, item, locator) => {
|
|
28
28
|
|
|
29
|
-
const { lineParser } = locator;
|
|
29
|
+
const { lineParser, source } = locator;
|
|
30
|
+
const maxLength = source.length;
|
|
31
|
+
let startOffset = item.start;
|
|
32
|
+
// extend left
|
|
33
|
+
while (startOffset > 0 && Util.isBlank(source[startOffset - 1])) {
|
|
34
|
+
startOffset -= 1;
|
|
35
|
+
}
|
|
30
36
|
|
|
31
37
|
// findLine 0-base
|
|
32
38
|
const currentLine = lineParser.findLine(item.end);
|
|
@@ -37,18 +43,34 @@ const addNextIgnore = (list, content, item, locator) => {
|
|
|
37
43
|
// getLine 1-base
|
|
38
44
|
const line = currentLine.line + 1;
|
|
39
45
|
const endLine = locator.getLine(line + n);
|
|
40
|
-
|
|
46
|
+
let endOffset = endLine ? endLine.end : maxLength;
|
|
47
|
+
// extend right
|
|
48
|
+
while (endOffset < maxLength && Util.isBlank(source[endOffset + 1])) {
|
|
49
|
+
endOffset += 1;
|
|
50
|
+
}
|
|
41
51
|
|
|
42
52
|
const nextItem = {
|
|
43
53
|
type: 'next',
|
|
44
54
|
n,
|
|
45
|
-
startOffset
|
|
55
|
+
startOffset,
|
|
46
56
|
endOffset
|
|
47
57
|
};
|
|
48
58
|
|
|
49
59
|
list.push(nextItem);
|
|
50
60
|
};
|
|
51
61
|
|
|
62
|
+
// both v8 or c8
|
|
63
|
+
const hasIgnoreKey = (content) => {
|
|
64
|
+
const v8Key = 'v8 ignore';
|
|
65
|
+
if (content.startsWith(v8Key)) {
|
|
66
|
+
return v8Key.length;
|
|
67
|
+
}
|
|
68
|
+
const c8Key = 'c8 ignore';
|
|
69
|
+
if (content.startsWith(c8Key)) {
|
|
70
|
+
return c8Key.length;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
52
74
|
const getIgnoredRanges = (locator, options) => {
|
|
53
75
|
if (!options.v8Ignore) {
|
|
54
76
|
return;
|
|
@@ -71,17 +93,17 @@ const getIgnoredRanges = (locator, options) => {
|
|
|
71
93
|
let content = block ? text.slice(2, -2) : text.slice(2);
|
|
72
94
|
content = content.trim();
|
|
73
95
|
|
|
74
|
-
const
|
|
75
|
-
if (!
|
|
96
|
+
const ignoreKey = hasIgnoreKey(content);
|
|
97
|
+
if (!ignoreKey) {
|
|
76
98
|
return;
|
|
77
99
|
}
|
|
78
100
|
|
|
79
|
-
content = content.slice(
|
|
101
|
+
content = content.slice(ignoreKey).trim();
|
|
80
102
|
|
|
81
103
|
if (ignoreStart) {
|
|
82
104
|
// v8 ignore stop
|
|
83
105
|
if (content === 'stop') {
|
|
84
|
-
ignoreStart.endOffset =
|
|
106
|
+
ignoreStart.endOffset = end;
|
|
85
107
|
ignoreStart = null;
|
|
86
108
|
}
|
|
87
109
|
return;
|
|
@@ -91,9 +113,9 @@ const getIgnoredRanges = (locator, options) => {
|
|
|
91
113
|
if (content === 'start') {
|
|
92
114
|
// add first for sort by start
|
|
93
115
|
ignoreStart = {
|
|
94
|
-
type: 'start',
|
|
95
|
-
startOffset:
|
|
96
|
-
endOffset:
|
|
116
|
+
type: 'start-stop',
|
|
117
|
+
startOffset: start,
|
|
118
|
+
endOffset: start
|
|
97
119
|
};
|
|
98
120
|
list.push(ignoreStart);
|
|
99
121
|
return;
|
package/lib/istanbul/istanbul.js
CHANGED
|
@@ -52,7 +52,7 @@ const saveIstanbulReports = (coverageData, fileSources, options) => {
|
|
|
52
52
|
coverageMap,
|
|
53
53
|
// The summarizer to default to (may be overridden by some reports)
|
|
54
54
|
// values can be nested/flat/pkg. Defaults to 'pkg'
|
|
55
|
-
defaultSummarizer: options.defaultSummarizer || '
|
|
55
|
+
defaultSummarizer: options.defaultSummarizer || 'nested',
|
|
56
56
|
|
|
57
57
|
dir: options.outputDir,
|
|
58
58
|
watermarks,
|