monocart-coverage-reports 2.5.9 → 2.6.0

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
@@ -1,779 +1,788 @@
1
- # Monocart Coverage Reports
2
-
3
- [![](https://img.shields.io/npm/v/monocart-coverage-reports)](https://www.npmjs.com/package/monocart-coverage-reports)
4
- [![](https://badgen.net/npm/dw/monocart-coverage-reports)](https://www.npmjs.com/package/monocart-coverage-reports)
5
- ![](https://img.shields.io/librariesio/github/cenfun/monocart-coverage-reports)
6
- ![](https://img.shields.io/github/license/cenfun/monocart-coverage-reports)
7
- ![](https://img.shields.io/github/actions/workflow/status/cenfun/monocart-coverage-reports/static.yml)
8
-
9
-
10
- > Code coverage tool to generate native [V8](https://v8.dev/blog/javascript-code-coverage) reports or [Istanbul](https://istanbul.js.org/) reports.
11
-
12
- * [Usage](#usage)
13
- * [Default Options](#default-options)
14
- * [Available Reports](#available-reports)
15
- * [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
16
- * [onEnd Hook](#onend-hook)
17
- * [`mcr` CLI](#mcr-cli)
18
- * [Compare Reports](#compare-reports)
19
- * [Compare Workflows](#compare-workflows)
20
- * [Collecting Istanbul Coverage Data](#collecting-istanbul-coverage-data)
21
- * [Collecting V8 Coverage Data](#collecting-v8-coverage-data)
22
- * [Manually Resolve the Sourcemap](#manually-resolve-the-sourcemap)
23
- * [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
24
- * [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
25
- * [Multiprocessing Support](#multiprocessing-support)
26
- * [Merge Coverage Reports](#merge-coverage-reports)
27
- * [Resolve `sourcePath` for the Source Files](#resolve-sourcepath-for-the-source-files)
28
- * [Adding Empty Coverage for Untested Files](#adding-empty-coverage-for-untested-files)
29
- * [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)
36
- * [Integration](#integration)
37
- - [Playwright](#playwright)
38
- - [Jest](#jest)
39
- - [Vitest](#vitest)
40
- - [CodeceptJS](#codeceptjs)
41
- - [WebdriverIO](#webdriverio)
42
- - [Codecov](#codecov)
43
- - [Coveralls](#coveralls)
44
- - [Sonar Cloud](#sonar-cloud)
45
- - [Integration with Any Testing Framework](#integration-with-any-testing-framework)
46
- * [Thanks](#thanks)
47
-
48
- ## Usage
49
- ```js
50
- const MCR = require('monocart-coverage-reports');
51
- const coverageOptions = {
52
- name: 'My Coverage Report - 2024-02-28',
53
- outputDir: './coverage-reports',
54
- reports: ["v8", "console-details"]
55
- }
56
- const coverageReport = MCR(coverageOptions);
57
- coverageReport.cleanCache();
58
-
59
- await coverageReport.add(coverageData1);
60
- await coverageReport.add(coverageData2);
61
-
62
- await coverageReport.generate();
63
-
64
- // Or
65
- // const { CoverageReport } = require('monocart-coverage-reports');
66
- // const coverageReport = new CoverageReport(coverageOptions);
67
- ```
68
- - [example v8](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js)
69
- - [example istanbul](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
70
-
71
- ## Default Options
72
- - [lib/default/options.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/lib/default/options.js)
73
-
74
- ## Available Reports
75
- > V8 build-in reports (V8 data only):
76
- - `v8`
77
- - 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/)
78
- - 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/)
79
-
80
- ![](./assets/v8.gif)
81
-
82
- - `v8-json`
83
- - [V8 coverage-report.json](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json)
84
- - `codecov`
85
- - coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
86
- - `console-details` Show file coverage and uncovered lines in the console. Like `text`, but for V8. For Github actions, we can enforce color with env: `FORCE_COLOR: true`.
87
-
88
- ![](./assets/console-details.png)
89
-
90
- > Istanbul build-in reports (both V8 and istanbul data):
91
- - `clover`
92
- - `cobertura`
93
- - `html`
94
- - [Istanbul html](https://cenfun.github.io/monocart-coverage-reports/istanbul/)
95
- - [V8 to Istanbul](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/istanbul)
96
- - `html-spa`
97
- - [Istanbul html-spa](https://cenfun.github.io/monocart-coverage-reports/istanbul/html-spa/)
98
- - `json`
99
- - `json-summary`
100
- - `lcov`
101
- - `lcovonly`
102
- - [V8 lcov.info](https://cenfun.github.io/monocart-coverage-reports/v8/lcov.info)
103
- - [Istanbul lcov.info](https://cenfun.github.io/monocart-coverage-reports/istanbul/lcov.info)
104
- - `none`
105
- - `teamcity`
106
- - `text`
107
- - `text-lcov`
108
- - `text-summary`
109
-
110
- > Other reports:
111
- - `console-summary` shows coverage summary in the console
112
-
113
- ![](./assets/console-summary.png)
114
-
115
- - `raw` only keep all original data, which can be used for other reports input with `inputDir`
116
- - see [Merge Coverage Reports](#merge-coverage-reports)
117
-
118
- - Custom Reporter
119
- ```js
120
- {
121
- reports: [
122
- [path.resolve('./test/custom-istanbul-reporter.js'), {
123
- type: 'istanbul',
124
- file: 'custom-istanbul-coverage.text'
125
- }],
126
- [path.resolve('./test/custom-v8-reporter.js'), {
127
- type: 'v8',
128
- outputFile: 'custom-v8-coverage.json'
129
- }],
130
- [path.resolve('./test/custom-v8-reporter.mjs'), {
131
- type: 'both'
132
- }]
133
- ]
134
- }
135
- ```
136
- - istanbul custom reporter
137
- > example: [./test/custom-istanbul-reporter.js](./test/custom-istanbul-reporter.js), see [istanbul built-in reporters' implementation](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) for reference.
138
- - v8 custom reporter
139
- > example: [./test/custom-v8-reporter.js](./test/custom-v8-reporter.js)
140
-
141
- ### Multiple Reports:
142
- ```js
143
- const MCR = require('monocart-coverage-reports');
144
- const coverageOptions = {
145
- outputDir: './coverage-reports',
146
- reports: [
147
- // build-in reports
148
- ['console-summary'],
149
- ['v8'],
150
- ['html', {
151
- subdir: 'istanbul'
152
- }],
153
- ['json', {
154
- file: 'my-json-file.json'
155
- }],
156
- 'lcovonly',
157
-
158
- // custom reports
159
- // Specify reporter name with the NPM package
160
- ["custom-reporter-1"],
161
- ["custom-reporter-2", {
162
- type: "istanbul",
163
- key: "value"
164
- }],
165
- // Specify reporter name with local path
166
- ['/absolute/path/to/custom-reporter.js']
167
-
168
- ]
169
- }
170
- const coverageReport = MCR(coverageOptions);
171
- coverageReport.cleanCache();
172
- ```
173
-
174
- ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
175
- When V8 coverage data collected, it actually contains the data of all entry files, for example:
176
- ```
177
- dist/main.js
178
- dist/vendor.js
179
- dist/something-else.js
180
- ```
181
- 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.
182
- ```
183
- dist/main.js
184
- ```
185
- 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`.
186
- ```
187
- > src/index.js
188
- > src/components/app.js
189
- > node_modules/dependency/dist/dependency.js
190
- ```
191
- We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
192
- ```
193
- > src/index.js
194
- > src/components/app.js
195
- ```
196
- For example:
197
- ```js
198
- const coverageOptions = {
199
- entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
200
- sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
201
- };
202
- ```
203
- Or using `minimatch` pattern:
204
- ```js
205
- const coverageOptions = {
206
- entryFilter: "**/main.js",
207
- sourceFilter: "**/src/**"
208
- };
209
- // supports multiple patterns:
210
- const coverageOptions = {
211
- entryFilter: {
212
- '**/vendor.js': false,
213
- '**/main.js': true
214
- },
215
- sourceFilter: {
216
- '**/src/**': true
217
- }
218
- };
219
- ```
220
-
221
- ## onEnd Hook
222
- For example, checking thresholds:
223
- ```js
224
- const EC = require('eight-colors');
225
- const coverageOptions = {
226
- name: 'My Coverage Report',
227
- outputDir: './coverage-reports',
228
- onEnd: (coverageResults) => {
229
- const thresholds = {
230
- bytes: 80,
231
- lines: 60
232
- };
233
- console.log('check thresholds ...', thresholds);
234
- const errors = [];
235
- const { summary } = coverageResults;
236
- Object.keys(thresholds).forEach((k) => {
237
- const pct = summary[k].pct;
238
- if (pct < thresholds[k]) {
239
- errors.push(`Coverage threshold for ${k} (${pct} %) not met: ${thresholds[k]} %`);
240
- }
241
- });
242
- if (errors.length) {
243
- const errMsg = errors.join('\n');
244
- console.log(EC.red(errMsg));
245
- // throw new Error(errMsg);
246
- // process.exit(1);
247
- }
248
- }
249
- }
250
- ```
251
-
252
- ## `mcr` CLI
253
- > 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`.
254
- - Global mode
255
- ```sh
256
- npm i monocart-coverage-reports -g
257
- mcr "node ./test/test-node-env.js" -r v8,console-summary --lcov
258
- ```
259
- - Current working directory mode
260
- ```sh
261
- npm i monocart-coverage-reports
262
- npx mcr "node ./test/test-node-env.js" -r v8,console-summary --lcov
263
- ```
264
- - CLI Options
265
- ```sh
266
- Usage: mcr [options] <command>
267
-
268
- CLI to generate coverage reports
269
-
270
- Arguments:
271
- command command to execute
272
-
273
- Options:
274
- -V, --version output the version number
275
- -c, --config <path> custom config path
276
- -o, --outputDir <dir> output dir for reports
277
- -r, --reports <name[,name]> coverage reports to use
278
- -n, --name <name> report name for title
279
- -i, --inputDir <dir> input dir for merging raw files
280
- --entryFilter <pattern> entry url filter
281
- --sourceFilter <pattern> source path filter
282
- --outputFile <path> output file for v8 report
283
- --inline inline html for v8 report
284
- --assetsPath <path> assets path if not inline
285
- --lcov generate lcov.info file
286
- --logging <logging> off, error, info, debug
287
- -h, --help display help for command
288
- ```
289
- - Supports loading default configuration file
290
- - `.mcrrc`
291
- - `mcr.config.json`
292
- - `mcr.config.mjs`
293
- - `mcr.config.cjs`
294
- - `mcr.config.js`
295
- - Specify custom configuration file
296
- ```sh
297
- mcr "node ./test.js" -c path-to/my-custom-config.js
298
- ```
299
-
300
- ## Compare Reports
301
- | | Istanbul | V8 | V8 to Istanbul |
302
- | :--------------| :------ | :------ | :---------------------- |
303
- | Coverage data | [Istanbul](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md) (Object) | [V8](#v8-coverage-data-format) (Array) | [V8](#v8-coverage-data-format) (Array) |
304
- | Output | [Istanbul reports](#available-reports) | [V8 reports](#available-reports) | [Istanbul reports](#available-reports) |
305
- | - Bytes | ❌ | ✅ | ❌ |
306
- | - Statements | ✅ | ✅ | ✅ |
307
- | - Branches | ✅ | ✅ | ✅ |
308
- | - Functions | ✅ | ✅ | ✅ |
309
- | - Lines | ✅ | ✅ | ✅ |
310
- | - Execution counts | ✅ | ✅ | ✅ |
311
- | CSS coverage | ❌ | ✅ | ✅ |
312
- | Minified code | ❌ | ✅ | ❌ |
313
-
314
- ## Compare Workflows
315
- - Istanbul Workflows
316
- - 1, [Collecting Istanbul coverage data](#collecting-istanbul-coverage-data)
317
- - 2, Adding coverage data and generating coverage report
318
-
319
- - V8 Workflows
320
- - 1, [Collecting V8 coverage data](#collecting-v8-coverage-data)
321
- - 3, Adding coverage data and generating coverage report
322
-
323
- ## Collecting Istanbul Coverage Data
324
- - Instrumenting source code
325
- > Before collecting Istanbul coverage data, It requires your source code is instrumented with Istanbul
326
- - 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)
327
- - rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
328
- - vite: [vite-plugin-istanbul](https://github.com/ifaxity/vite-plugin-istanbul)
329
- - Browser
330
- - Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
331
- - Node.js
332
- - Collecting coverage data from `global.__coverage__`
333
-
334
- ## Collecting V8 Coverage Data
335
- - For source code: enable `sourcemap` and do not compress/minify:
336
- - [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)
337
- - [rollup](https://rollupjs.org/configuration-options/): `sourcemap: true`
338
- - [vite](https://vitejs.dev/config/build-options.html): `sourcemap: true` and `minify: false`
339
- - [esbuild](https://esbuild.github.io/api/): `sourcemap: true` and `minify: false`
340
- - [Manually Resolve the Sourcemap](#manually-resolve-the-sourcemap)
341
- - Browser (Chromium Only)
342
- > Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api):
343
- - [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)
344
- - see [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
345
- - Node.js
346
- - see [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
347
-
348
- ## Manually Resolve the Sourcemap
349
- > 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.
350
- ```js
351
- const jsCoverage = await page.coverage.stopJSCoverage();
352
- jsCoverage.forEach((entry) => {
353
- // read sourcemap for the my-dist.js manually
354
- if (entry.url.endsWith('my-dist.js')) {
355
- entry.sourceMap = JSON.parse(fs.readFileSync('dist/my-dist.js.map').toString('utf-8'));
356
- }
357
- });
358
-
359
- await MCR(coverageOptions).add(jsCoverage);
360
-
361
- ```
362
-
363
- ## Collecting Raw V8 Coverage Data with Puppeteer
364
- > 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)
365
- ```js
366
- await Promise.all([
367
- page.coverage.startJSCoverage({
368
- resetOnNavigation: false,
369
- // provide raw v8 coverage data
370
- includeRawScriptCoverage: true
371
- }),
372
- page.coverage.startCSSCoverage({
373
- resetOnNavigation: false
374
- })
375
- ]);
376
-
377
- await page.goto(url);
378
-
379
- const [jsCoverage, cssCoverage] = await Promise.all([
380
- page.coverage.stopJSCoverage(),
381
- page.coverage.stopCSSCoverage()
382
- ]);
383
-
384
- // to raw V8 script coverage
385
- const coverageData = [... jsCoverage.map((it) => {
386
- return {
387
- source: it.text,
388
- ... it.rawScriptCoverage
389
- };
390
- }), ... cssCoverage];
391
- ```
392
-
393
- ## Node.js V8 Coverage Report for Server Side
394
- Possible solutions:
395
- - [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
396
- - 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.
397
- - Read the JSON file(s) from the `dir` and generate coverage report.
398
- - Example:
399
- > 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)
400
-
401
- - [V8](https://nodejs.org/docs/latest/api/v8.html#v8takecoverage) API + NODE_V8_COVERAGE
402
- - 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.
403
- - Example:
404
- > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage-api` node [./test/test-node-api.js](./test/test-node-api.js)
405
-
406
- - [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API
407
- - Connecting to the V8 inspector and enable V8 coverage.
408
- - Taking coverage data and adding it to the report.
409
- - Example:
410
- > node [./test/test-node-ins.js](./test/test-node-ins.js)
411
-
412
- - [CDP](https://chromedevtools.github.io/devtools-protocol/) API
413
- - Enabling [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started/).
414
- - Collecting coverage data with CDP API.
415
- - Example:
416
- > node --inspect=9229 [./test/test-node-cdp.js](./test/test-node-cdp.js)
417
-
418
- - [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started) + CDP + NODE_V8_COVERAGE + V8 API
419
- - 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.
420
- - Example for [koa](https://github.com/koajs/koa) web server:
421
- > node [./test/test-node-koa.js](./test/test-node-koa.js)
422
-
423
- - [Child Process](https://nodejs.org/docs/latest/api/child_process.html) + NODE_V8_COVERAGE
424
- - see [`mcr` CLI](#mcr-cli)
425
-
426
- ## Multiprocessing Support
427
- > 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)
428
- - Main process, before the start of testing
429
- ```js
430
- const MCR = require('monocart-coverage-reports');
431
- const coverageOptions = require('path-to/same-options.js');
432
- const coverageReport = MCR(coverageOptions);
433
- // clean previous cache before the start of testing
434
- // unless the running environment is new and no cache
435
- coverageReport.cleanCache();
436
- ```
437
-
438
- - Sub process 1, testing stage 1
439
- ```js
440
- const MCR = require('monocart-coverage-reports');
441
- const coverageOptions = require('path-to/same-options.js');
442
- const coverageReport = MCR(coverageOptions);
443
- // do not clean cache in the stage
444
- await coverageReport.add(coverageData1);
445
- ```
446
-
447
- - Sub process 2, testing stage 2
448
- ```js
449
- const MCR = require('monocart-coverage-reports');
450
- const coverageOptions = require('path-to/same-options.js');
451
- const coverageReport = MCR(coverageOptions);
452
- // do not clean cache in the stage
453
- await coverageReport.add(coverageData2);
454
- ```
455
-
456
- - Main process, after the completion of testing
457
- ```js
458
- // generate coverage reports after the completion of testing
459
- const MCR = require('monocart-coverage-reports');
460
- const coverageOptions = require('path-to/same-options.js');
461
- const coverageReport = MCR(coverageOptions);
462
- // do not clean cache before generating reports
463
- await coverageReport.generate();
464
- ```
465
-
466
- ## Merge Coverage Reports
467
- The following usage scenarios may require merging coverage reports:
468
- - 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.
469
- - 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.
470
- - 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.
471
-
472
- If the reports cannot be merged automatically, then here is how to manually merge the reports.
473
- First, using the `raw` report to export the original coverage data to the specified directory.
474
- ```js
475
- const coverageOptions = {
476
- name: 'My Unit Test Coverage Report',
477
- outputDir: "./coverage-reports/unit",
478
- reports: [
479
- ['raw', {
480
- // relative path will be "./coverage-reports/unit/raw"
481
- outputDir: "raw"
482
- }],
483
- ['v8'],
484
- ['console-summary']
485
- ]
486
- };
487
- ```
488
- Then, after all the tests are completed, generate a merged report with option `inputDir`:
489
- ```js
490
- // esm syntax
491
- import fs from "fs";
492
- import { CoverageReport } from 'monocart-coverage-reports';
493
- const coverageOptions = {
494
- name: 'My Merged Coverage Report',
495
- inputDir: [
496
- './coverage-reports/unit/raw',
497
- './coverage-reports/e2e/raw'
498
- ],
499
- outputDir: './coverage-reports/merged',
500
- reports: [
501
- ['v8'],
502
- ['console-summary']
503
- ],
504
- onEnd: () => {
505
- // remove the raw files if it useless
506
- fs.rmSync('./coverage-reports/unit/raw', {
507
- recursive: true,
508
- force: true
509
- })
510
- }
511
- };
512
- await new CoverageReport(coverageOptions).generate();
513
- ```
514
-
515
- ## Resolve `sourcePath` for the Source Files
516
- If the source file comes from the sourcemap, then its path is a virtual path. Using the `sourcePath` option to resolve a custom path.
517
- For example, we have tested multiple dist files, which contain some common files. We hope to merge the coverage of the same files, so we need to unify the `sourcePath` in order to be able to merge the coverage data.
518
- ```js
519
- const coverageOptions = {
520
- sourcePath: (filePath) => {
521
- // Remove the virtual prefix
522
- const list = ['my-dist-file1/', 'my-dist-file2/'];
523
- for (const str of list) {
524
- if (filePath.startsWith(str)) {
525
- return filePath.slice(str.length);
526
- }
527
- }
528
- return filePath;
529
- }
530
- };
531
- ```
532
- It also supports simple key/value replacement:
533
- ```js
534
- const coverageOptions = {
535
- sourcePath: {
536
- 'my-dist-file1/': '',
537
- 'my-dist-file2/': ''
538
- }
539
- };
540
- ```
541
-
542
- ## Adding Empty Coverage for Untested Files
543
- By default the untested files will not be included in the coverage report, we can add empty coverage data for all files with option `all`, the untested files will show 0% coverage.
544
- ```js
545
- const coverageOptions = {
546
- all: {
547
- dir: ['./src'],
548
- filter: (filePath) => {
549
- return true;
550
- }
551
- }
552
- };
553
- ```
554
- The filter also supports `minimatch` pattern:
555
- ```js
556
- const coverageOptions = {
557
- all: {
558
- dir: ['./src'],
559
- filter: '**/*.js'
560
- }
561
- };
562
- // or multiple patterns
563
- const coverageOptions = {
564
- all: {
565
- dir: ['./src'],
566
- filter: {
567
- // exclude files
568
- '**/ignored-*.js': false,
569
- '**/*.html': false,
570
- '**/*.ts': false,
571
- // empty css coverage
572
- '**/*.scss': "css",
573
- '**/*': true
574
- }
575
- }
576
- };
577
- ```
578
-
579
- ## Ignoring Uncovered Codes
580
- To ignore codes, use the special comment which starts with `v8 ignore `:
581
- - Ignoring all until stop
582
- ```js
583
- /* v8 ignore start */
584
- function uncovered() {
585
- }
586
- /* v8 ignore stop */
587
- ```
588
- - Ignoring the next line or next N lines
589
- ```js
590
- /* v8 ignore next */
591
- const os = platform === 'wind32' ? 'Windows' : 'Other';
592
-
593
- const os = platform === 'wind32' ? 'Windows' /* v8 ignore next */ : 'Other';
594
-
595
- // v8 ignore next 3
596
- if (platform === 'linux') {
597
- console.log('hello linux');
598
- }
599
- ```
600
-
601
- ## Chromium Coverage API
602
- - [V8 coverage report](https://v8.dev/blog/javascript-code-coverage) - Native support for JavaScript code coverage to V8. (Chromium only)
603
- - [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
604
- - [Puppeteer Coverage class](https://pptr.dev/api/puppeteer.coverage)
605
- - [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)
606
-
607
- ## V8 Coverage Data Format
608
- ```js
609
- // Coverage data for a source range.
610
- export interface CoverageRange {
611
- // JavaScript script source offset for the range start.
612
- startOffset: integer;
613
- // JavaScript script source offset for the range end.
614
- endOffset: integer;
615
- // Collected execution count of the source range.
616
- count: integer;
617
- }
618
-
619
- // Coverage data for a JavaScript function.
620
- /**
621
- * @functionName can be an empty string.
622
- * @ranges is always non-empty. The first range is called the "root range".
623
- * @isBlockCoverage indicates if the function has block coverage information.
624
- If this is false, it usually means that the functions was never called.
625
- It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
626
- */
627
- export interface FunctionCoverage {
628
- // JavaScript function name.
629
- functionName: string;
630
- // Source ranges inside the function with coverage data.
631
- ranges: CoverageRange[];
632
- // Whether coverage data for this function has block granularity.
633
- isBlockCoverage: boolean;
634
- }
635
-
636
- // Coverage data for a JavaScript script.
637
- export interface ScriptCoverage {
638
- // JavaScript script id.
639
- scriptId: Runtime.ScriptId;
640
- // JavaScript script name or url.
641
- url: string;
642
- // Functions contained in the script that has coverage data.
643
- functions: FunctionCoverage[];
644
- }
645
-
646
- export type V8CoverageData = ScriptCoverage[];
647
- ```
648
- see devtools-protocol [ScriptCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ScriptCoverage) and [v8-coverage](https://github.com/bcoe/v8-coverage)
649
-
650
- ## How to convert V8 to Istanbul
651
- ### Using [v8-to-istanbul](https://github.com/istanbuljs/v8-to-istanbul)
652
- 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:
653
- - 1, The source mapping does not work well if the position is between the two consecutive mappings. for example:
654
- ```js
655
- const a = tf ? 'true' : 'false';
656
- ^ ^ ^
657
- m1 p m2
658
- ```
659
- > `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.
660
-
661
- - 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.
662
-
663
- ### How Monocart Works
664
- We implemented new converter:
665
- - 1, Trying to fix the middle position if not found the exact mapping for the position.
666
- - 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`.
667
-
668
- | AST | V8 |
669
- | :---------------------| :------------- |
670
- | AssignmentPattern | 🛇 Not Support |
671
- | ConditionalExpression | ✔ |
672
- | IfStatement | ✔ |
673
- | LogicalExpression | ✔ |
674
- | SwitchStatement | ✔ |
675
-
676
- ## Debug for Coverage and Sourcemap
677
- > 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.
678
- - Start debugging for v8 report with option `logging: 'debug'`
679
- ```js
680
- const coverageOptions = {
681
- logging: 'debug',
682
- reports: [
683
- ['v8'],
684
- ['console-summary']
685
- ]
686
- };
687
- ```
688
- 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.
689
- ![](./assets/debug-coverage.png)
690
-
691
- - Check sourcemap with [Source Map Visualization](https://evanw.github.io/source-map-visualization/)
692
-
693
- ![](./assets/debug-sourcemap.png)
694
-
695
- ## Integration
696
-
697
- ### [Playwright](https://github.com/microsoft/playwright)
698
- - [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)
699
- - Coverage for component testing:
700
- - [playwright-ct-vue](https://github.com/cenfun/playwright-ct-vue)
701
- - [playwright-ct-react](https://github.com/cenfun/playwright-ct-react)
702
- - [playwright-ct-svelte](https://github.com/cenfun/playwright-ct-svelte)
703
- - Coverage for Next.js, both server side and client side:
704
- - [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright)
705
- - [nextjs-with-playwright-istanbul](https://github.com/cenfun/nextjs-with-playwright-istanbul)
706
-
707
- ### [Jest](https://github.com/jestjs/jest/)
708
- - [jest-monocart-coverage](https://github.com/cenfun/jest-monocart-coverage) - A Jest custom reporter for coverage reports
709
- - Example for Jest (unit) + Puppeteer (e2e) + Codecov: [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js)
710
-
711
- ### [Vitest](https://github.com/vitest-dev/vitest)
712
- - [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - A Vitest custom provider module for coverage reports
713
-
714
- ### [CodeceptJS](https://github.com/codeceptjs/CodeceptJS)
715
- - [codeceptjs-monocart-coverage](https://github.com/cenfun/codeceptjs-monocart-coverage) - A CodeceptJS plugin for coverage reports
716
-
717
- ### [WebdriverIO](https://github.com/webdriverio/webdriverio)
718
- - [wdio-monocart-service](https://github.com/cenfun/wdio-monocart-service) - A WebdriverIO service for coverage reports
719
-
720
- ### [Codecov](https://codecov.com/)
721
- [![codecov](https://codecov.io/gh/cenfun/monocart-coverage-reports/graph/badge.svg?token=H0LW7UKYU3)](https://codecov.io/gh/cenfun/monocart-coverage-reports)
722
- - Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
723
- ```js
724
- const coverageOptions = {
725
- outputDir: "./coverage-reports",
726
- reports: [
727
- ['codecov']
728
- ]
729
- };
730
- ```
731
- - Github actions example:
732
- ```yml
733
- - name: Codecov
734
- uses: codecov/codecov-action@v3
735
- with:
736
- files: ./coverage-reports/codecov.json
737
- ```
738
- ### [Coveralls](https://coveralls.io/)
739
- [![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)
740
- - Using `lcov` report:
741
- ```js
742
- const coverageOptions = {
743
- outputDir: "./coverage-reports",
744
- lcov: true
745
- };
746
- ```
747
- - Github actions example:
748
- ```yml
749
- - name: Coveralls
750
- uses: coverallsapp/github-action@v2
751
- with:
752
- files: ./coverage-reports/lcov.info
753
- ```
754
- ### [Sonar Cloud](https://sonarcloud.io/)
755
- [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=monocart-coverage-reports&metric=coverage)](https://sonarcloud.io/summary/new_code?id=monocart-coverage-reports)
756
- - Using `lcov` report. Github actions example:
757
- ```yml
758
- - name: Analyze with SonarCloud
759
- uses: sonarsource/sonarcloud-github-action@master
760
- env:
761
- SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
762
- with:
763
- projectBaseDir: ./
764
- args: >
765
- -Dsonar.organization=cenfun
766
- -Dsonar.projectKey=monocart-coverage-reports
767
- -Dsonar.projectName=monocart-coverage-reports
768
- -Dsonar.javascript.lcov.reportPaths=docs/mcr/lcov.info
769
- -Dsonar.sources=lib
770
- -Dsonar.tests=test
771
- -Dsonar.exclusions=dist/*,packages/*
772
- ```
773
- ### Integration with Any Testing Framework
774
- - Collecting coverage data when any stage of the test is completed, and adding the coverage data to the coverage reporter.
775
- - Generating the coverage reports after the completion of all tests.
776
- - see [Multiprocessing Support](#multiprocessing-support)
777
-
778
- ## Thanks
1
+ # Monocart Coverage Reports
2
+
3
+ [![](https://img.shields.io/npm/v/monocart-coverage-reports)](https://www.npmjs.com/package/monocart-coverage-reports)
4
+ [![](https://badgen.net/npm/dw/monocart-coverage-reports)](https://www.npmjs.com/package/monocart-coverage-reports)
5
+ ![](https://img.shields.io/librariesio/github/cenfun/monocart-coverage-reports)
6
+ ![](https://img.shields.io/github/license/cenfun/monocart-coverage-reports)
7
+ ![](https://img.shields.io/github/actions/workflow/status/cenfun/monocart-coverage-reports/static.yml)
8
+
9
+
10
+ > Code coverage tool to generate native [V8](https://v8.dev/blog/javascript-code-coverage) reports or [Istanbul](https://istanbul.js.org/) reports.
11
+
12
+ * [Usage](#usage)
13
+ * [Default Options](#default-options)
14
+ * [Available Reports](#available-reports)
15
+ * [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
16
+ * [onEnd Hook](#onend-hook)
17
+ * [`mcr` CLI](#mcr-cli)
18
+ * [Compare Reports](#compare-reports)
19
+ * [Compare Workflows](#compare-workflows)
20
+ * [Collecting Istanbul Coverage Data](#collecting-istanbul-coverage-data)
21
+ * [Collecting V8 Coverage Data](#collecting-v8-coverage-data)
22
+ * [Manually Resolve the Sourcemap](#manually-resolve-the-sourcemap)
23
+ * [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
24
+ * [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
25
+ * [Multiprocessing Support](#multiprocessing-support)
26
+ * [Merge Coverage Reports](#merge-coverage-reports)
27
+ * [Resolve `sourcePath` for the Source Files](#resolve-sourcepath-for-the-source-files)
28
+ * [Adding Empty Coverage for Untested Files](#adding-empty-coverage-for-untested-files)
29
+ * [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)
36
+ * [Integration](#integration)
37
+ - [Playwright](#playwright)
38
+ - [Jest](#jest)
39
+ - [Vitest](#vitest)
40
+ - [CodeceptJS](#codeceptjs)
41
+ - [WebdriverIO](#webdriverio)
42
+ - [Codecov](#codecov)
43
+ - [Coveralls](#coveralls)
44
+ - [Sonar Cloud](#sonar-cloud)
45
+ - [Integration with Any Testing Framework](#integration-with-any-testing-framework)
46
+ * [Thanks](#thanks)
47
+
48
+ ## Usage
49
+ ```js
50
+ const MCR = require('monocart-coverage-reports');
51
+ const coverageOptions = {
52
+ name: 'My Coverage Report - 2024-02-28',
53
+ outputDir: './coverage-reports',
54
+ reports: ["v8", "console-details"]
55
+ }
56
+ const coverageReport = MCR(coverageOptions);
57
+ coverageReport.cleanCache();
58
+
59
+ await coverageReport.add(coverageData1);
60
+ await coverageReport.add(coverageData2);
61
+
62
+ await coverageReport.generate();
63
+
64
+ // Or
65
+ // const { CoverageReport } = require('monocart-coverage-reports');
66
+ // const coverageReport = new CoverageReport(coverageOptions);
67
+ ```
68
+ - [example v8](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js)
69
+ - [example istanbul](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
70
+
71
+ ## Default Options
72
+ - [lib/default/options.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/lib/default/options.js)
73
+
74
+ ## Available Reports
75
+
76
+ > V8 build-in reports (V8 data only):
77
+
78
+ - `v8`
79
+ - 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/)
80
+ - 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/)
81
+
82
+ ![](./assets/v8.gif)
83
+
84
+ - `v8-json`
85
+ - [V8 coverage-report.json](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json)
86
+
87
+ > Istanbul build-in reports (both V8 and istanbul data):
88
+
89
+ - `clover`
90
+ - `cobertura`
91
+ - `html`
92
+ - [Istanbul html](https://cenfun.github.io/monocart-coverage-reports/istanbul/)
93
+ - [V8 to Istanbul](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/istanbul)
94
+ - `html-spa`
95
+ - [Istanbul html-spa](https://cenfun.github.io/monocart-coverage-reports/istanbul/html-spa/)
96
+ - `json`
97
+ - `json-summary`
98
+ - `lcov`
99
+ - `lcovonly`
100
+ - [V8 lcov.info](https://cenfun.github.io/monocart-coverage-reports/v8/lcov.info)
101
+ - [Istanbul lcov.info](https://cenfun.github.io/monocart-coverage-reports/istanbul/lcov.info)
102
+ - `none`
103
+ - `teamcity`
104
+ - `text`
105
+ - `text-lcov`
106
+ - `text-summary`
107
+
108
+ > Other build-in reports (both V8 and istanbul data):
109
+
110
+ - `codecov`
111
+ - coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
112
+
113
+ - `console-summary` shows coverage summary in the console
114
+
115
+ ![](./assets/console-summary.png)
116
+
117
+ - `console-details` Show file coverage and uncovered lines in the console. Like `text`, but for V8. For Github actions, we can enforce color with env: `FORCE_COLOR: true`.
118
+
119
+ ![](./assets/console-details.png)
120
+
121
+ - `raw` only keep all original data, which can be used for other reports input with `inputDir`
122
+ - see [Merge Coverage Reports](#merge-coverage-reports)
123
+
124
+ - Custom Reporter
125
+ ```js
126
+ {
127
+ reports: [
128
+ [path.resolve('./test/custom-istanbul-reporter.js'), {
129
+ type: 'istanbul',
130
+ file: 'custom-istanbul-coverage.text'
131
+ }],
132
+ [path.resolve('./test/custom-v8-reporter.js'), {
133
+ type: 'v8',
134
+ outputFile: 'custom-v8-coverage.json'
135
+ }],
136
+ [path.resolve('./test/custom-v8-reporter.mjs'), {
137
+ type: 'both'
138
+ }]
139
+ ]
140
+ }
141
+ ```
142
+ - istanbul custom reporter
143
+ > example: [./test/custom-istanbul-reporter.js](./test/custom-istanbul-reporter.js), see [istanbul built-in reporters' implementation](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) for reference.
144
+ - v8 custom reporter
145
+ > example: [./test/custom-v8-reporter.js](./test/custom-v8-reporter.js)
146
+
147
+ ### Multiple Reports:
148
+ ```js
149
+ const MCR = require('monocart-coverage-reports');
150
+ const coverageOptions = {
151
+ outputDir: './coverage-reports',
152
+ reports: [
153
+ // build-in reports
154
+ ['console-summary'],
155
+ ['v8'],
156
+ ['html', {
157
+ subdir: 'istanbul'
158
+ }],
159
+ ['json', {
160
+ file: 'my-json-file.json'
161
+ }],
162
+ 'lcovonly',
163
+
164
+ // custom reports
165
+ // Specify reporter name with the NPM package
166
+ ["custom-reporter-1"],
167
+ ["custom-reporter-2", {
168
+ type: "istanbul",
169
+ key: "value"
170
+ }],
171
+ // Specify reporter name with local path
172
+ ['/absolute/path/to/custom-reporter.js']
173
+
174
+ ]
175
+ }
176
+ const coverageReport = MCR(coverageOptions);
177
+ coverageReport.cleanCache();
178
+ ```
179
+
180
+ ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
181
+ When V8 coverage data collected, it actually contains the data of all entry files, for example:
182
+ ```
183
+ dist/main.js
184
+ dist/vendor.js
185
+ dist/something-else.js
186
+ ```
187
+ 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.
188
+ ```
189
+ dist/main.js
190
+ ```
191
+ 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`.
192
+ ```
193
+ > src/index.js
194
+ > src/components/app.js
195
+ > node_modules/dependency/dist/dependency.js
196
+ ```
197
+ We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
198
+ ```
199
+ > src/index.js
200
+ > src/components/app.js
201
+ ```
202
+ For example:
203
+ ```js
204
+ const coverageOptions = {
205
+ entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
206
+ sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
207
+ };
208
+ ```
209
+ Or using `minimatch` pattern:
210
+ ```js
211
+ const coverageOptions = {
212
+ entryFilter: "**/main.js",
213
+ sourceFilter: "**/src/**"
214
+ };
215
+ // supports multiple patterns:
216
+ const coverageOptions = {
217
+ entryFilter: {
218
+ '**/vendor.js': false,
219
+ '**/main.js': true
220
+ },
221
+ sourceFilter: {
222
+ '**/src/**': true
223
+ }
224
+ };
225
+ ```
226
+
227
+ ## onEnd Hook
228
+ For example, checking thresholds:
229
+ ```js
230
+ const EC = require('eight-colors');
231
+ const coverageOptions = {
232
+ name: 'My Coverage Report',
233
+ outputDir: './coverage-reports',
234
+ onEnd: (coverageResults) => {
235
+ const thresholds = {
236
+ bytes: 80,
237
+ lines: 60
238
+ };
239
+ console.log('check thresholds ...', thresholds);
240
+ const errors = [];
241
+ const { summary } = coverageResults;
242
+ Object.keys(thresholds).forEach((k) => {
243
+ const pct = summary[k].pct;
244
+ if (pct < thresholds[k]) {
245
+ errors.push(`Coverage threshold for ${k} (${pct} %) not met: ${thresholds[k]} %`);
246
+ }
247
+ });
248
+ if (errors.length) {
249
+ const errMsg = errors.join('\n');
250
+ console.log(EC.red(errMsg));
251
+ // throw new Error(errMsg);
252
+ // process.exit(1);
253
+ }
254
+ }
255
+ }
256
+ ```
257
+
258
+ ## `mcr` CLI
259
+ > 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`.
260
+ - Global mode
261
+ ```sh
262
+ npm i monocart-coverage-reports -g
263
+ mcr "node ./test/test-node-env.js" -r v8,console-summary --lcov
264
+ ```
265
+ - Current working directory mode
266
+ ```sh
267
+ npm i monocart-coverage-reports
268
+ npx mcr "node ./test/test-node-env.js" -r v8,console-summary --lcov
269
+ ```
270
+ - CLI Options
271
+ ```sh
272
+ Usage: mcr [options] <command>
273
+
274
+ CLI to generate coverage reports
275
+
276
+ Arguments:
277
+ command command to execute
278
+
279
+ Options:
280
+ -V, --version output the version number
281
+ -c, --config <path> custom config path
282
+ -o, --outputDir <dir> output dir for reports
283
+ -r, --reports <name[,name]> coverage reports to use
284
+ -n, --name <name> report name for title
285
+ -i, --inputDir <dir> input dir for merging raw files
286
+ --entryFilter <pattern> entry url filter
287
+ --sourceFilter <pattern> source path filter
288
+ --outputFile <path> output file for v8 report
289
+ --inline inline html for v8 report
290
+ --assetsPath <path> assets path if not inline
291
+ --lcov generate lcov.info file
292
+ --logging <logging> off, error, info, debug
293
+ -h, --help display help for command
294
+ ```
295
+ - Supports loading default configuration file
296
+ - `.mcrrc`
297
+ - `mcr.config.json`
298
+ - `mcr.config.mjs`
299
+ - `mcr.config.cjs`
300
+ - `mcr.config.js`
301
+ - Specify custom configuration file
302
+ ```sh
303
+ mcr "node ./test.js" -c path-to/my-custom-config.js
304
+ ```
305
+
306
+ ## Compare Reports
307
+ | | Istanbul | V8 | V8 to Istanbul |
308
+ | :--------------| :------ | :------ | :---------------------- |
309
+ | Coverage data | [Istanbul](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md) (Object) | [V8](#v8-coverage-data-format) (Array) | [V8](#v8-coverage-data-format) (Array) |
310
+ | Output | [Istanbul reports](#available-reports) | [V8 reports](#available-reports) | [Istanbul reports](#available-reports) |
311
+ | - Bytes | ❌ | ✅ | ❌ |
312
+ | - Statements | ✅ | ✅ | ✅ |
313
+ | - Branches | ✅ | ✅ | ✅ |
314
+ | - Functions | ✅ | ✅ | ✅ |
315
+ | - Lines | ✅ | ✅ | ✅ |
316
+ | - Execution counts | ✅ | ✅ | ✅ |
317
+ | CSS coverage | ❌ | ✅ | ✅ |
318
+ | Minified code | ❌ | ✅ | ❌ |
319
+
320
+ ## Compare Workflows
321
+ - Istanbul Workflows
322
+ - 1, [Collecting Istanbul coverage data](#collecting-istanbul-coverage-data)
323
+ - 2, Adding coverage data and generating coverage report
324
+
325
+ - V8 Workflows
326
+ - 1, [Collecting V8 coverage data](#collecting-v8-coverage-data)
327
+ - 3, Adding coverage data and generating coverage report
328
+
329
+ ## Collecting Istanbul Coverage Data
330
+ - Instrumenting source code
331
+ > Before collecting Istanbul coverage data, It requires your source code is instrumented with Istanbul
332
+ - 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)
333
+ - rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
334
+ - vite: [vite-plugin-istanbul](https://github.com/ifaxity/vite-plugin-istanbul)
335
+ - Browser
336
+ - Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
337
+ - Node.js
338
+ - Collecting coverage data from `global.__coverage__`
339
+
340
+ ## Collecting V8 Coverage Data
341
+ - For source code: enable `sourcemap` and do not compress/minify:
342
+ - [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)
343
+ - [rollup](https://rollupjs.org/configuration-options/): `sourcemap: true`
344
+ - [vite](https://vitejs.dev/config/build-options.html): `sourcemap: true` and `minify: false`
345
+ - [esbuild](https://esbuild.github.io/api/): `sourcemap: true` and `minify: false`
346
+ - [Manually Resolve the Sourcemap](#manually-resolve-the-sourcemap)
347
+ - Browser (Chromium Only)
348
+ > Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api):
349
+ - [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)
350
+ - see [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
351
+ - Node.js
352
+ - see [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
353
+
354
+ ## Manually Resolve the Sourcemap
355
+ > 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.
356
+ ```js
357
+ const jsCoverage = await page.coverage.stopJSCoverage();
358
+ jsCoverage.forEach((entry) => {
359
+ // read sourcemap for the my-dist.js manually
360
+ if (entry.url.endsWith('my-dist.js')) {
361
+ entry.sourceMap = JSON.parse(fs.readFileSync('dist/my-dist.js.map').toString('utf-8'));
362
+ }
363
+ });
364
+
365
+ await MCR(coverageOptions).add(jsCoverage);
366
+
367
+ ```
368
+
369
+ ## Collecting Raw V8 Coverage Data with Puppeteer
370
+ > 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)
371
+ ```js
372
+ await Promise.all([
373
+ page.coverage.startJSCoverage({
374
+ resetOnNavigation: false,
375
+ // provide raw v8 coverage data
376
+ includeRawScriptCoverage: true
377
+ }),
378
+ page.coverage.startCSSCoverage({
379
+ resetOnNavigation: false
380
+ })
381
+ ]);
382
+
383
+ await page.goto(url);
384
+
385
+ const [jsCoverage, cssCoverage] = await Promise.all([
386
+ page.coverage.stopJSCoverage(),
387
+ page.coverage.stopCSSCoverage()
388
+ ]);
389
+
390
+ // to raw V8 script coverage
391
+ const coverageData = [... jsCoverage.map((it) => {
392
+ return {
393
+ source: it.text,
394
+ ... it.rawScriptCoverage
395
+ };
396
+ }), ... cssCoverage];
397
+ ```
398
+
399
+ ## Node.js V8 Coverage Report for Server Side
400
+ Possible solutions:
401
+ - [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
402
+ - 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.
403
+ - Read the JSON file(s) from the `dir` and generate coverage report.
404
+ - Example:
405
+ > 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)
406
+
407
+ - [V8](https://nodejs.org/docs/latest/api/v8.html#v8takecoverage) API + NODE_V8_COVERAGE
408
+ - 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.
409
+ - Example:
410
+ > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage-api` node [./test/test-node-api.js](./test/test-node-api.js)
411
+
412
+ - [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API
413
+ - Connecting to the V8 inspector and enable V8 coverage.
414
+ - Taking coverage data and adding it to the report.
415
+ - Example:
416
+ > node [./test/test-node-ins.js](./test/test-node-ins.js)
417
+
418
+ - [CDP](https://chromedevtools.github.io/devtools-protocol/) API
419
+ - Enabling [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started/).
420
+ - Collecting coverage data with CDP API.
421
+ - Example:
422
+ > node --inspect=9229 [./test/test-node-cdp.js](./test/test-node-cdp.js)
423
+
424
+ - [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started) + CDP + NODE_V8_COVERAGE + V8 API
425
+ - 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.
426
+ - Example for [koa](https://github.com/koajs/koa) web server:
427
+ > node [./test/test-node-koa.js](./test/test-node-koa.js)
428
+
429
+ - [Child Process](https://nodejs.org/docs/latest/api/child_process.html) + NODE_V8_COVERAGE
430
+ - see [`mcr` CLI](#mcr-cli)
431
+
432
+ ## Multiprocessing Support
433
+ > 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)
434
+ - Main process, before the start of testing
435
+ ```js
436
+ const MCR = require('monocart-coverage-reports');
437
+ const coverageOptions = require('path-to/same-options.js');
438
+ const coverageReport = MCR(coverageOptions);
439
+ // clean previous cache before the start of testing
440
+ // unless the running environment is new and no cache
441
+ coverageReport.cleanCache();
442
+ ```
443
+
444
+ - Sub process 1, testing stage 1
445
+ ```js
446
+ const MCR = require('monocart-coverage-reports');
447
+ const coverageOptions = require('path-to/same-options.js');
448
+ const coverageReport = MCR(coverageOptions);
449
+ // do not clean cache in the stage
450
+ await coverageReport.add(coverageData1);
451
+ ```
452
+
453
+ - Sub process 2, testing stage 2
454
+ ```js
455
+ const MCR = require('monocart-coverage-reports');
456
+ const coverageOptions = require('path-to/same-options.js');
457
+ const coverageReport = MCR(coverageOptions);
458
+ // do not clean cache in the stage
459
+ await coverageReport.add(coverageData2);
460
+ ```
461
+
462
+ - Main process, after the completion of testing
463
+ ```js
464
+ // generate coverage reports after the completion of testing
465
+ const MCR = require('monocart-coverage-reports');
466
+ const coverageOptions = require('path-to/same-options.js');
467
+ const coverageReport = MCR(coverageOptions);
468
+ // do not clean cache before generating reports
469
+ await coverageReport.generate();
470
+ ```
471
+
472
+ ## Merge Coverage Reports
473
+ The following usage scenarios may require merging coverage reports:
474
+ - 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.
475
+ - 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.
476
+ - 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.
477
+
478
+ If the reports cannot be merged automatically, then here is how to manually merge the reports.
479
+ First, using the `raw` report to export the original coverage data to the specified directory.
480
+ ```js
481
+ const coverageOptions = {
482
+ name: 'My Unit Test Coverage Report',
483
+ outputDir: "./coverage-reports/unit",
484
+ reports: [
485
+ ['raw', {
486
+ // relative path will be "./coverage-reports/unit/raw"
487
+ outputDir: "raw"
488
+ }],
489
+ ['v8'],
490
+ ['console-summary']
491
+ ]
492
+ };
493
+ ```
494
+ Then, after all the tests are completed, generate a merged report with option `inputDir`:
495
+ ```js
496
+ // esm syntax
497
+ import fs from "fs";
498
+ import { CoverageReport } from 'monocart-coverage-reports';
499
+ const coverageOptions = {
500
+ name: 'My Merged Coverage Report',
501
+ inputDir: [
502
+ './coverage-reports/unit/raw',
503
+ './coverage-reports/e2e/raw'
504
+ ],
505
+ outputDir: './coverage-reports/merged',
506
+ reports: [
507
+ ['v8'],
508
+ ['console-summary']
509
+ ],
510
+ onEnd: () => {
511
+ // remove the raw files if it useless
512
+ fs.rmSync('./coverage-reports/unit/raw', {
513
+ recursive: true,
514
+ force: true
515
+ })
516
+ }
517
+ };
518
+ await new CoverageReport(coverageOptions).generate();
519
+ ```
520
+
521
+ ## Resolve `sourcePath` for the Source Files
522
+ If the source file comes from the sourcemap, then its path is a virtual path. Using the `sourcePath` option to resolve a custom path.
523
+ For example, we have tested multiple dist files, which contain some common files. We hope to merge the coverage of the same files, so we need to unify the `sourcePath` in order to be able to merge the coverage data.
524
+ ```js
525
+ const coverageOptions = {
526
+ sourcePath: (filePath) => {
527
+ // Remove the virtual prefix
528
+ const list = ['my-dist-file1/', 'my-dist-file2/'];
529
+ for (const str of list) {
530
+ if (filePath.startsWith(str)) {
531
+ return filePath.slice(str.length);
532
+ }
533
+ }
534
+ return filePath;
535
+ }
536
+ };
537
+ ```
538
+ It also supports simple key/value replacement:
539
+ ```js
540
+ const coverageOptions = {
541
+ sourcePath: {
542
+ 'my-dist-file1/': '',
543
+ 'my-dist-file2/': ''
544
+ }
545
+ };
546
+ ```
547
+
548
+ ## Adding Empty Coverage for Untested Files
549
+ By default the untested files will not be included in the coverage report, we can add empty coverage data for all files with option `all`, the untested files will show 0% coverage.
550
+ ```js
551
+ const coverageOptions = {
552
+ all: {
553
+ dir: ['./src'],
554
+ filter: (filePath) => {
555
+ return true;
556
+ }
557
+ }
558
+ };
559
+ ```
560
+ The filter also supports `minimatch` pattern:
561
+ ```js
562
+ const coverageOptions = {
563
+ all: {
564
+ dir: ['./src'],
565
+ filter: '**/*.js'
566
+ }
567
+ };
568
+ // or multiple patterns
569
+ const coverageOptions = {
570
+ all: {
571
+ dir: ['./src'],
572
+ filter: {
573
+ // exclude files
574
+ '**/ignored-*.js': false,
575
+ '**/*.html': false,
576
+ '**/*.ts': false,
577
+ // empty css coverage
578
+ '**/*.scss': "css",
579
+ '**/*': true
580
+ }
581
+ }
582
+ };
583
+ ```
584
+
585
+ ## Ignoring Uncovered Codes
586
+ To ignore codes, use the special comment which starts with `v8 ignore `:
587
+ - Ignoring all until stop
588
+ ```js
589
+ /* v8 ignore start */
590
+ function uncovered() {
591
+ }
592
+ /* v8 ignore stop */
593
+ ```
594
+ - Ignoring the next line or next N lines
595
+ ```js
596
+ /* v8 ignore next */
597
+ const os = platform === 'wind32' ? 'Windows' : 'Other';
598
+
599
+ const os = platform === 'wind32' ? 'Windows' /* v8 ignore next */ : 'Other';
600
+
601
+ // v8 ignore next 3
602
+ if (platform === 'linux') {
603
+ console.log('hello linux');
604
+ }
605
+ ```
606
+
607
+ ## Chromium Coverage API
608
+ - [V8 coverage report](https://v8.dev/blog/javascript-code-coverage) - Native support for JavaScript code coverage to V8. (Chromium only)
609
+ - [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
610
+ - [Puppeteer Coverage class](https://pptr.dev/api/puppeteer.coverage)
611
+ - [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)
612
+
613
+ ## V8 Coverage Data Format
614
+ ```js
615
+ // Coverage data for a source range.
616
+ export interface CoverageRange {
617
+ // JavaScript script source offset for the range start.
618
+ startOffset: integer;
619
+ // JavaScript script source offset for the range end.
620
+ endOffset: integer;
621
+ // Collected execution count of the source range.
622
+ count: integer;
623
+ }
624
+
625
+ // Coverage data for a JavaScript function.
626
+ /**
627
+ * @functionName can be an empty string.
628
+ * @ranges is always non-empty. The first range is called the "root range".
629
+ * @isBlockCoverage indicates if the function has block coverage information.
630
+ If this is false, it usually means that the functions was never called.
631
+ It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
632
+ */
633
+ export interface FunctionCoverage {
634
+ // JavaScript function name.
635
+ functionName: string;
636
+ // Source ranges inside the function with coverage data.
637
+ ranges: CoverageRange[];
638
+ // Whether coverage data for this function has block granularity.
639
+ isBlockCoverage: boolean;
640
+ }
641
+
642
+ // Coverage data for a JavaScript script.
643
+ export interface ScriptCoverage {
644
+ // JavaScript script id.
645
+ scriptId: Runtime.ScriptId;
646
+ // JavaScript script name or url.
647
+ url: string;
648
+ // Functions contained in the script that has coverage data.
649
+ functions: FunctionCoverage[];
650
+ }
651
+
652
+ export type V8CoverageData = ScriptCoverage[];
653
+ ```
654
+ see devtools-protocol [ScriptCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ScriptCoverage) and [v8-coverage](https://github.com/bcoe/v8-coverage)
655
+
656
+ ## How to convert V8 to Istanbul
657
+ ### Using [v8-to-istanbul](https://github.com/istanbuljs/v8-to-istanbul)
658
+ 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:
659
+ - 1, The source mapping does not work well if the position is between the two consecutive mappings. for example:
660
+ ```js
661
+ const a = tf ? 'true' : 'false';
662
+ ^ ^ ^
663
+ m1 p m2
664
+ ```
665
+ > `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.
666
+
667
+ - 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.
668
+
669
+ ### How Monocart Works
670
+ We implemented new converter:
671
+ - 1, Trying to fix the middle position if not found the exact mapping for the position.
672
+ - 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`.
673
+
674
+ | AST | V8 |
675
+ | :---------------------| :------------- |
676
+ | AssignmentPattern | 🛇 Not Support |
677
+ | ConditionalExpression | ✔ |
678
+ | IfStatement | ✔ |
679
+ | LogicalExpression | ✔ |
680
+ | SwitchStatement | ✔ |
681
+
682
+ ## Debug for Coverage and Sourcemap
683
+ > 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.
684
+ - Start debugging for v8 report with option `logging: 'debug'`
685
+ ```js
686
+ const coverageOptions = {
687
+ logging: 'debug',
688
+ reports: [
689
+ ['v8'],
690
+ ['console-summary']
691
+ ]
692
+ };
693
+ ```
694
+ 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.
695
+ ![](./assets/debug-coverage.png)
696
+
697
+ - Check sourcemap with [Source Map Visualization](https://evanw.github.io/source-map-visualization/)
698
+
699
+ ![](./assets/debug-sourcemap.png)
700
+
701
+ ## Integration
702
+
703
+ ### [Playwright](https://github.com/microsoft/playwright)
704
+ - [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)
705
+ - Coverage for component testing:
706
+ - [playwright-ct-vue](https://github.com/cenfun/playwright-ct-vue)
707
+ - [playwright-ct-react](https://github.com/cenfun/playwright-ct-react)
708
+ - [playwright-ct-svelte](https://github.com/cenfun/playwright-ct-svelte)
709
+ - Coverage for Next.js, both server side and client side:
710
+ - [nextjs-with-playwright](https://github.com/cenfun/nextjs-with-playwright)
711
+ - [nextjs-with-playwright-istanbul](https://github.com/cenfun/nextjs-with-playwright-istanbul)
712
+
713
+ ### [Jest](https://github.com/jestjs/jest/)
714
+ - [jest-monocart-coverage](https://github.com/cenfun/jest-monocart-coverage) - A Jest custom reporter for coverage reports
715
+ - Example for Jest (unit) + Puppeteer (e2e) + Codecov: [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js)
716
+
717
+ ### [Vitest](https://github.com/vitest-dev/vitest)
718
+ - [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - A Vitest custom provider module for coverage reports
719
+
720
+ ### [CodeceptJS](https://github.com/codeceptjs/CodeceptJS)
721
+ - [codeceptjs-monocart-coverage](https://github.com/cenfun/codeceptjs-monocart-coverage) - A CodeceptJS plugin for coverage reports
722
+
723
+ ### [WebdriverIO](https://github.com/webdriverio/webdriverio)
724
+ - [wdio-monocart-service](https://github.com/cenfun/wdio-monocart-service) - A WebdriverIO service for coverage reports
725
+
726
+ ### [Codecov](https://codecov.com/)
727
+ [![codecov](https://codecov.io/gh/cenfun/monocart-coverage-reports/graph/badge.svg?token=H0LW7UKYU3)](https://codecov.io/gh/cenfun/monocart-coverage-reports)
728
+ - Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
729
+ ```js
730
+ const coverageOptions = {
731
+ outputDir: "./coverage-reports",
732
+ reports: [
733
+ ['codecov']
734
+ ]
735
+ };
736
+ ```
737
+ - Github actions example:
738
+ ```yml
739
+ - name: Codecov
740
+ uses: codecov/codecov-action@v3
741
+ with:
742
+ files: ./coverage-reports/codecov.json
743
+ ```
744
+ ### [Coveralls](https://coveralls.io/)
745
+ [![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)
746
+ - Using `lcov` report:
747
+ ```js
748
+ const coverageOptions = {
749
+ outputDir: "./coverage-reports",
750
+ lcov: true
751
+ };
752
+ ```
753
+ - Github actions example:
754
+ ```yml
755
+ - name: Coveralls
756
+ uses: coverallsapp/github-action@v2
757
+ with:
758
+ files: ./coverage-reports/lcov.info
759
+ ```
760
+ ### [Sonar Cloud](https://sonarcloud.io/)
761
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=monocart-coverage-reports&metric=coverage)](https://sonarcloud.io/summary/new_code?id=monocart-coverage-reports)
762
+ - Using `lcov` report. Github actions example:
763
+ ```yml
764
+ - name: Analyze with SonarCloud
765
+ uses: sonarsource/sonarcloud-github-action@master
766
+ env:
767
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
768
+ with:
769
+ projectBaseDir: ./
770
+ args: >
771
+ -Dsonar.organization=cenfun
772
+ -Dsonar.projectKey=monocart-coverage-reports
773
+ -Dsonar.projectName=monocart-coverage-reports
774
+ -Dsonar.javascript.lcov.reportPaths=docs/mcr/lcov.info
775
+ -Dsonar.sources=lib
776
+ -Dsonar.tests=test
777
+ -Dsonar.exclusions=dist/*,packages/*
778
+ ```
779
+ ### Integration with Any Testing Framework
780
+ - Collecting coverage data when any stage of the test is completed, and adding the coverage data to the coverage reporter.
781
+ - Generating the coverage reports after the completion of all tests.
782
+ - see [Multiprocessing Support](#multiprocessing-support)
783
+
784
+ ### VSCode Extension
785
+ - [Coverage Gutters](https://github.com/ryanluker/vscode-coverage-gutters) - Display test coverage generated by lcov or xml in VSCode editor.
786
+
787
+ ## Thanks
779
788
  - Special thanks to [@edumserrano](https://github.com/edumserrano)