monocart-coverage-reports 2.7.5 → 2.7.7
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 +452 -374
- package/lib/cli.js +32 -27
- package/lib/client/cdp-client.js +2 -2
- package/lib/client/coverage-client.js +61 -19
- package/lib/client/{cdp-session.js → ws-session.js} +2 -2
- package/lib/converter/converter.js +10 -5
- package/lib/converter/find-original-range.js +24 -3
- package/lib/generate.js +3 -8
- package/lib/index.d.ts +19 -6
- package/lib/index.js +4 -1
- package/lib/index.mjs +2 -0
- package/lib/packages/monocart-coverage-vendor.js +2 -2
- package/lib/utils/request.js +4 -0
- package/lib/utils/util.js +13 -1
- package/package.json +97 -98
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
|
-
* [
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
* [
|
|
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
|
+
- [Collecting V8 Coverage Data from Node.js](#collecting-v8-coverage-data-from-nodejs)
|
|
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
|
-
* [
|
|
31
|
-
* [
|
|
32
|
-
* [
|
|
33
|
-
|
|
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
|
-
|
|
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,37 +42,46 @@
|
|
|
43
42
|
- [WebdriverIO](#webdriverio)
|
|
44
43
|
- [Storybook Test Runner](#storybook-test-runner)
|
|
45
44
|
- [TestCafe](#testcafe)
|
|
45
|
+
- [Selenium Webdriver](#selenium-webdriver)
|
|
46
|
+
- [Mocha](#mocha)
|
|
47
|
+
- [tsx](#tsx)
|
|
48
|
+
- [ts-node](#ts-node)
|
|
49
|
+
- [AVA](#ava)
|
|
46
50
|
- [Codecov](#codecov)
|
|
47
51
|
- [Coveralls](#coveralls)
|
|
48
52
|
- [Sonar Cloud](#sonar-cloud)
|
|
49
|
-
- [Integration with Any Testing Framework](#integration-with-any-testing-framework)
|
|
50
53
|
* [Thanks](#thanks)
|
|
51
54
|
|
|
52
55
|
## Usage
|
|
56
|
+
> It's recommended to use [Node.js 20+](https://nodejs.org/).
|
|
57
|
+
- API
|
|
53
58
|
```js
|
|
54
59
|
const MCR = require('monocart-coverage-reports');
|
|
55
|
-
const
|
|
60
|
+
const mcr = MCR({
|
|
56
61
|
name: 'My Coverage Report - 2024-02-28',
|
|
57
62
|
outputDir: './coverage-reports',
|
|
58
|
-
reports: ["v8", "console-details"]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
reports: ["v8", "console-details"],
|
|
64
|
+
cleanCache: true
|
|
65
|
+
});
|
|
66
|
+
await mcr.add(coverageData);
|
|
67
|
+
await mcr.generate();
|
|
68
|
+
```
|
|
69
|
+
Using `import` and load options from [config file](#config-file)
|
|
70
|
+
```js
|
|
71
|
+
import { CoverageReport } from 'monocart-coverage-reports';
|
|
72
|
+
const mcr = new CoverageReport();
|
|
73
|
+
await mcr.loadConfig();
|
|
74
|
+
```
|
|
75
|
+
For more information, see [Multiprocessing Support](#multiprocessing-support)
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
- CLI
|
|
78
|
+
```sh
|
|
79
|
+
mcr node my-app.js -r v8,console-details
|
|
71
80
|
```
|
|
72
|
-
|
|
73
|
-
- [example istanbul](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
|
|
81
|
+
For more information, see [Command Line](#command-line)
|
|
74
82
|
|
|
75
|
-
##
|
|
76
|
-
- [lib/default/options.js](
|
|
83
|
+
## Options
|
|
84
|
+
- Default Options: [lib/default/options.js](./lib/default/options.js)
|
|
77
85
|
- `reports` [Available Reports](#available-reports)
|
|
78
86
|
- `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
87
|
- `sourcePath` [Resolve `sourcePath` for the Source Files](#resolve-sourcepath-for-the-source-files)
|
|
@@ -82,7 +90,8 @@ await coverageReport.generate();
|
|
|
82
90
|
- `logging` [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
|
|
83
91
|
- `onEnd` [onEnd Hook](#onend-hook)
|
|
84
92
|
|
|
85
|
-
- Declaration [lib/index.d.ts](
|
|
93
|
+
- Declaration: [lib/index.d.ts](./lib/index.d.ts)
|
|
94
|
+
- [Config file](#config-file)
|
|
86
95
|
|
|
87
96
|
## Available Reports
|
|
88
97
|
|
|
@@ -95,16 +104,14 @@ await coverageReport.generate();
|
|
|
95
104
|
- Coverage for Any Runtime Code
|
|
96
105
|
- CSS Coverage Support
|
|
97
106
|
- 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/)
|
|
107
|
+
- Demos: [V8](https://cenfun.github.io/monocart-coverage-reports/v8) and [more](https://cenfun.github.io/monocart-coverage-reports/)
|
|
101
108
|
|
|
102
109
|

|
|
103
110
|
|
|
104
111
|
- `v8-json`
|
|
105
112
|
- [V8 coverage-report.json](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json)
|
|
106
113
|
|
|
107
|
-
> Istanbul build-in reports (both V8 and
|
|
114
|
+
> Istanbul build-in reports (both V8 and Istanbul data):
|
|
108
115
|
|
|
109
116
|
- `clover`
|
|
110
117
|
- `cobertura`
|
|
@@ -112,7 +119,6 @@ await coverageReport.generate();
|
|
|
112
119
|
- [Istanbul html](https://cenfun.github.io/monocart-coverage-reports/istanbul/)
|
|
113
120
|
- [V8 to Istanbul](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/istanbul)
|
|
114
121
|
- `html-spa`
|
|
115
|
-
- [Istanbul html-spa](https://cenfun.github.io/monocart-coverage-reports/istanbul/html-spa/)
|
|
116
122
|
- `json`
|
|
117
123
|
- `json-summary`
|
|
118
124
|
- `lcov`
|
|
@@ -125,7 +131,7 @@ await coverageReport.generate();
|
|
|
125
131
|
- `text-lcov`
|
|
126
132
|
- `text-summary`
|
|
127
133
|
|
|
128
|
-
> Other build-in reports (both V8 and
|
|
134
|
+
> Other build-in reports (both V8 and Istanbul data):
|
|
129
135
|
|
|
130
136
|
- `codecov`
|
|
131
137
|
- coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
|
|
@@ -159,9 +165,9 @@ await coverageReport.generate();
|
|
|
159
165
|
]
|
|
160
166
|
}
|
|
161
167
|
```
|
|
162
|
-
-
|
|
168
|
+
- Istanbul custom reporter
|
|
163
169
|
> 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.
|
|
164
|
-
-
|
|
170
|
+
- V8 custom reporter
|
|
165
171
|
> example: [./test/custom-v8-reporter.js](./test/custom-v8-reporter.js)
|
|
166
172
|
|
|
167
173
|
### Multiple Reports:
|
|
@@ -190,151 +196,11 @@ const coverageOptions = {
|
|
|
190
196
|
}],
|
|
191
197
|
// Specify reporter name with local path
|
|
192
198
|
['/absolute/path/to/custom-reporter.js']
|
|
193
|
-
|
|
194
199
|
]
|
|
195
200
|
}
|
|
196
|
-
const
|
|
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
|
|
201
|
+
const mcr = MCR(coverageOptions);
|
|
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,59 @@ 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
|
-
- Instrumenting source code
|
|
363
|
-
|
|
364
|
-
-
|
|
365
|
-
- rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
|
|
219
|
+
- Before coverage collection: Instrumenting source code with Istanbul
|
|
220
|
+
- webpack with babel loader: [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul), see example: [webpack.config-istanbul.js](./test/build/webpack.config-istanbul.js)
|
|
221
|
+
- CLI: [nyc instrument](https://github.com/istanbuljs/nyc/blob/master/docs/instrument.md) or API: [istanbul-lib-instrument](https://github.com/istanbuljs/istanbuljs/blob/main/packages/istanbul-lib-instrument/api.md)
|
|
366
222
|
- vite: [vite-plugin-istanbul](https://github.com/ifaxity/vite-plugin-istanbul)
|
|
223
|
+
- rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
|
|
224
|
+
- swc: [swc-plugin-coverage-instrument](https://github.com/kwonoj/swc-plugin-coverage-instrument)
|
|
367
225
|
- Browser
|
|
368
|
-
- Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](
|
|
226
|
+
- Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](./test/test-istanbul.js)
|
|
369
227
|
- Node.js
|
|
370
228
|
- Collecting coverage data from `global.__coverage__`
|
|
371
229
|
|
|
372
230
|
## Collecting V8 Coverage Data
|
|
373
|
-
-
|
|
374
|
-
- [webpack](https://webpack.js.org/configuration/): `devtool: source-map` and `mode: development`, example [webpack.config-v8.js](
|
|
231
|
+
- Before coverage collection: Enabling `sourcemap` for source code
|
|
232
|
+
- [webpack](https://webpack.js.org/configuration/): `devtool: source-map` and `mode: development`, example [webpack.config-v8.js](./test/build/webpack.config-v8.js)
|
|
375
233
|
- [rollup](https://rollupjs.org/configuration-options/): `sourcemap: true`
|
|
376
234
|
- [vite](https://vitejs.dev/config/build-options.html): `sourcemap: true` and `minify: false`
|
|
377
235
|
- [esbuild](https://esbuild.github.io/api/): `sourcemap: true` and `minify: false`
|
|
378
|
-
|
|
236
|
+
|
|
379
237
|
- Browser (Chromium Only)
|
|
380
|
-
|
|
381
|
-
- [
|
|
382
|
-
|
|
238
|
+
- [Collecting V8 Coverage Data with Playwright](#collecting-v8-coverage-data-with-playwright)
|
|
239
|
+
- [Collecting Raw V8 Coverage Data with Puppeteer](#collecting-raw-v8-coverage-data-with-puppeteer)
|
|
240
|
+
|
|
383
241
|
- Node.js
|
|
384
|
-
-
|
|
242
|
+
- [Collecting V8 Coverage Data from Node.js](#collecting-v8-coverage-data-from-nodejs)
|
|
243
|
+
|
|
385
244
|
- CDP
|
|
386
|
-
-
|
|
245
|
+
- [Collecting V8 Coverage Data with `CDPClient` API](#collecting-v8-coverage-data-with-cdpclient-api)
|
|
387
246
|
|
|
388
|
-
|
|
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.
|
|
247
|
+
### Collecting V8 Coverage Data with Playwright
|
|
390
248
|
```js
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
})
|
|
249
|
+
await Promise.all([
|
|
250
|
+
page.coverage.startJSCoverage({
|
|
251
|
+
resetOnNavigation: false
|
|
252
|
+
}),
|
|
253
|
+
page.coverage.startCSSCoverage({
|
|
254
|
+
resetOnNavigation: false
|
|
255
|
+
})
|
|
256
|
+
]);
|
|
398
257
|
|
|
399
|
-
await
|
|
258
|
+
await page.goto("your page url");
|
|
259
|
+
|
|
260
|
+
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
261
|
+
page.coverage.stopJSCoverage(),
|
|
262
|
+
page.coverage.stopCSSCoverage()
|
|
263
|
+
]);
|
|
264
|
+
|
|
265
|
+
const coverageData = [... jsCoverage, ... cssCoverage];
|
|
400
266
|
|
|
401
267
|
```
|
|
268
|
+
For more examples, see [./test/test-v8.js](./test/test-v8.js), and [anonymous](./test/test-anonymous.js), [css](./test/test-css.js)
|
|
402
269
|
|
|
403
|
-
|
|
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)
|
|
270
|
+
### Collecting Raw V8 Coverage Data with Puppeteer
|
|
405
271
|
```js
|
|
406
272
|
await Promise.all([
|
|
407
273
|
page.coverage.startJSCoverage({
|
|
@@ -414,7 +280,7 @@ await Promise.all([
|
|
|
414
280
|
})
|
|
415
281
|
]);
|
|
416
282
|
|
|
417
|
-
await page.goto(url);
|
|
283
|
+
await page.goto("your page url");
|
|
418
284
|
|
|
419
285
|
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
420
286
|
page.coverage.stopJSCoverage(),
|
|
@@ -429,8 +295,9 @@ const coverageData = [... jsCoverage.map((it) => {
|
|
|
429
295
|
};
|
|
430
296
|
}), ... cssCoverage];
|
|
431
297
|
```
|
|
298
|
+
Example: [./test/test-puppeteer.js](./test/test-puppeteer.js)
|
|
432
299
|
|
|
433
|
-
|
|
300
|
+
### Collecting V8 Coverage Data from Node.js
|
|
434
301
|
Possible solutions:
|
|
435
302
|
- [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
|
|
436
303
|
- 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,8 +330,8 @@ Possible solutions:
|
|
|
463
330
|
- [Child Process](https://nodejs.org/docs/latest/api/child_process.html) + NODE_V8_COVERAGE
|
|
464
331
|
- see [Command Line](#command-line)
|
|
465
332
|
|
|
466
|
-
|
|
467
|
-
- Work with node debugger `--inspect=9229`
|
|
333
|
+
### Collecting V8 Coverage Data with `CDPClient` API
|
|
334
|
+
- Work with node debugger port `--inspect=9229`
|
|
468
335
|
```js
|
|
469
336
|
const MCR = require('monocart-coverage-reports');
|
|
470
337
|
const client = await MCR.CDPClient({
|
|
@@ -506,94 +373,129 @@ await client.startCoverage();
|
|
|
506
373
|
await page.goto("your page url");
|
|
507
374
|
const coverageData = await client.stopCoverage();
|
|
508
375
|
```
|
|
509
|
-
|
|
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
|
+
- Work with [Selenium Webdriver](https://www.selenium.dev/documentation/webdriver/) WebSocket (Chrome/Edge Browser)
|
|
513
377
|
```js
|
|
378
|
+
const { Builder, Browser } = require('selenium-webdriver');
|
|
514
379
|
const MCR = require('monocart-coverage-reports');
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
-
|
|
380
|
+
const driver = await new Builder().forBrowser(Browser.CHROME).build();
|
|
381
|
+
const pageCdpConnection = await driver.createCDPConnection('page');
|
|
382
|
+
const session = new MCR.WSSession(pageCdpConnection._wsConnection);
|
|
383
|
+
const client = await MCR.CDPClient({
|
|
384
|
+
session
|
|
385
|
+
})
|
|
386
|
+
```
|
|
387
|
+
- `CDPClient` available APIs
|
|
388
|
+
```js
|
|
389
|
+
/** start js coverage */
|
|
390
|
+
startJSCoverage: () => Promise<void>;
|
|
391
|
+
/** stop and return js coverage */
|
|
392
|
+
stopJSCoverage: () => Promise<V8CoverageEntry[]>;
|
|
393
|
+
|
|
394
|
+
/** start css coverage */
|
|
395
|
+
startCSSCoverage: () => Promise<void>;
|
|
396
|
+
/** stop and return css coverage */
|
|
397
|
+
stopCSSCoverage: () => Promise<V8CoverageEntry[]>;
|
|
398
|
+
|
|
399
|
+
/** start both js and css coverage */
|
|
400
|
+
startCoverage: () => Promise<void>;
|
|
401
|
+
/** stop and return both js and css coverage */
|
|
402
|
+
stopCoverage: () => Promise<V8CoverageEntry[]>;
|
|
403
|
+
|
|
404
|
+
/** write the coverage started by NODE_V8_COVERAGE to disk on demand, returns v8 coverage dir */
|
|
405
|
+
writeCoverage: () => Promise<string>;
|
|
406
|
+
|
|
407
|
+
/** get istanbul coverage data */
|
|
408
|
+
getIstanbulCoverage: (coverageKey?: string) => Promise<any>;
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### V8 Coverage Data API
|
|
412
|
+
- [JavaScript code coverage in V8](https://v8.dev/blog/javascript-code-coverage)
|
|
413
|
+
- [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
|
|
414
|
+
- [Puppeteer Coverage Class](https://pptr.dev/api/puppeteer.coverage)
|
|
415
|
+
- [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage) see [ScriptCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ScriptCoverage) and [v8-coverage](https://github.com/bcoe/v8-coverage)
|
|
523
416
|
```js
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
417
|
+
// Coverage data for a source range.
|
|
418
|
+
export interface CoverageRange {
|
|
419
|
+
// JavaScript script source offset for the range start.
|
|
420
|
+
startOffset: integer;
|
|
421
|
+
// JavaScript script source offset for the range end.
|
|
422
|
+
endOffset: integer;
|
|
423
|
+
// Collected execution count of the source range.
|
|
424
|
+
count: integer;
|
|
425
|
+
}
|
|
426
|
+
// Coverage data for a JavaScript function.
|
|
427
|
+
/**
|
|
428
|
+
* @functionName can be an empty string.
|
|
429
|
+
* @ranges is always non-empty. The first range is called the "root range".
|
|
430
|
+
* @isBlockCoverage indicates if the function has block coverage information.
|
|
431
|
+
If this is false, it usually means that the functions was never called.
|
|
432
|
+
It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
|
|
433
|
+
*/
|
|
434
|
+
export interface FunctionCoverage {
|
|
435
|
+
// JavaScript function name.
|
|
436
|
+
functionName: string;
|
|
437
|
+
// Source ranges inside the function with coverage data.
|
|
438
|
+
ranges: CoverageRange[];
|
|
439
|
+
// Whether coverage data for this function has block granularity.
|
|
440
|
+
isBlockCoverage: boolean;
|
|
441
|
+
}
|
|
442
|
+
// Coverage data for a JavaScript script.
|
|
443
|
+
export interface ScriptCoverage {
|
|
444
|
+
// JavaScript script id.
|
|
445
|
+
scriptId: Runtime.ScriptId;
|
|
446
|
+
// JavaScript script name or url.
|
|
447
|
+
url: string;
|
|
448
|
+
// Functions contained in the script that has coverage data.
|
|
449
|
+
functions: FunctionCoverage[];
|
|
450
|
+
}
|
|
451
|
+
export type V8CoverageData = ScriptCoverage[];
|
|
529
452
|
```
|
|
530
453
|
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
```
|
|
454
|
+
## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
|
|
455
|
+
When V8 coverage data collected, it actually contains the data of all entry files, for example:
|
|
539
456
|
|
|
540
|
-
-
|
|
541
|
-
|
|
542
|
-
|
|
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();
|
|
548
|
-
```
|
|
457
|
+
- *dist/main.js*
|
|
458
|
+
- *dist/vendor.js*
|
|
459
|
+
- *dist/something-else.js*
|
|
549
460
|
|
|
550
|
-
|
|
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.
|
|
461
|
+
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.
|
|
555
462
|
|
|
556
|
-
|
|
557
|
-
|
|
463
|
+
- *dist/main.js*
|
|
464
|
+
|
|
465
|
+
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`.
|
|
466
|
+
|
|
467
|
+
- *src/index.js*
|
|
468
|
+
- *src/components/app.js*
|
|
469
|
+
- *node_modules/dependency/dist/dependency.js*
|
|
470
|
+
|
|
471
|
+
We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
|
|
472
|
+
|
|
473
|
+
- *src/index.js*
|
|
474
|
+
- *src/components/app.js*
|
|
475
|
+
|
|
476
|
+
For example:
|
|
558
477
|
```js
|
|
559
478
|
const coverageOptions = {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
reports: [
|
|
563
|
-
['raw', {
|
|
564
|
-
// relative path will be "./coverage-reports/unit/raw"
|
|
565
|
-
outputDir: "raw"
|
|
566
|
-
}],
|
|
567
|
-
['v8'],
|
|
568
|
-
['console-summary']
|
|
569
|
-
]
|
|
479
|
+
entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
|
|
480
|
+
sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
|
|
570
481
|
};
|
|
571
482
|
```
|
|
572
|
-
|
|
483
|
+
Or using `minimatch` pattern:
|
|
573
484
|
```js
|
|
574
|
-
// esm syntax
|
|
575
|
-
import fs from "fs";
|
|
576
|
-
import { CoverageReport } from 'monocart-coverage-reports';
|
|
577
485
|
const coverageOptions = {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
// remove the raw files if it useless
|
|
590
|
-
fs.rmSync('./coverage-reports/unit/raw', {
|
|
591
|
-
recursive: true,
|
|
592
|
-
force: true
|
|
593
|
-
})
|
|
486
|
+
entryFilter: "**/main.js",
|
|
487
|
+
sourceFilter: "**/src/**"
|
|
488
|
+
};
|
|
489
|
+
// supports multiple patterns:
|
|
490
|
+
const coverageOptions = {
|
|
491
|
+
entryFilter: {
|
|
492
|
+
'**/vendor.js': false,
|
|
493
|
+
'**/main.js': true
|
|
494
|
+
},
|
|
495
|
+
sourceFilter: {
|
|
496
|
+
'**/src/**': true
|
|
594
497
|
}
|
|
595
498
|
};
|
|
596
|
-
await new CoverageReport(coverageOptions).generate();
|
|
597
499
|
```
|
|
598
500
|
|
|
599
501
|
## Resolve `sourcePath` for the Source Files
|
|
@@ -660,6 +562,37 @@ const coverageOptions = {
|
|
|
660
562
|
};
|
|
661
563
|
```
|
|
662
564
|
|
|
565
|
+
## onEnd Hook
|
|
566
|
+
For example, checking thresholds:
|
|
567
|
+
```js
|
|
568
|
+
const EC = require('eight-colors');
|
|
569
|
+
const coverageOptions = {
|
|
570
|
+
name: 'My Coverage Report',
|
|
571
|
+
outputDir: './coverage-reports',
|
|
572
|
+
onEnd: (coverageResults) => {
|
|
573
|
+
const thresholds = {
|
|
574
|
+
bytes: 80,
|
|
575
|
+
lines: 60
|
|
576
|
+
};
|
|
577
|
+
console.log('check thresholds ...', thresholds);
|
|
578
|
+
const errors = [];
|
|
579
|
+
const { summary } = coverageResults;
|
|
580
|
+
Object.keys(thresholds).forEach((k) => {
|
|
581
|
+
const pct = summary[k].pct;
|
|
582
|
+
if (pct < thresholds[k]) {
|
|
583
|
+
errors.push(`Coverage threshold for ${k} (${pct} %) not met: ${thresholds[k]} %`);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
if (errors.length) {
|
|
587
|
+
const errMsg = errors.join('\n');
|
|
588
|
+
console.log(EC.red(errMsg));
|
|
589
|
+
// throw new Error(errMsg);
|
|
590
|
+
// process.exit(1);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
663
596
|
## Ignoring Uncovered Codes
|
|
664
597
|
To ignore codes, use the special comment which starts with `v8 ignore `:
|
|
665
598
|
- Ignoring all until stop
|
|
@@ -682,103 +615,196 @@ if (platform === 'linux') {
|
|
|
682
615
|
}
|
|
683
616
|
```
|
|
684
617
|
|
|
685
|
-
##
|
|
686
|
-
|
|
687
|
-
-
|
|
688
|
-
|
|
689
|
-
|
|
618
|
+
## Multiprocessing Support
|
|
619
|
+
> 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)
|
|
620
|
+
- Main process, before the start of testing
|
|
621
|
+
```js
|
|
622
|
+
const MCR = require('monocart-coverage-reports');
|
|
623
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
624
|
+
const mcr = MCR(coverageOptions);
|
|
625
|
+
// clean previous cache before the start of testing
|
|
626
|
+
// unless the running environment is new and no cache
|
|
627
|
+
mcr.cleanCache();
|
|
628
|
+
```
|
|
690
629
|
|
|
691
|
-
|
|
630
|
+
- Sub process 1, testing stage 1
|
|
692
631
|
```js
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
endOffset: integer;
|
|
699
|
-
// Collected execution count of the source range.
|
|
700
|
-
count: integer;
|
|
701
|
-
}
|
|
632
|
+
const MCR = require('monocart-coverage-reports');
|
|
633
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
634
|
+
const mcr = MCR(coverageOptions);
|
|
635
|
+
await mcr.add(coverageData1);
|
|
636
|
+
```
|
|
702
637
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
}
|
|
638
|
+
- Sub process 2, testing stage 2
|
|
639
|
+
```js
|
|
640
|
+
const MCR = require('monocart-coverage-reports');
|
|
641
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
642
|
+
const mcr = MCR(coverageOptions);
|
|
643
|
+
await mcr.add(coverageData2);
|
|
644
|
+
```
|
|
719
645
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
}
|
|
646
|
+
- Main process, after the completion of testing
|
|
647
|
+
```js
|
|
648
|
+
// generate coverage reports after the completion of testing
|
|
649
|
+
const MCR = require('monocart-coverage-reports');
|
|
650
|
+
const coverageOptions = require('path-to/same-options.js');
|
|
651
|
+
const mcr = MCR(coverageOptions);
|
|
652
|
+
await mcr.generate();
|
|
653
|
+
```
|
|
729
654
|
|
|
730
|
-
|
|
655
|
+
## Command Line
|
|
656
|
+
> 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`.
|
|
657
|
+
|
|
658
|
+
- Installing globally
|
|
659
|
+
```sh
|
|
660
|
+
npm i monocart-coverage-reports -g
|
|
661
|
+
mcr node ./test/specs/node.test.js -r v8,console-details --lcov
|
|
731
662
|
```
|
|
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
663
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
```js
|
|
739
|
-
const a = tf ? 'true' : 'false';
|
|
740
|
-
^ ^ ^
|
|
741
|
-
m1 p m2
|
|
664
|
+
- Locally in your project
|
|
665
|
+
```sh
|
|
666
|
+
npm i monocart-coverage-reports
|
|
667
|
+
npx mcr node ./test/specs/node.test.js -r v8,console-details --lcov
|
|
742
668
|
```
|
|
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
669
|
|
|
745
|
-
-
|
|
670
|
+
- CLI Options
|
|
671
|
+
```sh
|
|
672
|
+
Usage: mcr [options] [command]
|
|
746
673
|
|
|
747
|
-
|
|
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`.
|
|
674
|
+
CLI to generate coverage reports
|
|
751
675
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
| AssignmentPattern | 🛇 Not Support |
|
|
755
|
-
| ConditionalExpression | ✔ |
|
|
756
|
-
| IfStatement | ✔ |
|
|
757
|
-
| LogicalExpression | ✔ |
|
|
758
|
-
| SwitchStatement | ✔ |
|
|
676
|
+
Arguments:
|
|
677
|
+
command command to execute
|
|
759
678
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
-
|
|
679
|
+
Options:
|
|
680
|
+
-V, --version output the version number
|
|
681
|
+
-c, --config <path> custom config file path
|
|
682
|
+
-l, --logging <logging> off, error, info, debug
|
|
683
|
+
-n, --name <name> report name for title
|
|
684
|
+
-r, --reports <name[,name]> coverage reports to use
|
|
685
|
+
-o, --outputDir <dir> output dir for reports
|
|
686
|
+
-i, --inputDir <dir> input dir for merging raw files
|
|
687
|
+
--entryFilter <pattern> entry url filter
|
|
688
|
+
--sourceFilter <pattern> source path filter
|
|
689
|
+
--outputFile <path> output file for v8 report
|
|
690
|
+
--inline inline html for v8 report
|
|
691
|
+
--assetsPath <path> assets path if not inline
|
|
692
|
+
--lcov generate lcov.info file
|
|
693
|
+
--import <module> preload module at startup
|
|
694
|
+
--require <module> preload module at startup
|
|
695
|
+
-h, --help display help for command
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
- Use `--` to separate sub CLI args
|
|
699
|
+
```sh
|
|
700
|
+
mcr -c mcr.config.js -- sub-cli -c sub-cli.config.js
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
## Config File
|
|
704
|
+
Loading config file by priority:
|
|
705
|
+
- Custom config file:
|
|
706
|
+
- CLI: `mcr --config <my-config-file-path>`
|
|
707
|
+
- API: `await mcr.loadConfig("my-config-file-path")`
|
|
708
|
+
- `mcr.config.js`
|
|
709
|
+
- `mcr.config.cjs`
|
|
710
|
+
- `mcr.config.mjs`
|
|
711
|
+
- `mcr.config.json` - json format
|
|
712
|
+
- `mcr.config.ts` (requires preloading the ts execution module)
|
|
713
|
+
- `.mcrrc.js`
|
|
714
|
+
- `.mcrrc` - json format
|
|
715
|
+
|
|
716
|
+
## Merge Coverage Reports
|
|
717
|
+
The following usage scenarios may require merging coverage reports:
|
|
718
|
+
- 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.
|
|
719
|
+
- 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.
|
|
720
|
+
- 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.
|
|
721
|
+
|
|
722
|
+
If the reports cannot be merged automatically, then here is how to manually merge the reports.
|
|
723
|
+
First, using the `raw` report to export the original coverage data to the specified directory.
|
|
724
|
+
For example, we have `raw` coverage data from unit tests:
|
|
763
725
|
```js
|
|
764
726
|
const coverageOptions = {
|
|
765
|
-
|
|
727
|
+
name: 'My Unit Test Coverage Report',
|
|
728
|
+
outputDir: "./coverage-reports/unit",
|
|
766
729
|
reports: [
|
|
730
|
+
['raw', {
|
|
731
|
+
// relative path will be "./coverage-reports/unit/raw"
|
|
732
|
+
// defaults to raw
|
|
733
|
+
outputDir: "raw"
|
|
734
|
+
}],
|
|
767
735
|
['v8'],
|
|
768
|
-
['console-
|
|
736
|
+
['console-details']
|
|
769
737
|
]
|
|
770
738
|
};
|
|
771
739
|
```
|
|
772
|
-
|
|
773
|
-
|
|
740
|
+
We also have `raw` coverage data from e2e tests, which is output to `./coverage-reports/e2e/raw`.
|
|
741
|
+
After all the tests are completed, generate a merged report with option `inputDir`:
|
|
742
|
+
```js
|
|
743
|
+
// merge-coverage.js
|
|
744
|
+
const fs = require('fs');
|
|
745
|
+
const { CoverageReport } = require('monocart-coverage-reports');
|
|
746
|
+
const inputDir = [
|
|
747
|
+
'./coverage-reports/unit/raw',
|
|
748
|
+
'./coverage-reports/e2e/raw'
|
|
749
|
+
];
|
|
750
|
+
const coverageOptions = {
|
|
751
|
+
name: 'My Merged Coverage Report',
|
|
752
|
+
inputDir,
|
|
753
|
+
outputDir: './coverage-reports/merged',
|
|
774
754
|
|
|
775
|
-
|
|
755
|
+
// filter for both unit and e2e
|
|
756
|
+
entryFilter: {
|
|
757
|
+
'**/node_modules/**': false,
|
|
758
|
+
'**/*': true
|
|
759
|
+
},
|
|
760
|
+
sourceFilter: {
|
|
761
|
+
'**/node_modules/**': false,
|
|
762
|
+
'**/src/**': true
|
|
763
|
+
},
|
|
764
|
+
|
|
765
|
+
sourcePath: (filePath, info) => {
|
|
766
|
+
// Unify the file path for the same files
|
|
767
|
+
// For example, the file index.js has different paths:
|
|
768
|
+
// unit: unit-dist/src/index.js
|
|
769
|
+
// e2e: e2e-dist/src/index.js
|
|
770
|
+
// return filePath.replace("unit-dist/", "").replace("e2e-dist/", "")
|
|
771
|
+
return filePath;
|
|
772
|
+
},
|
|
776
773
|
|
|
777
|
-
|
|
774
|
+
reports: [
|
|
775
|
+
['v8'],
|
|
776
|
+
['console-details']
|
|
777
|
+
],
|
|
778
|
+
|
|
779
|
+
onEnd: () => {
|
|
780
|
+
// remove the raw files if it useless
|
|
781
|
+
// inputDir.forEach((p) => {
|
|
782
|
+
// fs.rmSync(p, {
|
|
783
|
+
// recursive: true,
|
|
784
|
+
// force: true
|
|
785
|
+
// });
|
|
786
|
+
// });
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
await new CoverageReport(coverageOptions).generate();
|
|
790
|
+
```
|
|
778
791
|
|
|
779
792
|
## Common issues
|
|
780
|
-
|
|
793
|
+
### Unexpected coverage
|
|
794
|
+
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:
|
|
795
|
+
```js
|
|
796
|
+
const a = tf ? 'true' : 'false';
|
|
797
|
+
^ ^ ^
|
|
798
|
+
m1 p m2
|
|
799
|
+
```
|
|
800
|
+
`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)
|
|
801
|
+
|
|
802
|
+
How `MCR` Works:
|
|
803
|
+
- 1, Trying to fix the middle original position with string comparison and [`diff-sequences`](https://github.com/jestjs/jest/tree/main/packages/diff-sequences). However, for non-JS code, such as Vue template, JSX, etc., it might be hard to find a perfect solution.
|
|
804
|
+
- 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`.
|
|
781
805
|
|
|
806
|
+
|
|
807
|
+
### Unparsable source
|
|
782
808
|
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
809
|
```js
|
|
784
810
|
import * as fs from "fs";
|
|
@@ -798,9 +824,38 @@ const coverageOptions = {
|
|
|
798
824
|
}
|
|
799
825
|
```
|
|
800
826
|
|
|
801
|
-
##
|
|
827
|
+
## Debug for Coverage and Sourcemap
|
|
828
|
+
> 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.
|
|
829
|
+
- Start debugging for v8 report with option `logging: 'debug'`
|
|
830
|
+
```js
|
|
831
|
+
const coverageOptions = {
|
|
832
|
+
logging: 'debug',
|
|
833
|
+
reports: [
|
|
834
|
+
['v8'],
|
|
835
|
+
['console-details']
|
|
836
|
+
]
|
|
837
|
+
};
|
|
838
|
+
```
|
|
839
|
+
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.
|
|
840
|
+

|
|
841
|
+
|
|
842
|
+
- Check sourcemap with [Source Map Visualization](https://evanw.github.io/source-map-visualization/)
|
|
843
|
+
|
|
844
|
+

|
|
845
|
+
|
|
846
|
+
## Integration with Any Testing Framework
|
|
847
|
+
- API
|
|
848
|
+
- Collecting coverage data when any stage of the test is completed, and adding the coverage data to the coverage reporter. `await mcr.add(coverageData)`
|
|
849
|
+
- Generating the coverage reports after the completion of all tests. `await mcr.generate()`
|
|
850
|
+
- see [Multiprocessing Support](#multiprocessing-support)
|
|
851
|
+
- CLI
|
|
852
|
+
- Wrapping with any CLI. `mcr your-cli --your-arguments`
|
|
853
|
+
- see [Command line](#command-line)
|
|
854
|
+
|
|
855
|
+
## Integration Examples
|
|
802
856
|
|
|
803
857
|
### [Playwright](https://github.com/microsoft/playwright)
|
|
858
|
+
- [playwright-coverage](https://github.com/cenfun/playwright-coverage) - A Example for Playwright Coverage Reports
|
|
804
859
|
- [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)
|
|
805
860
|
- Coverage for component testing:
|
|
806
861
|
- [playwright-ct-vue](https://github.com/cenfun/playwright-ct-vue)
|
|
@@ -812,6 +867,7 @@ const coverageOptions = {
|
|
|
812
867
|
|
|
813
868
|
### [Jest](https://github.com/jestjs/jest/)
|
|
814
869
|
- [jest-monocart-coverage](https://github.com/cenfun/jest-monocart-coverage) - A Jest custom reporter for coverage reports
|
|
870
|
+
- [jest-puppeteer-coverage](https://github.com/cenfun/jest-puppeteer-coverage) - A Jest Puppeteer Coverage Example
|
|
815
871
|
- Example for Jest (unit) + Puppeteer (e2e) + Codecov: [maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js)
|
|
816
872
|
|
|
817
873
|
### [Vitest](https://github.com/vitest-dev/vitest)
|
|
@@ -833,6 +889,32 @@ const coverageOptions = {
|
|
|
833
889
|
### [TestCafe](https://github.com/DevExpress/testcafe)
|
|
834
890
|
- [testcafe-reporter-coverage](https://github.com/cenfun/testcafe-reporter-coverage) - A TestCafe custom reporter for coverage reports
|
|
835
891
|
|
|
892
|
+
### [Selenium Webdriver](https://github.com/seleniumhq/selenium)
|
|
893
|
+
- [selenium-webdriver-coverage](https://github.com/cenfun/selenium-webdriver-coverage) - Selenium Webdriver V8 Coverage Example
|
|
894
|
+
|
|
895
|
+
### [Mocha](https://github.com/mochajs/mocha)
|
|
896
|
+
```sh
|
|
897
|
+
mcr mocha ./test/**/*.js
|
|
898
|
+
```
|
|
899
|
+
```sh
|
|
900
|
+
mcr --import tsx mocha ./test/**/*.ts
|
|
901
|
+
```
|
|
902
|
+
- see [mcr-tsx](https://github.com/cenfun/mcr-tsx)
|
|
903
|
+
|
|
904
|
+
### [tsx](https://github.com/privatenumber/tsx)
|
|
905
|
+
```sh
|
|
906
|
+
mcr --import tsx tsx ./src/example.ts
|
|
907
|
+
```
|
|
908
|
+
- see [mcr-tsx](https://github.com/cenfun/mcr-tsx)
|
|
909
|
+
|
|
910
|
+
### [ts-node](https://github.com/TypeStrong/ts-node)
|
|
911
|
+
- see [mcr-ts-node](https://github.com/cenfun/mcr-ts-node)
|
|
912
|
+
|
|
913
|
+
### [AVA](https://github.com/avajs/ava)
|
|
914
|
+
```sh
|
|
915
|
+
mcr ava
|
|
916
|
+
```
|
|
917
|
+
|
|
836
918
|
### [Codecov](https://codecov.com/)
|
|
837
919
|
[](https://codecov.io/gh/cenfun/monocart-coverage-reports)
|
|
838
920
|
- Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
|
|
@@ -886,10 +968,6 @@ const coverageOptions = {
|
|
|
886
968
|
-Dsonar.tests=test
|
|
887
969
|
-Dsonar.exclusions=dist/*,packages/*
|
|
888
970
|
```
|
|
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
971
|
|
|
894
972
|
### VSCode Extension
|
|
895
973
|
- [Coverage Gutters](https://github.com/ryanluker/vscode-coverage-gutters) - Display test coverage generated by lcov or xml in VSCode editor.
|