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 +4 -11
- package/lib/default/options.js +1 -1
- package/lib/default/template.html +36 -1
- package/lib/generate-report.js +26 -29
- package/lib/index.js +1 -1
- package/lib/merge-data.js +1 -1
- package/lib/platform/share.js +0 -24
- package/lib/plugins/comments.js +8 -4
- package/lib/plugins/coverage/coverage-utils.js +154 -0
- package/lib/plugins/coverage/coverage.js +86 -76
- package/lib/plugins/coverage/istanbul/istanbul.js +143 -24
- package/lib/plugins/coverage/v8/dedupe.js +64 -59
- package/lib/plugins/coverage/v8/position-mapping.js +49 -35
- package/lib/plugins/coverage/v8/source-map.js +218 -245
- package/lib/plugins/coverage/v8/v8-summary.js +9 -18
- package/lib/plugins/coverage/v8/v8.js +73 -100
- package/lib/plugins/network/network.js +19 -5
- package/lib/runtime/monocart-code-viewer.js +1 -1
- package/lib/runtime/monocart-common.js +1 -0
- package/lib/runtime/monocart-coverage.js +11 -11
- package/lib/runtime/monocart-formatter.js +1 -1
- package/lib/runtime/monocart-network.js +1 -1
- package/lib/runtime/monocart-reporter.js +1 -1
- package/lib/runtime/monocart-v8.js +1 -1
- package/lib/runtime/monocart-vendor.js +6 -6
- package/lib/utils/util.js +19 -7
- package/package.json +9 -9
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
|
|
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
|
package/lib/default/options.js
CHANGED
|
@@ -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
|
|
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>
|
package/lib/generate-report.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
28
|
+
outputDir,
|
|
29
|
+
reportDataFile: 'report-data.js',
|
|
30
|
+
assetsName: 'assets',
|
|
31
|
+
assetsRelative: ''
|
|
32
|
+
};
|
|
40
33
|
|
|
41
|
-
|
|
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,
|
|
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
|
|
155
|
+
const htmlFile = path.basename(outputFile);
|
|
156
|
+
|
|
165
157
|
// generate json
|
|
166
|
-
const jsonPath = generateJson(outputDir,
|
|
158
|
+
const jsonPath = await generateJson(outputDir, htmlFile, reportData);
|
|
159
|
+
|
|
167
160
|
// generate html
|
|
168
|
-
|
|
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
package/lib/merge-data.js
CHANGED
package/lib/platform/share.js
CHANGED
|
@@ -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;
|
package/lib/plugins/comments.js
CHANGED
|
@@ -6,10 +6,14 @@ const Util = require('../utils/util.js');
|
|
|
6
6
|
const cacheMap = new Map();
|
|
7
7
|
|
|
8
8
|
function getEmptyLines(lines) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
+
};
|