monocart-coverage-reports 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,19 +4,24 @@
4
4
  [![](https://badgen.net/npm/dw/monocart-coverage-reports)](https://www.npmjs.com/package/monocart-coverage-reports)
5
5
  ![](https://img.shields.io/github/license/cenfun/monocart-coverage-reports)
6
6
 
7
- > Code coverage tool to generate [V8](https://v8.dev/blog/javascript-code-coverage) or [Istanbul](https://istanbul.js.org/) coverage reports.
8
-
9
- ## Preview Reports
10
- - [V8](https://cenfun.github.io/monocart-coverage-reports/v8)
11
- - [V8 Rollup](https://cenfun.github.io/monocart-coverage-reports/v8-rollup)
12
- - [V8 Esbuild](https://cenfun.github.io/monocart-coverage-reports/v8-esbuild)
13
- - [V8 Node v8 API](https://cenfun.github.io/monocart-coverage-reports/v8-node-api)
14
- - [V8 Node env](https://cenfun.github.io/monocart-coverage-reports/v8-node-env)
15
- - [V8 Node Inspector](https://cenfun.github.io/monocart-coverage-reports/v8-node-ins)
16
- - [V8 Minify](https://cenfun.github.io/monocart-coverage-reports/v8-minify)
17
- - [V8 to Istanbul](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/istanbul)
18
- - [Istanbul](https://cenfun.github.io/monocart-coverage-reports/istanbul/)
19
- - [Integration with Monocart Reporter](https://cenfun.github.io/monocart-reporter/) - Playwright test report
7
+ > Code coverage tool to generate native [V8](https://v8.dev/blog/javascript-code-coverage) reports or [Istanbul](https://istanbul.js.org/) reports.
8
+
9
+ * [Usage](#usage)
10
+ * [Default Options](#default-options)
11
+ * [Available Reports](#available-reports)
12
+ * [Multiprocessing Support](#multiprocessing-support)
13
+ * [Compare Reports](#compare-reports)
14
+ * [Compare Workflows](#compare-workflows)
15
+ * [Collecting Istanbul Coverage Data](#collecting-istanbul-coverage-data)
16
+ * [Collecting V8 Coverage Data](#collecting-v8-coverage-data)
17
+ * [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
18
+ * [Using `entryFilter` and `sourceFilter` to filter the results for V8 report](#using-entryfilter-and-sourcefilter-to-filter-the-results-for-v8-report)
19
+ * [How to convert V8 to Istanbul](#how-to-convert-v8-to-istanbul)
20
+ - [Using `v8-to-istanbul`](#using-v8-to-istanbul)
21
+ - [How Monocart Works](#how-monocart-works)
22
+ * [Chromium Coverage API](#chromium-coverage-api)
23
+ * [Istanbul Introduction](#istanbul-introduction)
24
+ * [Thanks](#thanks)
20
25
 
21
26
  ## Usage
22
27
  ```js
@@ -37,30 +42,13 @@ console.log(coverageResults.summary);
37
42
  ## Default Options
38
43
  - [lib/default/options.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/lib/default/options.js)
39
44
 
40
- ## Multiple Reports
41
- ### V8 reports
42
- - v8 (html)
43
- - v8-json
44
- ### [Istanbul reports](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib)
45
- - clover
46
- - cobertura
47
- - html
48
- - html-spa
49
- - json
50
- - json-summary
51
- - lcov
52
- - lcovonly
53
- - none
54
- - teamcity
55
- - text
56
- - text-lcov
57
- - text-summary
58
-
45
+ ## Available Reports
59
46
  ```js
60
47
  const CoverageReport = require('monocart-coverage-reports');
61
48
  const options = {
62
49
  outputDir: './coverage-reports',
63
50
  reports: [
51
+ ['console-summary'],
64
52
  ['v8'],
65
53
  ['html', {
66
54
  subdir: 'istanbul'
@@ -73,6 +61,43 @@ const options = {
73
61
  }
74
62
  const coverageReport = new CoverageReport(options);
75
63
  ```
64
+ - `v8` (V8 data only)
65
+ - [V8 html](https://cenfun.github.io/monocart-coverage-reports/v8) - Example for browser, build with webpack, and also [Rollup](https://cenfun.github.io/monocart-coverage-reports/v8-rollup) and [Esbuild](https://cenfun.github.io/monocart-coverage-reports/v8-esbuild)
66
+ - [V8 Node](https://cenfun.github.io/monocart-coverage-reports/v8-node-env) - Example for Node.js using env, and also [V8 API](https://cenfun.github.io/monocart-coverage-reports/v8-node-api), [Inspector](https://cenfun.github.io/monocart-coverage-reports/v8-node-ins) and [CDP](https://cenfun.github.io/monocart-coverage-reports/v8-node-cdp)
67
+ - [V8 Minify](https://cenfun.github.io/monocart-coverage-reports/v8-minify)
68
+
69
+ ![](test/v8.gif)
70
+
71
+ - `v8-json` (V8 data only)
72
+ - [V8 coverage-report.json](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json)
73
+ - `console-summary` shows coverage summary in console
74
+
75
+ ![](test/console-summary.png)
76
+
77
+ > Following are [istanbul reports](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib)
78
+ - `clover`
79
+ - `cobertura`
80
+ - `html`
81
+ - [Istanbul html](https://cenfun.github.io/monocart-coverage-reports/istanbul/)
82
+ - [V8 to Istanbul](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/istanbul)
83
+ - `html-spa`
84
+ - [Istanbul html-spa](https://cenfun.github.io/monocart-coverage-reports/istanbul/html-spa/)
85
+ - `json`
86
+ - `json-summary`
87
+ - `lcov`
88
+ - `lcovonly`
89
+ - [V8 lcov.info](https://cenfun.github.io/monocart-coverage-reports/v8/lcov.info)
90
+ - [Istanbul lcov.info](https://cenfun.github.io/monocart-coverage-reports/istanbul/lcov.info)
91
+ - `none`
92
+ - `teamcity`
93
+ - `text`
94
+ - `text-lcov`
95
+ - `text-summary`
96
+
97
+ ## Integration
98
+ - [monocart-reporter](https://cenfun.github.io/monocart-reporter/) - Test reporter for [Playwright](https://github.com/microsoft/playwright)
99
+ - [vitest-monocart-coverage](https://github.com/cenfun/vitest-monocart-coverage) - Integration with [Vitest](https://github.com/vitest-dev/vitest) coverage
100
+
76
101
 
77
102
  ## Multiprocessing Support
78
103
  The data will be added to `[outputDir]/.cache`, and the cache will be removed after reports generated.
@@ -105,7 +130,6 @@ console.log(coverageResults.summary);
105
130
  ## Compare Reports
106
131
  | | Istanbul | V8 | V8 to Istanbul |
107
132
  | :--------------| :------ | :------ | :---------------------- |
108
- | Instrumenting code | Required | No | No |
109
133
  | Coverage data | Istanbul (Object) | V8 (Array) | V8 (Array) |
110
134
  | Output | [Istanbul reports](#istanbul-reports) | [V8 reports](#v8-reports) | [Istanbul reports](#istanbul-reports) |
111
135
  | - Bytes | ❌ | ✅ | ❌ |
@@ -121,70 +145,58 @@ console.log(coverageResults.summary);
121
145
 
122
146
  ## Compare Workflows
123
147
  - Istanbul Workflows
124
- - 1, Only for source code: instrumenting code with [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul),see [webpack.config-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-istanbul.js), or using [nyc](https://github.com/istanbuljs/nyc).
125
- - 2, Collecting coverage data from browser `window.__coverage__` or from node `global.__coverage__`, see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js).
126
- - 3, Generating coverage report.
148
+ - 1, [Collecting Istanbul coverage data](#collecting-istanbul-coverage-data)
149
+ - 2, Adding coverage data and generating coverage report
127
150
 
128
151
  - V8 Workflows
129
- - 1, For any runtime code: nothing to do. For source code: building code with `development` [mode](https://webpack.js.org/configuration/mode/) and [sourcemap](https://webpack.js.org/configuration/devtool/) support, see [webpack.config-v8.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-v8.js).
130
- - 2, Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api), see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js).
131
- - 3, Generating coverage report.
152
+ - 1, [Collecting V8 coverage data](#collecting-v8-coverage-data)
153
+ - 3, Adding coverage data and generating coverage report
132
154
 
133
- ## Chromium Coverage API
134
- - [V8 coverage report](https://v8.dev/blog/javascript-code-coverage) - Native support for JavaScript code coverage to V8. (Chromium only)
135
- - [Puppeteer Coverage Class](https://pptr.dev/api/puppeteer.coverage)
136
- - [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
137
- - [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)
155
+ ## Collecting Istanbul Coverage Data
156
+ - Instrumenting source code
157
+ > Before collecting Istanbul coverage data, It requires your source code is instrumented with Istanbul
158
+ - Webpack: [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul), example: [webpack.config-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-istanbul.js)
159
+ - Rollup: [rollup-plugin-istanbul](https://github.com/artberri/rollup-plugin-istanbul)
160
+ - Vite: [vite-plugin-istanbul](https://github.com/ifaxity/vite-plugin-istanbul)
161
+ - Browser
162
+ > Collecting coverage data from `window.__coverage__`, example: [test-istanbul.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-istanbul.js)
163
+ - Node.js
164
+ > Collecting coverage data from `global.__coverage__`
165
+
166
+ ## Collecting V8 Coverage Data
167
+ - For source code: enable `sourcemap` and do not compress/minify:
168
+ - Webpack: with `source-map` [devtool](https://webpack.js.org/configuration/devtool/) and `development` [mode](https://webpack.js.org/configuration/mode/), example [webpack.config-v8.js](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/webpack.config-v8.js)
169
+ - Rollup: [options](https://rollupjs.org/configuration-options/) `sourcemap: true`
170
+ - Browser (Chromium Only)
171
+ > Collecting coverage data with [Chromium Coverage API](#chromium-coverage-api), see [example](https://github.com/cenfun/monocart-coverage-reports/blob/main/test/test-v8.js)
172
+ - Node.js
173
+ > see [Node.js V8 Coverage Report for Server Side](#nodejs-v8-coverage-report-for-server-side)
138
174
 
139
175
 
140
176
  ## Node.js V8 Coverage Report for Server Side
141
- There are two ways to collect native V8 coverage data and generate coverage report:
142
177
  - Using Node.js env [NODE_V8_COVERAGE](https://nodejs.org/docs/latest/api/cli.html#node_v8_coveragedir)=`dir`
143
- - 1, 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.
144
- - 2, Read the json file(s) from the `dir` and generate coverage report. see example:
178
+ - 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
179
+ - Read the json file(s) from the `dir` and generate coverage report
180
+ - example:
145
181
  > 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)
146
-
147
- - Using [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API
148
- - 1, Connecting to the V8 inspector and enable V8 coverage.
149
- ```js
150
- const inspector = require('inspector');
151
- const startV8Coverage = async () => {
152
- const session = new inspector.Session();
153
- session.connect();
154
- await session.post('Profiler.enable');
155
- await session.post('Profiler.startPreciseCoverage', {
156
- callCount: true,
157
- detailed: true
158
- });
159
- return session;
160
- };
161
- ```
162
- - 2, Taking coverage data and adding to the report after your application runs.
163
- ```js
164
- const takeV8Coverage = (session) => {
165
- return new Promise((resolve) => {
166
- session.post('Profiler.takePreciseCoverage', (error, coverage) => {
167
- if (error) {
168
- console.log(error);
169
- resolve();
170
- return;
171
- }
172
- resolve(coverage.result);
173
- });
174
- });
175
- };
176
- const coverageList = await takeV8Coverage(session);
177
- // filter node internal files
178
- coverageList = coverageList.filter((entry) => entry.url && !entry.url.startsWith('node:'));
179
- await new CoverageReport(coverageOptions).add(coverageList);
180
- ```
181
- - 3, Generating coverage report. see example: [./test/test-node-ins.js](./test/test-node-ins.js)
182
- ```js
183
- await new CoverageReport(coverageOptions).generate();
184
- ```
182
+
183
+ - Using [V8](https://nodejs.org/docs/latest/api/v8.html#v8takecoverage) API
184
+ - Writing the coverage started by NODE_V8_COVERAGE to disk on demand with `v8.takeCoverage()` and stopping with `v8.stopCoverage()`.
185
+ - example:
186
+ > cross-env NODE_V8_COVERAGE=`.temp/v8-coverage` node [./test/test-node-api.js](./test/test-node-api.js)
187
+
188
+ - Using [Inspector](https://nodejs.org/docs/latest/api/inspector.html) API (or module [collect-v8-coverage](https://github.com/SimenB/collect-v8-coverage))
189
+ - Connecting to the V8 inspector and enable V8 coverage.
190
+ - Taking coverage data and adding to the report after your application runs.
191
+ - example: [./test/test-node-ins.js](./test/test-node-ins.js)
192
+
193
+ - Using [CDP](https://chromedevtools.github.io/devtools-protocol/) API
194
+ - Enabling [Node Debugging](https://nodejs.org/en/guides/debugging-getting-started/)
195
+ - Collecting coverage data with CDP API.
196
+ - example: [./test/test-node-cdp.js](./test/test-node-cdp.js)
185
197
 
186
198
  ## Using `entryFilter` and `sourceFilter` to filter the results for V8 report
187
- When you add coverage data with [Chromium Coverage API](#chromium-coverage-api), it actually contains the data of all entry files, for example:
199
+ When V8 coverage data collected, it actually contains the data of all entry files, for example:
188
200
  ```
189
201
  1, dist/main.js
190
202
  2, dist/vendor.js
@@ -213,12 +225,63 @@ const coverageOptions = {
213
225
  };
214
226
  ```
215
227
 
216
- ## Istanbul Coverage
228
+ ## How to convert V8 to Istanbul
229
+ ### Using [v8-to-istanbul](https://github.com/istanbuljs/v8-to-istanbul)
230
+ It is a popular library which is used to convert V8 coverage format to istanbul's coverage format. Most test frameworks are using it, such as [Jest](https://github.com/jestjs/jest/), [Vitest](https://github.com/vitest-dev/vitest), but it has two major problems:
231
+ - 1, The source mapping does not work well if the position is between the two consecutive mappings. for example:
232
+ ```js
233
+ const a = tf ? 'true' : 'false';
234
+ ^ ^ ^
235
+ m1 p m2
236
+ ```
237
+ > `m1` and `m2` are two consecutive mappings, `p` is the position we looking for. However, we can only get the position of the `m1` if we don't fix it to `p`. Especially the generated code is different from the original code, such as minified, compressed or converted, then it becomes very difficult to find the middle position between two mappings.
238
+
239
+ - 2, The coverage of functions and branches is incorrect. V8 only provided coverage at functions and it's blocks. But if a function is uncovered (count = 0), there is no information for it's blocks and sub-level functions.
240
+ And also there are some problems about counting the functions and branches:
241
+ ```js
242
+ functions.forEach(block => {
243
+ block.ranges.forEach((range, i) => {
244
+ if (block.isBlockCoverage) {
245
+ // v8-to-istanbul: count range as branch here.
246
+ // Problem: not every block is branch, and the first block is actually function.
247
+ if (block.functionName && i === 0) {
248
+ // v8-to-istanbul: count range as function here.
249
+ // Problem: there is no function name if the function is anonymous or expression.
250
+ }
251
+ } else if (block.functionName) {
252
+ // v8-to-istanbul: count range as function here.
253
+ // Problem: same as above function.
254
+ }
255
+ }
256
+ });
257
+ // Problem: When the function or parent-level function is uncovered, then its sub-level functions will never be counted.
258
+ ```
259
+ see source code [v8-to-istanbul.js](https://github.com/istanbuljs/v8-to-istanbul/blob/master/lib/v8-to-istanbul.js)
260
+
261
+ ### How Monocart Works
262
+ We have removed `v8-to-istanbul` because of the two major problems and implemented new converter:
263
+ - 1, Trying to fix the middle position if not found the exact mapping.
264
+ - 2, Finding all functions and branches by parsing the source code [AST](https://github.com/acornjs/acorn), however the V8 cannot provide effective branch coverage information, so the branches is still not perfect.
265
+
266
+ | AST | V8 |
267
+ | :--------------| :------ |
268
+ | AssignmentPattern | 🛇 Not Support |
269
+ | ConditionalExpression | ✔ Not Perfect |
270
+ | IfStatement | ✔ Not Perfect |
271
+ | LogicalExpression | ✔ Not Perfect |
272
+ | SwitchStatement | ✔ Not Perfect |
273
+
274
+ ## Chromium Coverage API
275
+ - [V8 coverage report](https://v8.dev/blog/javascript-code-coverage) - Native support for JavaScript code coverage to V8. (Chromium only)
276
+ - [Puppeteer Coverage Class](https://pptr.dev/api/puppeteer.coverage)
277
+ - [Playwright Coverage Class](https://playwright.dev/docs/api/class-coverage)
278
+ - [DevTools Protocol for Coverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)
279
+
280
+ ## Istanbul Introduction
217
281
  - [Istanbul coverage report](https://istanbul.js.org/) - Instrumenting source codes and generating coverage reports
218
282
  - [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul)
219
283
  - [istanbul-reports](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib)
220
284
  - [Code Coverage Introduction](https://docs.cypress.io/guides/tooling/code-coverage)
221
285
 
222
-
223
286
  ## Thanks
224
287
  - Special thanks to [@edumserrano](https://github.com/edumserrano)
@@ -162,6 +162,7 @@ const getJsAstInfo = (item, coverageList) => {
162
162
  allowReturnOutsideFunction: true,
163
163
  allowImportExportEverywhere: true,
164
164
  allowSuperOutsideMethod: true,
165
+ allowAwaitOutsideFunction: true,
165
166
  // first line: #!/usr/bin/env node
166
167
  allowHashBang: true
167
168
  };
@@ -79,56 +79,27 @@ const resolveSourceMap = (data) => {
79
79
  }
80
80
  };
81
81
 
82
- const collectInlineSourceMaps = async (v8list) => {
83
- let count = 0;
84
- const concurrency = new Concurrency();
85
- for (const item of v8list) {
86
-
87
- const { type, source } = item;
88
-
89
- // only for js
90
- if (type === 'js') {
91
- const converter = convertSourceMap.fromSource(source);
92
- if (converter) {
93
- item.sourceMap = resolveSourceMap(converter.sourcemap);
94
- count += 1;
95
- // source map comments is inline source map, remove it because it is big
96
- item.source = convertSourceMap.removeComments(source);
97
-
98
- continue;
99
- }
100
- const sourceMapUrl = getSourceMapUrl(source, item.url);
101
- if (sourceMapUrl) {
102
- // console.log('========================', sourceMapUrl);
103
- item.sourceMapUrl = sourceMapUrl;
104
- concurrency.addItem(item);
105
- }
106
- }
107
- }
108
- await concurrency.start(async (item) => {
109
- const data = await loadSourceMap(item.sourceMapUrl);
110
- if (data) {
111
- item.sourceMap = resolveSourceMap(data);
112
- count += 1;
113
- // source map comments is a link, no need remove
114
- }
115
- });
116
-
117
- return count;
82
+ const saveSourceFile = async (filePath, data) => {
83
+ await Util.writeFile(filePath, JSON.stringify(data));
118
84
  };
119
85
 
120
- const collectFileSourceMaps = async (v8list, options) => {
86
+ const collectSourceMaps = async (v8list, options) => {
87
+
121
88
  let count = 0;
122
89
  const concurrency = new Concurrency();
123
90
  for (const item of v8list) {
124
91
 
125
92
  const {
126
- type, url, source, id
93
+ type, url, id, source, sourceMap
127
94
  } = item;
128
95
 
129
- // remove source just keep functions to reduce artifacts size
96
+ // source and sourceMap will be saved as separated file (could be cached)
97
+ // just keep functions coverage ( could be multiple times, will be merged )
98
+ // so remove source and sourceMap
130
99
  delete item.source;
100
+ delete item.sourceMap;
131
101
 
102
+ // source and sourceMap already saved
132
103
  const sourcePath = Util.resolveCacheSourcePath(options.cacheDir, id);
133
104
  if (fs.existsSync(sourcePath)) {
134
105
  continue;
@@ -137,18 +108,23 @@ const collectFileSourceMaps = async (v8list, options) => {
137
108
  const sourceData = {
138
109
  url,
139
110
  id,
140
- source: convertSourceMap.removeComments(source)
111
+ // source,
112
+ source: convertSourceMap.removeComments(source),
113
+ // could be existed
114
+ sourceMap
141
115
  };
142
116
 
143
- // only for js
144
- if (type === 'js') {
145
- const converter = convertSourceMap.fromSource(source);
146
- if (converter) {
147
- sourceData.sourceMap = resolveSourceMap(converter.sourcemap);
117
+ // check sourceMap only for js
118
+ if (type === 'js' && !sourceData.sourceMap) {
119
+ // from inline sync
120
+ const smc = convertSourceMap.fromSource(source);
121
+ if (smc) {
122
+ sourceData.sourceMap = resolveSourceMap(smc.sourcemap);
148
123
  count += 1;
149
124
  await saveSourceFile(sourcePath, sourceData);
150
125
  continue;
151
126
  }
127
+ // from url async
152
128
  const sourceMapUrl = getSourceMapUrl(source, item.url);
153
129
  if (sourceMapUrl) {
154
130
  concurrency.addItem({
@@ -160,10 +136,12 @@ const collectFileSourceMaps = async (v8list, options) => {
160
136
  }
161
137
  }
162
138
 
139
+ // no need check sourceMap
163
140
  await saveSourceFile(sourcePath, sourceData);
164
141
 
165
142
  }
166
143
 
144
+ // from url concurrency
167
145
  await concurrency.start(async (item) => {
168
146
  const sourceData = item.sourceData;
169
147
  const data = await loadSourceMap(item.sourceMapUrl);
@@ -175,19 +153,6 @@ const collectFileSourceMaps = async (v8list, options) => {
175
153
  });
176
154
 
177
155
  return count;
178
- };
179
-
180
- const saveSourceFile = async (filePath, data) => {
181
- await Util.writeFile(filePath, JSON.stringify(data));
182
- };
183
-
184
- const collectSourceMaps = (v8list, options, inlineSourceMap) => {
185
-
186
- if (inlineSourceMap) {
187
- return collectInlineSourceMaps(v8list);
188
- }
189
-
190
- return collectFileSourceMaps(v8list, options);
191
156
 
192
157
  };
193
158
 
@@ -475,7 +475,7 @@ const generateCoverageForDist = (item, state) => {
475
475
 
476
476
  // ========================================================================================================
477
477
 
478
- const decodeSourceMappings = async (item, state, originalDecodedMap) => {
478
+ const decodeSourceMappings = async (state, originalDecodedMap) => {
479
479
 
480
480
  const generatedLocator = state.locator;
481
481
 
@@ -566,7 +566,7 @@ const getOriginalDecodedMappings = (originalDecodedMap, sourceIndex, locator) =>
566
566
  return decodedMappings;
567
567
  };
568
568
 
569
- const initOriginalList = (item, state, originalDecodedMap, options) => {
569
+ const initOriginalList = (state, originalDecodedMap, options) => {
570
570
 
571
571
  // source filter
572
572
  let sourceFilter = options.sourceFilter;
@@ -580,6 +580,7 @@ const initOriginalList = (item, state, originalDecodedMap, options) => {
580
580
  const originalMap = new Map();
581
581
 
582
582
  const { sources, sourcesContent } = state.sourceMap;
583
+
583
584
  sources.forEach((sourcePath, sourceIndex) => {
584
585
 
585
586
  // filter
@@ -860,12 +861,12 @@ const unpackSourceMap = async (item, state, options) => {
860
861
  const time_start_decode = Date.now();
861
862
  const originalDecodedMap = new Map();
862
863
  // for find-original-range
863
- state.decodedMappings = await decodeSourceMappings(item, state, originalDecodedMap);
864
+ state.decodedMappings = await decodeSourceMappings(state, originalDecodedMap);
864
865
  // only debug level
865
866
  Util.logTime(`decode source mappings: ${sourcePath}`, time_start_decode);
866
867
 
867
868
  // filter original list and init list
868
- const originalMap = initOriginalList(item, state, originalDecodedMap, options);
869
+ const originalMap = initOriginalList(state, originalDecodedMap, options);
869
870
 
870
871
  originalDecodedMap.clear();
871
872
 
package/lib/generate.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
-
3
+ const CG = require('console-grid');
4
+ const EC = require('eight-colors');
4
5
  const Util = require('./utils/util.js');
5
6
  const {
6
7
  initIstanbulData, mergeIstanbulCoverage, saveIstanbulReports
@@ -72,6 +73,9 @@ const getReportGroup = (reports, lcov, dataType) => {
72
73
  'v8',
73
74
  'v8-json',
74
75
 
76
+ // both
77
+ 'console-summary',
78
+
75
79
  // istanbul
76
80
  'clover',
77
81
  'cobertura',
@@ -95,6 +99,7 @@ const getReportGroup = (reports, lcov, dataType) => {
95
99
  return;
96
100
  }
97
101
 
102
+ // v8
98
103
  if (k.startsWith('v8')) {
99
104
  // v8
100
105
  if (!reportGroup.v8) {
@@ -104,6 +109,12 @@ const getReportGroup = (reports, lcov, dataType) => {
104
109
  return;
105
110
  }
106
111
 
112
+ // both
113
+ if (k === 'console-summary') {
114
+ reportGroup[k] = reportMap[k];
115
+ return;
116
+ }
117
+
107
118
  // istanbul
108
119
  if (!reportGroup.istanbul) {
109
120
  reportGroup.istanbul = {};
@@ -114,6 +125,8 @@ const getReportGroup = (reports, lcov, dataType) => {
114
125
  return reportGroup;
115
126
  };
116
127
 
128
+ // ========================================================================================================
129
+
117
130
 
118
131
  const generateCoverageReports = async (dataList, options) => {
119
132
 
@@ -144,6 +157,8 @@ const generateCoverageReports = async (dataList, options) => {
144
157
 
145
158
  };
146
159
 
160
+ // ========================================================================================================
161
+
147
162
 
148
163
  const getCoverageDataList = async (cacheDir) => {
149
164
  const files = fs.readdirSync(cacheDir).filter((f) => f.startsWith('coverage-'));
@@ -164,8 +179,105 @@ const getCoverageDataList = async (cacheDir) => {
164
179
  }
165
180
  };
166
181
 
182
+ // ========================================================================================================
183
+
184
+ const capitalizeFirstLetter = (string) => {
185
+ return string.charAt(0).toUpperCase() + string.slice(1);
186
+ };
187
+
188
+ const pFormatter = (v, row, column) => {
189
+ if (typeof v === 'number') {
190
+ const p = Util.PSF(v, 100, 2);
191
+ if (row.status === 'low') {
192
+ return EC.red(p);
193
+ }
194
+ if (row.status === 'medium') {
195
+ return EC.yellow(p);
196
+ }
197
+ if (row.status === 'high') {
198
+ return EC.green(p);
199
+ }
200
+ }
201
+ return v;
202
+ };
203
+
204
+ const nFormatter = (v) => {
205
+ if (typeof v === 'number') {
206
+ return Util.NF(v);
207
+ }
208
+ return v;
209
+ };
210
+
211
+ const showConsoleSummary = (coverageResults, options) => {
212
+
213
+ const reportGroup = options.reportGroup;
214
+ const consoleOptions = reportGroup['console-summary'];
215
+ if (!consoleOptions) {
216
+ return;
217
+ }
218
+
219
+ const {
220
+ summary, name, type
221
+ } = coverageResults;
222
+ if (name) {
223
+ EC.logCyan(name);
224
+ }
225
+
226
+ const indicators = {
227
+ v8: ['bytes', 'functions', 'branches', 'lines'],
228
+ istanbul: ['statements', 'functions', 'branches', 'lines']
229
+ };
230
+
231
+ const list = indicators[type] || indicators.v8;
232
+ const rows = list.map((k) => {
233
+ return {
234
+ ... summary[k],
235
+ name: capitalizeFirstLetter(k)
236
+ };
237
+ });
238
+
239
+ const columns = [{
240
+ id: 'name',
241
+ name: 'Name'
242
+ }, {
243
+ id: 'pct',
244
+ name: 'Coverage %',
245
+ align: 'right',
246
+ formatter: pFormatter
247
+ }, {
248
+ id: 'covered',
249
+ name: 'Covered',
250
+ align: 'right',
251
+ formatter: nFormatter
252
+ }, {
253
+ id: 'uncovered',
254
+ name: 'Uncovered',
255
+ align: 'right',
256
+ formatter: nFormatter
257
+ }, {
258
+ id: 'total',
259
+ name: 'Total',
260
+ align: 'right',
261
+ formatter: nFormatter
262
+ }];
263
+
264
+ if (type === 'istanbul') {
265
+ columns.splice(4, 0, {
266
+ id: 'skipped',
267
+ name: 'Skipped',
268
+ align: 'right',
269
+ formatter: nFormatter
270
+ });
271
+ }
272
+
273
+ CG({
274
+ columns,
275
+ rows
276
+ });
277
+ };
167
278
 
168
279
  module.exports = {
169
280
  getCoverageDataList,
170
- generateCoverageReports
281
+ generateCoverageReports,
282
+ showConsoleSummary
171
283
  };
package/lib/index.d.ts CHANGED
@@ -28,6 +28,7 @@ export type ReportDescription =
28
28
  ['v8-json'] | ["v8-json", {
29
29
  outputFile?: string
30
30
  }] |
31
+ ['console-summary'] |
31
32
  ['clover'] | ['clover', {
32
33
  file?: string
33
34
  }] |
package/lib/index.js CHANGED
@@ -4,7 +4,9 @@ const defaultOptions = require('./default/options.js');
4
4
  const Util = require('./utils/util.js');
5
5
 
6
6
  const { initV8ListAndSourcemap } = require('./v8/v8.js');
7
- const { getCoverageDataList, generateCoverageReports } = require('./generate.js');
7
+ const {
8
+ getCoverageDataList, generateCoverageReports, showConsoleSummary
9
+ } = require('./generate.js');
8
10
 
9
11
  class CoverageReport {
10
12
 
@@ -63,9 +65,7 @@ class CoverageReport {
63
65
 
64
66
  if (Array.isArray(data)) {
65
67
  results.type = 'v8';
66
- // init v8list and unpack sourcemap
67
- const inlineSourceMap = false;
68
- results.data = await initV8ListAndSourcemap(data, this.options, inlineSourceMap);
68
+ results.data = await initV8ListAndSourcemap(data, this.options);
69
69
  } else {
70
70
  results.type = 'istanbul';
71
71
  results.data = data;
@@ -109,6 +109,8 @@ class CoverageReport {
109
109
  Util.rmSync(cacheDir);
110
110
  }
111
111
 
112
+ showConsoleSummary(coverageResults, this.options);
113
+
112
114
  const onEnd = this.options.onEnd;
113
115
  if (typeof onEnd === 'function') {
114
116
  await onEnd(coverageResults);
@@ -37,6 +37,19 @@ class IstanbulSummary extends ReportBase {
37
37
  }
38
38
 
39
39
  getReport() {
40
+
41
+ // remove branchesTrue
42
+ delete this.summary.branchesTrue;
43
+
44
+ // add uncovered
45
+ Object.keys(this.summary).forEach((id) => {
46
+ const item = this.summary[id];
47
+ // NOTE: skipped
48
+ item.uncovered = item.total - item.covered - item.skipped;
49
+ });
50
+
51
+ // console.log(this.summary);
52
+
40
53
  return {
41
54
  summary: this.summary,
42
55
  files: this.files
@@ -0,0 +1,9 @@
1
+ const { parentPort } = require('worker_threads');
2
+ const { decode } = require('../packages/monocart-coverage-vendor.js');
3
+
4
+ parentPort.on('message', (message) => {
5
+ const result = decode(message);
6
+ parentPort.postMessage(result);
7
+ });
8
+
9
+ parentPort.postMessage('workerReady');
@@ -1,49 +1,35 @@
1
- const {
2
- Worker, isMainThread, parentPort
3
- } = require('worker_threads');
1
+ const path = require('path');
2
+ const { Worker } = require('worker_threads');
4
3
  const Util = require('./util.js');
5
4
 
6
- if (isMainThread) {
5
+ // do NOT use isMainThread because it could be executed in worker too. (worker in worker)
7
6
 
8
- const decodeMappings = (mappings = '') => {
9
- if (typeof text !== 'string') {
10
- mappings = String(mappings);
11
- }
7
+ const decodeMappings = (mappings = '') => {
8
+ if (typeof text !== 'string') {
9
+ mappings = String(mappings);
10
+ }
12
11
 
13
- return new Promise((resolve) => {
12
+ return new Promise((resolve) => {
14
13
 
15
- const worker = new Worker(__filename);
16
-
17
- worker.on('message', (message) => {
18
- if (message === 'workerReady') {
19
- worker.postMessage(mappings);
20
- return;
21
- }
22
- resolve(message);
23
- worker.terminate();
24
- });
25
-
26
- worker.on('error', (e) => {
27
- Util.logError(e.message);
28
- resolve([]);
29
- worker.terminate();
30
- });
14
+ const workerPath = path.resolve(__dirname, 'decode-mappings-worker.js');
15
+ const worker = new Worker(workerPath);
31
16
 
17
+ worker.on('message', (message) => {
18
+ if (message === 'workerReady') {
19
+ worker.postMessage(mappings);
20
+ return;
21
+ }
22
+ resolve(message);
23
+ worker.terminate();
32
24
  });
33
- };
34
-
35
- module.exports = decodeMappings;
36
-
37
- } else {
38
25
 
39
- const { decode } = require('../packages/monocart-coverage-vendor.js');
26
+ worker.on('error', (e) => {
27
+ Util.logError(e.message);
28
+ resolve([]);
29
+ worker.terminate();
30
+ });
40
31
 
41
- parentPort.on('message', (message) => {
42
- const result = decode(message);
43
- parentPort.postMessage(result);
44
32
  });
33
+ };
45
34
 
46
- parentPort.postMessage('workerReady');
47
-
48
- }
49
-
35
+ module.exports = decodeMappings;
package/lib/v8/v8.js CHANGED
@@ -7,7 +7,7 @@ const { mergeScriptCovs } = require('../packages/monocart-coverage-vendor.js');
7
7
  const collectSourceMaps = require('../converter/collect-source-maps.js');
8
8
  const version = require('../../package.json').version;
9
9
 
10
- const initV8ListAndSourcemap = async (v8list, options, inlineSourceMap) => {
10
+ const initV8ListAndSourcemap = async (v8list, options) => {
11
11
 
12
12
  // filter list first
13
13
  const entryFilter = options.entryFilter;
@@ -47,8 +47,8 @@ const initV8ListAndSourcemap = async (v8list, options, inlineSourceMap) => {
47
47
  data.type = 'js';
48
48
  data.functions = item.functions;
49
49
  data.source = item.source;
50
- // useless here
51
- // data.scriptId = item.scriptId;
50
+ // could be existed
51
+ data.sourceMap = item.sourceMap;
52
52
  } else if (item.ranges) {
53
53
  data.type = 'css';
54
54
  data.ranges = item.ranges;
@@ -72,7 +72,7 @@ const initV8ListAndSourcemap = async (v8list, options, inlineSourceMap) => {
72
72
 
73
73
  // collect sourcemap first
74
74
  const time_start = Date.now();
75
- const count = await collectSourceMaps(v8list, options, inlineSourceMap);
75
+ const count = await collectSourceMaps(v8list, options);
76
76
  if (count) {
77
77
  // debug level time
78
78
  Util.logTime(`loaded ${count} sourcemaps`, time_start);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monocart-coverage-reports",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "Monocart coverage reports",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
@@ -17,13 +17,15 @@
17
17
  "build-docs": "node ./scripts/build-docs.js",
18
18
  "build-test": "node ./scripts/build-test.js",
19
19
  "build": "sf lint && sf b -p && npm run build-test",
20
- "test-node": "npm run test-node-env && npm run test-node-ins && npm run test-node-api",
20
+ "test-node": "npm run test-node-env && npm run test-node-ins && npm run test-node-api && npm run test-node-cdp",
21
21
  "test-node-env": "cross-env NODE_V8_COVERAGE=.temp/v8-coverage node ./test/test-node-env.js && node ./test/generate-node-report.js",
22
22
  "test-node-ins": "node ./test/test-node-ins.js",
23
23
  "test-node-api": "cross-env NODE_V8_COVERAGE=.temp/v8-coverage node ./test/test-node-api.js",
24
+ "test-node-cdp": "node --inspect=9229 ./test/test-node-cdp.js",
24
25
  "test-browser": "node ./test/test.js",
25
26
  "test": "npm run test-browser && npm run test-node && npm run build-docs",
26
27
  "dev": "sf d v8",
28
+ "open": "node ./scripts/open.js",
27
29
  "patch": "npm run build && sf publish patch -r"
28
30
  },
29
31
  "workspaces": [
@@ -46,11 +48,10 @@
46
48
  "istanbul-reports": "~3.1.6",
47
49
  "lz-utils": "~2.0.1",
48
50
  "monocart-code-viewer": "^1.0.13",
49
- "monocart-formatter": "~2.2.0",
51
+ "monocart-formatter": "~2.2.1",
50
52
  "turbogrid": "~3.0.12"
51
53
  },
52
54
  "devDependencies": {
53
- "cross-env": "^7.0.3",
54
55
  "eslint": "^8.56.0",
55
56
  "eslint-config-plus": "^1.0.6",
56
57
  "eslint-plugin-html": "^7.1.0",