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
package/lib/utils/util.js CHANGED
@@ -4,9 +4,12 @@ const path = require('path');
4
4
  const os = require('os');
5
5
  const crypto = require('crypto');
6
6
  const EC = require('eight-colors');
7
+ const CG = require('console-grid');
7
8
  const Share = require('../platform/share.js');
8
9
  const { deflateSync } = require('lz-utils');
9
10
 
11
+ const assetsName = 'assets';
12
+
10
13
  const Util = {
11
14
 
12
15
  root: process.cwd(),
@@ -116,6 +119,23 @@ const Util = {
116
119
  return sourcePath;
117
120
  },
118
121
 
122
+ // empty or create dir
123
+ initDir: (dirPath) => {
124
+ if (fs.existsSync(dirPath)) {
125
+ Util.rmSync(dirPath);
126
+ }
127
+ fs.mkdirSync(dirPath, {
128
+ recursive: true
129
+ });
130
+ },
131
+
132
+ initAssetsDir: async (options) => {
133
+ const outputFile = await Util.resolveOutputFile(options.outputFile);
134
+ const outputDir = path.dirname(outputFile);
135
+ const assetsDir = path.resolve(outputDir, assetsName);
136
+ Util.initDir(assetsDir);
137
+ },
138
+
119
139
  saveHtmlReport: async (options) => {
120
140
 
121
141
  const {
@@ -127,7 +147,6 @@ const Util = {
127
147
 
128
148
  outputDir,
129
149
  reportDataFile,
130
- assetsName,
131
150
  assetsRelative
132
151
  } = options;
133
152
 
@@ -226,7 +245,7 @@ const Util = {
226
245
  writeJSONSync: function(filePath, json) {
227
246
  let content = Util.jsonString(json);
228
247
  if (!content) {
229
- EC.logRed('[MCR] invalid JSON object');
248
+ Util.logError('invalid JSON object');
230
249
  return false;
231
250
  }
232
251
  // end of line
@@ -325,7 +344,7 @@ const Util = {
325
344
  if (template) {
326
345
  Util.templateCache[templatePath] = template;
327
346
  } else {
328
- EC.logRed(`[MCR] not found template: ${templatePath}`);
347
+ Util.logError(`not found template: ${templatePath}`);
329
348
  }
330
349
  }
331
350
  return template;
@@ -352,6 +371,81 @@ const Util = {
352
371
  });
353
372
  });
354
373
  return option;
374
+ },
375
+
376
+ // ==========================================================================================
377
+
378
+ loggingLevels: {
379
+ off: 0,
380
+ error: 10,
381
+ info: 20,
382
+ debug: 30
383
+ },
384
+
385
+ initLoggingLevel: (level, from = '') => {
386
+ if (!level && Util.loggingType) {
387
+ return;
388
+ }
389
+ const types = {
390
+ off: 'off',
391
+ error: 'error',
392
+ info: 'info',
393
+ debug: 'debug'
394
+ };
395
+ const type = types[level] || types.info;
396
+ Util.loggingType = type;
397
+ Util.loggingLevel = Util.loggingLevels[type];
398
+
399
+ // console.log('=========================================');
400
+ // console.log(from, Util.loggingType, Util.loggingLevel);
401
+ },
402
+
403
+ logError: (message) => {
404
+ if (Util.loggingLevel < Util.loggingLevels.error) {
405
+ return;
406
+ }
407
+ EC.logRed(`[MCR] ${message}`);
408
+ },
409
+
410
+ logInfo: (message) => {
411
+ if (Util.loggingLevel < Util.loggingLevels.info) {
412
+ return;
413
+ }
414
+ console.log(`[MCR] ${message}`);
415
+ },
416
+
417
+ // grid is info level
418
+ logGrid: (gridData) => {
419
+ if (Util.loggingLevel < Util.loggingLevels.info) {
420
+ return;
421
+ }
422
+ CG(gridData);
423
+ },
424
+
425
+ logDebug: (message) => {
426
+ if (Util.loggingLevel < Util.loggingLevels.debug) {
427
+ return;
428
+ }
429
+ console.log(`[MCR] ${message}`);
430
+ },
431
+
432
+ // time is debug level
433
+ logTime: (message, time_start) => {
434
+ if (Util.loggingLevel < Util.loggingLevels.debug) {
435
+ return;
436
+ }
437
+ const duration = Date.now() - time_start;
438
+ const durationH = Util.TSF(duration);
439
+ const ls = [`[MCR] ${message}`, ' ('];
440
+ if (duration <= 100) {
441
+ ls.push(EC.green(durationH));
442
+ } else if (duration < 500) {
443
+ ls.push(EC.yellow(durationH));
444
+ } else {
445
+ ls.push(EC.red(durationH));
446
+ }
447
+ ls.push(')');
448
+ console.log(ls.join(''));
355
449
  }
356
450
 
357
451
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monocart-reporter",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "A playwright test reporter. Shows suites/cases/steps with tree style, markdown annotations, custom columns/formatters/data collection visitors, console logs, style tags, send email.",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -48,8 +48,7 @@
48
48
  "koa-static-resolver": "^1.0.4",
49
49
  "lz-utils": "^2.0.1",
50
50
  "nodemailer": "^6.9.3",
51
- "open": "^9.1.0",
52
- "source-map": "^0.7.4"
51
+ "open": "^9.1.0"
53
52
  },
54
53
  "devDependencies": {
55
54
  "@playwright/test": "^1.35.1",
@@ -58,8 +57,8 @@
58
57
  "eslint": "^8.43.0",
59
58
  "eslint-config-plus": "^1.0.6",
60
59
  "eslint-plugin-html": "^7.1.0",
61
- "eslint-plugin-vue": "^9.15.0",
62
- "stylelint": "^15.8.0",
60
+ "eslint-plugin-vue": "^9.15.1",
61
+ "stylelint": "^15.9.0",
63
62
  "stylelint-config-plus": "^1.0.3",
64
63
  "vine-ui": "^3.1.12"
65
64
  }
@@ -1,92 +0,0 @@
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
- class PositionMapping {
27
- constructor(source, sourceName) {
28
- this.source = source;
29
- this.sourceName = sourceName;
30
- this.lines = this.getLines(source);
31
- this.ranges = [];
32
- }
33
-
34
- // =============================================================================
35
-
36
- getSlice(s, e) {
37
- return this.source.slice(s, e);
38
- }
39
-
40
- locationToOffset(location) {
41
- const { line, column } = location;
42
- // 1-based
43
- const lineInfo = this.lines[line - 1];
44
- if (lineInfo) {
45
- if (column === Infinity) {
46
- return lineInfo.start + lineInfo.length;
47
- }
48
- return lineInfo.start + column;
49
- }
50
- return 0;
51
- }
52
-
53
- offsetToLocation(offset) {
54
- const lineInfo = findLine(this.lines, offset);
55
- const column = Math.min(Math.max(offset - lineInfo.start, 0), lineInfo.length);
56
-
57
- // 1-based
58
- const line = lineInfo.line + 1;
59
-
60
- return {
61
- line,
62
- column
63
- };
64
- }
65
-
66
- // =============================================================================
67
-
68
- getLines(content) {
69
- let pos = 0;
70
- const lines = content.split(/\n/).map((text, line) => {
71
- const length = text.length;
72
- const start = pos;
73
- const end = start + length;
74
-
75
- pos += length + 1;
76
-
77
- return {
78
- line,
79
- start,
80
- end,
81
- length,
82
- text
83
- };
84
- });
85
-
86
- return lines;
87
- }
88
-
89
- }
90
-
91
- module.exports = PositionMapping;
92
-
@@ -1,451 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const EC = require('eight-colors');
4
-
5
- const { SourceMapConsumer } = require('source-map');
6
-
7
- const PositionMapping = require('./position-mapping.js');
8
- const { dedupeCountRanges } = require('./dedupe.js');
9
- const { initSourceMapRootAndUrl } = require('../coverage-utils.js');
10
-
11
- const Concurrency = require('../../../platform/concurrency.js');
12
- const { convertSourceMap, axios } = require('../../../runtime/monocart-coverage.js');
13
- const Util = require('../../../utils/util.js');
14
-
15
- // SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
16
- // SourceMapConsumer.LEAST_UPPER_BOUND = 2;
17
- const BIAS = {
18
- left: SourceMapConsumer.GREATEST_LOWER_BOUND,
19
- right: SourceMapConsumer.LEAST_UPPER_BOUND
20
- };
21
-
22
- // =========================================================================================================
23
-
24
- const findOriginalPosition = (consumer, line, column, sides) => {
25
- let original;
26
- for (const side of sides) {
27
- original = consumer.originalPositionFor({
28
- line,
29
- column,
30
- bias: BIAS[side]
31
- });
32
- if (original.source !== null) {
33
- break;
34
- }
35
- }
36
- if (original && original.source !== null) {
37
- return original;
38
- }
39
- };
40
-
41
- const findOriginalStartPosition = (consumer, sLoc) => {
42
- const { line, column } = sLoc;
43
- return findOriginalPosition(consumer, line, column, ['right', 'left']);
44
- };
45
-
46
- const findOriginalEndInRange = (consumer, generatedMapping, range) => {
47
- const { start, end } = range;
48
- // from -2 (already -1)
49
- // > start (no need equal)
50
- for (let i = end - 2; i > start; i--) {
51
- const loc = generatedMapping.offsetToLocation(i);
52
- const op = findOriginalPosition(consumer, loc.line, loc.column, ['left', 'right']);
53
- if (op) {
54
- return op;
55
- }
56
- }
57
- };
58
-
59
- const findOriginalEndPosition = (consumer, eLoc, generatedMapping, range) => {
60
- const { line, column } = eLoc;
61
-
62
- // before end column must be >= 0
63
- const currentColumn = Math.max(column - 1, 0);
64
-
65
- let ep = findOriginalPosition(consumer, line, currentColumn, ['left', 'right']);
66
- if (!ep) {
67
- ep = findOriginalEndInRange(consumer, generatedMapping, range);
68
- if (!ep) {
69
- return;
70
- }
71
- }
72
-
73
- const afterEndMapping = consumer.generatedPositionFor({
74
- source: ep.source,
75
- line: ep.line,
76
- column: ep.column + 1,
77
- bias: BIAS.right
78
- });
79
-
80
- if (afterEndMapping.line === null) {
81
- return {
82
- source: ep.source,
83
- line: ep.line,
84
- column: Infinity
85
- };
86
- }
87
-
88
- const mapping = consumer.originalPositionFor(afterEndMapping);
89
- if (mapping.line !== ep.line) {
90
- return {
91
- source: ep.source,
92
- line: ep.line,
93
- column: Infinity
94
- };
95
- }
96
-
97
- return mapping;
98
- };
99
-
100
- // =========================================================================================================
101
-
102
- const getOriginalMappings = (consumer, options) => {
103
-
104
- // source filter
105
- let sourceList = consumer.sources;
106
- if (typeof options.sourceFilter === 'function') {
107
- sourceList = sourceList.filter((sourceName) => {
108
- return options.sourceFilter(sourceName);
109
- });
110
- }
111
-
112
- // create original content mappings
113
- const originalMappings = new Map();
114
- for (const sourceName of sourceList) {
115
- // console.log(`add source: ${k}`);
116
- const sourceContent = consumer.sourceContentFor(sourceName);
117
- if (typeof sourceContent !== 'string') {
118
- EC.logRed(`[MCR] not found source content: ${sourceName}`);
119
- continue;
120
- }
121
-
122
- const mapping = new PositionMapping(sourceContent, sourceName);
123
- originalMappings.set(sourceName, mapping);
124
- }
125
-
126
- return originalMappings;
127
- };
128
-
129
- // =========================================================================================================
130
-
131
- const addOriginalRange = (originalMapping, start, end, count) => {
132
- originalMapping.ranges.push({
133
- start,
134
- end,
135
- count
136
- });
137
- };
138
-
139
- // =========================================================================================================
140
-
141
-
142
- const unpackJsSourceMap = async (item, v8list, options) => {
143
- // console.log('------------------------------------------------------');
144
- // console.log(item.type, item.url);
145
- // console.log(Object.keys(item));
146
-
147
- const sourceMap = item.sourceMap;
148
-
149
- const fileUrls = {};
150
- const fileSources = {};
151
- initSourceMapRootAndUrl(sourceMap, fileUrls, fileSources);
152
-
153
- const generatedMapping = new PositionMapping(item.source);
154
- const consumer = await new SourceMapConsumer(sourceMap);
155
- const originalMappings = getOriginalMappings(consumer, options);
156
-
157
- // generated ranges to original ranges
158
- item.ranges.forEach((range) => {
159
-
160
- // find start location
161
- const sLoc = generatedMapping.offsetToLocation(range.start);
162
- const oSLoc = findOriginalStartPosition(consumer, sLoc);
163
- if (!oSLoc) {
164
- // not found start
165
- return;
166
- }
167
-
168
- // if source excluded
169
- const currentSource = oSLoc.source;
170
- const originalMapping = originalMappings.get(currentSource);
171
- if (!originalMapping) {
172
- // possible this source has been filtered
173
- // console.log(`not found source: ${currentSource}`);
174
- return;
175
- }
176
-
177
- const originalStart = originalMapping.locationToOffset(oSLoc);
178
-
179
- // find end location
180
- const eLoc = generatedMapping.offsetToLocation(range.end);
181
- const oELoc = findOriginalEndPosition(consumer, eLoc, generatedMapping, range);
182
- if (!oELoc) {
183
-
184
- // console.log(EC.red('not found end'));
185
- // console.log(item.url);
186
- // console.log(originalMapping.sourceName);
187
- // console.log('generated start', sLoc.line, sLoc.column, 'original start', oSLoc.line, oSLoc.column);
188
- // console.log('generated end', eLoc.line, eLoc.column);
189
-
190
- // can NOT use file end
191
-
192
- return;
193
- }
194
-
195
- if (oSLoc.source !== oELoc.source) {
196
- // console.log('ERROR: range crossed source file', range, oSLoc.source, oELoc.source);
197
- return;
198
- }
199
-
200
- const originalEnd = originalMapping.locationToOffset(oELoc);
201
- if (originalEnd < originalStart) {
202
- // range start greater than end
203
- addOriginalRange(originalMapping, originalEnd, originalStart, range.count);
204
- return;
205
- }
206
-
207
- addOriginalRange(originalMapping, originalStart, originalEnd, range.count);
208
-
209
- });
210
-
211
- consumer.destroy();
212
-
213
- // append to v8list
214
- originalMappings.forEach((originalMapping, currentSource) => {
215
-
216
- const url = fileUrls[currentSource] || currentSource;
217
- const ranges = dedupeCountRanges(originalMapping.ranges);
218
-
219
- // console.log('add source url', url);
220
-
221
- // original source and id
222
- const source = originalMapping.source;
223
- const id = Util.calculateSha1(url + source);
224
-
225
- let ext = path.extname(currentSource);
226
- let type = '';
227
- if (ext) {
228
- ext = ext.slice(1);
229
- const reg = /^[a-z0-9]+$/;
230
- if (reg.test(ext)) {
231
- type = ext;
232
- }
233
- }
234
-
235
-
236
- v8list.push({
237
- url,
238
- id,
239
- type,
240
- sourcePath: currentSource,
241
- distFile: item.sourceMap.file,
242
- ranges,
243
- source
244
- });
245
-
246
- });
247
- };
248
-
249
- // =========================================================================================================
250
-
251
- const request = async (options) => {
252
- if (typeof options === 'string') {
253
- options = {
254
- url: options
255
- };
256
- }
257
- let err;
258
- const res = await axios(options).catch((e) => {
259
- err = e;
260
- });
261
- return [err, res];
262
- };
263
-
264
- const getSourceMapUrl = (content, url) => {
265
-
266
- const m = content.match(convertSourceMap.mapFileCommentRegex);
267
- if (!m) {
268
- return;
269
- }
270
-
271
- const comment = m.pop();
272
- const r = convertSourceMap.mapFileCommentRegex.exec(comment);
273
- // for some odd reason //# .. captures in 1 and /* .. */ in 2
274
- const filename = r[1] || r[2];
275
-
276
- let mapUrl;
277
-
278
- try {
279
- mapUrl = new URL(filename, url);
280
- } catch (e) {
281
- // console.log(e)
282
- }
283
- if (mapUrl) {
284
- return mapUrl.toString();
285
- }
286
- };
287
-
288
- const resolveSourceMap = (data) => {
289
- if (data) {
290
- const { sources, sourcesContent } = data;
291
- if (!sources || !sourcesContent) {
292
- return;
293
- }
294
- return data;
295
- }
296
- };
297
-
298
- const collectInlineSourceMaps = async (v8list) => {
299
- const concurrency = new Concurrency();
300
- for (const item of v8list) {
301
-
302
- const { type, source } = item;
303
-
304
- // only for js
305
- if (type === 'js') {
306
- const converter = convertSourceMap.fromSource(source);
307
- if (converter) {
308
- item.sourceMap = resolveSourceMap(converter.sourcemap);
309
- continue;
310
- }
311
- const sourceMapUrl = getSourceMapUrl(source, item.url);
312
- if (sourceMapUrl) {
313
- item.sourceMapUrl = sourceMapUrl;
314
- concurrency.addItem(item);
315
- }
316
- }
317
- }
318
- await concurrency.start(async (item) => {
319
- const [err, res] = await request({
320
- url: item.sourceMapUrl
321
- });
322
- if (!err && res) {
323
- item.sourceMap = resolveSourceMap(res.data);
324
- }
325
- });
326
- };
327
-
328
- const collectFileSourceMaps = async (v8list, options) => {
329
- const concurrency = new Concurrency();
330
- for (const item of v8list) {
331
-
332
- const {
333
- type, url, source, id
334
- } = item;
335
-
336
- // remove source just keep functions to reduce artifacts size
337
- delete item.source;
338
-
339
- const sourcePath = Util.resolveArtifactSourcePath(options.artifactsDir, id);
340
- if (fs.existsSync(sourcePath)) {
341
- continue;
342
- }
343
-
344
- const sourceData = {
345
- url,
346
- id,
347
- source: convertSourceMap.removeComments(source)
348
- };
349
-
350
- // only for js
351
- if (type === 'js') {
352
- const converter = convertSourceMap.fromSource(source);
353
- if (converter) {
354
- sourceData.sourceMap = resolveSourceMap(converter.sourcemap);
355
- await saveSourceFile(sourcePath, sourceData);
356
- continue;
357
- }
358
- const sourceMapUrl = getSourceMapUrl(source, item.url);
359
- if (sourceMapUrl) {
360
- concurrency.addItem({
361
- sourceMapUrl,
362
- sourcePath,
363
- sourceData
364
- });
365
- continue;
366
- }
367
- }
368
-
369
- await saveSourceFile(sourcePath, sourceData);
370
-
371
- }
372
-
373
- await concurrency.start(async (item) => {
374
- const [err, res] = await request({
375
- url: item.sourceMapUrl
376
- });
377
- const sourceData = item.sourceData;
378
- if (!err && res) {
379
- sourceData.sourceMap = resolveSourceMap(res.data);
380
- }
381
- await saveSourceFile(item.sourcePath, sourceData);
382
- });
383
- };
384
-
385
- const saveSourceFile = async (filePath, data) => {
386
- await Util.writeFile(filePath, JSON.stringify(data));
387
- };
388
-
389
- const collectSourceMaps = async (v8list, options, inlineSourceMap) => {
390
-
391
- if (inlineSourceMap) {
392
- await collectInlineSourceMaps(v8list);
393
- return;
394
- }
395
-
396
- await collectFileSourceMaps(v8list, options);
397
-
398
- };
399
-
400
- // =========================================================================================================
401
- const filterSourceMapList = (v8list, options) => {
402
- const sourceMapList = [];
403
- const indexes = [];
404
-
405
- v8list.forEach((item, i) => {
406
- const sourceMap = item.sourceMap;
407
- if (!sourceMap) {
408
- return;
409
- }
410
- sourceMapList.push(item);
411
- indexes.push(i);
412
- });
413
-
414
- if (!sourceMapList.length) {
415
- return;
416
- }
417
-
418
- // do not remove in debug mode
419
- if (!options.debug) {
420
- // remove dist file if found sourceMap
421
- indexes.reverse();
422
- indexes.forEach((i) => {
423
- v8list.splice(i, 1);
424
- });
425
- }
426
-
427
- return sourceMapList;
428
- };
429
-
430
- // requires ranges before unpack
431
- const unpackSourceMaps = async (v8list, options) => {
432
-
433
- const sourceMapList = filterSourceMapList(v8list, options);
434
- if (!sourceMapList) {
435
- // nothing to unpack
436
- return;
437
- }
438
-
439
- // console.log(sourceMapList);
440
-
441
- // only js
442
- for (const item of sourceMapList) {
443
- await unpackJsSourceMap(item, v8list, options);
444
- }
445
- };
446
-
447
-
448
- module.exports = {
449
- collectSourceMaps,
450
- unpackSourceMaps
451
- };