monocart-coverage-reports 2.13.2 → 3.0.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 +267 -114
- package/README.zh-Hans.md +268 -115
- package/lib/assets.js +6 -6
- package/lib/cli.js +191 -95
- package/lib/client/cdp-client.js +74 -43
- package/lib/client/coverage-client.js +24 -8
- package/lib/client/ws-session.js +115 -37
- package/lib/converter/ast-visitor.js +5 -7
- package/lib/converter/ast.js +6 -6
- package/lib/converter/collect-source-maps.js +9 -9
- package/lib/converter/converter.js +41 -115
- package/lib/converter/fake-source-coverage.js +64 -0
- package/lib/converter/find-original-range.js +3 -3
- package/lib/converter/flatten-source-maps.js +2 -4
- package/lib/converter/ignore.js +2 -4
- package/lib/converter/info-branch.js +5 -3
- package/lib/converter/info-function.js +5 -3
- package/lib/converter/info-statement.js +5 -3
- package/lib/converter/range-index.js +78 -0
- package/lib/converter/source-range-merge.js +103 -0
- package/lib/converter/untested.js +27 -55
- package/lib/default/options.js +13 -1
- package/lib/generate.js +44 -26
- package/lib/index.d.ts +52 -4
- package/lib/index.js +35 -41
- package/lib/istanbul/istanbul-summary.js +2 -2
- package/lib/istanbul/istanbul.js +35 -13
- package/lib/packages/chunks/chunk-RVOSNMVU.js +2 -0
- package/lib/packages/monocart-coverage-assets.js +4 -3
- package/lib/packages/monocart-coverage-converter.js +27 -0
- package/lib/packages/monocart-coverage-vendor.js +2 -41
- package/lib/packages/monocart-coverage-vendor.js.LICENSE +3 -221
- package/lib/platform/concurrency.js +47 -43
- package/lib/platform/share.js +1 -1
- package/lib/register/hooks.js +19 -13
- package/lib/register/register.js +5 -3
- package/lib/reports/codacy.js +3 -5
- package/lib/reports/codecov.js +3 -5
- package/lib/reports/console-details.js +5 -5
- package/lib/reports/console-summary.js +4 -4
- package/lib/reports/custom.js +8 -24
- package/lib/reports/markdown-details.js +4 -6
- package/lib/reports/markdown-summary.js +4 -6
- package/lib/reports/raw.js +10 -12
- package/lib/reports/v8-json.js +3 -5
- package/lib/reports/v8.js +3 -5
- package/lib/utils/dedupe.js +1 -1
- package/lib/utils/gc.js +30 -14
- package/lib/utils/markdown.js +2 -2
- package/lib/utils/merge/merge.js +2 -4
- package/lib/utils/merge/range-tree.js +1 -1
- package/lib/utils/request.js +85 -73
- package/lib/utils/snapshot.js +4 -4
- package/lib/utils/source-path.js +5 -5
- package/lib/utils/util.js +61 -41
- package/lib/v8/v8-summary.js +2 -4
- package/lib/v8/v8.js +11 -11
- package/migrate-to-3.md +175 -0
- package/package.json +153 -149
- package/skills/monocart-coverage-reports/SKILL.md +106 -0
- package/skills/monocart-coverage-reports/references/cli.md +108 -0
- package/skills/monocart-coverage-reports/references/collection.md +152 -0
- package/skills/monocart-coverage-reports/references/filtering-and-sourcemaps.md +170 -0
- package/skills/monocart-coverage-reports/references/reports-and-ci.md +219 -0
- package/skills/monocart-coverage-reports/references/repository-map.md +55 -0
- package/lib/index.mjs +0 -9
- package/lib/register/hooks.mjs +0 -3
- package/lib/register/register.mjs +0 -3
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* [Multiprocessing Support](#multiprocessing-support)
|
|
32
32
|
* [Command Line](#command-line)
|
|
33
33
|
* [Config File](#config-file)
|
|
34
|
+
* [Run Tests with `mcr test`](#run-tests-with-mcr-test)
|
|
34
35
|
* [Merge Coverage Reports](#merge-coverage-reports)
|
|
35
36
|
- [Automatic Merging](#automatic-merging)
|
|
36
37
|
- [Manual Merging](#manual-merging)
|
|
@@ -66,18 +67,21 @@
|
|
|
66
67
|
* [Thanks](#thanks)
|
|
67
68
|
|
|
68
69
|
## Usage
|
|
69
|
-
> [Node.js 22
|
|
70
|
+
> **MCR 3.x requires [Node.js >= 22.15.0](https://nodejs.org/)** for all APIs and CLI commands, including ordinary commands, `merge` and `test`.
|
|
71
|
+
|
|
72
|
+
**Upgrading from 2.x:** See the [3.x migration guide](migrate-to-3.md).
|
|
73
|
+
|
|
70
74
|
- Install
|
|
71
75
|
```sh
|
|
72
76
|
npm install monocart-coverage-reports
|
|
73
77
|
```
|
|
74
78
|
- API
|
|
75
79
|
```js
|
|
76
|
-
|
|
80
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
77
81
|
const mcr = MCR({
|
|
78
82
|
name: 'My Coverage Report - 2024-02-28',
|
|
79
83
|
outputDir: './coverage-reports',
|
|
80
|
-
reports: [
|
|
84
|
+
reports: ['v8', 'console-details'],
|
|
81
85
|
cleanCache: true
|
|
82
86
|
});
|
|
83
87
|
await mcr.add(coverageData);
|
|
@@ -99,6 +103,21 @@ mcr node my-app.js -r v8,console-details
|
|
|
99
103
|
```
|
|
100
104
|
For more information, see [Command Line](#command-line)
|
|
101
105
|
|
|
106
|
+
## AI Agent Skill
|
|
107
|
+
|
|
108
|
+
The reusable [Monocart Coverage Reports skill](skills/monocart-coverage-reports/SKILL.md) helps AI coding agents collect coverage, configure the CLI, debug sourcemaps and filters, merge raw reports, and enforce CI thresholds. It uses a main `SKILL.md` with task-specific references loaded on demand, targeting MCR 3.x only and checking its runtime requirements.
|
|
109
|
+
|
|
110
|
+
The npm package includes `skills/monocart-coverage-reports/`. Copy this **entire directory**, including `references/`, into your agent's supported skills location, or ask the agent to read `SKILL.md` directly. Installing MCR does not automatically register the skill or modify agent settings. The instructions are in English; requests and responses can use any language.
|
|
111
|
+
|
|
112
|
+
From the host project root, locate the installed skill and package manifest (older releases may not include the skill):
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
node -p "require.resolve('monocart-coverage-reports/skills/monocart-coverage-reports/SKILL.md')"
|
|
116
|
+
node -p "require.resolve('monocart-coverage-reports/package.json')"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Read the resolved skill, then follow its relative reference links. Use the installed package's runtime, API, and version requirements rather than assuming all examples apply to older releases. These commands resolve files; they do not import Markdown as JavaScript.
|
|
120
|
+
|
|
102
121
|
## Options
|
|
103
122
|
- Default options: [lib/default/options.js](./lib/default/options.js)
|
|
104
123
|
- Options declaration see `CoverageReportOptions` [lib/index.d.ts](./lib/index.d.ts)
|
|
@@ -115,6 +134,7 @@ For more information, see [Command Line](#command-line)
|
|
|
115
134
|
| `dataDir` | `string` | `null` | Coverage data directory loaded automatically in `generate()` — alternative to `addFromDir()`. |
|
|
116
135
|
| `entryFilter` | `string \| object \| function` | `null` | (V8 only) Filter V8 entries by url. See [Filtering Results](#filtering-results). |
|
|
117
136
|
| `sourceFilter` | `string \| object \| function` | `null` | (V8 only) Filter sources unpacked from sourcemaps. |
|
|
137
|
+
| `outputFilter` | `string \| object \| function` | `null` | (V8 only) Filter final files by `sourcePath` after sourcemap conversion. See [Filtering Results](#filtering-results). |
|
|
118
138
|
| `filter` | `string \| object \| function` | `null` | Combined filter replacing both `entryFilter` and `sourceFilter`. |
|
|
119
139
|
| `sourcePath` | `object \| function` | `null` | Rewrite source paths; see [Resolve `sourcePath`](#resolve-sourcepath-for-the-source-files). |
|
|
120
140
|
| `all` | `string \| string[] \| object` | `null` | Include untested files as empty coverage. See [Adding Empty Coverage](#adding-empty-coverage-for-untested-files). |
|
|
@@ -127,7 +147,7 @@ For more information, see [Command Line](#command-line)
|
|
|
127
147
|
| `watermarks` | `[number, number] \| object` | `[50, 80]` | Low/high thresholds (percent). Accepts per-metric object like `{ bytes:[50,80], lines:[50,80] }`. |
|
|
128
148
|
| `clean` | `boolean` | `true` | Clean previous reports in `outputDir` before generating (the `.cache` dir is preserved unless `cleanCache` is also `true`). |
|
|
129
149
|
| `cleanCache` | `boolean` | `false` | Clean the cache dir on start. When both `clean` and `cleanCache` are `false`, cached coverage data from previous runs is preserved and can be reused across processes. |
|
|
130
|
-
| `gc` | `number` | `null` | Memory threshold in MB; force GC at critical stages when
|
|
150
|
+
| `gc` | `number` | `null` | Memory threshold in MB; force GC at critical stages when JavaScript heap usage (`heapUsed`) exceeds it. Useful for very large data sets, see [JavaScript heap out of memory](#javascript-heap-out-of-memory). |
|
|
131
151
|
| `sourceMap` | `boolean` | `false` | Save source/sourcemap files to cache for debugging (requires `logging: "debug"`). |
|
|
132
152
|
| `sourceMapResolver` | `(url, defaultResolver) => Promise<string \| object>` | `null` | Custom sourcemap loader (e.g. from a build cache). Call `defaultResolver(url)` to fall back. |
|
|
133
153
|
| `onEntry` | `(entry) => void \| Promise<void>` | `null` | (V8 only) Per-entry hook; see [Hooks](#hooks). |
|
|
@@ -202,7 +222,7 @@ cat path-to/coverage-summary.md >> $GITHUB_STEP_SUMMARY
|
|
|
202
222
|
|
|
203
223
|
- Custom Reporter
|
|
204
224
|
```js
|
|
205
|
-
{
|
|
225
|
+
const coverageOptions = {
|
|
206
226
|
reports: [
|
|
207
227
|
[path.resolve('./test/custom-istanbul-reporter.js'), {
|
|
208
228
|
type: 'istanbul',
|
|
@@ -216,21 +236,23 @@ cat path-to/coverage-summary.md >> $GITHUB_STEP_SUMMARY
|
|
|
216
236
|
type: 'both'
|
|
217
237
|
}]
|
|
218
238
|
]
|
|
219
|
-
}
|
|
239
|
+
};
|
|
220
240
|
```
|
|
221
241
|
- Istanbul custom reporter
|
|
222
242
|
> 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.
|
|
223
243
|
- V8 custom reporter
|
|
224
244
|
> example: [./test/custom-v8-reporter.js](./test/custom-v8-reporter.js)
|
|
225
245
|
|
|
226
|
-
The `type` option
|
|
227
|
-
- `'istanbul'` — the reporter
|
|
228
|
-
- `'v8'` —
|
|
229
|
-
- `'both'` — the
|
|
246
|
+
The `type` option selects the reporter interface and when it runs:
|
|
247
|
+
- `'istanbul'` — uses the Istanbul reporter interface: `new Reporter(reportOptions)` followed by `execute(context)`. The context provides Istanbul report trees and source lookup. V8 input is converted to Istanbul coverage first.
|
|
248
|
+
- `'v8'` (default) — uses `new Reporter(reportOptions, globalOptions)` followed by `await generate(reportData)` for V8 input. `reportData` is the V8 coverage result, with `type`, `summary` and `files`; each file contains V8 ranges and summaries.
|
|
249
|
+
- `'both'` — uses the same `generate(reportData)` interface for either V8 or Istanbul input. Check `reportData.type` to handle the corresponding result structure. This does **not** supply both raw formats together; there are no `v8Data` or `istanbulData` properties.
|
|
250
|
+
|
|
251
|
+
Reporters are loaded with native ESM `import()`. Use `export default Reporter` for ESM; Node's built-in interoperability also accepts CommonJS `module.exports = Reporter` (use `.cjs` inside an ESM project). Local paths must include the file extension; extensionless paths and directory entries are not resolved. No separate CommonJS loader is provided. Reporter loading and initialization errors reject report generation instead of silently skipping the reporter.
|
|
230
252
|
|
|
231
253
|
### Multiple Reports:
|
|
232
254
|
```js
|
|
233
|
-
|
|
255
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
234
256
|
const coverageOptions = {
|
|
235
257
|
outputDir: './coverage-reports',
|
|
236
258
|
reports: [
|
|
@@ -247,15 +269,15 @@ const coverageOptions = {
|
|
|
247
269
|
|
|
248
270
|
// custom reports
|
|
249
271
|
// Specify reporter name with the NPM package
|
|
250
|
-
[
|
|
251
|
-
[
|
|
252
|
-
type:
|
|
253
|
-
key:
|
|
272
|
+
['custom-reporter-1'],
|
|
273
|
+
['custom-reporter-2', {
|
|
274
|
+
type: 'istanbul',
|
|
275
|
+
key: 'value'
|
|
254
276
|
}],
|
|
255
277
|
// Specify reporter name with local path
|
|
256
278
|
['/absolute/path/to/custom-reporter.js']
|
|
257
279
|
]
|
|
258
|
-
}
|
|
280
|
+
};
|
|
259
281
|
const mcr = MCR(coverageOptions);
|
|
260
282
|
```
|
|
261
283
|
|
|
@@ -322,7 +344,7 @@ await Promise.all([
|
|
|
322
344
|
})
|
|
323
345
|
]);
|
|
324
346
|
|
|
325
|
-
await page.goto(
|
|
347
|
+
await page.goto('your page url');
|
|
326
348
|
|
|
327
349
|
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
328
350
|
page.coverage.stopJSCoverage(),
|
|
@@ -350,7 +372,7 @@ await Promise.all([
|
|
|
350
372
|
})
|
|
351
373
|
]);
|
|
352
374
|
|
|
353
|
-
await page.goto(
|
|
375
|
+
await page.goto('your page url');
|
|
354
376
|
|
|
355
377
|
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
356
378
|
page.coverage.stopJSCoverage(),
|
|
@@ -403,29 +425,34 @@ Possible solutions:
|
|
|
403
425
|
- see [Command Line](#command-line)
|
|
404
426
|
|
|
405
427
|
### Collecting V8 Coverage Data with `CDPClient` API
|
|
428
|
+
|
|
429
|
+
`timeout` controls HTTP discovery and WebSocket connection deadlines (default: 10000ms). `requestTimeout` controls each CDP command on built-in WebSocket sessions (default: 30000ms). Set either to `0` to disable its deadline. Custom Playwright/Puppeteer sessions manage their own command timeouts. For custom sockets, use `new MCR.WSSession(ws, { timeout: 30000 })`.
|
|
430
|
+
|
|
406
431
|
- `CDPClient` available APIs
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
|
|
432
|
+
```ts
|
|
433
|
+
interface CoverageClient {
|
|
434
|
+
startJSCoverage: () => Promise<void>;
|
|
435
|
+
stopJSCoverage: () => Promise<V8CoverageEntry[]>;
|
|
410
436
|
|
|
411
|
-
startCSSCoverage: () => Promise<void>;
|
|
412
|
-
stopCSSCoverage: () => Promise<V8CoverageEntry[]>;
|
|
437
|
+
startCSSCoverage: () => Promise<void>;
|
|
438
|
+
stopCSSCoverage: () => Promise<V8CoverageEntry[]>;
|
|
413
439
|
|
|
414
|
-
/** start both js and css coverage */
|
|
415
|
-
startCoverage: () => Promise<void>;
|
|
416
|
-
/** stop and return both js and css coverage */
|
|
417
|
-
stopCoverage: () => Promise<V8CoverageEntry[]>;
|
|
440
|
+
/** start both js and css coverage */
|
|
441
|
+
startCoverage: () => Promise<void>;
|
|
442
|
+
/** stop and return both js and css coverage */
|
|
443
|
+
stopCoverage: () => Promise<V8CoverageEntry[]>;
|
|
418
444
|
|
|
419
|
-
/** write the coverage started by NODE_V8_COVERAGE to disk on demand, returns v8 coverage dir */
|
|
420
|
-
writeCoverage: () => Promise<string>;
|
|
445
|
+
/** write the coverage started by NODE_V8_COVERAGE to disk on demand, returns v8 coverage dir */
|
|
446
|
+
writeCoverage: () => Promise<string>;
|
|
421
447
|
|
|
422
|
-
/** get istanbul coverage data */
|
|
423
|
-
getIstanbulCoverage: (coverageKey?: string) => Promise<any>;
|
|
448
|
+
/** get istanbul coverage data */
|
|
449
|
+
getIstanbulCoverage: (coverageKey?: string) => Promise<any>;
|
|
450
|
+
}
|
|
424
451
|
```
|
|
425
452
|
|
|
426
453
|
- Work with node debugger port `--inspect=9229` or browser debugging port `--remote-debugging-port=9229`
|
|
427
454
|
```js
|
|
428
|
-
|
|
455
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
429
456
|
const client = await MCR.CDPClient({
|
|
430
457
|
port: 9229
|
|
431
458
|
});
|
|
@@ -436,8 +463,8 @@ const coverageData = await client.stopJSCoverage();
|
|
|
436
463
|
|
|
437
464
|
- Work with [Playwright CDPSession](https://playwright.dev/docs/api/class-cdpsession)
|
|
438
465
|
```js
|
|
439
|
-
|
|
440
|
-
|
|
466
|
+
import { chromium } from 'playwright';
|
|
467
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
441
468
|
const browser = await chromium.launch();
|
|
442
469
|
const page = await browser.newPage();
|
|
443
470
|
const session = await page.context().newCDPSession(page);
|
|
@@ -447,14 +474,14 @@ const client = await MCR.CDPClient({
|
|
|
447
474
|
// both js and css coverage
|
|
448
475
|
await client.startCoverage();
|
|
449
476
|
// run your test page here
|
|
450
|
-
await page.goto(
|
|
477
|
+
await page.goto('your page url');
|
|
451
478
|
const coverageData = await client.stopCoverage();
|
|
452
479
|
```
|
|
453
480
|
|
|
454
481
|
- Work with [Puppeteer CDPSession](https://pptr.dev/api/puppeteer.cdpsession)
|
|
455
482
|
```js
|
|
456
|
-
|
|
457
|
-
|
|
483
|
+
import puppeteer from 'puppeteer';
|
|
484
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
458
485
|
const browser = await puppeteer.launch({});
|
|
459
486
|
const page = await browser.newPage();
|
|
460
487
|
const session = await page.target().createCDPSession();
|
|
@@ -464,20 +491,20 @@ const client = await MCR.CDPClient({
|
|
|
464
491
|
// both js and css coverage
|
|
465
492
|
await client.startCoverage();
|
|
466
493
|
// run your test page here
|
|
467
|
-
await page.goto(
|
|
494
|
+
await page.goto('your page url');
|
|
468
495
|
const coverageData = await client.stopCoverage();
|
|
469
496
|
```
|
|
470
497
|
|
|
471
498
|
- Work with [Selenium Webdriver](https://www.selenium.dev/documentation/webdriver/) WebSocket (Chrome/Edge Browser)
|
|
472
499
|
```js
|
|
473
|
-
|
|
474
|
-
|
|
500
|
+
import { Builder, Browser } from 'selenium-webdriver';
|
|
501
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
475
502
|
const driver = await new Builder().forBrowser(Browser.CHROME).build();
|
|
476
503
|
const pageCdpConnection = await driver.createCDPConnection('page');
|
|
477
504
|
const session = new MCR.WSSession(pageCdpConnection._wsConnection);
|
|
478
505
|
const client = await MCR.CDPClient({
|
|
479
506
|
session
|
|
480
|
-
})
|
|
507
|
+
});
|
|
481
508
|
```
|
|
482
509
|
|
|
483
510
|
### V8 Coverage Data API
|
|
@@ -485,7 +512,7 @@ const client = await MCR.CDPClient({
|
|
|
485
512
|
- [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
|
|
486
513
|
- [Puppeteer Coverage Class](https://pptr.dev/api/puppeteer.coverage)
|
|
487
514
|
- [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)
|
|
488
|
-
```
|
|
515
|
+
```ts
|
|
489
516
|
// Coverage data for a source range.
|
|
490
517
|
export interface CoverageRange {
|
|
491
518
|
// JavaScript script source offset for the range start.
|
|
@@ -574,15 +601,15 @@ We can use `sourceFilter` to filter the source files. For example, we should rem
|
|
|
574
601
|
For example:
|
|
575
602
|
```js
|
|
576
603
|
const coverageOptions = {
|
|
577
|
-
entryFilter: (entry) => entry.url.indexOf(
|
|
604
|
+
entryFilter: (entry) => entry.url.indexOf('main.js') !== -1,
|
|
578
605
|
sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
|
|
579
606
|
};
|
|
580
607
|
```
|
|
581
608
|
Or using [`minimatch`](https://github.com/isaacs/minimatch) pattern:
|
|
582
609
|
```js
|
|
583
610
|
const coverageOptions = {
|
|
584
|
-
entryFilter:
|
|
585
|
-
sourceFilter:
|
|
611
|
+
entryFilter: '**/main.js',
|
|
612
|
+
sourceFilter: '**/src/**'
|
|
586
613
|
};
|
|
587
614
|
```
|
|
588
615
|
Support multiple patterns:
|
|
@@ -607,10 +634,17 @@ Note, those patterns will be transformed to a function, and the order of the pat
|
|
|
607
634
|
```js
|
|
608
635
|
const coverageOptions = {
|
|
609
636
|
entryFilter: (entry) => {
|
|
610
|
-
if (minimatch(entry.url, '**/node_modules/**')) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
637
|
+
if (minimatch(entry.url, '**/node_modules/**')) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
if (minimatch(entry.url, '**/vendor.js')) {
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
if (minimatch(entry.url, '**/src/**')) {
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
// else unmatched
|
|
647
|
+
return false;
|
|
614
648
|
}
|
|
615
649
|
};
|
|
616
650
|
```
|
|
@@ -629,6 +663,42 @@ const coverageOptions = {
|
|
|
629
663
|
};
|
|
630
664
|
```
|
|
631
665
|
|
|
666
|
+
### Using `outputFilter` to filter final files
|
|
667
|
+
> **Version requirement:** `outputFilter` is introduced in **v3.0.0**. It is not supported in v2.13.x or earlier.
|
|
668
|
+
|
|
669
|
+
**Choose one filtering approach.** `outputFilter` and the existing `entryFilter` + `sourceFilter` approach serve the same purpose: deciding which files appear in the report. They are alternatives, not additional filters you need to configure together. The combined `filter` option is shorthand for `entryFilter` + `sourceFilter`; it does not configure `outputFilter`.
|
|
670
|
+
|
|
671
|
+
| Approach | When and what it filters | Advantages | Trade-offs |
|
|
672
|
+
| :-- | :-- | :-- | :-- |
|
|
673
|
+
| `entryFilter` + `sourceFilter` (or `filter`) | Filters inputs during discovery: `entryFilter` matches entry URLs, and `sourceFilter` matches source paths as sourcemaps are unpacked. | Excludes unwanted files earlier, avoiding unnecessary downstream processing. Prefer this approach for better performance, especially with large projects or bundles. | Requires understanding entry files versus sourcemap sources and configuring both stages. Excluding a bundle via `entryFilter` also removes any desired sources inside it. |
|
|
674
|
+
| `outputFilter` | Filters final files by `sourcePath` after sourcemap conversion, merging and adding untested files (`all`), immediately before report generation. | Simpler configuration: use one filter against the final file list, without distinguishing entries from unpacked sources. | Unwanted files are still discovered and processed before being discarded. This does not save their earlier processing cost and can increase generation time and memory usage, especially when many files will ultimately be excluded. |
|
|
675
|
+
|
|
676
|
+
**Recommendation:** use input filtering (`entryFilter` + `sourceFilter`, or `filter`) when performance matters. Choose `outputFilter` when simpler configuration and filtering the final results are more important, and the extra processing cost is acceptable. If switching to `outputFilter`, remove the old input filters for the same scope: output filtering cannot restore files already excluded during input filtering.
|
|
677
|
+
|
|
678
|
+
For V8 input, `outputFilter` filters both V8 reports and converted Istanbul reports. It defaults to `null` (disabled).
|
|
679
|
+
|
|
680
|
+
Like `entryFilter` and `sourceFilter`, it supports strings and ordered pattern objects (the first matching pattern wins; unmatched files are excluded). Patterns match the final `sourcePath`, including any `sourcePath` rewrites. A function receives the final file entry, not a path string.
|
|
681
|
+
|
|
682
|
+
```js
|
|
683
|
+
const coverageOptions = {
|
|
684
|
+
outputFilter: {
|
|
685
|
+
'**/node_modules/**': false,
|
|
686
|
+
'**/src/**': true
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
// Alternatively:
|
|
690
|
+
// outputFilter: '**/src/**'
|
|
691
|
+
// outputFilter: (entry) => entry.sourcePath.startsWith('src/')
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
CLI examples (quote patterns to prevent shell expansion):
|
|
695
|
+
```sh
|
|
696
|
+
mcr merge --inputDir ./raw --outputFilter "**/src/**"
|
|
697
|
+
mcr merge --inputDir ./raw --outputFilter "{'**/node_modules/**':false,'**/src/**':true}"
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
`outputFilter` does not change raw coverage exports and does not apply to Istanbul input.
|
|
701
|
+
|
|
632
702
|
## Resolve `sourcePath` for the Source Files
|
|
633
703
|
If the source file comes from the sourcemap, then its path is a virtual path. Using the `sourcePath` option to resolve a custom path.
|
|
634
704
|
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.
|
|
@@ -650,34 +720,34 @@ It also supports simple key/value replacement:
|
|
|
650
720
|
```js
|
|
651
721
|
const coverageOptions = {
|
|
652
722
|
sourcePath: {
|
|
653
|
-
'my-dist-file1/': '',
|
|
723
|
+
'my-dist-file1/': '',
|
|
654
724
|
'my-dist-file2/': ''
|
|
655
725
|
}
|
|
656
726
|
};
|
|
657
727
|
```
|
|
658
728
|
Normalize the full path of the file:
|
|
659
729
|
```js
|
|
660
|
-
|
|
730
|
+
import path from 'node:path';
|
|
661
731
|
|
|
662
732
|
// MCR coverage options
|
|
663
733
|
const coverageOptions = {
|
|
664
|
-
sourcePath: (filePath, info)=> {
|
|
734
|
+
sourcePath: (filePath, info) => {
|
|
665
735
|
if (!filePath.includes('/') && info.distFile) {
|
|
666
736
|
return `${path.dirname(info.distFile)}/${filePath}`;
|
|
667
737
|
}
|
|
668
738
|
return filePath;
|
|
669
739
|
}
|
|
670
|
-
}
|
|
740
|
+
};
|
|
671
741
|
```
|
|
672
742
|
|
|
673
743
|
## Adding Empty Coverage for Untested Files
|
|
674
744
|
By default the untested files will not be included in the coverage report. Use `all` to include them with 0% coverage. This also works when no coverage data has been collected: a V8 report is generated from the matching untested files.
|
|
675
745
|
```js
|
|
676
746
|
const coverageOptions = {
|
|
677
|
-
all: './src'
|
|
747
|
+
all: './src'
|
|
678
748
|
|
|
679
749
|
// or multiple dirs
|
|
680
|
-
all: ['./src', './lib']
|
|
750
|
+
// all: ['./src', './lib']
|
|
681
751
|
};
|
|
682
752
|
```
|
|
683
753
|
The untested files will apply to the `sourceFilter`. And it also supports additional `filter` (return the file type for js or css coverage):
|
|
@@ -690,30 +760,29 @@ const coverageOptions = {
|
|
|
690
760
|
'**/ignored-*.js': false,
|
|
691
761
|
'**/*.html': false,
|
|
692
762
|
// empty css coverage
|
|
693
|
-
'**/*.scss':
|
|
763
|
+
'**/*.scss': 'css',
|
|
694
764
|
'**/*': true
|
|
695
765
|
}
|
|
696
766
|
}
|
|
697
767
|
};
|
|
698
768
|
```
|
|
699
|
-
|
|
769
|
+
Untested files can be transformed into JavaScript for AST analysis. This example handles **TypeScript without JSX** and assumes `@swc/core` is installed. Use an appropriate compiler/parser for JSX/TSX or Vue; this is not a universal transformer. Preserve the full filename to keep same-basename files in different directories distinct, and parse SWC's JSON map before assigning it.
|
|
700
770
|
```js
|
|
701
|
-
|
|
702
|
-
|
|
771
|
+
import { fileURLToPath } from 'node:url';
|
|
772
|
+
import { transform } from '@swc/core';
|
|
703
773
|
const coverageOptions = {
|
|
704
774
|
all: {
|
|
705
775
|
dir: ['./src'],
|
|
776
|
+
filter: '**/*.ts',
|
|
706
777
|
transformer: async (entry) => {
|
|
707
|
-
const { code, map } = await
|
|
708
|
-
filename:
|
|
778
|
+
const { code, map } = await transform(entry.source, {
|
|
779
|
+
filename: fileURLToPath(entry.url),
|
|
709
780
|
sourceMaps: true,
|
|
710
781
|
isModule: true,
|
|
711
782
|
jsc: {
|
|
712
783
|
parser: {
|
|
713
|
-
syntax:
|
|
714
|
-
|
|
715
|
-
},
|
|
716
|
-
transform: {}
|
|
784
|
+
syntax: 'typescript'
|
|
785
|
+
}
|
|
717
786
|
}
|
|
718
787
|
});
|
|
719
788
|
entry.source = code;
|
|
@@ -726,36 +795,45 @@ const coverageOptions = {
|
|
|
726
795
|
## Hooks
|
|
727
796
|
|
|
728
797
|
### `onEnd` — after the report is generated
|
|
729
|
-
Typical use case: enforce coverage thresholds.
|
|
798
|
+
Typical use case: enforce coverage thresholds. Throw on failure so API callers can handle the error and the CLI exits nonzero, without forcing `process.exit()` before cleanup finishes.
|
|
730
799
|
```js
|
|
731
|
-
const
|
|
800
|
+
const getCoverageError = (metric, summary, minimum) => {
|
|
801
|
+
if (!summary || !(summary.total > 0)) {
|
|
802
|
+
return `${metric}: missing or empty coverage`;
|
|
803
|
+
}
|
|
804
|
+
const { pct } = summary;
|
|
805
|
+
if (typeof pct !== 'number' || !Number.isFinite(pct) || pct < minimum) {
|
|
806
|
+
return `${metric}: ${pct}%, required ${minimum}%`;
|
|
807
|
+
}
|
|
808
|
+
return '';
|
|
809
|
+
};
|
|
732
810
|
const coverageOptions = {
|
|
733
811
|
name: 'My Coverage Report',
|
|
734
812
|
outputDir: './coverage-reports',
|
|
735
813
|
onEnd: (coverageResults) => {
|
|
814
|
+
if (!coverageResults) {
|
|
815
|
+
throw new Error('No coverage results');
|
|
816
|
+
}
|
|
736
817
|
const thresholds = {
|
|
737
818
|
bytes: 80,
|
|
738
819
|
lines: 60
|
|
739
820
|
};
|
|
740
|
-
console.log('check thresholds ...', thresholds);
|
|
741
821
|
const errors = [];
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
errors.push(`Coverage threshold for ${k} (${pct} %) not met: ${thresholds[k]} %`);
|
|
822
|
+
for (const [metric, minimum] of Object.entries(thresholds)) {
|
|
823
|
+
const error = getCoverageError(metric, coverageResults.summary?.[metric], minimum);
|
|
824
|
+
if (error) {
|
|
825
|
+
errors.push(error);
|
|
747
826
|
}
|
|
748
|
-
}
|
|
827
|
+
}
|
|
749
828
|
if (errors.length) {
|
|
750
|
-
|
|
751
|
-
console.log(EC.red(errMsg));
|
|
752
|
-
// throw new Error(errMsg);
|
|
753
|
-
// process.exit(1);
|
|
829
|
+
throw new Error(errors.join('\n'));
|
|
754
830
|
}
|
|
755
831
|
}
|
|
756
|
-
}
|
|
832
|
+
};
|
|
757
833
|
```
|
|
758
834
|
|
|
835
|
+
This policy rejects missing/zero-total metrics; adapt it explicitly if a metric is legitimately empty. With no usable input, `generate()` may return `undefined` without calling `onEnd`. API callers must check that result; ordinary CLI workflows should use a fresh output directory and verify a newly generated nonempty report. See the [strict CI recipes](skills/monocart-coverage-reports/references/reports-and-ci.md#reject-stale-reports-and-missing-collection-in-ci).
|
|
836
|
+
|
|
759
837
|
### `onEntry` — before each V8 entry is processed (V8 only)
|
|
760
838
|
Use this to mutate the entry (for example, transform non-standard sources into valid ECMAScript before the AST parser runs). See [Unparsable source](#unparsable-source).
|
|
761
839
|
```js
|
|
@@ -772,7 +850,7 @@ Only available when using `mcr <command>`:
|
|
|
772
850
|
- `onReady(coverageReport, nodeV8CoverageDir, subprocess)` — runs after the child exits, before MCR reads coverage from `nodeV8CoverageDir`. Useful when the child hasn't finished flushing coverage data and needs to be waited on.
|
|
773
851
|
```js
|
|
774
852
|
// mcr.config.js
|
|
775
|
-
|
|
853
|
+
export default {
|
|
776
854
|
onReady: async (coverageReport, nodeV8CoverageDir, subprocess) => {
|
|
777
855
|
// wait for something, or pre-process the raw files
|
|
778
856
|
}
|
|
@@ -780,6 +858,8 @@ module.exports = {
|
|
|
780
858
|
```
|
|
781
859
|
|
|
782
860
|
## Ignoring Uncovered Codes
|
|
861
|
+
<!-- readme-lint-disable -->
|
|
862
|
+
|
|
783
863
|
To ignore codes, use the special comment which starts with `v8 ignore `:
|
|
784
864
|
- Ignoring all until stop
|
|
785
865
|
```js
|
|
@@ -814,12 +894,14 @@ function uncovered() {
|
|
|
814
894
|
```
|
|
815
895
|
- To disable comment-based ignoring entirely, set `v8Ignore: false`.
|
|
816
896
|
|
|
897
|
+
<!-- readme-lint-enable -->
|
|
898
|
+
|
|
817
899
|
## Multiprocessing Support
|
|
818
900
|
> 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)
|
|
819
901
|
- Main process, before the start of testing
|
|
820
902
|
```js
|
|
821
|
-
|
|
822
|
-
|
|
903
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
904
|
+
import coverageOptions from 'path-to/same-options.js';
|
|
823
905
|
const mcr = MCR(coverageOptions);
|
|
824
906
|
// clean previous cache before the start of testing
|
|
825
907
|
// unless the running environment is new and no cache
|
|
@@ -828,16 +910,16 @@ mcr.cleanCache();
|
|
|
828
910
|
|
|
829
911
|
- Sub process 1, testing stage 1
|
|
830
912
|
```js
|
|
831
|
-
|
|
832
|
-
|
|
913
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
914
|
+
import coverageOptions from 'path-to/same-options.js';
|
|
833
915
|
const mcr = MCR(coverageOptions);
|
|
834
916
|
await mcr.add(coverageData1);
|
|
835
917
|
```
|
|
836
918
|
|
|
837
919
|
- Sub process 2, testing stage 2
|
|
838
920
|
```js
|
|
839
|
-
|
|
840
|
-
|
|
921
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
922
|
+
import coverageOptions from 'path-to/same-options.js';
|
|
841
923
|
const mcr = MCR(coverageOptions);
|
|
842
924
|
await mcr.add(coverageData2);
|
|
843
925
|
```
|
|
@@ -845,8 +927,8 @@ await mcr.add(coverageData2);
|
|
|
845
927
|
- Main process, after the completion of testing
|
|
846
928
|
```js
|
|
847
929
|
// generate coverage reports after the completion of testing
|
|
848
|
-
|
|
849
|
-
|
|
930
|
+
import { MCR } from 'monocart-coverage-reports';
|
|
931
|
+
import coverageOptions from 'path-to/same-options.js';
|
|
850
932
|
const mcr = MCR(coverageOptions);
|
|
851
933
|
await mcr.generate();
|
|
852
934
|
```
|
|
@@ -868,14 +950,17 @@ npx mcr node ./test/specs/node.test.js -r v8,console-details --lcov
|
|
|
868
950
|
|
|
869
951
|
- CLI Options
|
|
870
952
|
|
|
953
|
+
Commands are executed with Execa's default options, without enabling shell mode. Pass the executable and its arguments separately, for example `mcr node "./my app.js"`; do not quote an entire command as a single argument. Argument boundaries are preserved: MCR does not expand environment variables or globs, or interpret pipes, redirections, `&&` or other shell syntax. Move command sequences into a script instead. Use `mcr [options] -- executable ...args` to keep child options separate from MCR options. Execa handles platform-specific command execution, including escaped batch-file invocation through `cmd.exe` when needed on Windows.
|
|
954
|
+
|
|
871
955
|
The options below are only available when using the CLI (`mcr <command>`):
|
|
872
956
|
|
|
873
957
|
| Option | Type | Default | Description |
|
|
874
958
|
| :-- | :-- | :-- | :-- |
|
|
875
959
|
| `-c, --config` | `string` | — | Custom config file path |
|
|
876
|
-
| `--import` | `string` | `null` | Preload ESM module via `NODE_OPTIONS`, e.g. `tsx` for TypeScript.
|
|
877
|
-
| `--require` | `string` | `null` |
|
|
878
|
-
| `--env` | `string` | `null` | Load environment variables from a dotenv file (defaults to `.env`).
|
|
960
|
+
| `--import` | `string` | `null` | Preload ESM module via `NODE_OPTIONS`, e.g. `tsx` for TypeScript. |
|
|
961
|
+
| `--require` | `string` | `null` | Deprecated alias for `--import`; uses ESM loading, not CommonJS resolution |
|
|
962
|
+
| `--env` | `string` | `null` | Load environment variables from a dotenv file (defaults to `.env`). |
|
|
963
|
+
| `test` (config only) | `string \| string[] \| null` | `null` | Default test file paths or glob patterns accepted by `node --test`, passed unchanged when `mcr test` has no positional targets; ignored by other commands and the API. Requires Node.js `>= 22.15.0` |
|
|
879
964
|
| `onStart` | `(coverageReport) => Promise<void>` | `null` | Runs before the child process is spawned; see [Hooks](#hooks) |
|
|
880
965
|
| `onReady` | `(coverageReport, nodeV8CoverageDir, subprocess) => Promise<void>` | `null` | Runs after the child exits, before MCR reads coverage; see [Hooks](#hooks) |
|
|
881
966
|
|
|
@@ -886,16 +971,18 @@ For other options (e.g. `--name`, `--reports`, `--outputDir`, `--lcov`, etc.), s
|
|
|
886
971
|
mcr -c mcr.config.js -- sub-cli -c sub-cli.config.js
|
|
887
972
|
```
|
|
888
973
|
|
|
889
|
-
- `--import <module>` / `--require <module>`: forward a preload module to the child process via `NODE_OPTIONS`. Use this for TypeScript/JSX runtimes (`tsx`, `ts-node`, etc.) and also when loading a `mcr.config.ts`.
|
|
974
|
+
- `--import <module>` / `--require <module>`: forward a preload module to the child process via `NODE_OPTIONS`. Use this for TypeScript/JSX runtimes (`tsx`, `ts-node`, etc.) and also when loading a `mcr.config.ts`. Available on all supported Node.js versions. Local paths are relative to the current working directory and must include the file extension; paths containing spaces are supported. Package names use native ESM resolution from MCR's module location. Both flags use `import()` and forward `--import` to Node; `--require` is a deprecated alias, not a CommonJS loader. CJS modules work only through Node's built-in import interoperability.
|
|
890
975
|
```sh
|
|
891
976
|
mcr --import tsx node ./test.ts
|
|
892
977
|
```
|
|
893
978
|
|
|
894
|
-
- `--env [path]`: load environment variables from a dotenv file before the child starts (defaults to `.env`).
|
|
979
|
+
- `--env [path]`: load environment variables from a dotenv file before the child starts (defaults to `.env`). Uses `process.loadEnvFile`, available on all supported Node.js versions.
|
|
895
980
|
```sh
|
|
896
981
|
mcr --env .env.test node ./test.js
|
|
897
982
|
```
|
|
898
983
|
|
|
984
|
+
- `test`: run Node.js tests and generate coverage reports. See [Run Tests with `mcr test`](#run-tests-with-mcr-test).
|
|
985
|
+
|
|
899
986
|
- `merge`: merge raw coverage data from `inputDir`(s) and generate merged reports directly, without running a child process.
|
|
900
987
|
```sh
|
|
901
988
|
mcr merge -r v8,console-details --inputDir ./coverage-reports/unit/raw,./coverage-reports/e2e/raw --outputDir ./coverage-reports/merged
|
|
@@ -908,6 +995,8 @@ See [Merge Coverage Reports](#merge-coverage-reports).
|
|
|
908
995
|
- [AVA](#ava)
|
|
909
996
|
|
|
910
997
|
## Config File
|
|
998
|
+
In MCR 3.x, `loadConfig()` throws if an explicitly requested config does not exist or a discovered config fails to load (including JSON parsing, module syntax and startup errors). All CLI commands exit with a non-zero status for these errors. No `strict` option is needed. If no custom config is requested and no default config is found, execution continues with existing options.
|
|
999
|
+
|
|
911
1000
|
Loading config file by priority:
|
|
912
1001
|
- Custom config file:
|
|
913
1002
|
- CLI: `mcr --config <my-config-file-path>`
|
|
@@ -923,6 +1012,59 @@ mcr --import tsx -c mcr.config.ts node ./test.js
|
|
|
923
1012
|
|
|
924
1013
|
> If no custom path is given, MCR walks up from the current working directory to find the default config file (via [`find-up`](https://github.com/sindresorhus/find-up)). Files outside the search path will be ignored.
|
|
925
1014
|
|
|
1015
|
+
## Run Tests with `mcr test`
|
|
1016
|
+
|
|
1017
|
+
**New in MCR 3.x:** Run tests with the built-in [Node.js test runner](https://nodejs.org/docs/latest/api/test.html) and automatically generate coverage reports with one command. Requires **Node.js >= 22.15.0**.
|
|
1018
|
+
|
|
1019
|
+
Install MCR and run your test files directly — no config file is required:
|
|
1020
|
+
|
|
1021
|
+
```sh
|
|
1022
|
+
npm install --save-dev monocart-coverage-reports
|
|
1023
|
+
npx mcr test "./test/**/*.test.js" -r v8,console-summary -o ./coverage-reports
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
This runs the matching tests, prints a coverage summary in the terminal, and writes the V8 HTML report to `./coverage-reports/index.html`. Quote glob patterns so Node.js, rather than your shell, handles matching.
|
|
1027
|
+
|
|
1028
|
+
For repeated runs, save the test targets and coverage options in `mcr.config.js` (ESM; use `"type": "module"` in `package.json`, or name the config `mcr.config.mjs`):
|
|
1029
|
+
|
|
1030
|
+
```js
|
|
1031
|
+
export default {
|
|
1032
|
+
test: ['./test/unit/**/*.test.js', './test/integration/**/*.test.js'],
|
|
1033
|
+
reports: ['v8', 'console-summary'],
|
|
1034
|
+
outputDir: './coverage-reports'
|
|
1035
|
+
};
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
```sh
|
|
1039
|
+
# Run the targets from the config
|
|
1040
|
+
npx mcr test
|
|
1041
|
+
# Override the configured targets and forward Node.js test runner options after --
|
|
1042
|
+
npx mcr test "./test/unit/**/*.test.js" -- --test-reporter=spec
|
|
1043
|
+
|
|
1044
|
+
# Use an explicit config file
|
|
1045
|
+
npx mcr test "../tests/**/*.test.js" -c ./mcr.config.js
|
|
1046
|
+
|
|
1047
|
+
# Choose reports from the command line
|
|
1048
|
+
npx mcr test "../tests/**/*.test.js" -r v8,console-summary
|
|
1049
|
+
|
|
1050
|
+
# Run multiple test files
|
|
1051
|
+
npx mcr test ./test/first.test.js ./test/second.test.js
|
|
1052
|
+
|
|
1053
|
+
# Filter test names and select a reporter
|
|
1054
|
+
npx mcr test "../tests/**/*.test.js" -c ./mcr.config.js -- --test-name-pattern="my test" --test-reporter=spec
|
|
1055
|
+
|
|
1056
|
+
# Place the test command after an initial separator
|
|
1057
|
+
npx mcr -c ./mcr.config.js -- test "../tests/**/*.test.js" -- --test-reporter=spec
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
Positional targets override (rather than append to) the config `test` option. If no positional targets are given, the config option is used. A config file is not required when targets are supplied on the command line. Existing MCR options such as `-c`, `-r`, `-o` and `--lcov` can appear before or after targets. Put Node runner options after `--`; those arguments are forwarded unchanged, not parsed as MCR options or target overrides. Quote globs to prevent shell expansion. In the alternative `mcr [options] -- test ...` form, put all MCR options before the first `--`; a second `--` separates Node runner options.
|
|
1061
|
+
|
|
1062
|
+
Targets are passed unchanged to `node --test`. MCR does not check their types or filesystem existence, expand directories/globs, resolve or normalize paths, escape special characters, or deduplicate targets. `node --test` accepts positional test file paths or glob patterns, not a JavaScript configuration object. In MCR config, a string represents one such argument (it is not split on spaces), and a string array represents multiple arguments forwarded in their original order. `null` is only MCR's skip default and is never passed to Node. Node handles discovery, matching, interpretation and errors. Relative paths use the current working directory, not the config file directory. A bare directory is not promised to trigger recursive discovery; use a glob such as `"../tests/**/*.test.js"` for recursive matching, following the installed Node version's native behavior. TypeScript execution depends on your Node version or a loader such as `--import tsx`.
|
|
1063
|
+
|
|
1064
|
+
`test` defaults to `null`; when no positional targets are supplied, `null`, an omitted value, an empty string or `[]` skips tests without generating a report. MCR only checks whether targets were supplied; invalid or unmatched targets are left to Node. If no coverage data is produced and no report can be generated, the command fails at report generation. Missing explicit config files and failed config loading also fail the command rather than silently skipping tests. Test failures still produce coverage reports and retain a non-zero exit status; report generation failures also fail the command. Existing coverage options and CLI hooks are supported. Use coverage filters to exclude test files if needed. **The `test` option has no effect on other CLI commands or API calls**, including `merge` and `generate()`.
|
|
1065
|
+
|
|
1066
|
+
See [Command Line](#command-line) for other CLI options.
|
|
1067
|
+
|
|
926
1068
|
## Merge Coverage Reports
|
|
927
1069
|
The following usage scenarios may require merging coverage reports:
|
|
928
1070
|
- 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.
|
|
@@ -943,12 +1085,12 @@ First, using the `raw` report to export the original coverage data to the specif
|
|
|
943
1085
|
```js
|
|
944
1086
|
const coverageOptions = {
|
|
945
1087
|
name: 'My Unit Test Coverage Report',
|
|
946
|
-
outputDir:
|
|
1088
|
+
outputDir: './coverage-reports/unit',
|
|
947
1089
|
reports: [
|
|
948
1090
|
['raw', {
|
|
949
1091
|
// relative path will be "./coverage-reports/unit/raw"
|
|
950
1092
|
// defaults to raw
|
|
951
|
-
outputDir:
|
|
1093
|
+
outputDir: 'raw'
|
|
952
1094
|
}],
|
|
953
1095
|
['v8'],
|
|
954
1096
|
['console-details']
|
|
@@ -964,8 +1106,8 @@ const coverageOptions = {
|
|
|
964
1106
|
- Then create a script `merge-coverage.js` to generate a merged report with option `inputDir`.
|
|
965
1107
|
```js
|
|
966
1108
|
// merge-coverage.js
|
|
967
|
-
|
|
968
|
-
|
|
1109
|
+
import fs from 'fs';
|
|
1110
|
+
import { CoverageReport } from 'monocart-coverage-reports';
|
|
969
1111
|
const inputDir = [
|
|
970
1112
|
'./coverage-reports/unit/raw',
|
|
971
1113
|
'./coverage-reports/e2e/raw'
|
|
@@ -984,7 +1126,7 @@ const coverageOptions = {
|
|
|
984
1126
|
'**/node_modules/**': false,
|
|
985
1127
|
'**/src/**': true
|
|
986
1128
|
},
|
|
987
|
-
|
|
1129
|
+
|
|
988
1130
|
sourcePath: (filePath, info) => {
|
|
989
1131
|
// Unify the file path for the same files
|
|
990
1132
|
// For example, the file index.js has different paths:
|
|
@@ -998,7 +1140,7 @@ const coverageOptions = {
|
|
|
998
1140
|
['v8'],
|
|
999
1141
|
['console-details']
|
|
1000
1142
|
],
|
|
1001
|
-
|
|
1143
|
+
|
|
1002
1144
|
onEnd: () => {
|
|
1003
1145
|
// remove the raw files if no longer needed
|
|
1004
1146
|
// inputDir.forEach((p) => {
|
|
@@ -1027,7 +1169,7 @@ see example: [merge-code-coverage](https://github.com/cenfun/merge-code-coverage
|
|
|
1027
1169
|
## Common issues
|
|
1028
1170
|
### Unexpected coverage
|
|
1029
1171
|
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 configuration of build tools. Let's take a look at an example:
|
|
1030
|
-
```
|
|
1172
|
+
```text
|
|
1031
1173
|
const a = tf ? 'true' : 'false';
|
|
1032
1174
|
^ ^ ^
|
|
1033
1175
|
m1 p m2
|
|
@@ -1043,21 +1185,21 @@ How `MCR` Works:
|
|
|
1043
1185
|
### Unparsable source
|
|
1044
1186
|
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.
|
|
1045
1187
|
```js
|
|
1046
|
-
import * as fs from
|
|
1047
|
-
import * as path from
|
|
1048
|
-
import { fileURLToPath } from
|
|
1188
|
+
import * as fs from 'fs';
|
|
1189
|
+
import * as path from 'path';
|
|
1190
|
+
import { fileURLToPath } from 'url';
|
|
1049
1191
|
import * as TsNode from 'ts-node';
|
|
1050
1192
|
const coverageOptions = {
|
|
1051
|
-
onEntry:
|
|
1052
|
-
const filePath = fileURLToPath(entry.url)
|
|
1053
|
-
const originalSource = fs.readFileSync(filePath).toString(
|
|
1193
|
+
onEntry: (entry) => {
|
|
1194
|
+
const filePath = fileURLToPath(entry.url);
|
|
1195
|
+
const originalSource = fs.readFileSync(filePath).toString('utf-8');
|
|
1054
1196
|
const fileName = path.basename(filePath);
|
|
1055
1197
|
const tn = TsNode.create({});
|
|
1056
1198
|
const source = tn.compile(originalSource, fileName);
|
|
1057
1199
|
entry.fake = false;
|
|
1058
1200
|
entry.source = source;
|
|
1059
1201
|
}
|
|
1060
|
-
}
|
|
1202
|
+
};
|
|
1061
1203
|
```
|
|
1062
1204
|
|
|
1063
1205
|
### JavaScript heap out of memory
|
|
@@ -1098,7 +1240,7 @@ const coverageOptions = {
|
|
|
1098
1240
|
|
|
1099
1241
|
- Show time logs with env `MCR_LOG_TIME`
|
|
1100
1242
|
```js
|
|
1101
|
-
process.env.MCR_LOG_TIME = true
|
|
1243
|
+
process.env.MCR_LOG_TIME = true;
|
|
1102
1244
|
```
|
|
1103
1245
|
|
|
1104
1246
|
## Integration with Any Testing Framework
|
|
@@ -1181,7 +1323,7 @@ mcr mocha ./test/**/*.js
|
|
|
1181
1323
|
```sh
|
|
1182
1324
|
cross-env NODE_OPTIONS="--import tsx" npx mcr tsx ./src/example.ts
|
|
1183
1325
|
cross-env NODE_OPTIONS="--import tsx" npx mcr mocha ./test/**/*.ts
|
|
1184
|
-
# Node.js
|
|
1326
|
+
# MCR 3.x: Node.js >= 22.15.0
|
|
1185
1327
|
mcr --import tsx tsx ./src/example.ts
|
|
1186
1328
|
mcr --import tsx mocha ./test/**/*.ts
|
|
1187
1329
|
```
|
|
@@ -1201,7 +1343,7 @@ mcr ava
|
|
|
1201
1343
|
- Supports native `codecov` built-in report ([specification](https://docs.codecov.com/docs/codecov-custom-coverage-format))
|
|
1202
1344
|
```js
|
|
1203
1345
|
const coverageOptions = {
|
|
1204
|
-
outputDir:
|
|
1346
|
+
outputDir: './coverage-reports',
|
|
1205
1347
|
reports: [
|
|
1206
1348
|
['codecov']
|
|
1207
1349
|
]
|
|
@@ -1221,7 +1363,7 @@ const coverageOptions = {
|
|
|
1221
1363
|
- Using `lcov` report:
|
|
1222
1364
|
```js
|
|
1223
1365
|
const coverageOptions = {
|
|
1224
|
-
outputDir:
|
|
1366
|
+
outputDir: './coverage-reports',
|
|
1225
1367
|
lcov: true
|
|
1226
1368
|
};
|
|
1227
1369
|
```
|
|
@@ -1239,7 +1381,7 @@ const coverageOptions = {
|
|
|
1239
1381
|
- Using `lcov` report:
|
|
1240
1382
|
```js
|
|
1241
1383
|
const coverageOptions = {
|
|
1242
|
-
outputDir:
|
|
1384
|
+
outputDir: './coverage-reports',
|
|
1243
1385
|
lcov: true
|
|
1244
1386
|
};
|
|
1245
1387
|
```
|
|
@@ -1272,17 +1414,25 @@ const coverageOptions = {
|
|
|
1272
1414
|
```
|
|
1273
1415
|
|
|
1274
1416
|
## Contributing
|
|
1275
|
-
- Node.js
|
|
1417
|
+
- Latest Node.js 24 LTS recommended for development; minimum package runtime is 22.15.0. Development tools may require a newer patch than the minimum runtime.
|
|
1276
1418
|
- VSCode (extensions: eslint/stylelint/vue)
|
|
1277
1419
|
```sh
|
|
1278
1420
|
npm install
|
|
1279
1421
|
npx playwright install --with-deps
|
|
1280
1422
|
|
|
1281
1423
|
npm run build
|
|
1424
|
+
npm run lint
|
|
1282
1425
|
npm run test
|
|
1283
1426
|
|
|
1284
1427
|
npm run dev
|
|
1285
1428
|
```
|
|
1429
|
+
README JavaScript examples are checked with the project's ESLint configuration via `npm run lint:readme` and `npm run lint`. Only `no-undef` and `no-unused-vars` are disabled for snippets that omit surrounding context; syntax, style, and other correctness rules remain enabled. Use `ts` fences for TypeScript declarations and `text` for diagrams. For intentionally nonstandard syntax demonstrations, keep `js` highlighting and wrap the region in standalone `<!-- readme-lint-disable -->` / `<!-- readme-lint-enable -->` comments. Such regions are neither linted nor autofixed. Markers must be paired and cannot be nested; markers inside code fences are treated as example text.
|
|
1430
|
+
|
|
1431
|
+
```sh
|
|
1432
|
+
npm run lint:readme
|
|
1433
|
+
npm run lint:readme -- --fix
|
|
1434
|
+
```
|
|
1435
|
+
|
|
1286
1436
|
- Refreshing `eol=lf` for snapshot of test (Windows)
|
|
1287
1437
|
```sh
|
|
1288
1438
|
git add . -u
|
|
@@ -1293,4 +1443,7 @@ npm run eol
|
|
|
1293
1443
|
|
|
1294
1444
|
## Thanks
|
|
1295
1445
|
- [@bcoe](https://github.com/bcoe)
|
|
1296
|
-
- [@edumserrano](https://github.com/edumserrano)
|
|
1446
|
+
- [@edumserrano](https://github.com/edumserrano)
|
|
1447
|
+
|
|
1448
|
+
|
|
1449
|
+

|