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 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-node-report.js](./test/generate-node-report.js)
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
+ - [![codecov](https://codecov.io/gh/cenfun/monocart-coverage-reports/graph/badge.svg?token=H0LW7UKYU3)](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
+ - [![Coverage Status](https://coveralls.io/repos/github/cenfun/monocart-coverage-reports/badge.svg?branch=main)](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
+ - [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=monocart-coverage-reports&metric=coverage)](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
- if (fs.existsSync(dir)) {
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')
@@ -43,7 +43,7 @@ const collectAstInfo = (ast, state) => {
43
43
  count: functionRoot.count,
44
44
  range: functionRoot
45
45
  };
46
- createBranches(ast, rootFunctionInfo, branchMap);
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, functionInfo, branchMap);
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 createBranches = (ast, functionInfo, branchMap) => {
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
- Util.visitAst(ast, {
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 using BinaryExpression
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](group, parentCount, noBlockList, blockCount);
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.startOffset) {
9
+ if (position < item[startKey]) {
10
10
  end = i;
11
11
  continue;
12
12
  }
13
- if (position > item.endOffset) {
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.startOffset) {
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.startOffset && end <= range.endOffset) {
32
+ const range = quickFindRange(start, ranges, startKey, endKey);
33
+ if (start >= range[startKey] && end <= range[endKey]) {
34
34
  return range;
35
35
  }
36
36
  };
@@ -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
- const endOffset = endLine ? endLine.end : locator.source.length;
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: item.end,
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 v8IgnoreKey = 'v8 ignore';
75
- if (!content.startsWith(v8IgnoreKey)) {
96
+ const ignoreKey = hasIgnoreKey(content);
97
+ if (!ignoreKey) {
76
98
  return;
77
99
  }
78
100
 
79
- content = content.slice(v8IgnoreKey.length).trim();
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 = start;
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: end,
96
- endOffset: end
116
+ type: 'start-stop',
117
+ startOffset: start,
118
+ endOffset: start
97
119
  };
98
120
  list.push(ignoreStart);
99
121
  return;
@@ -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 || 'pkg',
55
+ defaultSummarizer: options.defaultSummarizer || 'nested',
56
56
 
57
57
  dir: options.outputDir,
58
58
  watermarks,