monocart-reporter 1.6.25 → 1.6.26

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
@@ -123,10 +123,10 @@ Separated metadata file (Already included in the above HTML and compressed, it c
123
123
  // global coverage settings for addCoverageReport API
124
124
  coverage: null,
125
125
  // coverage: {
126
+ // entryFilter: (entry) => true,
126
127
  // unpackSourceMap: true,
127
128
  // excludeDistFile: true,
128
129
  // sourceFilter: (sourceName) => sourceName.search(/\/src\/.+/) !== -1,
129
- // entryFilter: (entry) => {}
130
130
  // },
131
131
 
132
132
  // trend data handler
@@ -642,10 +642,10 @@ Attach a code coverage report with API `attachCoverageReport(data, testInfo, opt
642
642
  - V8 only:
643
643
  - `toIstanbul` (Boolean) Whether to convert to Istanbul report
644
644
  - `watermarks` (Array) Defaults to `[50, 80]`
645
+ - `entryFilter` (Function) A filter function to execute for each element in the V8 list.
645
646
  - `unpackSourceMap` (Boolean) Whether to unpack all sources from the source map if a related source map file is found.
646
647
  - `excludeDistFile` (Boolean) Whether to exclude the dist file (usually minified) if the sources are successfully unpacked from the source map.
647
648
  - `sourceFilter` (Function) A filter function to execute for each element in the sources which unpacked from the source map.
648
- - `entryFilter` (Function) A filter function to execute for each element in the V8 list.
649
649
  - `inline` (Boolean) Whether inline all scripts to the single HTML file.
650
650
 
651
651
  (see example: [report-coverage.spec.js](https://github.com/cenfun/monocart-reporter/blob/main/tests/report-coverage/report-coverage.spec.js))
@@ -758,10 +758,10 @@ module.exports = {
758
758
  outputFile: './test-results/report.html',
759
759
  // global coverage report options
760
760
  coverage: {
761
+ entryFilter: (entry) => true,
761
762
  unpackSourceMap: true,
762
763
  excludeDistFile: true,
763
764
  sourceFilter: (sourceName) => sourceName.search(/\/src\/.+/) !== -1,
764
- entryFilter: (entry) => {}
765
765
  }
766
766
  }]
767
767
  ]
@@ -14,10 +14,10 @@ module.exports = {
14
14
  // global coverage settings for addCoverageReport API
15
15
  coverage: null,
16
16
  // coverage: {
17
+ // entryFilter: (entry) => true,
17
18
  // unpackSourceMap: true,
18
19
  // excludeDistFile: true,
19
20
  // sourceFilter: (sourceName) => sourceName.search(/\/src\/.+/) !== -1,
20
- // entryFilter: (entry) => {}
21
21
  // },
22
22
 
23
23
  // trend data handler
@@ -2,11 +2,46 @@
2
2
  <html>
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <link rel="icon" href="data:,">
7
7
  <title>{title}</title>
8
+ <style>
9
+ @keyframes fs-loading-animation {
10
+ 0% {
11
+ transform: rotate(0deg);
12
+ }
13
+
14
+ 100% {
15
+ transform: rotate(360deg);
16
+ }
17
+ }
18
+
19
+ .fs-loading {
20
+ position: absolute;
21
+ top: 50%;
22
+ left: 50%;
23
+ z-index: 100;
24
+ width: 50px;
25
+ height: 50px;
26
+ margin-top: -25px;
27
+ margin-left: -25px;
28
+ animation: 0.382s fs-loading-animation linear infinite;
29
+ }
30
+
31
+ .fs-loading svg {
32
+ display: block;
33
+ width: 100%;
34
+ height: 100%;
35
+ pointer-events: none;
36
+ }
37
+ </style>
8
38
  </head>
9
39
  <body>
40
+ <div class="fs-loading">
41
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
42
+ <path d="M1,8 A7 7 0 1 1 8 15" stroke="#999" stroke-width="2" stroke-linecap="round" fill="none"/>
43
+ </svg>
44
+ </div>
10
45
  {content}
11
46
  </body>
12
47
  </html>
@@ -158,30 +158,6 @@ const Util = {
158
158
  return info;
159
159
  },
160
160
 
161
- getFlatRanges: (functions) => {
162
- const flatRanges = [];
163
- if (functions && functions.length) {
164
- functions.forEach((fun) => {
165
- const ranges = fun.ranges;
166
- if (ranges && ranges.length) {
167
- ranges.forEach((range) => {
168
- flatRanges.push(range);
169
- });
170
- }
171
- });
172
- flatRanges.sort((a, b) => {
173
- if (a.startOffset === b.startOffset) {
174
- if (a.endOffset === b.endOffset) {
175
- return a.count - b.count;
176
- }
177
- return a.endOffset - b.endOffset;
178
- }
179
- return a.startOffset - b.startOffset;
180
- });
181
- }
182
- return flatRanges;
183
- },
184
-
185
161
  isTagItem: (item) => {
186
162
  if (item.type === 'case' || (item.type === 'suite' && item.suiteType === 'describe')) {
187
163
  return true;
@@ -6,10 +6,14 @@ const Util = require('../utils/util.js');
6
6
  const cacheMap = new Map();
7
7
 
8
8
  function getEmptyLines(lines) {
9
- return lines.map((line, i) => ({
10
- code: line.trim(),
11
- num: i + 1
12
- })).filter((line) => !line.code).map((line) => line.num);
9
+ const emptyLines = [];
10
+ const reg = /\S/;
11
+ lines.forEach((text, i) => {
12
+ if (!reg.test(text)) {
13
+ emptyLines.push(i + 1);
14
+ }
15
+ });
16
+ return emptyLines;
13
17
  }
14
18
 
15
19
  const getFileComments = (filePath, parserOptions = {}) => {
@@ -0,0 +1,154 @@
1
+ const Util = require('../../utils/util.js');
2
+ const Concurrency = require('../../platform/concurrency.js');
3
+ const { convertSourceMap, axios } = require('../../runtime/monocart-coverage.js');
4
+
5
+ const sortRanges = (ranges) => {
6
+ ranges.sort((a, b) => {
7
+ if (a.start === b.start) {
8
+ return a.end - b.end;
9
+ }
10
+ return a.start - b.start;
11
+ });
12
+ };
13
+
14
+ const resolveUrl = (input) => {
15
+ let url;
16
+ try {
17
+ url = new URL(input);
18
+ } catch (e) {
19
+ // console.error('error url', input);
20
+ return;
21
+ }
22
+ return url;
23
+ };
24
+
25
+ const filterPath = (str) => {
26
+ str = decodeURI(str);
27
+ // remove / of start, end or double, ./ ../
28
+ const ls = str.split('/').map((it) => {
29
+ it = it.trim();
30
+ // remove illegal characters except /
31
+ it = it.replace(/[\\:*?"<>|]/g, '');
32
+ // space
33
+ it = it.replace(/\s+/g, '-');
34
+ return it;
35
+ }).filter((item) => {
36
+ if (!item || item === '.' || item === '..') {
37
+ return false;
38
+ }
39
+ return true;
40
+ });
41
+ return ls.join('/');
42
+ };
43
+
44
+ const getSourcePath = (url, index = '', type = '') => {
45
+
46
+ if (!url) {
47
+ // anonymous scripts will have __playwright_evaluation_script__ as their URL.
48
+ url = ['file://anonymous', index ? `-${index}` : '', type ? `.${type}` : ''].join('');
49
+ }
50
+
51
+ const u = resolveUrl(url);
52
+ if (u) {
53
+ const host = [u.hostname, u.port].filter((it) => it).join('-');
54
+ // always start with '/'
55
+ const pathname = u.pathname;
56
+
57
+ let p = host + pathname;
58
+ // webpack://monocart-v8/packages/v8/src/app.vue?5cc4
59
+ if (u.search) {
60
+ p += `/${u.search}`;
61
+ }
62
+
63
+ return filterPath(p);
64
+ }
65
+
66
+ const relPath = Util.relativePath(url);
67
+ return filterPath(relPath);
68
+ };
69
+
70
+ // ================================================================================================
71
+
72
+ const request = async (options) => {
73
+ if (typeof options === 'string') {
74
+ options = {
75
+ url: options
76
+ };
77
+ }
78
+ let err;
79
+ const res = await axios(options).catch((e) => {
80
+ err = e;
81
+ });
82
+ return [err, res];
83
+ };
84
+
85
+ const getSourceMapUrl = (content, url) => {
86
+
87
+ const m = content.match(convertSourceMap.mapFileCommentRegex);
88
+ if (!m) {
89
+ return;
90
+ }
91
+
92
+ const comment = m.pop();
93
+ const r = convertSourceMap.mapFileCommentRegex.exec(comment);
94
+ // for some odd reason //# .. captures in 1 and /* .. */ in 2
95
+ const filename = r[1] || r[2];
96
+
97
+ let mapUrl;
98
+
99
+ try {
100
+ mapUrl = new URL(filename, url);
101
+ } catch (e) {
102
+ // console.log(e)
103
+ }
104
+ if (mapUrl) {
105
+ return mapUrl.toString();
106
+ }
107
+ };
108
+
109
+ const resolveSourceMap = (data) => {
110
+ if (data) {
111
+ const { sources, sourcesContent } = data;
112
+ if (!sources || !sourcesContent) {
113
+ return;
114
+ }
115
+ return data;
116
+ }
117
+ };
118
+
119
+ const collectSourceMaps = async (v8list) => {
120
+ const concurrency = new Concurrency();
121
+ for (const item of v8list) {
122
+ // useless for css
123
+ if (item.type === 'css') {
124
+ continue;
125
+ }
126
+
127
+ const source = item.source;
128
+ const converter = convertSourceMap.fromSource(source);
129
+ if (converter) {
130
+ item.sourceMap = resolveSourceMap(converter.sourcemap);
131
+ continue;
132
+ }
133
+ const sourceMapUrl = getSourceMapUrl(source, item.url);
134
+ if (sourceMapUrl) {
135
+ item.sourceMapUrl = sourceMapUrl;
136
+ concurrency.addItem(item);
137
+ }
138
+ }
139
+ await concurrency.start(async (item) => {
140
+ const [err, res] = await request({
141
+ url: item.sourceMapUrl
142
+ });
143
+ if (!err && res) {
144
+ item.sourceMap = resolveSourceMap(res.data);
145
+ }
146
+ });
147
+ };
148
+
149
+
150
+ module.exports = {
151
+ sortRanges,
152
+ getSourcePath,
153
+ collectSourceMaps
154
+ };
@@ -4,7 +4,9 @@ const EC = require('eight-colors');
4
4
 
5
5
  const Util = require('../../utils/util.js');
6
6
 
7
- const { saveIstanbulReport, saveV8ToIstanbulReport } = require('./istanbul/istanbul.js');
7
+ const {
8
+ convertV8ToIstanbul, mergeIstanbulList, saveIstanbulReport
9
+ } = require('./istanbul/istanbul.js');
8
10
 
9
11
  const {
10
12
  initV8List, mergeV8List, saveV8Report
@@ -12,26 +14,18 @@ const {
12
14
 
13
15
  // ========================================================================================================
14
16
 
15
- // high performance
16
- // str.search(reg) -> index
17
- // reg.test(str) -> boolean
18
- const defaultSourceFilter = (sourceName) => {
19
- // sourceName.search(/\/src\/.+/) !== -1
20
- return true;
21
- };
22
-
23
17
  const defaultV8Options = {
24
18
  // (Boolean) Whether to convert to Istanbul report
25
19
  toIstanbul: false,
26
20
 
21
+ // (Function) A filter function to execute for each element in the V8 list.
22
+ entryFilter: null,
27
23
  // (Boolean) Whether to unpack all sources from the source map if a related source map file is found.
28
24
  unpackSourceMap: true,
29
25
  // (Boolean) Whether to exclude the dist file (usually minified) if the sources are successfully unpacked from the source map.
30
26
  excludeDistFile: true,
31
27
  // (Function) A filter function to execute for each element in the sources which unpacked from the source map.
32
- sourceFilter: defaultSourceFilter,
33
- // (Function) A filter function to execute for each element in the V8 list.
34
- entryFilter: null
28
+ sourceFilter: null
35
29
 
36
30
  // (Array) Defaults to `[50, 80]`
37
31
  // watermarks: [50, 80],
@@ -42,6 +36,11 @@ const defaultV8Options = {
42
36
 
43
37
  const defaultIstanbulOptions = {
44
38
 
39
+ // when toIstanbul = true
40
+ entryFilter: null,
41
+ unpackSourceMap: true,
42
+ sourceFilter: null,
43
+
45
44
  // (usually not used) source finder for Istanbul HTML report
46
45
  sourceFinder: null,
47
46
 
@@ -70,42 +69,51 @@ const saveReportAttachment = (testInfo, report, htmlDir) => {
70
69
 
71
70
  // ========================================================================================================
72
71
 
73
- const generateIstanbulReport = (istanbulCoverage, testInfo, options) => {
72
+ const generateIstanbulReport = (coverageData, testInfo, options) => {
74
73
 
75
74
  options = {
76
75
  ... defaultIstanbulOptions,
77
76
  ... options
78
77
  };
79
78
 
80
- const report = saveIstanbulReport(istanbulCoverage, options);
79
+ const report = saveIstanbulReport(coverageData, options.fileSources, options);
81
80
 
82
81
  saveReportAttachment(testInfo, report, options.htmlDir);
83
82
 
84
83
  return report;
85
84
  };
86
85
 
86
+
87
87
  // ========================================================================================================
88
88
 
89
- const toIstanbulReport = async (v8list, testInfo, options) => {
89
+ const generateV8Coverage = async (v8list, testInfo, options) => {
90
90
 
91
+ // v8list options, also for init / source map handler
91
92
  options = {
92
- ... defaultIstanbulOptions,
93
+ ... defaultV8Options,
93
94
  ... options
94
95
  };
95
96
 
96
- const report = await saveV8ToIstanbulReport(v8list, options);
97
+ // ================================================================
97
98
 
98
- saveReportAttachment(testInfo, report, options.htmlDir);
99
+ if (options.toIstanbul) {
100
+ options = {
101
+ ... defaultIstanbulOptions,
102
+ ... options
103
+ };
99
104
 
100
- return report;
101
- };
105
+ const { coverageData, fileSources } = await convertV8ToIstanbul(v8list, options);
102
106
 
103
- // ========================================================================================================
107
+ const report = await saveIstanbulReport(coverageData, fileSources, options);
104
108
 
109
+ saveReportAttachment(testInfo, report, options.htmlDir);
105
110
 
106
- const generateV8Report = async (v8list, testInfo, options) => {
111
+ return report;
112
+ }
107
113
 
108
- options.title = `Coverage Report - ${testInfo.title}`;
114
+ // ================================================================
115
+
116
+ v8list = await initV8List(v8list, options);
109
117
 
110
118
  const report = await saveV8Report(v8list, options);
111
119
 
@@ -114,31 +122,6 @@ const generateV8Report = async (v8list, testInfo, options) => {
114
122
  return report;
115
123
  };
116
124
 
117
- // ========================================================================================================
118
-
119
- const generateV8Coverage = async (v8list, testInfo, options) => {
120
-
121
- // v8list options, also for init / source map handler
122
- options = {
123
- ... defaultV8Options,
124
- ... options
125
- };
126
-
127
- v8list = await initV8List(v8list, options);
128
-
129
- if (options.toIstanbul) {
130
- return toIstanbulReport(v8list, testInfo, options);
131
- }
132
-
133
- return generateV8Report(v8list, testInfo, options);
134
- };
135
-
136
- /**
137
- * @parameters
138
- * (@input istanbul.__coverage__: Object, testInfo, options: { watermarks: {} }) -> @output istanbul report
139
- * (@input v8list: Array, testInfo, options: { toIstanbul: true, watermarks: {} }) -> @output istanbul report (without css supported)
140
- * (@input v8list: Array, testInfo, options: { watermarks: [], inline: Boolean }) -> @output v8 report (css supported and minified code formatting)
141
- */
142
125
  const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
143
126
 
144
127
  if (!coverageInput) {
@@ -147,6 +130,7 @@ const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
147
130
  }
148
131
 
149
132
  options = {
133
+ title: `Coverage Report - ${testInfo.title}`,
150
134
  outputDir: Util.resolveOutputDir(testInfo),
151
135
  outputName: `coverage-${Util.shortTestId(testInfo.testId)}`,
152
136
  ... options
@@ -169,10 +153,25 @@ const attachCoverageReport = (coverageInput, testInfo, options = {}) => {
169
153
 
170
154
  // ========================================================================================================
171
155
 
172
- // add coverage report to global
173
- const addCoverageReport = async (v8list, testInfo) => {
156
+ const generateArtifactData = (v8list, options) => {
157
+ options = {
158
+ ... defaultV8Options,
159
+ ... options
160
+ };
161
+ if (options.toIstanbul) {
162
+ options = {
163
+ ... defaultIstanbulOptions,
164
+ ... options
165
+ };
166
+ return convertV8ToIstanbul(v8list, options);
167
+ }
168
+ return initV8List(v8list, options);
169
+ };
170
+
171
+ // add coverage report to global, v8list only
172
+ const addCoverageReport = async (coverageInput, testInfo) => {
174
173
 
175
- if (!Util.isList(v8list)) {
174
+ if (!coverageInput) {
176
175
  EC.logRed('[MCR] invalid coverage input');
177
176
  return;
178
177
  }
@@ -188,33 +187,29 @@ const addCoverageReport = async (v8list, testInfo) => {
188
187
  // use reporter dir as output dir, NOT test output dir
189
188
  outputDir,
190
189
  outputName: 'coverage',
191
-
192
- ... defaultV8Options,
193
190
  ... coverageOptions
194
191
  };
195
192
 
196
- // The source map must be fetched before page closed
197
- // or it may be deleted
198
- v8list = await initV8List(v8list, options);
199
-
200
193
  const id = Util.shortTestId(testInfo.testId);
201
194
  const filename = `artifact-${id}.json`;
202
195
  const jsonDir = path.resolve(options.outputDir, options.outputName);
203
196
  const jsonPath = path.resolve(jsonDir, filename);
204
197
 
198
+ const data = await generateArtifactData(coverageInput, options);
199
+
200
+ // console.log('addCoverageReport keys', Object.keys(data.coverageData));
201
+
205
202
  const report = {
206
203
  id,
207
- title: testInfo.title,
208
204
  outputFile: Util.relativePath(jsonPath),
209
- // current v8 only
210
- type: 'v8',
211
- data: v8list
205
+ data
212
206
  };
213
207
 
214
208
  const artifactContent = JSON.stringify({
215
209
  type: 'coverage',
216
210
  data: report
217
211
  });
212
+
218
213
  await Util.writeFile(jsonPath, artifactContent);
219
214
 
220
215
  const definition = Util.attachments.artifact;
@@ -227,6 +222,34 @@ const addCoverageReport = async (v8list, testInfo) => {
227
222
  return report;
228
223
  };
229
224
 
225
+ // ========================================================================================================
226
+
227
+ const generateGlobalCoverageReport = async (dataList, options) => {
228
+
229
+ options = {
230
+ ... defaultV8Options,
231
+ ... options
232
+ };
233
+
234
+ if (options.toIstanbul) {
235
+ options = {
236
+ ... defaultIstanbulOptions,
237
+ ... options
238
+ };
239
+ const istanbulList = dataList.map((it) => it.data);
240
+ const { coverageData, fileSources } = mergeIstanbulList(istanbulList);
241
+ return saveIstanbulReport(coverageData, fileSources, options);
242
+ }
243
+
244
+ let v8list = [];
245
+ dataList.forEach((item) => {
246
+ v8list = v8list.concat(item.data);
247
+ });
248
+ // merge list again for multiple v8list, maybe collected multiple times
249
+ v8list = await mergeV8List(v8list, options);
250
+ return saveV8Report(v8list, options);
251
+ };
252
+
230
253
  // global coverage report, run different process with addCoverageReport
231
254
  const generateCoverageReport = async (dataList, reporterOptions) => {
232
255
 
@@ -236,6 +259,7 @@ const generateCoverageReport = async (dataList, reporterOptions) => {
236
259
 
237
260
  const coverageOptions = reporterOptions.coverage || {};
238
261
  const options = {
262
+ title: `Coverage Report - ${reporterOptions.name}`,
239
263
  outputDir,
240
264
  outputName: 'coverage',
241
265
  ... coverageOptions
@@ -249,21 +273,7 @@ const generateCoverageReport = async (dataList, reporterOptions) => {
249
273
  }
250
274
  options.htmlDir = htmlDir;
251
275
 
252
- let v8list = [];
253
- dataList.forEach((item) => {
254
- v8list = v8list.concat(item.data);
255
- });
256
-
257
- // merge list again for multiple v8list, maybe collected multiple times
258
- v8list = await mergeV8List(v8list, options);
259
-
260
- // const v8Path = path.resolve(__dirname, '../../.temp/v8-data.js');
261
- // const v8Data = `module.exports = ${JSON.stringify(v8list, null, 4)};`;
262
- // fs.writeFileSync(v8Path, v8Data);
263
-
264
- options.title = `Coverage Report - ${reporterOptions.name}`;
265
-
266
- const report = await saveV8Report(v8list, options);
276
+ const report = await generateGlobalCoverageReport(dataList, options);
267
277
 
268
278
  return {
269
279
  type: 'coverage',