monocart-reporter 1.6.25 → 1.6.27

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
  ]
@@ -992,18 +992,11 @@ Please create an [Incoming Webhooks](https://learn.microsoft.com/en-us/microsoft
992
992
  - [weixin-webhook.js](/tests/common/weixin-webhook.js)
993
993
  - [feishu-webhook.js](/tests/common/feishu-webhook.js)
994
994
 
995
- ## Dependencies and Packages
995
+ ## Dependencies
996
996
  - UI Framework [Vue 3](https://github.com/vuejs/core)
997
997
  - Lightweight UI Components [vine-ui](https://github.com/cenfun/vine-ui)
998
998
  - High Performance Grid [turbogrid](https://github.com/cenfun/turbogrid)
999
999
  - String compress/decompress [lz-utils](https://github.com/cenfun/lz-utils)
1000
- - Packages
1001
- - `packages/app` Monocart report UI
1002
- - `packages/coverage` Coverage report libs
1003
- - `packages/network` Network HAR report libs
1004
- - `packages/v8` V8 HTML report UI
1005
- - `packages/vendor` Third-party libs
1006
-
1007
1000
 
1008
1001
  ## Contributing
1009
1002
  ```sh
@@ -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>
@@ -1,15 +1,14 @@
1
- const fs = require('fs');
2
1
  const path = require('path');
3
2
  const EC = require('eight-colors');
4
3
  const CG = require('console-grid');
5
- const { deflateSync } = require('lz-utils');
6
4
  const { nodemailer } = require('./runtime/monocart-vendor.js');
7
5
  const Util = require('./utils/util.js');
8
6
  const emailPlugin = require('./plugins/email.js');
9
7
 
10
8
  // ===========================================================================
11
9
 
12
- const generateJson = (outputDir, filename, reportData) => {
10
+ const generateJson = (outputDir, htmlFile, reportData) => {
11
+ const filename = path.basename(htmlFile, '.html');
13
12
  let jsonPath = path.resolve(outputDir, `${filename}.json`);
14
13
  Util.writeJSONSync(jsonPath, reportData);
15
14
  jsonPath = Util.relativePath(jsonPath);
@@ -17,30 +16,22 @@ const generateJson = (outputDir, filename, reportData) => {
17
16
  return jsonPath;
18
17
  };
19
18
 
20
- const generateHtml = (outputDir, filename, reportData) => {
21
- const reportDistPath = path.resolve(__dirname, 'runtime/monocart-reporter.js');
22
- const errMsg = `Not found runtime lib: ${Util.formatPath(reportDistPath)}`;
19
+ const generateHtml = async (outputDir, htmlFile, reportData, inline) => {
23
20
 
24
- let reportJs = `console.error("${errMsg}");`;
25
- if (fs.existsSync(reportDistPath)) {
26
- reportJs = Util.readFileSync(reportDistPath);
27
- } else {
28
- EC.logRed(errMsg);
29
- }
30
-
31
- const reportDataStr = deflateSync(JSON.stringify(reportData));
32
- const content = `<script>\nwindow.reportData = '${reportDataStr}';\n${reportJs}\n</script>`;
21
+ const options = {
22
+ inline,
23
+ reportData,
24
+ jsFiles: ['monocart-common.js', 'monocart-reporter.js'],
25
+ htmlDir: outputDir,
26
+ htmlFile,
33
27
 
34
- // replace template
35
- let html = Util.getTemplate(path.resolve(__dirname, 'default/template.html'));
36
- html = Util.replace(html, {
37
- title: reportData.name,
38
- content: content
39
- });
28
+ outputDir,
29
+ reportDataFile: 'report-data.js',
30
+ assetsName: 'assets',
31
+ assetsRelative: ''
32
+ };
40
33
 
41
- let htmlPath = path.resolve(outputDir, `${filename}.html`);
42
- Util.writeFileSync(htmlPath, html);
43
- htmlPath = Util.relativePath(htmlPath);
34
+ const htmlPath = await Util.saveHtmlReport(options);
44
35
  console.log(`[MCR] html report: ${EC.cyan(htmlPath)}`);
45
36
 
46
37
  const cmd = `npx monocart show-report ${htmlPath}`;
@@ -141,7 +132,7 @@ const showTestResults = (reportData) => {
141
132
  };
142
133
 
143
134
 
144
- const generateReport = (reportData, onEnd) => {
135
+ const generateReport = async (reportData, options) => {
145
136
 
146
137
  console.log('[MCR] generating test report ...');
147
138
  showTestResults(reportData);
@@ -161,20 +152,26 @@ const generateReport = (reportData, onEnd) => {
161
152
  }
162
153
 
163
154
  // console.log(reportData);
164
- const filename = path.basename(outputFile, '.html');
155
+ const htmlFile = path.basename(outputFile);
156
+
165
157
  // generate json
166
- const jsonPath = generateJson(outputDir, filename, reportData);
158
+ const jsonPath = await generateJson(outputDir, htmlFile, reportData);
159
+
167
160
  // generate html
168
- const htmlPath = generateHtml(outputDir, filename, reportData);
161
+ let inline = true;
162
+ if (typeof options.inline === 'boolean') {
163
+ inline = options.inline;
164
+ }
165
+ const htmlPath = await generateHtml(outputDir, htmlFile, reportData, inline);
169
166
 
170
167
  // onEnd callback
168
+ const onEnd = options.onEnd;
171
169
  if (typeof onEnd !== 'function') {
172
170
  return;
173
171
  }
174
172
 
175
173
  // for onEnd after saved
176
174
  Object.assign(reportData, {
177
- filename,
178
175
  htmlPath,
179
176
  jsonPath
180
177
  });
package/lib/index.js CHANGED
@@ -131,7 +131,7 @@ class Reporter {
131
131
  trends: this.trends
132
132
  });
133
133
 
134
- return generateReport(reportData, this.options.onEnd);
134
+ return generateReport(reportData, this.options);
135
135
  }
136
136
 
137
137
  }
package/lib/merge-data.js CHANGED
@@ -178,6 +178,6 @@ module.exports = async (reportDataList, userOptions = {}) => {
178
178
 
179
179
  const reportData = await mergeDataList(dataList, options);
180
180
 
181
- return generateReport(reportData, options.onEnd);
181
+ return generateReport(reportData, options);
182
182
 
183
183
  };
@@ -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
+ };