monocart-coverage-reports 2.7.5 → 2.7.6

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
@@ -9,32 +9,31 @@
9
9
  > A code coverage tool to generate native [V8](https://v8.dev/blog/javascript-code-coverage) reports or [Istanbul](https://istanbul.js.org/) reports.
10
10
 
11
11
  * [Usage](#usage)
12
- * [Default Options](#default-options)
12
+ * [Options](#options)
13
13
  * [Available Reports](#available-reports)
14
- * [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
15
- * [onEnd Hook](#onend-hook)
16
- * [Command Line](#command-line)
17
14
  * [Compare Reports](#compare-reports)
18
- * [Compare Workflows](#compare-workflows)
19
15
  * [Collecting Istanbul Coverage Data](#collecting-istanbul-coverage-data)
20
16
  * [Collecting V8 Coverage Data](#collecting-v8-coverage-data)
21
- * [Manually Resolve the Sourcemap](#manually-resolve-the-sourcemap)
22
- * [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
23
- * [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
24
- * [Collecting V8 Coverage Data with `CDPClient` API](#collecting-v8-coverage-data-with-cdpclient-api)
25
- * [Multiprocessing Support](#multiprocessing-support)
26
- * [Merge Coverage Reports](#merge-coverage-reports)
17
+ - [Collecting V8 Coverage Data with Playwright](#collecting-v8-coverage-data-with-playwright)
18
+ - [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
19
+ - [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
20
+ - [Collecting V8 Coverage Data with `CDPClient` API](#collecting-v8-coverage-data-with-cdpclient-api)
21
+ - [V8 Coverage Data API](#v8-coverage-data-api)
22
+ * [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
27
23
  * [Resolve `sourcePath` for the Source Files](#resolve-sourcepath-for-the-source-files)
28
24
  * [Adding Empty Coverage for Untested Files](#adding-empty-coverage-for-untested-files)
25
+ * [onEnd Hook](#onend-hook)
29
26
  * [Ignoring Uncovered Codes](#ignoring-uncovered-codes)
30
- * [Chromium Coverage API](#chromium-coverage-api)
31
- * [V8 Coverage Data Format](#v8-coverage-data-format)
32
- * [How to convert V8 to Istanbul](#how-to-convert-v8-to-istanbul)
33
- - [Using `v8-to-istanbul`](#using-v8-to-istanbul)
34
- - [How Monocart Works](#how-monocart-works)
35
- * [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
27
+ * [Multiprocessing Support](#multiprocessing-support)
28
+ * [Command Line](#command-line)
29
+ * [Config File](#config-file)
30
+ * [Merge Coverage Reports](#merge-coverage-reports)
36
31
  * [Common issues](#common-issues)
37
- * [Integration](#integration)
32
+ - [Unexpected coverage](#unexpected-coverage)
33
+ - [Unparsable source](#unparsable-source)
34
+ * [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
35
+ * [Integration with Any Testing Framework](#integration-with-any-testing-framework)
36
+ * [Integration Examples](#integration-examples)
38
37
  - [Playwright](#playwright)
39
38
  - [Jest](#jest)
40
39
  - [Vitest](#vitest)
@@ -43,13 +42,18 @@
43
42
  - [WebdriverIO](#webdriverio)
44
43
  - [Storybook Test Runner](#storybook-test-runner)
45
44
  - [TestCafe](#testcafe)
45
+ - [Mocha](#mocha)
46
+ - [tsx](#tsx)
47
+ - [ts-node](#ts-node)
48
+ - [AVA](#ava)
46
49
  - [Codecov](#codecov)
47
50
  - [Coveralls](#coveralls)
48
51
  - [Sonar Cloud](#sonar-cloud)
49
- - [Integration with Any Testing Framework](#integration-with-any-testing-framework)
50
52
  * [Thanks](#thanks)
51
53
 
52
54
  ## Usage
55
+ > It's recommended to use [Node.js 20+](https://nodejs.org/).
56
+ - [API](#multiprocessing-support)
53
57
  ```js
54
58
  const MCR = require('monocart-coverage-reports');
55
59
  const coverageOptions = {
@@ -57,23 +61,25 @@ const coverageOptions = {
57
61
  outputDir: './coverage-reports',
58
62
  reports: ["v8", "console-details"]
59
63
  }
60
- const coverageReport = MCR(coverageOptions);
61
- coverageReport.cleanCache();
64
+ const mcr = MCR(coverageOptions);
65
+ mcr.cleanCache();
62
66
 
63
- await coverageReport.add(coverageData1);
64
- await coverageReport.add(coverageData2);
67
+ await mcr.add(coverageData1);
68
+ await mcr.add(coverageData2);
65
69
 
66
- await coverageReport.generate();
70
+ await mcr.generate();
67
71
 
68
72
  // Or
69
73
  // const { CoverageReport } = require('monocart-coverage-reports');
70
- // const coverageReport = new CoverageReport(coverageOptions);
74
+ // const mcr = new CoverageReport(coverageOptions);
75
+ ```
76
+ - [CLI](#command-line)
77
+ ```sh
78
+ mcr node my-app.js -r v8,console-details
71
79
  ```
72
- - [example v8](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js)
73
- - [example istanbul](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
74
80
 
75
- ## Default Options
76
- - [lib/default/options.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/lib/default/options.js)
81
+ ## Options
82
+ - Default Options: [lib/default/options.js](./lib/default/options.js)
77
83
  - `reports` [Available Reports](#available-reports)
78
84
  - `entryFilter` and `sourceFilter` [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
79
85
  - `sourcePath` [Resolve `sourcePath` for the Source Files](#resolve-sourcepath-for-the-source-files)
@@ -82,7 +88,8 @@ await coverageReport.generate();
82
88
  - `logging` [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
83
89
  - `onEnd` [onEnd Hook](#onend-hook)
84
90
 
85
- - Declaration [lib/index.d.ts](https://github.com/cenfun/monocart-coverage-reports/blob/main/lib/index.d.ts)
91
+ - Declaration: [lib/index.d.ts](./lib/index.d.ts)
92
+ - [Config file](#config-file)
86
93
 
87
94
  ## Available Reports
88
95
 
@@ -95,9 +102,7 @@ await coverageReport.generate();
95
102
  - Coverage for Any Runtime Code
96
103
  - CSS Coverage Support
97
104
  - Better Support for Sourcemap Conversion
98
- - Demos:
99
- - Browser: Build with webpack [V8](https://cenfun.github.io/monocart-coverage-reports/v8) and [Minify](https://cenfun.github.io/monocart-coverage-reports/minify); Build with [Rollup](https://cenfun.github.io/monocart-coverage-reports/rollup) and [Esbuild](https://cenfun.github.io/monocart-coverage-reports/esbuild); Collect with [puppeteer](https://cenfun.github.io/monocart-coverage-reports/puppeteer/); [anonymous](https://cenfun.github.io/monocart-coverage-reports/anonymous/) and [css](https://cenfun.github.io/monocart-coverage-reports/css/)
100
- - Node.js: Collect with [env](https://cenfun.github.io/monocart-coverage-reports/node-env), and also V8 [API](https://cenfun.github.io/monocart-coverage-reports/node-api), [Inspector](https://cenfun.github.io/monocart-coverage-reports/node-ins) and [CDP](https://cenfun.github.io/monocart-coverage-reports/node-cdp); Web server example: [koa](https://cenfun.github.io/monocart-coverage-reports/node-koa/)
105
+ - Demos: [V8](https://cenfun.github.io/monocart-coverage-reports/v8) and [more](https://cenfun.github.io/monocart-coverage-reports/)
101
106
 
102
107
  ![](./assets/v8.gif)
103
108
 
@@ -112,7 +117,6 @@ await coverageReport.generate();
112
117
  - [Istanbul html](https://cenfun.github.io/monocart-coverage-reports/istanbul/)
113
118
  - [V8 to Istanbul](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/istanbul)
114
119
  - `html-spa`
115
- - [Istanbul html-spa](https://cenfun.github.io/monocart-coverage-reports/istanbul/html-spa/)
116
120
  - `json`
117
121
  - `json-summary`
118
122
  - `lcov`
@@ -193,148 +197,10 @@ const coverageOptions = {
193
197
 
194
198
  ]
195
199
  }
196
- const coverageReport = MCR(coverageOptions);
197
- coverageReport.cleanCache();
198
- ```
199
-
200
- ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
201
- When V8 coverage data collected, it actually contains the data of all entry files, for example:
202
- ```
203
- dist/main.js
204
- dist/vendor.js
205
- dist/something-else.js
206
- ```
207
- 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.
208
- ```
209
- dist/main.js
210
- ```
211
- 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`.
212
- ```
213
- > src/index.js
214
- > src/components/app.js
215
- > node_modules/dependency/dist/dependency.js
216
- ```
217
- We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
218
- ```
219
- > src/index.js
220
- > src/components/app.js
221
- ```
222
- For example:
223
- ```js
224
- const coverageOptions = {
225
- entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
226
- sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
227
- };
228
- ```
229
- Or using `minimatch` pattern:
230
- ```js
231
- const coverageOptions = {
232
- entryFilter: "**/main.js",
233
- sourceFilter: "**/src/**"
234
- };
235
- // supports multiple patterns:
236
- const coverageOptions = {
237
- entryFilter: {
238
- '**/vendor.js': false,
239
- '**/main.js': true
240
- },
241
- sourceFilter: {
242
- '**/src/**': true
243
- }
244
- };
245
- ```
246
-
247
- ## onEnd Hook
248
- For example, checking thresholds:
249
- ```js
250
- const EC = require('eight-colors');
251
- const coverageOptions = {
252
- name: 'My Coverage Report',
253
- outputDir: './coverage-reports',
254
- onEnd: (coverageResults) => {
255
- const thresholds = {
256
- bytes: 80,
257
- lines: 60
258
- };
259
- console.log('check thresholds ...', thresholds);
260
- const errors = [];
261
- const { summary } = coverageResults;
262
- Object.keys(thresholds).forEach((k) => {
263
- const pct = summary[k].pct;
264
- if (pct < thresholds[k]) {
265
- errors.push(`Coverage threshold for ${k} (${pct} %) not met: ${thresholds[k]} %`);
266
- }
267
- });
268
- if (errors.length) {
269
- const errMsg = errors.join('\n');
270
- console.log(EC.red(errMsg));
271
- // throw new Error(errMsg);
272
- // process.exit(1);
273
- }
274
- }
275
- }
276
- ```
277
-
278
- ## Command Line
279
- > 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`.
280
-
281
- - Installing globally
282
- ```sh
283
- npm i monocart-coverage-reports -g
284
- mcr node ./test/specs/node.test.js -r v8,console-summary --lcov
285
- ```
286
-
287
- - Locally in your project
288
- ```sh
289
- npm i monocart-coverage-reports
290
- npx mcr node ./test/specs/node.test.js -r v8,console-summary --lcov
291
- ```
292
-
293
- - CLI Options
294
- ```sh
295
- Usage: mcr [options] <command>
296
-
297
- CLI to generate coverage reports
298
-
299
- Arguments:
300
- command command to execute
301
-
302
- Options:
303
- -V, --version output the version number
304
- -c, --config <path> custom config file path
305
- --logging <logging> off, error, info, debug
306
- -n, --name <name> report name for title
307
- -r, --reports <name[,name]> coverage reports to use
308
- -o, --outputDir <dir> output dir for reports
309
- -i, --inputDir <dir> input dir for merging raw files
310
- --entryFilter <pattern> entry url filter
311
- --sourceFilter <pattern> source path filter
312
- --outputFile <path> output file for v8 report
313
- --inline inline html for v8 report
314
- --assetsPath <path> assets path if not inline
315
- --lcov generate lcov.info file
316
- --import <module> preload module at startup
317
- --require <module> preload module at startup
318
- -h, --help display help for command
200
+ const mcr = MCR(coverageOptions);
201
+ mcr.cleanCache();
319
202
  ```
320
203
 
321
- - Loading config file by priority:
322
- - Custom config file with `-c` or `--config`
323
- - `mcr.config.js`
324
- - `mcr.config.cjs`
325
- - `mcr.config.mjs`
326
- - `mcr.config.json` - json format
327
- - `mcr.config.ts` (requires preloading the ts execution module)
328
- - `.mcrrc.js`
329
- - `.mcrrc` - json format
330
-
331
- - Working with `tsx`, see [mcr-tsx](https://github.com/cenfun/mcr-tsx)
332
- ```sh
333
- npx mcr --import tsx tsx ./src/example.ts
334
- ```
335
-
336
- - Working with `ts-node`, see [mcr-ts-node](https://github.com/cenfun/mcr-ts-node)
337
-
338
204
  ## Compare Reports
339
205
  | | Istanbul | V8 | V8 to Istanbul |
340
206
  | :--------------| :------ | :------ | :---------------------- |
@@ -349,59 +215,58 @@ Options:
349
215
  | CSS coverage | ❌ | ✅ | ✅ |
350
216
  | Minified code | ❌ | ✅ | ❌ |
351
217
 
352
- ## Compare Workflows
353
- - Istanbul Workflows
354
- - 1, [Collecting Istanbul coverage data](#collecting-istanbul-coverage-data)
355
- - 2, Adding coverage data and generating coverage report
356
-
357
- - V8 Workflows
358
- - 1, [Collecting V8 coverage data](#collecting-v8-coverage-data)
359
- - 3, Adding coverage data and generating coverage report
360
-
361
218
  ## Collecting Istanbul Coverage Data
362
219
  - Instrumenting source code
363
220
  > Before collecting Istanbul coverage data, It requires your source code is instrumented with Istanbul
364
- - 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)
221
+ - webpack: [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul), example: [webpack.config-istanbul.js](./test/webpack.config-istanbul.js)
365
222
  - rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
366
223
  - vite: [vite-plugin-istanbul](https://github.com/ifaxity/vite-plugin-istanbul)
367
224
  - Browser
368
- - Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
225
+ - Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](./test/test-istanbul.js)
369
226
  - Node.js
370
227
  - Collecting coverage data from `global.__coverage__`
371
228
 
372
229
  ## Collecting V8 Coverage Data
373
- - For source code: enable `sourcemap` and do not compress/minify:
374
- - [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)
230
+ - Sourcemap for source code: enable `sourcemap` and do not compress/minify:
231
+ - [webpack](https://webpack.js.org/configuration/): `devtool: source-map` and `mode: development`, example [webpack.config-v8.js](./test/webpack.config-v8.js)
375
232
  - [rollup](https://rollupjs.org/configuration-options/): `sourcemap: true`
376
233
  - [vite](https://vitejs.dev/config/build-options.html): `sourcemap: true` and `minify: false`
377
234
  - [esbuild](https://esbuild.github.io/api/): `sourcemap: true` and `minify: false`
378
- - [Manually Resolve the Sourcemap](#manually-resolve-the-sourcemap)
235
+
379
236
  - Browser (Chromium Only)
380
- > Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api):
381
- - [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)
382
- - see [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
237
+ - [Collecting V8 Coverage Data with Playwright](#collecting-v8-coverage-data-with-playwright)
238
+ - [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
239
+
383
240
  - Node.js
384
- - see [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
241
+ - [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
242
+
385
243
  - CDP
386
- - see [Collecting V8 Coverage Data with `CDPClient` API](#collecting-v8-coverage-data-with-cdpclient-api)
244
+ - [Collecting V8 Coverage Data with `CDPClient` API](#collecting-v8-coverage-data-with-cdpclient-api)
387
245
 
388
- ## Manually Resolve the Sourcemap
389
- > Sometimes, the sourcemap file cannot be successfully loaded with the `sourceMappingURL`, you can try to manually read the sourcemap file before the coverage data is added to the report.
246
+ ### Collecting V8 Coverage Data with Playwright
390
247
  ```js
391
- const jsCoverage = await page.coverage.stopJSCoverage();
392
- jsCoverage.forEach((entry) => {
393
- // read sourcemap for the my-dist.js manually
394
- if (entry.url.endsWith('my-dist.js')) {
395
- entry.sourceMap = JSON.parse(fs.readFileSync('dist/my-dist.js.map').toString('utf-8'));
396
- }
397
- });
248
+ await Promise.all([
249
+ page.coverage.startJSCoverage({
250
+ resetOnNavigation: false
251
+ }),
252
+ page.coverage.startCSSCoverage({
253
+ resetOnNavigation: false
254
+ })
255
+ ]);
398
256
 
399
- await MCR(coverageOptions).add(jsCoverage);
257
+ await page.goto("your page url");
258
+
259
+ const [jsCoverage, cssCoverage] = await Promise.all([
260
+ page.coverage.stopJSCoverage(),
261
+ page.coverage.stopCSSCoverage()
262
+ ]);
263
+
264
+ const coverageData = [... jsCoverage, ... cssCoverage];
400
265
 
401
266
  ```
267
+ see [./test/test-v8.js](./test/test-v8.js), and [anonymous](./test/test-anonymous.js), [css](./test/test-css.js)
402
268
 
403
- ## Collecting Raw V8 Coverage Data with Puppeteer
404
- > Puppeteer does not provide raw v8 coverage data by default. A simple conversion is required, see example: [./test/test-puppeteer.js](./test/test-puppeteer.js)
269
+ ### Collecting Raw V8 Coverage Data with Puppeteer
405
270
  ```js
406
271
  await Promise.all([
407
272
  page.coverage.startJSCoverage({
@@ -414,7 +279,7 @@ await Promise.all([
414
279
  })
415
280
  ]);
416
281
 
417
- await page.goto(url);
282
+ await page.goto("your page url");
418
283
 
419
284
  const [jsCoverage, cssCoverage] = await Promise.all([
420
285
  page.coverage.stopJSCoverage(),
@@ -429,8 +294,9 @@ const coverageData = [... jsCoverage.map((it) => {
429
294
  };
430
295
  }), ... cssCoverage];
431
296
  ```
297
+ see example: [./test/test-puppeteer.js](./test/test-puppeteer.js)
432
298
 
433
- ## Node.js V8 Coverage Report for Server Side
299
+ ### Node.js V8 Coverage Report for Server Side
434
300
  Possible solutions:
435
301
  - [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
436
302
  - 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.
@@ -463,7 +329,7 @@ Possible solutions:
463
329
  - [Child Process](https://nodejs.org/docs/latest/api/child_process.html) + NODE_V8_COVERAGE
464
330
  - see [Command Line](#command-line)
465
331
 
466
- ## Collecting V8 Coverage Data with `CDPClient` API
332
+ ### Collecting V8 Coverage Data with `CDPClient` API
467
333
  - Work with node debugger `--inspect=9229`
468
334
  ```js
469
335
  const MCR = require('monocart-coverage-reports');
@@ -507,93 +373,97 @@ await page.goto("your page url");
507
373
  const coverageData = await client.stopCoverage();
508
374
  ```
509
375
 
510
- ## Multiprocessing Support
511
- > The data will be added to `[outputDir]/.cache`, After the generation of the report, this data will be removed unless debugging has been enabled or a raw report has been used, see [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
512
- - Main process, before the start of testing
376
+ ### V8 Coverage Data API
377
+ - [V8 coverage report](https://v8.dev/blog/javascript-code-coverage) - Native support for JavaScript code coverage to V8. (Chromium only)
378
+ - [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
379
+ - [Puppeteer Coverage class](https://pptr.dev/api/puppeteer.coverage)
380
+ - [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage) see devtools-protocol [ScriptCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ScriptCoverage) and [v8-coverage](https://github.com/bcoe/v8-coverage)
513
381
  ```js
514
- const MCR = require('monocart-coverage-reports');
515
- const coverageOptions = require('path-to/same-options.js');
516
- const coverageReport = MCR(coverageOptions);
517
- // clean previous cache before the start of testing
518
- // unless the running environment is new and no cache
519
- coverageReport.cleanCache();
520
- ```
382
+ // Coverage data for a source range.
383
+ export interface CoverageRange {
384
+ // JavaScript script source offset for the range start.
385
+ startOffset: integer;
386
+ // JavaScript script source offset for the range end.
387
+ endOffset: integer;
388
+ // Collected execution count of the source range.
389
+ count: integer;
390
+ }
521
391
 
522
- - Sub process 1, testing stage 1
523
- ```js
524
- const MCR = require('monocart-coverage-reports');
525
- const coverageOptions = require('path-to/same-options.js');
526
- const coverageReport = MCR(coverageOptions);
527
- // do not clean cache in the stage
528
- await coverageReport.add(coverageData1);
529
- ```
392
+ // Coverage data for a JavaScript function.
393
+ /**
394
+ * @functionName can be an empty string.
395
+ * @ranges is always non-empty. The first range is called the "root range".
396
+ * @isBlockCoverage indicates if the function has block coverage information.
397
+ If this is false, it usually means that the functions was never called.
398
+ It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
399
+ */
400
+ export interface FunctionCoverage {
401
+ // JavaScript function name.
402
+ functionName: string;
403
+ // Source ranges inside the function with coverage data.
404
+ ranges: CoverageRange[];
405
+ // Whether coverage data for this function has block granularity.
406
+ isBlockCoverage: boolean;
407
+ }
530
408
 
531
- - Sub process 2, testing stage 2
532
- ```js
533
- const MCR = require('monocart-coverage-reports');
534
- const coverageOptions = require('path-to/same-options.js');
535
- const coverageReport = MCR(coverageOptions);
536
- // do not clean cache in the stage
537
- await coverageReport.add(coverageData2);
538
- ```
409
+ // Coverage data for a JavaScript script.
410
+ export interface ScriptCoverage {
411
+ // JavaScript script id.
412
+ scriptId: Runtime.ScriptId;
413
+ // JavaScript script name or url.
414
+ url: string;
415
+ // Functions contained in the script that has coverage data.
416
+ functions: FunctionCoverage[];
417
+ }
539
418
 
540
- - Main process, after the completion of testing
541
- ```js
542
- // generate coverage reports after the completion of testing
543
- const MCR = require('monocart-coverage-reports');
544
- const coverageOptions = require('path-to/same-options.js');
545
- const coverageReport = MCR(coverageOptions);
546
- // do not clean cache before generating reports
547
- await coverageReport.generate();
419
+ export type V8CoverageData = ScriptCoverage[];
548
420
  ```
549
421
 
550
- ## Merge Coverage Reports
551
- The following usage scenarios may require merging coverage reports:
552
- - When the code is executed in different environments, like Node.js Server Side and browser Client Side (Next.js for instance). Each environment may generate its own coverage report. Merging them can give a more comprehensive view of the test coverage. see example [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright) for automatic report merging.
553
- - When the code is subjected to different kinds of testing. For example, unit tests with Jest might cover certain parts of the code, while end-to-end tests with Playwright might cover other parts. Merging these different coverage reports can provide a holistic view of what code has been tested.
554
- - When tests are run on different machines or different shards, each might produce its own coverage report. Merging these can give a complete picture of the test coverage across all machines or shards.
555
-
556
- If the reports cannot be merged automatically, then here is how to manually merge the reports.
557
- First, using the `raw` report to export the original coverage data to the specified directory.
422
+ ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
423
+ When V8 coverage data collected, it actually contains the data of all entry files, for example:
424
+ ```
425
+ dist/main.js
426
+ dist/vendor.js
427
+ dist/something-else.js
428
+ ```
429
+ 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.
430
+ ```
431
+ dist/main.js
432
+ ```
433
+ 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`.
434
+ ```
435
+ > src/index.js
436
+ > src/components/app.js
437
+ > node_modules/dependency/dist/dependency.js
438
+ ```
439
+ We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
440
+ ```
441
+ > src/index.js
442
+ > src/components/app.js
443
+ ```
444
+ For example:
558
445
  ```js
559
446
  const coverageOptions = {
560
- name: 'My Unit Test Coverage Report',
561
- outputDir: "./coverage-reports/unit",
562
- reports: [
563
- ['raw', {
564
- // relative path will be "./coverage-reports/unit/raw"
565
- outputDir: "raw"
566
- }],
567
- ['v8'],
568
- ['console-summary']
569
- ]
447
+ entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
448
+ sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
570
449
  };
571
450
  ```
572
- Then, after all the tests are completed, generate a merged report with option `inputDir`:
451
+ Or using `minimatch` pattern:
573
452
  ```js
574
- // esm syntax
575
- import fs from "fs";
576
- import { CoverageReport } from 'monocart-coverage-reports';
577
453
  const coverageOptions = {
578
- name: 'My Merged Coverage Report',
579
- inputDir: [
580
- './coverage-reports/unit/raw',
581
- './coverage-reports/e2e/raw'
582
- ],
583
- outputDir: './coverage-reports/merged',
584
- reports: [
585
- ['v8'],
586
- ['console-summary']
587
- ],
588
- onEnd: () => {
589
- // remove the raw files if it useless
590
- fs.rmSync('./coverage-reports/unit/raw', {
591
- recursive: true,
592
- force: true
593
- })
454
+ entryFilter: "**/main.js",
455
+ sourceFilter: "**/src/**"
456
+ };
457
+ // supports multiple patterns:
458
+ const coverageOptions = {
459
+ entryFilter: {
460
+ '**/vendor.js': false,
461
+ '**/main.js': true
462
+ },
463
+ sourceFilter: {
464
+ '**/src/**': true
594
465
  }
595
466
  };
596
- await new CoverageReport(coverageOptions).generate();
597
467
  ```
598
468
 
599
469
  ## Resolve `sourcePath` for the Source Files
@@ -660,6 +530,37 @@ const coverageOptions = {
660
530
  };
661
531
  ```
662
532
 
533
+ ## onEnd Hook
534
+ For example, checking thresholds:
535
+ ```js
536
+ const EC = require('eight-colors');
537
+ const coverageOptions = {
538
+ name: 'My Coverage Report',
539
+ outputDir: './coverage-reports',
540
+ onEnd: (coverageResults) => {
541
+ const thresholds = {
542
+ bytes: 80,
543
+ lines: 60
544
+ };
545
+ console.log('check thresholds ...', thresholds);
546
+ const errors = [];
547
+ const { summary } = coverageResults;
548
+ Object.keys(thresholds).forEach((k) => {
549
+ const pct = summary[k].pct;
550
+ if (pct < thresholds[k]) {
551
+ errors.push(`Coverage threshold for ${k} (${pct} %) not met: ${thresholds[k]} %`);
552
+ }
553
+ });
554
+ if (errors.length) {
555
+ const errMsg = errors.join('\n');
556
+ console.log(EC.red(errMsg));
557
+ // throw new Error(errMsg);
558
+ // process.exit(1);
559
+ }
560
+ }
561
+ }
562
+ ```
563
+
663
564
  ## Ignoring Uncovered Codes
664
565
  To ignore codes, use the special comment which starts with `v8 ignore `:
665
566
  - Ignoring all until stop
@@ -682,103 +583,191 @@ if (platform === 'linux') {
682
583
  }
683
584
  ```
684
585
 
685
- ## Chromium Coverage API
686
- - [V8 coverage report](https://v8.dev/blog/javascript-code-coverage) - Native support for JavaScript code coverage to V8. (Chromium only)
687
- - [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
688
- - [Puppeteer Coverage class](https://pptr.dev/api/puppeteer.coverage)
689
- - [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)
586
+ ## Multiprocessing Support
587
+ > The data will be added to `[outputDir]/.cache`, After the generation of the report, this data will be removed unless debugging has been enabled or a raw report has been used, see [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
588
+ - Main process, before the start of testing
589
+ ```js
590
+ const MCR = require('monocart-coverage-reports');
591
+ const coverageOptions = require('path-to/same-options.js');
592
+ const mcr = MCR(coverageOptions);
593
+ // clean previous cache before the start of testing
594
+ // unless the running environment is new and no cache
595
+ mcr.cleanCache();
596
+ ```
690
597
 
691
- ## V8 Coverage Data Format
598
+ - Sub process 1, testing stage 1
692
599
  ```js
693
- // Coverage data for a source range.
694
- export interface CoverageRange {
695
- // JavaScript script source offset for the range start.
696
- startOffset: integer;
697
- // JavaScript script source offset for the range end.
698
- endOffset: integer;
699
- // Collected execution count of the source range.
700
- count: integer;
701
- }
600
+ const MCR = require('monocart-coverage-reports');
601
+ const coverageOptions = require('path-to/same-options.js');
602
+ const mcr = MCR(coverageOptions);
603
+ // do not clean cache in the stage
604
+ await mcr.add(coverageData1);
605
+ ```
702
606
 
703
- // Coverage data for a JavaScript function.
704
- /**
705
- * @functionName can be an empty string.
706
- * @ranges is always non-empty. The first range is called the "root range".
707
- * @isBlockCoverage indicates if the function has block coverage information.
708
- If this is false, it usually means that the functions was never called.
709
- It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
710
- */
711
- export interface FunctionCoverage {
712
- // JavaScript function name.
713
- functionName: string;
714
- // Source ranges inside the function with coverage data.
715
- ranges: CoverageRange[];
716
- // Whether coverage data for this function has block granularity.
717
- isBlockCoverage: boolean;
718
- }
607
+ - Sub process 2, testing stage 2
608
+ ```js
609
+ const MCR = require('monocart-coverage-reports');
610
+ const coverageOptions = require('path-to/same-options.js');
611
+ const mcr = MCR(coverageOptions);
612
+ // do not clean cache in the stage
613
+ await mcr.add(coverageData2);
614
+ ```
719
615
 
720
- // Coverage data for a JavaScript script.
721
- export interface ScriptCoverage {
722
- // JavaScript script id.
723
- scriptId: Runtime.ScriptId;
724
- // JavaScript script name or url.
725
- url: string;
726
- // Functions contained in the script that has coverage data.
727
- functions: FunctionCoverage[];
728
- }
616
+ - Main process, after the completion of testing
617
+ ```js
618
+ // generate coverage reports after the completion of testing
619
+ const MCR = require('monocart-coverage-reports');
620
+ const coverageOptions = require('path-to/same-options.js');
621
+ const mcr = MCR(coverageOptions);
622
+ // do not clean cache before generating reports
623
+ await mcr.generate();
624
+ ```
729
625
 
730
- export type V8CoverageData = ScriptCoverage[];
626
+ ## Command Line
627
+ > 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`.
628
+
629
+ - Installing globally
630
+ ```sh
631
+ npm i monocart-coverage-reports -g
632
+ mcr node ./test/specs/node.test.js -r v8,console-summary --lcov
731
633
  ```
732
- see devtools-protocol [ScriptCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ScriptCoverage) and [v8-coverage](https://github.com/bcoe/v8-coverage)
733
634
 
734
- ## How to convert V8 to Istanbul
735
- ### Using [v8-to-istanbul](https://github.com/istanbuljs/v8-to-istanbul)
736
- 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:
737
- - 1, The source mapping does not work well if the position is between the two consecutive mappings. for example:
738
- ```js
739
- const a = tf ? 'true' : 'false';
740
- ^ ^ ^
741
- m1 p m2
635
+ - Locally in your project
636
+ ```sh
637
+ npm i monocart-coverage-reports
638
+ npx mcr node ./test/specs/node.test.js -r v8,console-summary --lcov
742
639
  ```
743
- > `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.
744
640
 
745
- - 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. And also there are some problems about counting the functions and branches.
641
+ - CLI Options
642
+ ```sh
643
+ Usage: mcr [options] [command]
746
644
 
747
- ### How Monocart Works
748
- We implemented new converter:
749
- - 1, Trying to fix the middle position if not found the exact mapping for the position.
750
- - 2, Finding all functions, statements and branches by parsing the source code [AST](https://github.com/acornjs/acorn). However, there's a small issue, which is the V8 cannot provide effective branch coverage information for `AssignmentPattern`.
645
+ CLI to generate coverage reports
751
646
 
752
- | AST | V8 |
753
- | :---------------------| :------------- |
754
- | AssignmentPattern | 🛇 Not Support |
755
- | ConditionalExpression | ✔ |
756
- | IfStatement | ✔ |
757
- | LogicalExpression | ✔ |
758
- | SwitchStatement | ✔ |
647
+ Arguments:
648
+ command command to execute
759
649
 
760
- ## Debug for Coverage and Sourcemap
761
- > Sometimes, the coverage is not what we expect. The next step is to figure out why, and we can easily find out the answer step by step through debugging.
762
- - Start debugging for v8 report with option `logging: 'debug'`
650
+ Options:
651
+ -V, --version output the version number
652
+ -c, --config <path> custom config file path
653
+ -l, --logging <logging> off, error, info, debug
654
+ -n, --name <name> report name for title
655
+ -r, --reports <name[,name]> coverage reports to use
656
+ -o, --outputDir <dir> output dir for reports
657
+ -i, --inputDir <dir> input dir for merging raw files
658
+ --entryFilter <pattern> entry url filter
659
+ --sourceFilter <pattern> source path filter
660
+ --outputFile <path> output file for v8 report
661
+ --inline inline html for v8 report
662
+ --assetsPath <path> assets path if not inline
663
+ --lcov generate lcov.info file
664
+ --import <module> preload module at startup
665
+ --require <module> preload module at startup
666
+ -h, --help display help for command
667
+ ```
668
+
669
+ - Use `--` to separate sub CLI args
670
+ ```sh
671
+ mcr -c mcr.config.js -- sub-cli -c sub-cli.config.js
672
+ ```
673
+
674
+ ## Config File
675
+ Loading config file by priority:
676
+ - Custom config file:
677
+ - CLI: `mcr --config <my-config-file-path>`
678
+ - API: `await mcr.loadConfig("my-config-file-path")`
679
+ - `mcr.config.js`
680
+ - `mcr.config.cjs`
681
+ - `mcr.config.mjs`
682
+ - `mcr.config.json` - json format
683
+ - `mcr.config.ts` (requires preloading the ts execution module)
684
+ - `.mcrrc.js`
685
+ - `.mcrrc` - json format
686
+
687
+ ## Merge Coverage Reports
688
+ The following usage scenarios may require merging coverage reports:
689
+ - When the code is executed in different environments, like Node.js Server Side and browser Client Side (Next.js for instance). Each environment may generate its own coverage report. Merging them can give a more comprehensive view of the test coverage. see example [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright) for automatic report merging.
690
+ - When the code is subjected to different kinds of testing. For example, unit tests with Jest might cover certain parts of the code, while end-to-end tests with Playwright might cover other parts. Merging these different coverage reports can provide a holistic view of what code has been tested.
691
+ - When tests are run on different machines or different shards, each might produce its own coverage report. Merging these can give a complete picture of the test coverage across all machines or shards.
692
+
693
+ If the reports cannot be merged automatically, then here is how to manually merge the reports.
694
+ First, using the `raw` report to export the original coverage data to the specified directory.
763
695
  ```js
764
696
  const coverageOptions = {
765
- logging: 'debug',
697
+ name: 'My Unit Test Coverage Report',
698
+ outputDir: "./coverage-reports/unit",
766
699
  reports: [
700
+ ['raw', {
701
+ // relative path will be "./coverage-reports/unit/raw"
702
+ outputDir: "raw"
703
+ }],
767
704
  ['v8'],
768
705
  ['console-summary']
769
706
  ]
770
707
  };
771
708
  ```
772
- When `logging` is `debug`, the raw report data will be preserved in `[outputDir]/.cache` or `[outputDir]/raw` if `raw` report is used. And the dist file will be preserved in the V8 list, and by opening the browser's devtool, it makes data verification visualization effortless.
773
- ![](./assets/debug-coverage.png)
709
+ Then, after all the tests are completed, generate a merged report with option `inputDir`:
710
+ ```js
711
+ const fs = require('fs');
712
+ const { CoverageReport } = require('monocart-coverage-reports');
713
+ const inputDir = [
714
+ './coverage-reports/unit/raw',
715
+ './coverage-reports/e2e/raw'
716
+ ];
717
+ const coverageOptions = {
718
+ name: 'My Merged Coverage Report',
719
+ inputDir,
720
+ outputDir: './coverage-reports/merged',
774
721
 
775
- - Check sourcemap with [Source Map Visualization](https://evanw.github.io/source-map-visualization/)
722
+ // filter for both unit and e2e
723
+ entryFilter: {
724
+ '**/node_modules/**': false,
725
+ '**/*': true
726
+ },
727
+ sourceFilter: {
728
+ '**/node_modules/**': false,
729
+ '**/src/**': true
730
+ },
731
+
732
+ sourcePath: (filePath, info) => {
733
+ // Unify the file path for the same files
734
+ return filePath;
735
+ },
776
736
 
777
- ![](./assets/debug-sourcemap.png)
737
+ reports: [
738
+ ['v8'],
739
+ ['console-details']
740
+ ],
741
+
742
+ onEnd: () => {
743
+ // remove the raw files if it useless
744
+ inputDir.forEach((p) => {
745
+ fs.rmSync(p, {
746
+ recursive: true,
747
+ force: true
748
+ });
749
+ });
750
+ }
751
+ };
752
+ await new CoverageReport(coverageOptions).generate();
753
+ ```
778
754
 
779
755
  ## Common issues
780
- - `Unparsable source`
756
+ ### Unexpected coverage
757
+ In most cases, it happens when the coverage of the generated code is converted to the coverage of the original code through a sourcemap. In other words, it's an issue with the sourcemap. Most of the time, we can solve this by setting `minify` to `false` in the build tools configuration. Let's take a look at an example:
758
+ ```js
759
+ const a = tf ? 'true' : 'false';
760
+ ^ ^ ^
761
+ m1 p m2
762
+ ```
763
+ `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. You can try [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
781
764
 
765
+ How `MCR` Works:
766
+ - 1, Trying to fix the middle position if not found the exact mapping for the position. However, for non-JS code, such as Vue template, JSX, etc., it might be hard to find a perfect solution.
767
+ - 2, Finding all functions, statements and branches by parsing the source code [AST](https://github.com/acornjs/acorn). However, there's a small issue, which is the V8 cannot provide effective branch coverage information for `AssignmentPattern`.
768
+
769
+
770
+ ### Unparsable source
782
771
  It happens during the parsing of the source code into AST, if the source code is not in the standard ECMAScript. For example `ts`, `jsx` and so on. There is a option to fix it, which is to manually compile the source code for these files.
783
772
  ```js
784
773
  import * as fs from "fs";
@@ -798,7 +787,35 @@ const coverageOptions = {
798
787
  }
799
788
  ```
800
789
 
801
- ## Integration
790
+ ## Debug for Coverage and Sourcemap
791
+ > Sometimes, the coverage is not what we expect. The next step is to figure out why, and we can easily find out the answer step by step through debugging.
792
+ - Start debugging for v8 report with option `logging: 'debug'`
793
+ ```js
794
+ const coverageOptions = {
795
+ logging: 'debug',
796
+ reports: [
797
+ ['v8'],
798
+ ['console-summary']
799
+ ]
800
+ };
801
+ ```
802
+ When `logging` is `debug`, the raw report data will be preserved in `[outputDir]/.cache` or `[outputDir]/raw` if `raw` report is used. And the dist file will be preserved in the V8 list, and by opening the browser's devtool, it makes data verification visualization effortless.
803
+ ![](./assets/debug-coverage.png)
804
+
805
+ - Check sourcemap with [Source Map Visualization](https://evanw.github.io/source-map-visualization/)
806
+
807
+ ![](./assets/debug-sourcemap.png)
808
+
809
+ ## Integration with Any Testing Framework
810
+ - API
811
+ - Collecting coverage data when any stage of the test is completed, and adding the coverage data to the coverage reporter. `await mcr.add(coverageData)`
812
+ - Generating the coverage reports after the completion of all tests. `await mcr.generate()`
813
+ - see [Multiprocessing Support](#multiprocessing-support)
814
+ - CLI
815
+ - Wrapping with any CLI. `mcr your-cli --your-arguments`
816
+ - see [Command line](#command-line)
817
+
818
+ ## Integration Examples
802
819
 
803
820
  ### [Playwright](https://github.com/microsoft/playwright)
804
821
  - [monocart-reporter](https://github.com/cenfun/monocart-reporter) - A Playwright custom reporter, supports generating [Code Coverage Report](https://github.com/cenfun/monocart-reporter?#code-coverage-report)
@@ -833,6 +850,29 @@ const coverageOptions = {
833
850
  ### [TestCafe](https://github.com/DevExpress/testcafe)
834
851
  - [testcafe-reporter-coverage](https://github.com/cenfun/testcafe-reporter-coverage) - A TestCafe custom reporter for coverage reports
835
852
 
853
+ ### [Mocha](https://github.com/mochajs/mocha)
854
+ ```sh
855
+ mcr mocha ./test/**/*.js
856
+ ```
857
+ ```sh
858
+ mcr --import tsx mocha ./test/**/*.ts
859
+ ```
860
+ - see [mcr-tsx](https://github.com/cenfun/mcr-tsx)
861
+
862
+ ### [tsx](https://github.com/privatenumber/tsx)
863
+ ```sh
864
+ mcr --import tsx tsx ./src/example.ts
865
+ ```
866
+ - see [mcr-tsx](https://github.com/cenfun/mcr-tsx)
867
+
868
+ ### [ts-node](https://github.com/TypeStrong/ts-node)
869
+ - see [mcr-ts-node](https://github.com/cenfun/mcr-ts-node)
870
+
871
+ ### [AVA](https://github.com/avajs/ava)
872
+ ```sh
873
+ mcr ava
874
+ ```
875
+
836
876
  ### [Codecov](https://codecov.com/)
837
877
  [![codecov](https://codecov.io/gh/cenfun/monocart-coverage-reports/graph/badge.svg?token=H0LW7UKYU3)](https://codecov.io/gh/cenfun/monocart-coverage-reports)
838
878
  - Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
@@ -886,10 +926,6 @@ const coverageOptions = {
886
926
  -Dsonar.tests=test
887
927
  -Dsonar.exclusions=dist/*,packages/*
888
928
  ```
889
- ### Integration with Any Testing Framework
890
- - Collecting coverage data when any stage of the test is completed, and adding the coverage data to the coverage reporter.
891
- - Generating the coverage reports after the completion of all tests.
892
- - see [Multiprocessing Support](#multiprocessing-support)
893
929
 
894
930
  ### VSCode Extension
895
931
  - [Coverage Gutters](https://github.com/ryanluker/vscode-coverage-gutters) - Display test coverage generated by lcov or xml in VSCode editor.