monocart-coverage-reports 2.0.8 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,7 @@
10
10
  * [Default Options](#default-options)
11
11
  * [Available Reports](#available-reports)
12
12
  * [Multiprocessing Support](#multiprocessing-support)
13
+ * [onEnd Hook](#onend-hook)
13
14
  * [Compare Reports](#compare-reports)
14
15
  * [Compare Workflows](#compare-workflows)
15
16
  * [Collecting Istanbul Coverage Data](#collecting-istanbul-coverage-data)
@@ -21,6 +22,7 @@
21
22
  - [How Monocart Works](#how-monocart-works)
22
23
  * [Ignoring Uncovered Lines](#ignoring-uncovered-lines)
23
24
  * [Chromium Coverage API](#chromium-coverage-api)
25
+ * [V8 Coverage Data Format](#v8-coverage-data-format)
24
26
  * [Istanbul Introduction](#istanbul-introduction)
25
27
  * [Thanks](#thanks)
26
28
 
@@ -129,11 +131,42 @@ const coverageResults = await coverageReport.generate();
129
131
  console.log(coverageResults.summary);
130
132
  ```
131
133
 
134
+ ## onEnd Hook
135
+ For example, checking thresholds:
136
+ ```js
137
+ const EC = require('eight-colors');
138
+ const coverageOptions = {
139
+ name: 'My Coverage Report',
140
+ outputDir: './coverage-reports',
141
+ onEnd: (coverageResults) => {
142
+ const thresholds = {
143
+ bytes: 80,
144
+ lines: 60
145
+ };
146
+ console.log('check thresholds ...', thresholds);
147
+ const errors = [];
148
+ const { summary } = coverageResults;
149
+ Object.keys(thresholds).forEach((k) => {
150
+ const pct = summary[k].pct;
151
+ if (pct < thresholds[k]) {
152
+ errors.push(`Coverage threshold for ${k} (${pct} %) not met: ${thresholds[k]} %`);
153
+ }
154
+ });
155
+ if (errors.length) {
156
+ const errMsg = errors.join('\n');
157
+ console.log(EC.red(errMsg));
158
+ // throw new Error(errMsg);
159
+ // process.exit(1);
160
+ }
161
+ }
162
+ }
163
+ ```
164
+
132
165
  ## Compare Reports
133
166
  | | Istanbul | V8 | V8 to Istanbul |
134
167
  | :--------------| :------ | :------ | :---------------------- |
135
- | Coverage data | Istanbul (Object) | V8 (Array) | V8 (Array) |
136
- | Output | [Istanbul reports](#istanbul-reports) | [V8 reports](#v8-reports) | [Istanbul reports](#istanbul-reports) |
168
+ | Coverage data | [Istanbul](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md) (Object) | [V8](#v8-coverage-data-format) (Array) | [V8](#v8-coverage-data-format) (Array) |
169
+ | Output | [Istanbul reports](#available-reports) | [V8 reports](#available-reports) | [Istanbul reports](#available-reports) |
137
170
  | - Bytes | ❌ | ✅ | ❌ |
138
171
  | - Statements | ✅ | ❌ | ☑️❔ |
139
172
  | - Branches | ✅ | ☑️❔ | ☑️❔ |
@@ -181,12 +214,15 @@ console.log(coverageResults.summary);
181
214
  - Before running your Node.js application, set env `NODE_V8_COVERAGE`=`dir`. After the application runs and exits, the coverage data will be saved to the `dir` directory in JSON file format
182
215
  - Read the json file(s) from the `dir` and generate coverage report
183
216
  - example:
184
- > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage` node [./test/test-node-env.js](./test/test-node-env.js) && node [./test/generate-node-report.js](./test/generate-node-report.js)
217
+ > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage-env` node [./test/test-node-env.js](./test/test-node-env.js) && node [./test/generate-node-report.js](./test/generate-node-report.js)
185
218
 
186
- - Using [V8](https://nodejs.org/docs/latest/api/v8.html#v8takecoverage) API
219
+ - Using [V8](https://nodejs.org/docs/latest/api/v8.html#v8takecoverage) API + NODE_V8_COVERAGE
187
220
  - Writing the coverage started by NODE_V8_COVERAGE to disk on demand with `v8.takeCoverage()` and stopping with `v8.stopCoverage()`.
188
221
  - example:
189
- > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage` node [./test/test-node-api.js](./test/test-node-api.js)
222
+ > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage-api` node [./test/test-node-api.js](./test/test-node-api.js)
223
+
224
+ - Using [foreground-child](https://github.com/tapjs/foreground-child) + NODE_V8_COVERAGE
225
+ - example: [./test/test-node-fgc.js](./test/test-node-fgc.js)
190
226
 
191
227
  - Using [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API (or module [collect-v8-coverage](https://github.com/SimenB/collect-v8-coverage))
192
228
  - Connecting to the V8 inspector and enable V8 coverage.
@@ -201,24 +237,24 @@ console.log(coverageResults.summary);
201
237
  ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
202
238
  When V8 coverage data collected, it actually contains the data of all entry files, for example:
203
239
  ```
204
- 1, dist/main.js
205
- 2, dist/vendor.js
206
- 3, dist/something-else.js
240
+ dist/main.js
241
+ dist/vendor.js
242
+ dist/something-else.js
207
243
  ```
208
244
  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.
209
245
  ```
210
- 1, dist/main.js
246
+ dist/main.js
211
247
  ```
212
248
  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`.
213
249
  ```
214
- 1, src/index.js
215
- 2, src/components/app.js
216
- 3, node_modules/dependency/dist/dependency.js
250
+ > src/index.js
251
+ > src/components/app.js
252
+ > node_modules/dependency/dist/dependency.js
217
253
  ```
218
254
  We can use `sourceFilter` to filter the source files. For example, we should remove `dependency.js` if it is not in our coverage scope.
219
255
  ```
220
- 1, src/index.js
221
- 2, src/components/app.js
256
+ > src/index.js
257
+ > src/components/app.js
222
258
  ```
223
259
  Example:
224
260
  ```js
@@ -245,13 +281,15 @@ And also there are some problems about counting the functions and branches:
245
281
  functions.forEach(block => {
246
282
  block.ranges.forEach((range, i) => {
247
283
  if (block.isBlockCoverage) {
248
- // v8-to-istanbul: count range as branch here.
284
+ // v8-to-istanbul: new CovBranch()
249
285
  // Problem: not every block is branch, and the first block is actually function.
250
286
  if (block.functionName && i === 0) {
251
- // v8-to-istanbul: count range as function here.
287
+ // v8-to-istanbul: new CovFunction()
288
+ // Problem: no anonymous function
252
289
  }
253
290
  } else if (block.functionName) {
254
- // v8-to-istanbul: count range as function here.
291
+ // v8-to-istanbul: new CovFunction()
292
+ // Problem: no anonymous function
255
293
  }
256
294
  }
257
295
  });
@@ -300,6 +338,50 @@ if (platform === 'linux') {
300
338
  - [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
301
339
  - [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)
302
340
 
341
+ ## V8 Coverage Data Format
342
+ ```js
343
+ // Coverage data for a source range.
344
+ export interface CoverageRange {
345
+ // JavaScript script source offset for the range start.
346
+ startOffset: integer;
347
+ // JavaScript script source offset for the range end.
348
+ endOffset: integer;
349
+ // Collected execution count of the source range.
350
+ count: integer;
351
+ }
352
+
353
+ // Coverage data for a JavaScript function.
354
+ /**
355
+ * @functionName can be an empty string.
356
+ * @ranges is always non-empty. The first range is called the "root range".
357
+ * @isBlockCoverage indicates if the function has block coverage information.
358
+ If this is false, it usually means that the functions was never called.
359
+ It seems to be equivalent to ranges.length === 1 && ranges[0].count === 0.
360
+ */
361
+ export interface FunctionCoverage {
362
+ // JavaScript function name.
363
+ functionName: string;
364
+ // Source ranges inside the function with coverage data.
365
+ ranges: CoverageRange[];
366
+ // Whether coverage data for this function has block granularity.
367
+ isBlockCoverage: boolean;
368
+ }
369
+
370
+ // Coverage data for a JavaScript script.
371
+ export interface ScriptCoverage {
372
+ // JavaScript script id.
373
+ scriptId: Runtime.ScriptId;
374
+ // JavaScript script name or url.
375
+ url: string;
376
+ // Functions contained in the script that has coverage data.
377
+ functions: FunctionCoverage[];
378
+ }
379
+
380
+ export type V8CoverageData = ScriptCoverage[];
381
+ ```
382
+ see devtools-protocol [ScriptCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ScriptCoverage)
383
+
384
+
303
385
  ## Istanbul Introduction
304
386
  - [Istanbul coverage report](https://istanbul.js.org/) - Instrumenting source codes and generating coverage reports
305
387
  - [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul)
@@ -30,11 +30,11 @@ const createBranchGroup = (type, branchStart, parents, branchMap, functionInfo)
30
30
  const reverseParents = [].concat(parents).reverse();
31
31
  const group = {
32
32
  type,
33
- loc: {
34
- start: branchStart,
35
- // could be updated if multiple locations
36
- end: branchStart
37
- },
33
+
34
+ start: branchStart,
35
+ // could be updated if multiple locations
36
+ end: branchStart,
37
+
38
38
  locations: [],
39
39
  reverseParents,
40
40
  functionInfo
@@ -46,7 +46,7 @@ const createBranchGroup = (type, branchStart, parents, branchMap, functionInfo)
46
46
  const addBranch = (group, node, locationMap, functionInfo) => {
47
47
  const { start, end } = node;
48
48
 
49
- const branchStart = group.loc.start;
49
+ const branchStart = group.start;
50
50
 
51
51
  // the block is not exact correct, if there is a block wrapped
52
52
  // [x8]{ var a = b || [x4]c }, b is no block, but it can be found in x8 block
@@ -66,7 +66,7 @@ const addBranch = (group, node, locationMap, functionInfo) => {
66
66
  group.locations.push(branchInfo);
67
67
 
68
68
  // update group end
69
- group.loc.end = end;
69
+ group.end = end;
70
70
 
71
71
  locationMap.set(start, branchInfo);
72
72
 
@@ -224,12 +224,12 @@ const createBranches = (ast, functionInfo, branchMap) => {
224
224
  // first, left a and right c
225
225
  // then, left a and right b
226
226
  if (prevLocation) {
227
- const { loc, locations } = group;
227
+ const { locations } = group;
228
228
  locations.sort((a, b) => {
229
229
  return a.start - b.start;
230
230
  });
231
231
  // update group end after sorted
232
- loc.end = locations[locations.length - 1].end;
232
+ group.end = locations[locations.length - 1].end;
233
233
  }
234
234
  },
235
235
 
@@ -272,7 +272,7 @@ const IfStatement = (group, parentCount, noBlockList, blockCount) => {
272
272
  if (!noBlockList.length) {
273
273
  return;
274
274
  }
275
- // console.log(parentCount, 'uncovered list', noBlockList.length, group.loc.start);
275
+ // console.log(parentCount, 'uncovered list', noBlockList.length, group.start);
276
276
  let count = parentCount - blockCount;
277
277
  noBlockList.forEach((item) => {
278
278
  item.count = count;
@@ -291,7 +291,7 @@ const LogicalExpression = (group, parentCount, noBlockList, blockCount) => {
291
291
  item.count = count;
292
292
  });
293
293
 
294
- // const start = group.loc.start;
294
+ // const start = group.start;
295
295
  // if (start > 7874 && start < 7922) {
296
296
  // console.log(group.type, parentCount, 'locations', group.locations);
297
297
  // }
@@ -381,16 +381,26 @@ const collectBranches = (branchMap) => {
381
381
  // init branches
382
382
  const branches = [];
383
383
  branchMap.forEach((group) => {
384
+
385
+ // add start/end for none with group start/end
386
+ group.locations.forEach((item) => {
387
+ if (item.none) {
388
+ item.start = group.start;
389
+ item.end = group.end;
390
+ }
391
+ });
392
+
384
393
  branches.push({
385
394
  type: group.type,
386
- loc: group.loc,
395
+ start: group.start,
396
+ end: group.end,
387
397
  locations: group.locations
388
398
  });
389
399
  });
390
400
 
391
401
  // sort branches
392
402
  branches.sort((a, b) => {
393
- return a.loc.start - b.loc.start;
403
+ return a.start - b.start;
394
404
  });
395
405
 
396
406
  return branches;