monocart-coverage-reports 2.8.5 → 2.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![](https://badgen.net/npm/dw/monocart-coverage-reports)](https://www.npmjs.com/package/monocart-coverage-reports)
5
5
  ![](https://img.shields.io/librariesio/github/cenfun/monocart-coverage-reports)
6
6
  ![](https://img.shields.io/github/license/cenfun/monocart-coverage-reports)
7
- ![](https://img.shields.io/github/actions/workflow/status/cenfun/monocart-coverage-reports/static.yml)
7
+ ![](https://img.shields.io/github/actions/workflow/status/cenfun/monocart-coverage-reports/ci.yml)
8
8
 
9
9
  🌐 English | [简体中文](README.zh-Hans.md)
10
10
 
@@ -35,6 +35,7 @@
35
35
  * [Common issues](#common-issues)
36
36
  - [Unexpected coverage](#unexpected-coverage)
37
37
  - [Unparsable source](#unparsable-source)
38
+ - [JavaScript heap out of memory](#javascript-heap-out-of-memory)
38
39
  * [Debug for Coverage and Sourcemap](#debug-for-coverage-and-sourcemap)
39
40
  * [Integration with Any Testing Framework](#integration-with-any-testing-framework)
40
41
  * [Integration Examples](#integration-examples)
@@ -141,6 +142,9 @@ For more information, see [Command Line](#command-line)
141
142
  - `codecov`
142
143
  - coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
143
144
 
145
+ - `codacy`
146
+ - coverage data for [Codacy](https://api.codacy.com/swagger#tocscoveragereport) API
147
+
144
148
  - `console-summary` shows coverage summary in the console
145
149
 
146
150
  ![](./assets/console-summary.png)
@@ -881,6 +885,15 @@ const coverageOptions = {
881
885
  }
882
886
  ```
883
887
 
888
+ ### JavaScript heap out of memory
889
+ When there are a lot of raw v8 coverage files to process, it may cause OOM. We can try the following Node.js options:
890
+ ```sh
891
+ - run: npm run test:coverage
892
+ env:
893
+ NODE_OPTIONS: --max-old-space-size=8192
894
+ ```
895
+
896
+
884
897
  ## Debug for Coverage and Sourcemap
885
898
  > Sometimes, the coverage is not what we expect. The next step is to figure out why, and we can easily find out the answer step by step through debugging.
886
899
  - Start debugging for v8 report with option `logging: 'debug'`
@@ -900,6 +913,11 @@ When `logging` is `debug`, the raw report data will be preserved in `[outputDir]
900
913
 
901
914
  ![](./assets/debug-sourcemap.png)
902
915
 
916
+ - Show time logs with env `MCR_LOG_TIME`
917
+ ```js
918
+ process.env.MCR_LOG_TIME = true
919
+ ```
920
+
903
921
  ## Integration with Any Testing Framework
904
922
  - API
905
923
  - Collecting coverage data when any stage of the test is completed, and adding the coverage data to the coverage reporter. `await mcr.add(coverageData)`
package/README.zh-Hans.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![](https://badgen.net/npm/dw/monocart-coverage-reports)](https://www.npmjs.com/package/monocart-coverage-reports)
5
5
  ![](https://img.shields.io/librariesio/github/cenfun/monocart-coverage-reports)
6
6
  ![](https://img.shields.io/github/license/cenfun/monocart-coverage-reports)
7
- ![](https://img.shields.io/github/actions/workflow/status/cenfun/monocart-coverage-reports/static.yml)
7
+ ![](https://img.shields.io/github/actions/workflow/status/cenfun/monocart-coverage-reports/ci.yml)
8
8
 
9
9
  🌐 [English](README.md) | 简体中文
10
10
 
@@ -35,6 +35,7 @@
35
35
  * [常见问题](#common-issues)
36
36
  - [Unexpected coverage](#unexpected-coverage)
37
37
  - [Unparsable source](#unparsable-source)
38
+ - [JavaScript heap out of memory](#javascript-heap-out-of-memory)
38
39
  * [如何调试覆盖率数据和查看sourcemap](#debug-for-coverage-and-sourcemap)
39
40
  * [如何跟其他框架集成](#integration-with-any-testing-framework)
40
41
  * [集成的例子](#integration-examples)
@@ -141,6 +142,9 @@ mcr node my-app.js -r v8,console-details
141
142
  - `codecov`
142
143
  - 专属支持[Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format)的报告, see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
143
144
 
145
+ - `codacy`
146
+ - 专属支持[Codacy](https://api.codacy.com/swagger#tocscoveragereport) API的报告
147
+
144
148
  - `console-summary` 在控制台显示覆盖率概要
145
149
 
146
150
  ![](./assets/console-summary.png)
@@ -884,6 +888,15 @@ const coverageOptions = {
884
888
  }
885
889
  ```
886
890
 
891
+ ### JavaScript heap out of memory
892
+ 内存溢出问题可能出现在有太多的原生V8覆盖率文件要处理. 我们可以使用Node.js的一个选项来增加内存使用:
893
+ ```sh
894
+ - run: npm run test:coverage
895
+ env:
896
+ NODE_OPTIONS: --max-old-space-size=8192
897
+ ```
898
+
899
+
887
900
  ## Debug for Coverage and Sourcemap
888
901
  > 当你觉得覆盖率存在问题的时候,`MCR`支持自行调试来核验覆盖率的准确性
889
902
  - 首先打开调试设置`logging: 'debug'`
@@ -903,6 +916,11 @@ const coverageOptions = {
903
916
 
904
917
  ![](./assets/debug-sourcemap.png)
905
918
 
919
+ - 使用环境变量`MCR_LOG_TIME`显示时间日志
920
+ ```js
921
+ process.env.MCR_LOG_TIME = true
922
+ ```
923
+
906
924
  ## Integration with Any Testing Framework
907
925
  通用集成方案
908
926
  - 通过API接口在程序集成
@@ -128,18 +128,6 @@ const resolveSourceMap = (data, url) => {
128
128
 
129
129
  };
130
130
 
131
- const saveSourceFile = async (filePath, data) => {
132
- await Util.writeFile(filePath, JSON.stringify(data));
133
-
134
- // save source and sourcemap file for debug
135
- // https://evanw.github.io/source-map-visualization
136
- // if (data.sourceMap) {
137
- // await Util.writeFile(`${filePath}.js`, data.source);
138
- // await Util.writeFile(`${filePath}.js.map`, JSON.stringify(data.sourceMap));
139
- // }
140
-
141
- };
142
-
143
131
  const getInlineSourceMap = (content) => {
144
132
  let smc;
145
133
  try {
@@ -153,7 +141,8 @@ const getInlineSourceMap = (content) => {
153
141
 
154
142
  const collectSourceMaps = async (v8list, options) => {
155
143
 
156
- const smList = [];
144
+ const sourceList = [];
145
+ const sourcemapList = [];
157
146
  const concurrency = new Concurrency();
158
147
  for (const item of v8list) {
159
148
 
@@ -168,8 +157,8 @@ const collectSourceMaps = async (v8list, options) => {
168
157
  delete item.sourceMap;
169
158
 
170
159
  // source and sourceMap already saved
171
- const sourcePath = Util.resolveCacheSourcePath(options.cacheDir, id);
172
- if (fs.existsSync(sourcePath)) {
160
+ const { cachePath } = Util.getCacheFileInfo('source', id, options.cacheDir);
161
+ if (fs.existsSync(cachePath)) {
173
162
  continue;
174
163
  }
175
164
 
@@ -192,11 +181,11 @@ const collectSourceMaps = async (v8list, options) => {
192
181
  const smc = getInlineSourceMap(source);
193
182
  if (smc) {
194
183
  sourceData.sourceMap = resolveSourceMap(smc.sourcemap, url);
195
- smList.push({
184
+ sourcemapList.push({
196
185
  url,
197
186
  inline: true
198
187
  });
199
- await saveSourceFile(sourcePath, sourceData);
188
+ sourceList.push(sourceData);
200
189
  continue;
201
190
  }
202
191
  // from url async
@@ -204,7 +193,6 @@ const collectSourceMaps = async (v8list, options) => {
204
193
  if (sourceMapUrl) {
205
194
  concurrency.addItem({
206
195
  sourceMapUrl,
207
- sourcePath,
208
196
  sourceData
209
197
  });
210
198
  continue;
@@ -212,7 +200,7 @@ const collectSourceMaps = async (v8list, options) => {
212
200
  }
213
201
 
214
202
  // no need check sourceMap
215
- await saveSourceFile(sourcePath, sourceData);
203
+ sourceList.push(sourceData);
216
204
 
217
205
  }
218
206
 
@@ -223,15 +211,20 @@ const collectSourceMaps = async (v8list, options) => {
223
211
  const data = await loadSourceMap(sourceMapUrl);
224
212
  if (data) {
225
213
  sourceData.sourceMap = resolveSourceMap(data, url);
226
- smList.push({
214
+ sourcemapList.push({
227
215
  url,
228
216
  sourceMapUrl
229
217
  });
230
218
  }
231
- await saveSourceFile(item.sourcePath, sourceData);
219
+
220
+ sourceList.push(sourceData);
221
+
232
222
  });
233
223
 
234
- return smList;
224
+ return {
225
+ sourceList,
226
+ sourcemapList
227
+ };
235
228
 
236
229
  };
237
230
 
@@ -147,8 +147,7 @@ const handleLinesCoverage = (bytes, locator, ignoredRanges) => {
147
147
  blank: blankCount,
148
148
  comment: commentCount
149
149
  };
150
- // data lines for codecov
151
- // https://docs.codecov.com/docs/codecov-custom-coverage-format
150
+ // data lines
152
151
  const dataLines = {};
153
152
 
154
153
  // no ignore items
@@ -1239,15 +1238,17 @@ const initJsCoverageList = (item) => {
1239
1238
  inUncoveredRange.coveredList.push(range);
1240
1239
  }
1241
1240
  }
1242
- } else {
1243
- if (count === 0) {
1244
- uncoveredBlocks.push({
1245
- ... range,
1246
- index: i,
1247
- ranges: block.ranges
1248
- });
1249
- }
1241
+ return;
1250
1242
  }
1243
+
1244
+ if (count === 0) {
1245
+ uncoveredBlocks.push({
1246
+ ... range,
1247
+ index: i,
1248
+ ranges: block.ranges
1249
+ });
1250
+ }
1251
+
1251
1252
  });
1252
1253
  });
1253
1254
 
@@ -1283,7 +1284,7 @@ const initJsCoverageList = (item) => {
1283
1284
  });
1284
1285
  }
1285
1286
 
1286
- ranges.splice.apply(ranges, args);
1287
+ ranges.splice(... args);
1287
1288
 
1288
1289
  });
1289
1290
  }
@@ -1493,6 +1494,8 @@ const convertV8List = (v8list, options) => {
1493
1494
  for (const item of v8list) {
1494
1495
  // console.log([item.id]);
1495
1496
 
1497
+ const time_start_ast = Date.now();
1498
+
1496
1499
  const {
1497
1500
  type, source, fake, sourcePath
1498
1501
  } = item;
@@ -1513,7 +1516,6 @@ const convertV8List = (v8list, options) => {
1513
1516
  }
1514
1517
 
1515
1518
  // ============================
1516
- const time_start_ast = Date.now();
1517
1519
  // move functions and ranges to coverageList
1518
1520
  let coverageList = [];
1519
1521
  let astInfo;
@@ -1532,11 +1534,14 @@ const convertV8List = (v8list, options) => {
1532
1534
  delete item.ranges;
1533
1535
  }
1534
1536
  }
1535
- Util.logTime(`- parsed ast: ${sourcePath} (${EC.cyan(Util.BSF(maxContentLength))})`, time_start_ast);
1537
+
1538
+ Util.logTime(` ┌ [convert] parsed ast: ${sourcePath} (${EC.cyan(Util.BSF(maxContentLength))})`, time_start_ast);
1536
1539
 
1537
1540
  // console.log(sourcePath, astInfo.statements.length);
1538
1541
  // ============================
1539
1542
 
1543
+ const time_start_unpack = Date.now();
1544
+
1540
1545
  // current file and it's sources from sourceMap
1541
1546
  // see const originalState
1542
1547
  const state = {
@@ -1549,7 +1554,11 @@ const convertV8List = (v8list, options) => {
1549
1554
  locator,
1550
1555
  maxContentLength,
1551
1556
  decodedMappings: [],
1552
- cacheMap: new Map(),
1557
+ rangeCache: new Map(),
1558
+ diffCache: new Map(),
1559
+
1560
+ // alignTextList: [],
1561
+
1553
1562
  // coverage info
1554
1563
  bytes: [],
1555
1564
  functions: [],
@@ -1563,19 +1572,25 @@ const convertV8List = (v8list, options) => {
1563
1572
  v8Data: item
1564
1573
  };
1565
1574
 
1566
- const time_start_unpack = Date.now();
1567
1575
  unpackDistFile(item, state, options);
1568
1576
  const unpackedFiles = EC.cyan(`${state.originalList.length} files`);
1569
- Util.logTime(`- unpacked dist: ${sourcePath} (${unpackedFiles})`, time_start_unpack);
1570
1577
 
1571
1578
  stateList.push(state);
1572
1579
 
1580
+ Util.logTime(` ┌ [convert] unpacked sourcemap: ${sourcePath} (${unpackedFiles})`, time_start_unpack);
1581
+
1573
1582
  }
1574
1583
 
1575
1584
  const time_start_convert = Date.now();
1585
+
1576
1586
  const dataList = generateV8DataList(stateList, options);
1577
1587
  const dataFiles = EC.cyan(`${dataList.v8DataList.length} files`);
1578
- Util.logTime(`- generated data list (${dataFiles})`, time_start_convert);
1588
+
1589
+ // stateList.forEach((st, i) => {
1590
+ // Util.writeFileSync(`align-text-${i}.js`, `window.gridData = ${JSON.stringify(st.alignTextList, null, 4)}`);
1591
+ // });
1592
+
1593
+ Util.logTime(` ┌ [convert] converted data list (${dataFiles})`, time_start_convert);
1579
1594
 
1580
1595
  return dataList;
1581
1596
  };
@@ -39,7 +39,16 @@ const findMapping = (list, offset) => {
39
39
 
40
40
  // ========================================================================================================
41
41
 
42
- const alignText = (gt, ot) => {
42
+ const alignText = (gt, ot, info) => {
43
+
44
+ const { diffCache } = info.state;
45
+
46
+ if (diffCache.has(gt)) {
47
+ const subMap = diffCache.get(gt);
48
+ if (subMap.has(ot)) {
49
+ return subMap.get(ot);
50
+ }
51
+ }
43
52
 
44
53
  const toList = (s) => {
45
54
  return s.split('').map((v, i) => {
@@ -115,6 +124,15 @@ const alignText = (gt, ot) => {
115
124
  mergeList(gList, oList);
116
125
  }
117
126
 
127
+ if (diffCache.has(gt)) {
128
+ const subMap = diffCache.get(gt);
129
+ subMap.set(ot, gl);
130
+ } else {
131
+ const subMap = new Map();
132
+ diffCache.set(gt, subMap);
133
+ subMap.set(ot, gl);
134
+ }
135
+
118
136
  // console.log(gl);
119
137
  return gl;
120
138
  };
@@ -136,10 +154,19 @@ const getWordEndPosition = (list, gp, direction) => {
136
154
  }
137
155
  };
138
156
 
139
- const getAlignPosition = (gt, gp, ot, direction) => {
157
+ const getAlignPosition = (info, direction) => {
158
+
159
+ const gt = info.generatedText;
160
+ const gp = info.generatedPos;
161
+ const ot = info.originalText;
162
+
163
+ // const alignTextItem = {
164
+ // gt, ot, gp
165
+ // };
166
+ // info.state.alignTextList.push(it);
140
167
 
141
168
  // there is no need to align for long text
142
- const maxLength = 500;
169
+ const maxLength = 100;
143
170
  if (gt.length > maxLength || ot.length > maxLength) {
144
171
  // console.log(gt.length, ot.length, gp);
145
172
  return;
@@ -155,16 +182,16 @@ const getAlignPosition = (gt, gp, ot, direction) => {
155
182
  // generatedText: '1;else',
156
183
  // generatedLeft: '1;'
157
184
 
158
- // generatedText: ' var id = options.base ? item[0] + options.base : item[0];\r\n',
159
- // originalText: ' var id = options.base ? item[0] + options.base : item[0];\n',
160
-
161
185
  // right matched
162
186
  // only for original first line text
163
187
 
164
188
  // exclusive
165
- const list = alignText(gt, ot);
189
+ const list = alignText(gt, ot, info);
166
190
  const item = list[gp];
167
191
  if (item && item.original) {
192
+
193
+ // alignTextItem.op = item.original.index;
194
+
168
195
  return {
169
196
  pos: item.original.index
170
197
  };
@@ -280,10 +307,15 @@ const getComparedPosition = (info, direction) => {
280
307
  }
281
308
 
282
309
  // =============================
283
- // trim right
284
- const gtt = gt.trimEnd();
285
- const ott = ot.trimEnd();
310
+ // trim
311
+
312
+ const gtt = gt.trim();
313
+ // no generated content after trim
314
+ if (!gtt) {
315
+ return;
316
+ }
286
317
 
318
+ const ott = ot.trim();
287
319
  // no original content after trim
288
320
  if (!ott) {
289
321
  return {
@@ -291,21 +323,21 @@ const getComparedPosition = (info, direction) => {
291
323
  };
292
324
  }
293
325
 
294
- // no generated content after trim
295
- if (!gtt) {
296
- return;
297
- }
298
-
299
326
  // same content
300
327
  if (gtt === ott) {
328
+ // fix indent
329
+ const blankBlock = /\S/;
330
+ const gi = gt.search(blankBlock);
331
+ const oi = ot.search(blankBlock);
332
+
301
333
  return {
302
- pos: gp
334
+ pos: gp - gi + oi
303
335
  };
304
336
  }
305
337
 
306
338
  // =============================
307
339
 
308
- return getAlignPosition(gt, gp, ot, direction);
340
+ return getAlignPosition(info, direction);
309
341
 
310
342
  };
311
343
 
@@ -325,7 +357,7 @@ const getSimilarPosition = (info, direction) => {
325
357
  }
326
358
 
327
359
  // no need comparison for fake source
328
- if (info.fake) {
360
+ if (info.state.fake) {
329
361
  return;
330
362
  }
331
363
 
@@ -388,31 +420,48 @@ const getOriginalText = (m1, m2, cache) => {
388
420
 
389
421
  const o1 = m1.originalOffset;
390
422
  const o2 = m2.originalOffset;
423
+ const sameLine = m2.originalLine === m1.originalLine;
391
424
 
392
425
  // o1 < o2: most of time
393
426
  if (o1 < o2) {
427
+
428
+ // could be in comments
429
+ // it must be wrong sourcemap, do not fix it here
430
+ // if (originalLocator.lineParser.commentParser.isComment(o1, o2)) {
431
+ // console.log('in comments', originalLocator.getSlice(o1, o2), originalLocator.offsetToLocation(o1), originalLocator.offsetToLocation(o2), cache.originalState.sourcePath);
432
+ // return {
433
+ // originalOffset: o1,
434
+ // originalText: ''
435
+ // };
436
+ // }
437
+
438
+ if (sameLine) {
439
+ return {
440
+ originalOffset: o1,
441
+ originalText: originalLocator.getSlice(o1, o2)
442
+ };
443
+ }
444
+
394
445
  // could be multiple lines for original text
395
446
  return {
396
447
  originalOffset: o1,
397
- originalText: originalLocator.getSlice(o1, o2)
448
+ originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, cache))
398
449
  };
399
450
  }
400
451
 
401
452
  // esbuild fixing two mapping have same original
402
453
  // m1 to end line
403
454
  if (o1 === o2) {
404
- const originalEndOffset = getOriginalEndOffset(m1, cache);
405
455
  return {
406
456
  originalOffset: o1,
407
- originalText: originalLocator.getSlice(o1, originalEndOffset)
457
+ originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, cache))
408
458
  };
409
459
  }
410
460
 
411
461
  // o1 > o2: should be wrong sourcemap
412
462
 
413
- // just reverse offsets if few cross lines
414
- const crossLines = m1.originalLine - m2.originalLine;
415
- if (crossLines < 2) {
463
+ // just reverse offsets if same line
464
+ if (sameLine) {
416
465
  return {
417
466
  originalOffset: o2,
418
467
  originalText: originalLocator.getSlice(o2, o1)
@@ -426,10 +475,9 @@ const getOriginalText = (m1, m2, cache) => {
426
475
  // }
427
476
 
428
477
  // should be wrong, ignore m2
429
- const originalEndOffset = getOriginalEndOffset(m1, cache);
430
478
  return {
431
479
  originalOffset: o1,
432
- originalText: originalLocator.getSlice(o1, originalEndOffset)
480
+ originalText: originalLocator.getSlice(o1, getOriginalEndOffset(m1, cache))
433
481
  };
434
482
 
435
483
  };
@@ -505,7 +553,7 @@ const getFixedOriginalStart = (start, mappings, state, cache) => {
505
553
  const direction = 'start';
506
554
 
507
555
  const info = {
508
- fake: state.fake,
556
+ state,
509
557
  generatedText,
510
558
  generatedPos,
511
559
  originalText,
@@ -558,7 +606,7 @@ const getFixedOriginalEnd = (end, mappings, state, cache) => {
558
606
  const direction = 'end';
559
607
 
560
608
  const info = {
561
- fake: state.fake,
609
+ state,
562
610
  offset: end,
563
611
  generatedText,
564
612
  generatedPos,
@@ -826,11 +874,11 @@ const getMappingInfo = (start, end, state) => {
826
874
 
827
875
  const findOriginalRange = (start, end, state, originalMap) => {
828
876
 
829
- const { sourcePath, cacheMap } = state;
877
+ const { sourcePath, rangeCache } = state;
830
878
 
831
879
  const key = `${start}_${end}`;
832
- if (cacheMap.has(key)) {
833
- return cacheMap.get(key);
880
+ if (rangeCache.has(key)) {
881
+ return rangeCache.get(key);
834
882
  }
835
883
 
836
884
  const createMappingError = (errors) => {
@@ -843,7 +891,7 @@ const findOriginalRange = (start, end, state, originalMap) => {
843
891
  };
844
892
 
845
893
  // cache error response
846
- cacheMap.set(key, res);
894
+ rangeCache.set(key, res);
847
895
 
848
896
  return res;
849
897
  };
@@ -884,7 +932,7 @@ const findOriginalRange = (start, end, state, originalMap) => {
884
932
  };
885
933
 
886
934
  // cache response
887
- cacheMap.set(key, res);
935
+ rangeCache.set(key, res);
888
936
 
889
937
  return res;
890
938
 
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">