monocart-coverage-reports 2.2.0 → 2.2.2

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
@@ -11,7 +11,7 @@
11
11
  * [Usage](#usage)
12
12
  * [Default Options](#default-options)
13
13
  * [Available Reports](#available-reports)
14
- * [Multiprocessing Support](#multiprocessing-support)
14
+ * [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
15
15
  * [onEnd Hook](#onend-hook)
16
16
  * [`mcr` CLI](#mcr-cli)
17
17
  * [Compare Reports](#compare-reports)
@@ -19,13 +19,14 @@
19
19
  * [Collecting Istanbul Coverage Data](#collecting-istanbul-coverage-data)
20
20
  * [Collecting V8 Coverage Data](#collecting-v8-coverage-data)
21
21
  * [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
22
- * [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
22
+ * [Multiprocessing Support](#multiprocessing-support)
23
+ * [Integration](#integration)
24
+ * [Ignoring Uncovered Codes](#ignoring-uncovered-codes)
25
+ * [Chromium Coverage API](#chromium-coverage-api)
26
+ * [V8 Coverage Data Format](#v8-coverage-data-format)
23
27
  * [How to convert V8 to Istanbul](#how-to-convert-v8-to-istanbul)
24
28
  - [Using `v8-to-istanbul`](#using-v8-to-istanbul)
25
29
  - [How Monocart Works](#how-monocart-works)
26
- * [Ignoring Uncovered Lines](#ignoring-uncovered-lines)
27
- * [Chromium Coverage API](#chromium-coverage-api)
28
- * [V8 Coverage Data Format](#v8-coverage-data-format)
29
30
  * [Istanbul Introduction](#istanbul-introduction)
30
31
  * [Thanks](#thanks)
31
32
 
@@ -54,6 +55,7 @@ console.log(coverageResults.summary);
54
55
  - Build with webpack: [V8](https://cenfun.github.io/monocart-coverage-reports/v8) and [V8 Minify](https://cenfun.github.io/monocart-coverage-reports/v8-minify)
55
56
  - Build with [Rollup](https://cenfun.github.io/monocart-coverage-reports/v8-rollup) and [Esbuild](https://cenfun.github.io/monocart-coverage-reports/v8-esbuild)
56
57
  - Collect with [puppeteer](https://cenfun.github.io/monocart-coverage-reports/puppeteer/)
58
+ - [anonymous](https://cenfun.github.io/monocart-coverage-reports/anonymous/) and [css](https://cenfun.github.io/monocart-coverage-reports/css/)
57
59
  - Test in Node.js
58
60
  - Collect with [env](https://cenfun.github.io/monocart-coverage-reports/v8-node-env), and also V8 [API](https://cenfun.github.io/monocart-coverage-reports/v8-node-api), [Inspector](https://cenfun.github.io/monocart-coverage-reports/v8-node-ins) and [CDP](https://cenfun.github.io/monocart-coverage-reports/v8-node-cdp)
59
61
  - Web server example: [koa](https://cenfun.github.io/monocart-coverage-reports/v8-node-koa/)
@@ -62,6 +64,8 @@ console.log(coverageResults.summary);
62
64
 
63
65
  - `v8-json` (V8 data only)
64
66
  - [V8 coverage-report.json](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json)
67
+ - `codecov` (V8 data only)
68
+ - coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
65
69
  - `console-summary` shows coverage summary in console
66
70
 
67
71
  ![](test/console-summary.png)
@@ -105,37 +109,34 @@ const options = {
105
109
  const coverageReport = MCR(options);
106
110
  ```
107
111
 
108
- ## Integration
109
- - [monocart-reporter](https://cenfun.github.io/monocart-reporter/) - Test reporter for [Playwright](https://github.com/microsoft/playwright)
110
- - [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - Integration with [Vitest](https://github.com/vitest-dev/vitest) coverage
111
-
112
-
113
- ## Multiprocessing Support
114
- The data will be added to `[outputDir]/.cache`, and the cache will be removed after reports generated.
115
- - sub process 1
116
- ```js
117
- const MCR = require('monocart-coverage-reports');
118
- const options = require('path-to/same-options.js');
119
- const coverageReport = MCR(options);
120
- await coverageReport.add(coverageData1);
112
+ ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
113
+ When V8 coverage data collected, it actually contains the data of all entry files, for example:
121
114
  ```
122
-
123
- - sub process 2
124
- ```js
125
- const MCR = require('monocart-coverage-reports');
126
- const options = require('path-to/same-options.js');
127
- const coverageReport = MCR(options);
128
- await coverageReport.add(coverageData2);
115
+ dist/main.js
116
+ dist/vendor.js
117
+ dist/something-else.js
129
118
  ```
130
-
131
- - main process
119
+ We can use `entryFilter` to filter the entry files. For example, we should remove `vendor.js` and `something-else.js` if they are not in our coverage scope.
120
+ ```
121
+ dist/main.js
122
+ ```
123
+ When inline or linked sourcemap exists to the entry file, the source files will be extracted from the sourcemap for the entry file, and the entry file will be removed if `logging` is not `debug`.
124
+ ```
125
+ > src/index.js
126
+ > src/components/app.js
127
+ > node_modules/dependency/dist/dependency.js
128
+ ```
129
+ We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
130
+ ```
131
+ > src/index.js
132
+ > src/components/app.js
133
+ ```
134
+ Example:
132
135
  ```js
133
- // after all sub processes finished
134
- const MCR = require('monocart-coverage-reports');
135
- const options = require('path-to/same-options.js');
136
- const coverageReport = MCR(options);
137
- const coverageResults = await coverageReport.generate();
138
- console.log(coverageResults.summary);
136
+ const coverageOptions = {
137
+ entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
138
+ sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
139
+ };
139
140
  ```
140
141
 
141
142
  ## onEnd Hook
@@ -170,6 +171,7 @@ const coverageOptions = {
170
171
  ```
171
172
 
172
173
  ## `mcr` CLI
174
+ > The CLI will run the program as a [child process](https://nodejs.org/docs/latest/api/child_process.html) with `NODE_V8_COVERAGE=dir` until it exits gracefully, and generate the coverage report with the coverage data from the `dir`.
173
175
  - Global mode
174
176
  ```sh
175
177
  npm i monocart-coverage-reports -g
@@ -201,7 +203,7 @@ Options:
201
203
  --lcov generate lcov.info file
202
204
  -h, --help display help for command
203
205
  ```
204
- - Using configuration file for more options
206
+ - Using configuration file for more [options](#default-options)
205
207
  ```sh
206
208
  mcr "node ./test/test-node-env.js" -c test/cli-options.js
207
209
  ```
@@ -234,139 +236,103 @@ mcr "node ./test/test-node-env.js" -c test/cli-options.js
234
236
  ## Collecting Istanbul Coverage Data
235
237
  - Instrumenting source code
236
238
  > Before collecting Istanbul coverage data, It requires your source code is instrumented with Istanbul
237
- - Webpack: [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul), example: [webpack.config-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-istanbul.js)
238
- - Rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
239
- - Vite: [vite-plugin-istanbul](https://github.com/ifaxity/vite-plugin-istanbul)
239
+ - webpack: [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul), example: [webpack.config-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-istanbul.js)
240
+ - rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
241
+ - vite: [vite-plugin-istanbul](https://github.com/ifaxity/vite-plugin-istanbul)
240
242
  - Browser
241
- > Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
243
+ - Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
242
244
  - Node.js
243
- > Collecting coverage data from `global.__coverage__`
245
+ - Collecting coverage data from `global.__coverage__`
244
246
 
245
247
  ## Collecting V8 Coverage Data
246
248
  - For source code: enable `sourcemap` and do not compress/minify:
247
- - 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)
248
- - Rollup: build with [options](https://rollupjs.org/configuration-options/) `sourcemap: true`
249
- - Vite: build with [options](https://vitejs.dev/config/build-options.html) `sourcemap: true` and `minify: false`
249
+ - [webpack](https://webpack.js.org/configuration/): `devtool: source-map` and `mode: development`, example [webpack.config-v8.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-v8.js)
250
+ - [rollup](https://rollupjs.org/configuration-options/): `sourcemap: true`
251
+ - [vite](https://vitejs.dev/config/build-options.html): `sourcemap: true` and `minify: false`
252
+ - [esbuild](https://esbuild.github.io/api/): `sourcemap: true` and `minify: false`
250
253
  - Browser (Chromium Only)
251
- > Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api), see [playwright example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js) or [puppeteer example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-puppeteer.js)
254
+ > Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api):
255
+ - [Playwright example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js), and [anonymous](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-anonymous.js), [css](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-css.js)
256
+ - [Puppeteer example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-puppeteer.js)
252
257
  - Node.js
253
- > see [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
258
+ - see following [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
254
259
 
255
260
 
256
261
  ## Node.js V8 Coverage Report for Server Side
257
262
  Possible solutions:
258
- - Node.js env [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
259
- - Before running your Node.js application, set env `NODE_V8_COVERAGE`=`dir`. After the application runs and exits, the coverage data will be saved to the `dir` directory in JSON file format
260
- - Read the json file(s) from the `dir` and generate coverage report
261
- - example:
263
+ - [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
264
+ - 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.
265
+ - Read the JSON file(s) from the `dir` and generate coverage report.
266
+ - Example:
262
267
  > 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)
263
268
 
264
269
  - [V8](https://nodejs.org/docs/latest/api/v8.html#v8takecoverage) API + NODE_V8_COVERAGE
265
- - Writing the coverage started by NODE_V8_COVERAGE to disk on demand with `v8.takeCoverage()` and stopping with `v8.stopCoverage()`.
266
- - example:
270
+ - 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.
271
+ - Example:
267
272
  > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage-api` node [./test/test-node-api.js](./test/test-node-api.js)
268
273
 
269
- - [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API (or module [collect-v8-coverage](https://github.com/SimenB/collect-v8-coverage))
274
+ - [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API
270
275
  - Connecting to the V8 inspector and enable V8 coverage.
271
- - Taking coverage data and adding to the report after your application runs.
272
- - example: [./test/test-node-ins.js](./test/test-node-ins.js)
276
+ - Taking coverage data and adding it to the report.
277
+ - Example:
278
+ > node [./test/test-node-ins.js](./test/test-node-ins.js)
273
279
 
274
280
  - [CDP](https://chromedevtools.github.io/devtools-protocol/) API
275
- - Enabling [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started/)
281
+ - Enabling [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started/).
276
282
  - Collecting coverage data with CDP API.
277
- - example: [./test/test-node-cdp.js](./test/test-node-cdp.js)
283
+ - Example:
284
+ > node --inspect=9229 [./test/test-node-cdp.js](./test/test-node-cdp.js)
278
285
 
279
- - [Debugger](https://nodejs.org/en/guides/debugging-getting-started) + NODE_V8_COVERAGE + CDP + V8 API
280
- > cross-env NODE_V8_COVERAGE=.temp/v8-coverage-koa node --inspect=9229 [./test/koa.js](./test/koa.js)
281
- - example:
286
+ - [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started) + CDP + NODE_V8_COVERAGE + V8 API
287
+ - When the program starts a server, it will not exit on its own, thus requiring a manual invocation of the `v8.takeCoverage()` interface to manually collect coverage data. Remote invocation of the `v8.takeCoverage()` interface can be accomplished through the `Runtime.evaluate` of the CDP.
288
+ - Example for [koa](https://github.com/koajs/koa) web server:
282
289
  > node [./test/test-node-koa.js](./test/test-node-koa.js)
283
290
 
284
- ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
285
- When V8 coverage data collected, it actually contains the data of all entry files, for example:
286
- ```
287
- dist/main.js
288
- dist/vendor.js
289
- dist/something-else.js
290
- ```
291
- We can use `entryFilter` to filter the entry files. For example, we should remove `vendor.js` and `something-else.js` if they are not in our coverage scope.
292
- ```
293
- dist/main.js
294
- ```
295
- When inline or linked sourcemap exists to the entry file, the source files will be extracted from the sourcemap for the entry file, and the entry file will be removed if `logging` is not `debug`.
296
- ```
297
- > src/index.js
298
- > src/components/app.js
299
- > node_modules/dependency/dist/dependency.js
300
- ```
301
- We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
302
- ```
303
- > src/index.js
304
- > src/components/app.js
305
- ```
306
- Example:
291
+ - [Child Process](https://nodejs.org/docs/latest/api/child_process.html) + NODE_V8_COVERAGE
292
+ - see [`mcr` CLI](#mcr-cli)
293
+
294
+ ## Multiprocessing Support
295
+ The data will be added to `[outputDir]/.cache`, and the cache will be removed after reports generated.
296
+ - sub process 1
307
297
  ```js
308
- const coverageOptions = {
309
- entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
310
- sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
311
- };
298
+ const MCR = require('monocart-coverage-reports');
299
+ const options = require('path-to/same-options.js');
300
+ const coverageReport = MCR(options);
301
+ await coverageReport.add(coverageData1);
312
302
  ```
313
303
 
314
- ## How to convert V8 to Istanbul
315
- ### Using [v8-to-istanbul](https://github.com/istanbuljs/v8-to-istanbul)
316
- It is a popular library which is used to convert V8 coverage format to istanbul's coverage format. Most test frameworks are using it, such as [Jest](https://github.com/jestjs/jest/), [Vitest](https://github.com/vitest-dev/vitest), but it has two major problems:
317
- - 1, The source mapping does not work well if the position is between the two consecutive mappings. for example:
304
+ - sub process 2
318
305
  ```js
319
- const a = tf ? 'true' : 'false';
320
- ^ ^ ^
321
- m1 p m2
306
+ const MCR = require('monocart-coverage-reports');
307
+ const options = require('path-to/same-options.js');
308
+ const coverageReport = MCR(options);
309
+ await coverageReport.add(coverageData2);
322
310
  ```
323
- > `m1` and `m2` are two consecutive mappings, `p` is the position we looking for. However, we can only get the position of the `m1` if we don't fix it to `p`. Especially the generated code is different from the original code, such as minified, compressed or converted, then it becomes very difficult to find the middle position between two mappings.
324
311
 
325
- - 2, The coverage of functions and branches is incorrect. V8 only provided coverage at functions and it's blocks. But if a function is uncovered (count = 0), there is no information for it's blocks and sub-level functions.
326
- And also there are some problems about counting the functions and branches:
312
+ - main process
327
313
  ```js
328
- functions.forEach(block => {
329
- block.ranges.forEach((range, i) => {
330
- if (block.isBlockCoverage) {
331
- // v8-to-istanbul: new CovBranch()
332
- // Problem: not every block is branch, and the first block is actually function.
333
- if (block.functionName && i === 0) {
334
- // v8-to-istanbul: new CovFunction()
335
- // Problem: no anonymous function
336
- }
337
- } else if (block.functionName) {
338
- // v8-to-istanbul: new CovFunction()
339
- // Problem: no anonymous function
340
- }
341
- }
342
- });
343
- // Problem: When the function or parent-level function is uncovered, then its sub-level functions will never be counted.
314
+ // after all sub processes finished
315
+ const MCR = require('monocart-coverage-reports');
316
+ const options = require('path-to/same-options.js');
317
+ const coverageReport = MCR(options);
318
+ const coverageResults = await coverageReport.generate();
319
+ console.log(coverageResults.summary);
344
320
  ```
345
- see source code [v8-to-istanbul.js](https://github.com/istanbuljs/v8-to-istanbul/blob/master/lib/v8-to-istanbul.js)
346
-
347
- ### How Monocart Works
348
- We have removed `v8-to-istanbul` because of the two major problems and implemented new converter:
349
- - 1, Trying to fix the middle position if not found the exact mapping.
350
- - 2, Finding all functions and branches by parsing the source code [AST](https://github.com/acornjs/acorn), however the V8 cannot provide effective branch coverage information, so the branches is still not perfect.
351
321
 
352
- | AST | V8 |
353
- | :--------------| :------ |
354
- | AssignmentPattern | 🛇 Not Support |
355
- | ConditionalExpression | ✔ Not Perfect |
356
- | IfStatement | ✔ Not Perfect |
357
- | LogicalExpression | ✔ Not Perfect |
358
- | SwitchStatement | ✔ Not Perfect |
322
+ ## Integration
323
+ - [monocart-reporter](https://cenfun.github.io/monocart-reporter/) - Test reporter for [Playwright](https://github.com/microsoft/playwright)
324
+ - [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - Integration with [Vitest](https://github.com/vitest-dev/vitest) coverage
359
325
 
360
326
  ## Ignoring Uncovered Codes
361
327
  To ignore codes, use the special comment which starts with `v8 ignore `:
362
- - ignoring all until told
328
+ - Ignoring all until stop
363
329
  ```js
364
330
  /* v8 ignore start */
365
331
  function uncovered() {
366
332
  }
367
333
  /* v8 ignore stop */
368
334
  ```
369
- - ignoring the next line or next N lines
335
+ - Ignoring the next line or next N lines
370
336
  ```js
371
337
  /* v8 ignore next */
372
338
  const os = platform === 'wind32' ? 'Windows' : 'Other';
@@ -448,6 +414,52 @@ const rawV8CoverageData = jsCoverage.map((it) => {
448
414
  ```
449
415
  see example: [./test/test-puppeteer.js](./test/test-puppeteer.js)
450
416
 
417
+ ## How to convert V8 to Istanbul
418
+ ### Using [v8-to-istanbul](https://github.com/istanbuljs/v8-to-istanbul)
419
+ It is a popular library which is used to convert V8 coverage format to istanbul's coverage format. Most test frameworks are using it, such as [Jest](https://github.com/jestjs/jest/), [Vitest](https://github.com/vitest-dev/vitest), but it has two major problems:
420
+ - 1, The source mapping does not work well if the position is between the two consecutive mappings. for example:
421
+ ```js
422
+ const a = tf ? 'true' : 'false';
423
+ ^ ^ ^
424
+ m1 p m2
425
+ ```
426
+ > `m1` and `m2` are two consecutive mappings, `p` is the position we looking for. However, we can only get the position of the `m1` or `m2` if we don't fix it to `p`. Especially the generated code is different from the original code, such as the code was minified, compressed or converted, it is difficult to find the exact position.
427
+
428
+ - 2, The coverage of functions and branches is incorrect. V8 only provided coverage at functions and it's blocks. But if a function is uncovered (count = 0), there is no information for it's blocks and sub-level functions.
429
+ And also there are some problems about counting the functions and branches:
430
+ ```js
431
+ // Problem: When the function or parent-level function is uncovered, then its sub-level functions will never be counted.
432
+ functions.forEach(block => {
433
+ block.ranges.forEach((range, i) => {
434
+ if (block.isBlockCoverage) {
435
+ // v8-to-istanbul: new CovBranch()
436
+ // Problem: not every block is branch, and the first block could be function.
437
+ if (block.functionName && i === 0) {
438
+ // v8-to-istanbul: new CovFunction()
439
+ // Problem: no anonymous function
440
+ }
441
+ } else if (block.functionName) {
442
+ // v8-to-istanbul: new CovFunction()
443
+ // Problem: no anonymous function
444
+ }
445
+ }
446
+ });
447
+ ```
448
+ see source code [v8-to-istanbul.js](https://github.com/istanbuljs/v8-to-istanbul/blob/master/lib/v8-to-istanbul.js)
449
+
450
+ ### How Monocart Works
451
+ We implemented new converter instead of v8-to-istanbul:
452
+ - 1, Trying to fix the middle position if not found the exact mapping for the position.
453
+ - 2, Finding all functions and branches by parsing the source code [AST](https://github.com/acornjs/acorn), however the V8 cannot provide effective branch coverage information, so the branches is still not perfect but close to reality.
454
+
455
+ | AST | V8 |
456
+ | :---------------------| :------------- |
457
+ | AssignmentPattern | 🛇 Not Support |
458
+ | ConditionalExpression | ✔ Not Perfect |
459
+ | IfStatement | ✔ Not Perfect |
460
+ | LogicalExpression | ✔ Not Perfect |
461
+ | SwitchStatement | ✔ Not Perfect |
462
+
451
463
  ## Istanbul Introduction
452
464
  - [Istanbul coverage report](https://istanbul.js.org/) - Instrumenting source codes and generating coverage reports
453
465
  - [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul)
package/lib/cli.js CHANGED
@@ -38,7 +38,7 @@ const initOptions = (cliOptions) => {
38
38
 
39
39
  // init reports to list with `,`
40
40
  const reports = cliOptions.reports;
41
- if (reports) {
41
+ if (reports && typeof reports === 'string') {
42
42
  cliOptions.reports = reports.split(',');
43
43
  }
44
44
 
@@ -313,9 +313,21 @@ const collectFileCoverage = (item, state, coverageData, options) => {
313
313
  b: {}
314
314
  };
315
315
 
316
+ // for codecov https://docs.codecov.com/docs/codecov-custom-coverage-format
317
+ data.lines = {};
318
+
316
319
  lines.filter((it) => !it.ignored).forEach((line, index) => {
317
320
  istanbulCoverage.statementMap[`${index}`] = line.generate();
318
321
  istanbulCoverage.s[`${index}`] = line.count;
322
+
323
+ let count = 0;
324
+ if (line.covered) {
325
+ count = line.count;
326
+ } else if (!line.covered && line.count > 0) {
327
+ count = '1/2';
328
+ }
329
+ // 1-base
330
+ data.lines[`${line.line}`] = count;
319
331
  });
320
332
 
321
333
  functions.filter((it) => !it.ignored).forEach((fn, index) => {
@@ -987,6 +999,7 @@ const convertV8List = async (v8list, options) => {
987
999
  if (sourceList.length) {
988
1000
  sourceList.forEach((item) => {
989
1001
 
1002
+ // second time filter for empty source
990
1003
  // exists same id, mark previous item as dedupe
991
1004
  const prevItem = v8list.find((it) => it.id === item.id);
992
1005
  if (prevItem) {
package/lib/generate.js CHANGED
@@ -67,59 +67,50 @@ const getReportGroup = (reports, lcov, dataType) => {
67
67
  reportMap[defaultReport] = {};
68
68
  }
69
69
 
70
- const allSupportedReports = [
70
+ const allSupportedReports = {
71
71
 
72
72
  // v8
73
- 'v8',
74
- 'v8-json',
73
+ 'v8': 'v8',
74
+ 'v8-json': 'v8',
75
+ 'codecov': 'v8',
75
76
 
76
77
  // both
77
- 'console-summary',
78
+ 'console-summary': 'both',
78
79
 
79
80
  // istanbul
80
- 'clover',
81
- 'cobertura',
82
- 'html',
83
- 'html-spa',
84
- 'json',
85
- 'json-summary',
86
- 'lcov',
87
- 'lcovonly',
88
- 'none',
89
- 'teamcity',
90
- 'text',
91
- 'text-lcov',
92
- 'text-summary'
93
- ];
81
+ 'clover': 'istanbul',
82
+ 'cobertura': 'istanbul',
83
+ 'html': 'istanbul',
84
+ 'html-spa': 'istanbul',
85
+ 'json': 'istanbul',
86
+ 'json-summary': 'istanbul',
87
+ 'lcov': 'istanbul',
88
+ 'lcovonly': 'istanbul',
89
+ 'none': 'istanbul',
90
+ 'teamcity': 'istanbul',
91
+ 'text': 'istanbul',
92
+ 'text-lcov': 'istanbul',
93
+ 'text-summary': 'istanbul'
94
+ };
94
95
 
95
96
  // group v8 and istanbul
96
97
  const reportGroup = {};
97
98
  Object.keys(reportMap).forEach((k) => {
98
- if (!allSupportedReports.includes(k)) {
99
- return;
100
- }
101
99
 
102
- // v8
103
- if (k.startsWith('v8')) {
104
- // v8
105
- if (!reportGroup.v8) {
106
- reportGroup.v8 = {};
107
- }
108
- reportGroup.v8[k] = reportMap[k];
100
+ const groupName = allSupportedReports[k];
101
+ if (!groupName) {
102
+ Util.logError(`Unsupported report: ${k}`);
109
103
  return;
110
104
  }
111
105
 
112
- // both
113
- if (k === 'console-summary') {
114
- reportGroup[k] = reportMap[k];
115
- return;
106
+ let group = reportGroup[groupName];
107
+ if (!group) {
108
+ group = {};
109
+ reportGroup[groupName] = group;
116
110
  }
117
111
 
118
- // istanbul
119
- if (!reportGroup.istanbul) {
120
- reportGroup.istanbul = {};
121
- }
122
- reportGroup.istanbul[k] = reportMap[k];
112
+ group[k] = reportMap[k];
113
+
123
114
  });
124
115
 
125
116
  return reportGroup;
@@ -156,8 +147,12 @@ const nFormatter = (v) => {
156
147
 
157
148
  const showConsoleSummary = (coverageResults, options) => {
158
149
 
159
- const reportGroup = options.reportGroup;
160
- const consoleOptions = reportGroup['console-summary'];
150
+ const bothGroup = options.reportGroup.both;
151
+ if (!bothGroup) {
152
+ return;
153
+ }
154
+
155
+ const consoleOptions = bothGroup['console-summary'];
161
156
  if (!consoleOptions) {
162
157
  return;
163
158
  }
package/lib/index.d.ts CHANGED
@@ -34,6 +34,9 @@ declare namespace MCR {
34
34
  ['v8-json'] | ["v8-json", {
35
35
  outputFile?: string;
36
36
  }] |
37
+ ['codecov'] | ["codecov", {
38
+ outputFile?: string;
39
+ }] |
37
40
  ['console-summary'] | ['console-summary', {
38
41
  metrics?: Array<"bytes" | "functions" | "branches" | "lines" | "statements">;
39
42
  }] |
@@ -87,9 +87,9 @@ const saveIstanbulReports = (coverageData, fileSources, options) => {
87
87
  // create a context for report generation
88
88
  const context = istanbulLibReport.createContext(contextOptions);
89
89
 
90
- const istanbulMap = options.reportGroup.istanbul;
91
- Object.keys(istanbulMap).forEach((reportName) => {
92
- const report = istanbulReports.create(reportName, istanbulMap[reportName]);
90
+ const istanbulGroup = options.reportGroup.istanbul;
91
+ Object.keys(istanbulGroup).forEach((reportName) => {
92
+ const report = istanbulReports.create(reportName, istanbulGroup[reportName]);
93
93
  report.execute(context);
94
94
  });
95
95
 
@@ -1,5 +1,5 @@
1
1
  const path = require('path');
2
- const { fileURLToPath } = require('url');
2
+ const { fileURLToPath, pathToFileURL } = require('url');
3
3
  const Util = require('./util.js');
4
4
 
5
5
  const resolveUrl = (input) => {
@@ -36,7 +36,7 @@ const getSourcePath = (url, index = '', type = '') => {
36
36
 
37
37
  if (!url) {
38
38
  // anonymous scripts will have __playwright_evaluation_script__ as their URL.
39
- url = ['file://anonymous', index ? `-${index}` : '', type ? `.${type}` : ''].join('');
39
+ url = pathToFileURL(['anonymous', index ? `-${index}` : '', type ? `.${type}` : ''].join('')).toString();
40
40
  }
41
41
 
42
42
  if (url.startsWith('file:')) {
package/lib/v8/v8.js CHANGED
@@ -21,9 +21,18 @@ const initV8ListAndSourcemap = async (v8list, options) => {
21
21
 
22
22
  v8list = v8list.filter((item) => {
23
23
  if (typeof item.source === 'string' && item.functions) {
24
+ item.type = 'js';
24
25
  return true;
25
26
  }
26
27
  if (typeof item.text === 'string' && item.ranges) {
28
+ item.type = 'css';
29
+ return true;
30
+ }
31
+ // allow empty coverage
32
+ if (item.empty) {
33
+ if (!['js', 'css'].includes(item.type)) {
34
+ item.type = 'js';
35
+ }
27
36
  return true;
28
37
  }
29
38
  // 404 css, text will be empty
@@ -39,22 +48,30 @@ const initV8ListAndSourcemap = async (v8list, options) => {
39
48
  // init id, sourcePath
40
49
  v8list = v8list.map((item, i) => {
41
50
 
51
+ const type = item.type;
52
+
42
53
  const data = {
43
- url: item.url
54
+ url: item.url,
55
+ type
44
56
  };
45
57
 
46
- if (item.functions) {
47
- data.type = 'js';
58
+ if (type === 'js') {
48
59
  data.functions = item.functions;
49
60
  data.source = item.source;
50
61
  // could be existed
51
62
  data.sourceMap = item.sourceMap;
52
- } else if (item.ranges) {
53
- data.type = 'css';
63
+ } else {
64
+ // css
54
65
  data.ranges = item.ranges;
55
66
  data.source = item.text;
56
67
  }
57
68
 
69
+
70
+ // no functions and ranges
71
+ if (item.empty) {
72
+ data.empty = true;
73
+ }
74
+
58
75
  const idList = [];
59
76
  // could be existed
60
77
  const distFile = item.distFile;
@@ -122,17 +139,17 @@ const mergeJsFunctions = (itemList) => {
122
139
 
123
140
  const mergeV8Coverage = async (dataList, options) => {
124
141
 
125
- let list = [];
142
+ let allList = [];
126
143
  dataList.forEach((d) => {
127
- list = list.concat(d.data);
144
+ allList = allList.concat(d.data);
128
145
  });
129
146
 
130
- // console.log('before merge', list.map((it) => it.url));
147
+ const coverageList = allList.filter((it) => !it.empty);
131
148
 
132
149
  // connect all functions and ranges
133
150
  const itemMap = {};
134
151
  const mergeMap = {};
135
- list.forEach((item) => {
152
+ coverageList.forEach((item) => {
136
153
  const { id } = item;
137
154
  const prev = itemMap[id];
138
155
  if (prev) {
@@ -158,11 +175,17 @@ const mergeV8Coverage = async (dataList, options) => {
158
175
  }
159
176
  }
160
177
 
161
- list = Object.values(itemMap);
178
+ // first time filter for empty, (not for sources)
179
+ const emptyList = allList.filter((it) => it.empty).filter((it) => !itemMap[it.id]);
180
+
181
+ // empty list + merged list
182
+ const mergedList = emptyList.concat(Object.values(itemMap));
162
183
 
163
184
  // try to load coverage and source by id
164
- for (const item of list) {
165
- const { id } = item;
185
+ for (const item of mergedList) {
186
+ const {
187
+ id, type, empty
188
+ } = item;
166
189
  const sourcePath = Util.resolveCacheSourcePath(options.cacheDir, id);
167
190
  const content = await Util.readFile(sourcePath);
168
191
  if (content) {
@@ -173,24 +196,63 @@ const mergeV8Coverage = async (dataList, options) => {
173
196
  Util.logError(`failed to read source: ${item.url}`);
174
197
  item.source = '';
175
198
  }
199
+
200
+ // add empty coverage after merged
201
+ if (empty) {
202
+ if (type === 'js') {
203
+ item.functions = [{
204
+ functionName: '',
205
+ ranges: [{
206
+ startOffset: 0,
207
+ endOffset: item.source.length,
208
+ count: 0
209
+ }]
210
+ }];
211
+ } else {
212
+ // empty css
213
+ item.ranges = [];
214
+ }
215
+ }
216
+
176
217
  }
177
218
 
178
- return list;
219
+ return mergedList;
179
220
  };
180
221
 
181
222
  // ============================================================
182
223
 
224
+ const saveCodecovReport = async (reportData, options, codecovOptions) => {
225
+ const mergedOptions = {
226
+ outputFile: 'codecov.json',
227
+ ... codecovOptions
228
+ };
229
+
230
+ // console.log(mergedOptions);
231
+ const jsonPath = path.resolve(options.outputDir, mergedOptions.outputFile);
232
+
233
+ // https://docs.codecov.com/docs/codecov-custom-coverage-format
234
+ const coverage = {};
235
+ reportData.files.forEach((item) => {
236
+ const { sourcePath, data } = item;
237
+ coverage[sourcePath] = data.lines;
238
+ });
239
+ const codecovData = {
240
+ coverage
241
+ };
242
+
243
+ await Util.writeFile(jsonPath, JSON.stringify(codecovData));
244
+ return Util.relativePath(jsonPath);
245
+ };
246
+
183
247
  const saveV8JsonReport = async (reportData, options, v8JsonOptions) => {
184
- const v8MergedOptions = {
248
+ const mergedOptions = {
185
249
  outputFile: 'coverage-report.json',
186
250
  ... v8JsonOptions
187
251
  };
188
252
 
189
- // console.log(v8MergedOptions);
190
- const jsonPath = path.resolve(options.outputDir, v8MergedOptions.outputFile);
191
- // console.log(reportPath);
253
+ // console.log(mergedOptions);
254
+ const jsonPath = path.resolve(options.outputDir, mergedOptions.outputFile);
192
255
  await Util.writeFile(jsonPath, JSON.stringify(reportData));
193
-
194
256
  return Util.relativePath(jsonPath);
195
257
  };
196
258
 
@@ -255,10 +317,10 @@ const saveV8Report = async (v8list, options) => {
255
317
  let outputPath;
256
318
 
257
319
  // v8 reports
258
- const v8Map = options.reportGroup.v8;
320
+ const v8Group = options.reportGroup.v8;
259
321
 
260
322
  // v8 html
261
- const v8Options = v8Map.v8;
323
+ const v8Options = v8Group.v8;
262
324
  if (v8Options) {
263
325
 
264
326
  // V8 only options, merged with root options
@@ -276,12 +338,25 @@ const saveV8Report = async (v8list, options) => {
276
338
  outputPath = await saveV8HtmlReport(reportData, options, v8HtmlOptions);
277
339
  }
278
340
 
279
- // v8 json
280
- const v8JsonOptions = v8Map['v8-json'];
341
+ // v8 json (after html for metrics data)
342
+ const v8JsonOptions = v8Group['v8-json'];
281
343
  if (v8JsonOptions) {
282
- outputPath = await saveV8JsonReport(reportData, options, v8JsonOptions);
344
+ const jsonPath = await saveV8JsonReport(reportData, options, v8JsonOptions);
345
+ if (!outputPath) {
346
+ outputPath = jsonPath;
347
+ }
348
+ }
349
+
350
+ // codecov
351
+ const codecovOptions = v8Group.codecov;
352
+ if (codecovOptions) {
353
+ const jsonPath = await saveCodecovReport(reportData, options, codecovOptions);
354
+ if (!outputPath) {
355
+ outputPath = jsonPath;
356
+ }
283
357
  }
284
358
 
359
+ // outputPath last one is html
285
360
  const reportPath = Util.resolveReportPath(options, () => {
286
361
  return outputPath;
287
362
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monocart-coverage-reports",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Monocart coverage reports",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -28,7 +28,7 @@
28
28
  "test-node-cdp": "node --inspect=9229 ./test/test-node-cdp.js",
29
29
  "test-node-koa": "node ./test/test-node-koa.js",
30
30
  "test-browser": "node ./test/test.js",
31
- "test-cli": "npx mcr \"node ./test/test-node-env.js\" -o docs/cli -r v8,console-summary --lcov -c test/cli-options.js",
31
+ "test-cli": "npx mcr \"node ./test/test-node-env.js\" -o docs/cli -c test/cli-options.js",
32
32
  "test": "npm run test-browser && npm run test-node && npm run test-cli && npm run build-docs",
33
33
  "dev": "sf d v8",
34
34
  "open": "node ./scripts/open.js",