monocart-reporter 1.7.0 → 1.7.2

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.
Files changed (38) hide show
  1. package/README.md +8 -12
  2. package/lib/cli.js +1 -1
  3. package/lib/common.js +2 -3
  4. package/lib/default/options.js +4 -3
  5. package/lib/generate-data.js +2 -7
  6. package/lib/generate-report.js +10 -11
  7. package/lib/index.d.ts +2 -9
  8. package/lib/index.js +6 -0
  9. package/lib/merge-data.js +7 -6
  10. package/lib/plugins/audit/audit.js +4 -2
  11. package/lib/plugins/comments.js +2 -3
  12. package/lib/plugins/coverage/converter/collect-source-maps.js +194 -0
  13. package/lib/plugins/coverage/converter/converter.js +547 -0
  14. package/lib/plugins/coverage/converter/decode-mappings.js +49 -0
  15. package/lib/plugins/coverage/{v8 → converter}/dedupe.js +8 -1
  16. package/lib/plugins/coverage/converter/find-original-range.js +576 -0
  17. package/lib/plugins/coverage/converter/info-branch.js +30 -0
  18. package/lib/plugins/coverage/converter/info-function.js +29 -0
  19. package/lib/plugins/coverage/converter/info-line.js +20 -0
  20. package/lib/plugins/coverage/converter/position-mapping.js +183 -0
  21. package/lib/plugins/coverage/{coverage-utils.js → converter/source-path.js} +26 -42
  22. package/lib/plugins/coverage/coverage.js +61 -57
  23. package/lib/plugins/coverage/istanbul/istanbul.js +21 -174
  24. package/lib/plugins/coverage/v8/v8.js +22 -30
  25. package/lib/plugins/network/network.js +4 -13
  26. package/lib/plugins/state/client.js +3 -4
  27. package/lib/plugins/state/state.js +6 -3
  28. package/lib/runtime/monocart-code-viewer.js +1 -1
  29. package/lib/runtime/monocart-coverage.js +13 -14
  30. package/lib/runtime/monocart-formatter.js +1 -1
  31. package/lib/runtime/monocart-network.js +1 -1
  32. package/lib/runtime/monocart-reporter.js +1 -1
  33. package/lib/runtime/monocart-v8.js +1 -1
  34. package/lib/runtime/monocart-vendor.js +13 -13
  35. package/lib/utils/util.js +97 -3
  36. package/package.json +5 -6
  37. package/lib/plugins/coverage/v8/position-mapping.js +0 -92
  38. package/lib/plugins/coverage/v8/source-map.js +0 -464
@@ -0,0 +1,183 @@
1
+
2
+ const findLine = function(list, position) {
3
+ let start = 0;
4
+ let end = list.length - 1;
5
+ while (end - start > 1) {
6
+ const i = Math.floor((start + end) * 0.5);
7
+ const item = list[i];
8
+ if (position < item.start) {
9
+ end = i;
10
+ continue;
11
+ }
12
+ if (position > item.end) {
13
+ start = i;
14
+ continue;
15
+ }
16
+ return list[i];
17
+ }
18
+ // last two items, less is start
19
+ const endItem = list[end];
20
+ if (position < endItem.start) {
21
+ return list[start];
22
+ }
23
+ return list[end];
24
+ };
25
+
26
+ // =======================================================================================
27
+
28
+ const isLineSingleCommented = (codeStr) => {
29
+ const singleBlock = /^\s*\/\//g;
30
+ return singleBlock.test(codeStr);
31
+ };
32
+
33
+ const isLineStartCommented = (codeStr) => {
34
+ const multiStartBlock = /^\s*\/\*/g;
35
+ return multiStartBlock.test(codeStr);
36
+ };
37
+
38
+ // multi-comment end but not at end
39
+ const isLineEndCommented = (codeStr) => {
40
+ const multiEndBlock = /.*\*\//g;
41
+ return multiEndBlock.test(codeStr);
42
+ };
43
+
44
+ const isLineEndCommentedToEnd = (codeStr) => {
45
+ const multiEndBlock = /.*\*\/\s*$/g;
46
+ return multiEndBlock.test(codeStr);
47
+ };
48
+
49
+ const isLineBlank = (codeStr) => {
50
+ const blankBlock = /\S/;
51
+ return !blankBlock.test(codeStr);
52
+ };
53
+
54
+ class PositionMapping {
55
+ constructor(source) {
56
+ this.source = source;
57
+ this.lines = this.buildLines(source);
58
+ this.commentedLines = [];
59
+ this.blankLines = [];
60
+ this.parseLines(this.lines);
61
+ }
62
+
63
+ // =============================================================================
64
+
65
+ getSlice(s, e) {
66
+ return this.source.slice(s, e);
67
+ }
68
+
69
+ // 1-base
70
+ locationToOffset(location) {
71
+ const { line, column } = location;
72
+ // 1-based
73
+ const lineInfo = this.lines[line - 1];
74
+ if (lineInfo) {
75
+ if (column === Infinity) {
76
+ return lineInfo.start + lineInfo.length;
77
+ }
78
+ return lineInfo.start + column;
79
+ }
80
+ return 0;
81
+ }
82
+
83
+ // 1-base
84
+ offsetToLocation(offset) {
85
+ const lineInfo = findLine(this.lines, offset);
86
+
87
+ let indent = lineInfo.text.search(/\S/);
88
+ if (indent === -1) {
89
+ indent = lineInfo.length;
90
+ }
91
+
92
+ // 1-base
93
+ const line = lineInfo.line + 1;
94
+ const column = Math.min(Math.max(offset - lineInfo.start, 0), lineInfo.length);
95
+
96
+ return {
97
+ ... lineInfo,
98
+ line,
99
+ column,
100
+ indent
101
+ };
102
+ }
103
+
104
+ // 1-base
105
+ getLine(line) {
106
+ return this.lines[line - 1];
107
+ }
108
+
109
+ // =============================================================================
110
+
111
+ buildLines(content) {
112
+ let pos = 0;
113
+ const lines = content.split(/\n/).map((text, line) => {
114
+
115
+ let n = 1;
116
+ // may ends with \r (don't consider double \r\r for now)
117
+ const reg = /\r$/;
118
+ if (reg.test(text)) {
119
+ text = text.slice(0, -1);
120
+ n += 1;
121
+ }
122
+
123
+ const length = text.length;
124
+ const start = pos;
125
+ const end = start + length;
126
+
127
+ pos += length + n;
128
+
129
+ return {
130
+ line,
131
+ start,
132
+ end,
133
+ length,
134
+ text
135
+ };
136
+ });
137
+
138
+ return lines;
139
+ }
140
+
141
+ parseLines(lines) {
142
+ const commentedLines = [];
143
+ const blankLines = [];
144
+
145
+ let startCommented = false;
146
+
147
+ const multiEndHandler = (text, i) => {
148
+ if (isLineEndCommented(text)) {
149
+ startCommented = false;
150
+ if (isLineEndCommentedToEnd(text)) {
151
+ commentedLines.push(i);
152
+ }
153
+ return;
154
+ }
155
+ commentedLines.push(i);
156
+ };
157
+
158
+ lines.forEach((line, i) => {
159
+ const text = line.text;
160
+ if (startCommented) {
161
+ return multiEndHandler(text, i);
162
+ }
163
+ if (isLineStartCommented(text)) {
164
+ startCommented = true;
165
+ return multiEndHandler(text, i);
166
+ }
167
+ if (isLineSingleCommented(text)) {
168
+ commentedLines.push(i);
169
+ return;
170
+ }
171
+ if (isLineBlank(text)) {
172
+ blankLines.push(i);
173
+ }
174
+ });
175
+
176
+ this.commentedLines = commentedLines;
177
+ this.blankLines = blankLines;
178
+ }
179
+
180
+ }
181
+
182
+ module.exports = PositionMapping;
183
+
@@ -1,13 +1,5 @@
1
- const Util = require('../../utils/util.js');
2
-
3
- const sortRanges = (ranges) => {
4
- ranges.sort((a, b) => {
5
- if (a.start === b.start) {
6
- return a.end - b.end;
7
- }
8
- return a.start - b.start;
9
- });
10
- };
1
+ const path = require('path');
2
+ const Util = require('../../../utils/util.js');
11
3
 
12
4
  const resolveUrl = (input) => {
13
5
  let url;
@@ -65,6 +57,19 @@ const getSourcePath = (url, index = '', type = '') => {
65
57
  return filterPath(relPath);
66
58
  };
67
59
 
60
+ const getSourceType = (sourcePath) => {
61
+ let ext = path.extname(sourcePath);
62
+ let type = '';
63
+ if (ext) {
64
+ ext = ext.slice(1);
65
+ const reg = /^[a-z0-9]+$/;
66
+ if (reg.test(ext)) {
67
+ type = ext;
68
+ }
69
+ }
70
+ return type;
71
+ };
72
+
68
73
  // ========================================================================================================
69
74
 
70
75
  const mergeSourceRoot = (sourceRoot, sourceName) => {
@@ -74,51 +79,30 @@ const mergeSourceRoot = (sourceRoot, sourceName) => {
74
79
  return sourceRoot + sourceName;
75
80
  };
76
81
 
77
- const initSourceMapRootAndUrl = (sourceMap, fileUrls, fileSources) => {
82
+ const initSourceMapSourcePath = (sourceMap, fileUrls, sourcePathHandler) => {
78
83
  // reset sourceRoot
79
84
  const sourceRoot = sourceMap.sourceRoot || '';
80
85
  sourceMap.sourceRoot = '';
81
86
 
82
87
  sourceMap.sources = sourceMap.sources.map((sourceName, i) => {
83
88
  const sourceUrl = mergeSourceRoot(sourceRoot, sourceName);
84
- const sourcePath = getSourcePath(sourceUrl, i + 1);
85
- fileUrls[sourcePath] = sourceUrl;
86
- fileSources[sourcePath] = sourceMap.sourcesContent[i];
87
- return sourcePath;
88
- });
89
89
 
90
- };
91
-
92
- // ================================================================================================
93
-
94
- const convertFunctionsToRanges = (functions) => {
95
-
96
- if (!Util.isList(functions)) {
97
- return [];
98
- }
99
-
100
- const ranges = [];
101
- for (const fun of functions) {
102
- if (fun.ranges) {
103
- for (const range of fun.ranges) {
104
- // rename startOffset/endOffset to start/end, keep count
105
- ranges.push({
106
- start: range.startOffset,
107
- end: range.endOffset,
108
- count: range.count
109
- });
90
+ let sourcePath = getSourcePath(sourceUrl, i + 1);
91
+ if (typeof sourcePathHandler === 'function') {
92
+ const newSourcePath = sourcePathHandler(sourcePath);
93
+ if (typeof newSourcePath === 'string' && newSourcePath) {
94
+ sourcePath = newSourcePath;
110
95
  }
111
96
  }
112
- }
113
97
 
114
- sortRanges(ranges);
98
+ fileUrls[sourcePath] = sourceUrl;
99
+ return sourcePath;
100
+ });
115
101
 
116
- return ranges;
117
102
  };
118
103
 
119
104
  module.exports = {
120
- sortRanges,
121
105
  getSourcePath,
122
- initSourceMapRootAndUrl,
123
- convertFunctionsToRanges
106
+ getSourceType,
107
+ initSourceMapSourcePath
124
108
  };
@@ -1,13 +1,16 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const EC = require('eight-colors');
4
3
 
5
4
  const Util = require('../../utils/util.js');
6
- const { convertV8ToIstanbul, saveIstanbulReport } = require('./istanbul/istanbul.js');
5
+ const { saveIstanbulReport } = require('./istanbul/istanbul.js');
7
6
  const {
8
- initV8ListAndSourcemap, unpackV8List, mergeV8Coverage, saveV8Report
7
+ initV8ListAndSourcemap, mergeV8Coverage, saveV8Report
9
8
  } = require('./v8/v8.js');
10
9
 
10
+ const { convertV8List } = require('./converter/converter.js');
11
+
12
+ const artifactsDirName = '.artifacts';
13
+
11
14
  const defaultOptions = {
12
15
 
13
16
  // Defaults to test title
@@ -15,15 +18,12 @@ const defaultOptions = {
15
18
  // outputDir
16
19
  // outputName
17
20
 
18
- // (Boolean) Whether to convert to Istanbul report
21
+ // (Boolean) Whether to convert to Istanbul report from V8 list. Defaults to `html-spa` report | (String) or using `html` report
19
22
  toIstanbul: false,
20
23
 
21
24
  // (Function) A filter function to execute for each element in the V8 list.
22
25
  entryFilter: null,
23
- // (Boolean) Whether to unpack all sources from the source map if a related source map file is found.
24
- unpackSourceMap: true,
25
- // (Boolean) Whether to exclude the dist file (usually minified) if the sources are successfully unpacked from the source map.
26
- excludeDistFile: true,
26
+
27
27
  // (Function) A filter function to execute for each element in the sources which unpacked from the source map.
28
28
  sourceFilter: null,
29
29
 
@@ -36,19 +36,35 @@ const defaultOptions = {
36
36
  sourceFinder: null,
37
37
 
38
38
  // (Boolean) Whether to create `lcov.info`
39
- lcov: false
39
+ lcov: false,
40
40
 
41
41
  // (Object) Istanbul watermarks, see [here](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-lib-report)
42
42
  // watermarks: {},
43
43
  // (Array) V8 watermarks, Defaults to `[50, 80]`
44
44
  // watermarks: [50, 80],
45
45
 
46
- // (Boolean) Whether inline all scripts to the single HTML file.
47
- // inline: false,
46
+ // (Boolean) Whether inline all scripts to the single HTML file. V8 only.
47
+ inline: false
48
48
 
49
- // debug: false
50
49
  };
51
50
 
51
+ // ========================================================================================================
52
+
53
+ const generateV8ListReport = async (v8list, coverageData, fileSources, options) => {
54
+ const { toIstanbul, lcov } = options;
55
+ const istanbulReport = toIstanbul || lcov;
56
+ const v8Report = !toIstanbul;
57
+
58
+ let report;
59
+ if (istanbulReport) {
60
+ report = await saveIstanbulReport(coverageData, fileSources, options);
61
+ }
62
+ if (v8Report) {
63
+ report = await saveV8Report(v8list, options);
64
+ }
65
+
66
+ return report;
67
+ };
52
68
 
53
69
  // ========================================================================================================
54
70
 
@@ -72,6 +88,11 @@ const generateIstanbulReport = (coverageData, testInfo, options) => {
72
88
 
73
89
  const fileSources = options.fileSources || {};
74
90
 
91
+ // force to istanbul
92
+ if (!options.toIstanbul) {
93
+ options.toIstanbul = true;
94
+ }
95
+
75
96
  const report = saveIstanbulReport(coverageData, fileSources, options);
76
97
 
77
98
  saveReportAttachment(testInfo, report, options.htmlDir);
@@ -88,25 +109,9 @@ const generateV8Coverage = async (v8list, testInfo, options) => {
88
109
  const inlineSourceMap = true;
89
110
  v8list = await initV8ListAndSourcemap(v8list, options, inlineSourceMap);
90
111
 
91
- // ================================================================
92
-
93
- if (options.toIstanbul) {
112
+ const { coverageData, fileSources } = await convertV8List(v8list, options);
94
113
 
95
- const { coverageData, fileSources } = await convertV8ToIstanbul(v8list, options);
96
-
97
- const report = await saveIstanbulReport(coverageData, fileSources, options);
98
-
99
- saveReportAttachment(testInfo, report, options.htmlDir);
100
-
101
- return report;
102
- }
103
-
104
- // ================================================================
105
-
106
- // functions to ranges, and unpack source maps
107
- await unpackV8List(v8list, options);
108
-
109
- const report = await saveV8Report(v8list, options);
114
+ const report = await generateV8ListReport(v8list, coverageData, fileSources, options);
110
115
 
111
116
  saveReportAttachment(testInfo, report, options.htmlDir);
112
117
 
@@ -115,8 +120,11 @@ const generateV8Coverage = async (v8list, testInfo, options) => {
115
120
 
116
121
  const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
117
122
 
123
+ const reporterOptions = Util.resolveReporterOptions(testInfo);
124
+ Util.initLoggingLevel(reporterOptions.logging, 'coverage-attach');
125
+
118
126
  if (!coverageInput) {
119
- EC.logRed('[MCR] invalid coverage input');
127
+ Util.logError('invalid coverage input');
120
128
  return;
121
129
  }
122
130
 
@@ -138,9 +146,7 @@ const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
138
146
  i += 1;
139
147
  }
140
148
 
141
- fs.mkdirSync(htmlDir, {
142
- recursive: true
143
- });
149
+ Util.initDir(htmlDir);
144
150
  options.htmlDir = htmlDir;
145
151
 
146
152
  if (Array.isArray(coverageInput)) {
@@ -155,12 +161,14 @@ const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
155
161
  // add coverage report to global, v8list only
156
162
  const addCoverageReport = async (v8list, testInfo) => {
157
163
 
164
+ const reporterOptions = Util.resolveReporterOptions(testInfo);
165
+ Util.initLoggingLevel(reporterOptions.logging, 'coverage-add');
166
+
158
167
  if (!Util.isList(v8list)) {
159
- EC.logRed(`[MCR] invalid v8 list for test: ${testInfo.title}`);
168
+ Util.logError(`invalid v8 list for test: ${testInfo.title}`);
160
169
  return;
161
170
  }
162
171
 
163
- const reporterOptions = Util.resolveReporterOptions(testInfo);
164
172
  const coverageOptions = reporterOptions.coverage || {};
165
173
 
166
174
  // reporter outputFile
@@ -176,9 +184,10 @@ const addCoverageReport = async (v8list, testInfo) => {
176
184
  };
177
185
 
178
186
  const reportDir = path.resolve(options.outputDir, options.outputName);
187
+ // do NOT empty dir, there are previous artifacts generated
179
188
 
180
189
  // artifactsDir for temp artifact files
181
- const artifactsDir = path.resolve(reportDir, '.artifacts');
190
+ const artifactsDir = path.resolve(reportDir, artifactsDirName);
182
191
  options.artifactsDir = artifactsDir;
183
192
 
184
193
  const filename = `coverage-${Util.resolveTestIdWithRetry(testInfo)}.json`;
@@ -191,7 +200,6 @@ const addCoverageReport = async (v8list, testInfo) => {
191
200
  // console.log('addCoverageReport', coverageInput.map((it) => it.url));
192
201
 
193
202
  const report = {
194
- // title for debug
195
203
  title: testInfo.title,
196
204
  // merge with data
197
205
  data: v8list
@@ -222,21 +230,9 @@ const getGlobalCoverageData = async (dataList, options) => {
222
230
  const v8list = await mergeV8Coverage(dataList, options);
223
231
  // console.log('after merge', v8list.map((it) => it.url));
224
232
 
225
- if (options.toIstanbul) {
233
+ const { coverageData, fileSources } = await convertV8List(v8list, options);
226
234
 
227
- const { coverageData, fileSources } = await convertV8ToIstanbul(v8list, options);
228
-
229
- const report = await saveIstanbulReport(coverageData, fileSources, options);
230
-
231
- // console.log(report);
232
-
233
- return report;
234
- }
235
-
236
- // functions to ranges, and unpack source maps
237
- await unpackV8List(v8list, options);
238
-
239
- const report = await saveV8Report(v8list, options);
235
+ const report = await generateV8ListReport(v8list, coverageData, fileSources, options);
240
236
 
241
237
  return report;
242
238
  };
@@ -244,6 +240,8 @@ const getGlobalCoverageData = async (dataList, options) => {
244
240
  // global coverage report, run different process with addCoverageReport
245
241
  const generateGlobalCoverageReport = async (dataList, reporterOptions) => {
246
242
 
243
+ Util.logInfo('generating global coverage report ...');
244
+
247
245
  // reporter outputFile
248
246
  const outputFile = await Util.resolveOutputFile(reporterOptions.outputFile);
249
247
  const outputDir = path.dirname(outputFile);
@@ -258,15 +256,21 @@ const generateGlobalCoverageReport = async (dataList, reporterOptions) => {
258
256
  };
259
257
 
260
258
  const reportDir = path.resolve(options.outputDir, options.outputName);
261
- if (!fs.existsSync(reportDir)) {
262
- fs.mkdirSync(reportDir, {
263
- recursive: true
259
+ // empty dir except artifacts dir
260
+
261
+ if (fs.existsSync(reportDir)) {
262
+ fs.readdirSync(reportDir).forEach((itemName) => {
263
+ if (itemName === artifactsDirName) {
264
+ return;
265
+ }
266
+ Util.rmSync(path.resolve(reportDir, itemName));
264
267
  });
265
268
  }
269
+
266
270
  options.htmlDir = reportDir;
267
271
 
268
272
  // artifactsDir for temp artifact files
269
- const artifactsDir = path.resolve(reportDir, '.artifacts');
273
+ const artifactsDir = path.resolve(reportDir, artifactsDirName);
270
274
  options.artifactsDir = artifactsDir;
271
275
 
272
276
  const report = await getGlobalCoverageData(dataList, options);
@@ -274,7 +278,7 @@ const generateGlobalCoverageReport = async (dataList, reporterOptions) => {
274
278
 
275
279
  // =============================================================
276
280
  // remove artifacts dir after report generated
277
- if (!options.debug) {
281
+ if (Util.loggingType !== 'debug') {
278
282
  Util.rmSync(artifactsDir);
279
283
  }
280
284
  // =============================================================