monocart-reporter 1.7.1 → 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 +7 -5
  2. package/lib/cli.js +1 -1
  3. package/lib/common.js +2 -3
  4. package/lib/default/options.js +4 -1
  5. package/lib/generate-data.js +2 -7
  6. package/lib/generate-report.js +10 -11
  7. package/lib/index.d.ts +2 -4
  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 +51 -67
  23. package/lib/plugins/coverage/istanbul/istanbul.js +15 -174
  24. package/lib/plugins/coverage/v8/v8.js +22 -13
  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 +4 -5
  37. package/lib/plugins/coverage/v8/position-mapping.js +0 -92
  38. package/lib/plugins/coverage/v8/source-map.js +0 -451
@@ -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,15 +1,15 @@
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
7
  initV8ListAndSourcemap, mergeV8Coverage, saveV8Report
9
8
  } = require('./v8/v8.js');
10
9
 
11
- const { convertFunctionsToRanges } = require('./coverage-utils.js');
12
- const { unpackSourceMaps } = require('./v8/source-map.js');
10
+ const { convertV8List } = require('./converter/converter.js');
11
+
12
+ const artifactsDirName = '.artifacts';
13
13
 
14
14
  const defaultOptions = {
15
15
 
@@ -18,7 +18,7 @@ const defaultOptions = {
18
18
  // outputDir
19
19
  // outputName
20
20
 
21
- // (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
22
22
  toIstanbul: false,
23
23
 
24
24
  // (Function) A filter function to execute for each element in the V8 list.
@@ -44,26 +44,26 @@ const defaultOptions = {
44
44
  // watermarks: [50, 80],
45
45
 
46
46
  // (Boolean) Whether inline all scripts to the single HTML file. V8 only.
47
- inline: false,
47
+ inline: false
48
48
 
49
- // (Boolean) The dist file which has source map will be included, and the temporary artifacts will not be removed.
50
- debug: false
51
49
  };
52
50
 
53
51
  // ========================================================================================================
54
52
 
55
- const unpackV8List = async (v8list, options) => {
56
- v8list.forEach((item, i) => {
57
- if (item.type === 'js') {
58
- item.ranges = convertFunctionsToRanges(item.functions);
59
- delete item.functions;
60
- }
61
- });
53
+ const generateV8ListReport = async (v8list, coverageData, fileSources, options) => {
54
+ const { toIstanbul, lcov } = options;
55
+ const istanbulReport = toIstanbul || lcov;
56
+ const v8Report = !toIstanbul;
62
57
 
63
- // requires ranges before unpack
64
- await unpackSourceMaps(v8list, options);
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
65
 
66
- // console.log(v8list.length);
66
+ return report;
67
67
  };
68
68
 
69
69
  // ========================================================================================================
@@ -88,6 +88,11 @@ const generateIstanbulReport = (coverageData, testInfo, options) => {
88
88
 
89
89
  const fileSources = options.fileSources || {};
90
90
 
91
+ // force to istanbul
92
+ if (!options.toIstanbul) {
93
+ options.toIstanbul = true;
94
+ }
95
+
91
96
  const report = saveIstanbulReport(coverageData, fileSources, options);
92
97
 
93
98
  saveReportAttachment(testInfo, report, options.htmlDir);
@@ -104,25 +109,9 @@ const generateV8Coverage = async (v8list, testInfo, options) => {
104
109
  const inlineSourceMap = true;
105
110
  v8list = await initV8ListAndSourcemap(v8list, options, inlineSourceMap);
106
111
 
107
- // ================================================================
108
-
109
- if (options.toIstanbul) {
112
+ const { coverageData, fileSources } = await convertV8List(v8list, options);
110
113
 
111
- const { coverageData, fileSources } = await convertV8ToIstanbul(v8list, options);
112
-
113
- const report = await saveIstanbulReport(coverageData, fileSources, options);
114
-
115
- saveReportAttachment(testInfo, report, options.htmlDir);
116
-
117
- return report;
118
- }
119
-
120
- // =================================================================
121
-
122
- // functions to ranges, and unpack source maps
123
- await unpackV8List(v8list, options);
124
-
125
- const report = await saveV8Report(v8list, options);
114
+ const report = await generateV8ListReport(v8list, coverageData, fileSources, options);
126
115
 
127
116
  saveReportAttachment(testInfo, report, options.htmlDir);
128
117
 
@@ -131,8 +120,11 @@ const generateV8Coverage = async (v8list, testInfo, options) => {
131
120
 
132
121
  const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
133
122
 
123
+ const reporterOptions = Util.resolveReporterOptions(testInfo);
124
+ Util.initLoggingLevel(reporterOptions.logging, 'coverage-attach');
125
+
134
126
  if (!coverageInput) {
135
- EC.logRed('[MCR] invalid coverage input');
127
+ Util.logError('invalid coverage input');
136
128
  return;
137
129
  }
138
130
 
@@ -154,9 +146,7 @@ const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
154
146
  i += 1;
155
147
  }
156
148
 
157
- fs.mkdirSync(htmlDir, {
158
- recursive: true
159
- });
149
+ Util.initDir(htmlDir);
160
150
  options.htmlDir = htmlDir;
161
151
 
162
152
  if (Array.isArray(coverageInput)) {
@@ -171,12 +161,14 @@ const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
171
161
  // add coverage report to global, v8list only
172
162
  const addCoverageReport = async (v8list, testInfo) => {
173
163
 
164
+ const reporterOptions = Util.resolveReporterOptions(testInfo);
165
+ Util.initLoggingLevel(reporterOptions.logging, 'coverage-add');
166
+
174
167
  if (!Util.isList(v8list)) {
175
- EC.logRed(`[MCR] invalid v8 list for test: ${testInfo.title}`);
168
+ Util.logError(`invalid v8 list for test: ${testInfo.title}`);
176
169
  return;
177
170
  }
178
171
 
179
- const reporterOptions = Util.resolveReporterOptions(testInfo);
180
172
  const coverageOptions = reporterOptions.coverage || {};
181
173
 
182
174
  // reporter outputFile
@@ -192,9 +184,10 @@ const addCoverageReport = async (v8list, testInfo) => {
192
184
  };
193
185
 
194
186
  const reportDir = path.resolve(options.outputDir, options.outputName);
187
+ // do NOT empty dir, there are previous artifacts generated
195
188
 
196
189
  // artifactsDir for temp artifact files
197
- const artifactsDir = path.resolve(reportDir, '.artifacts');
190
+ const artifactsDir = path.resolve(reportDir, artifactsDirName);
198
191
  options.artifactsDir = artifactsDir;
199
192
 
200
193
  const filename = `coverage-${Util.resolveTestIdWithRetry(testInfo)}.json`;
@@ -207,7 +200,6 @@ const addCoverageReport = async (v8list, testInfo) => {
207
200
  // console.log('addCoverageReport', coverageInput.map((it) => it.url));
208
201
 
209
202
  const report = {
210
- // title for debug
211
203
  title: testInfo.title,
212
204
  // merge with data
213
205
  data: v8list
@@ -238,25 +230,9 @@ const getGlobalCoverageData = async (dataList, options) => {
238
230
  const v8list = await mergeV8Coverage(dataList, options);
239
231
  // console.log('after merge', v8list.map((it) => it.url));
240
232
 
241
- // ================================================================
242
-
243
- if (options.toIstanbul) {
244
-
245
- const { coverageData, fileSources } = await convertV8ToIstanbul(v8list, options);
246
-
247
- const report = await saveIstanbulReport(coverageData, fileSources, options);
233
+ const { coverageData, fileSources } = await convertV8List(v8list, options);
248
234
 
249
- // console.log(report);
250
-
251
- return report;
252
- }
253
-
254
- // ================================================================
255
-
256
- // functions to ranges, and unpack source maps
257
- await unpackV8List(v8list, options);
258
-
259
- const report = await saveV8Report(v8list, options);
235
+ const report = await generateV8ListReport(v8list, coverageData, fileSources, options);
260
236
 
261
237
  return report;
262
238
  };
@@ -264,6 +240,8 @@ const getGlobalCoverageData = async (dataList, options) => {
264
240
  // global coverage report, run different process with addCoverageReport
265
241
  const generateGlobalCoverageReport = async (dataList, reporterOptions) => {
266
242
 
243
+ Util.logInfo('generating global coverage report ...');
244
+
267
245
  // reporter outputFile
268
246
  const outputFile = await Util.resolveOutputFile(reporterOptions.outputFile);
269
247
  const outputDir = path.dirname(outputFile);
@@ -278,15 +256,21 @@ const generateGlobalCoverageReport = async (dataList, reporterOptions) => {
278
256
  };
279
257
 
280
258
  const reportDir = path.resolve(options.outputDir, options.outputName);
281
- if (!fs.existsSync(reportDir)) {
282
- fs.mkdirSync(reportDir, {
283
- 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));
284
267
  });
285
268
  }
269
+
286
270
  options.htmlDir = reportDir;
287
271
 
288
272
  // artifactsDir for temp artifact files
289
- const artifactsDir = path.resolve(reportDir, '.artifacts');
273
+ const artifactsDir = path.resolve(reportDir, artifactsDirName);
290
274
  options.artifactsDir = artifactsDir;
291
275
 
292
276
  const report = await getGlobalCoverageData(dataList, options);
@@ -294,7 +278,7 @@ const generateGlobalCoverageReport = async (dataList, reporterOptions) => {
294
278
 
295
279
  // =============================================================
296
280
  // remove artifacts dir after report generated
297
- if (!options.debug) {
281
+ if (Util.loggingType !== 'debug') {
298
282
  Util.rmSync(artifactsDir);
299
283
  }
300
284
  // =============================================================