monocart-reporter 2.6.5 → 2.7.0
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 +14 -7
- package/lib/cli.js +1 -1
- package/lib/generate-data.js +32 -1
- package/lib/merge-data.js +122 -19
- package/lib/packages/monocart-reporter-assets.js +1 -1
- package/lib/plugins/coverage/coverage.js +24 -1
- package/lib/visitor.js +48 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1087,24 +1087,20 @@ npx playwright test --shard=2/3
|
|
|
1087
1087
|
npx playwright test --shard=3/3
|
|
1088
1088
|
```
|
|
1089
1089
|
There are 3 reports will be generated. Using `merge(reportDataList, options)` API to merge all reports into one.
|
|
1090
|
-
> Note: One more suite level "shard" will be added, its title will be the machine hostname, and the summary will be restated.
|
|
1090
|
+
> Note: One more suite level "shard" will be added, its title will be the machine hostname, and the summary will be restated. All attachments will be copied to the merged output directory.
|
|
1091
1091
|
```js
|
|
1092
1092
|
import { merge } from 'monocart-reporter';
|
|
1093
1093
|
|
|
1094
|
+
// json file path
|
|
1094
1095
|
const reportDataList = [
|
|
1095
|
-
// json file path
|
|
1096
1096
|
'path-to/shard1/index.json',
|
|
1097
1097
|
'path-to/shard2/index.json',
|
|
1098
|
-
|
|
1099
|
-
JSON.parse(fs.readFileSync(path.resolve('path-to/shard3/index.json')))
|
|
1098
|
+
'path-to/shard3/index.json'
|
|
1100
1099
|
];
|
|
1101
1100
|
|
|
1102
1101
|
await merge(reportDataList, {
|
|
1103
1102
|
name: 'My Merged Report',
|
|
1104
1103
|
outputFile: 'merged-report/index.html',
|
|
1105
|
-
attachmentPath: (currentPath, extras) => {
|
|
1106
|
-
// return `https://cenfun.github.io/monocart-reporter/${currentPath}`;
|
|
1107
|
-
},
|
|
1108
1104
|
onEnd: async (reportData, helper) => {
|
|
1109
1105
|
// send email or third party integration
|
|
1110
1106
|
}
|
|
@@ -1112,6 +1108,17 @@ await merge(reportDataList, {
|
|
|
1112
1108
|
```
|
|
1113
1109
|
see example [merge.js](https://github.com/cenfun/monocart-reporter-examples/blob/main/scripts/merge.js)
|
|
1114
1110
|
|
|
1111
|
+
> Note: The coverage reports will be merged automatically if we specify the `raw` report in coverage options:
|
|
1112
|
+
```js
|
|
1113
|
+
// global coverage options
|
|
1114
|
+
coverage: {
|
|
1115
|
+
reports: [
|
|
1116
|
+
// for merging coverage reports
|
|
1117
|
+
'raw'
|
|
1118
|
+
]
|
|
1119
|
+
}
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1115
1122
|
## onEnd Hook
|
|
1116
1123
|
The `onEnd` function will be executed after report generated. Arguments:
|
|
1117
1124
|
- `reportData` all report data, properties:
|
package/lib/cli.js
CHANGED
package/lib/generate-data.js
CHANGED
|
@@ -33,6 +33,37 @@ const getReportDate = (timestampStart, timezoneOffset = 0) => {
|
|
|
33
33
|
return timestampStart + offset * 60 * 1000;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
const generateMermaid = (options) => {
|
|
37
|
+
const mermaidOptions = options.mermaid;
|
|
38
|
+
if (mermaidOptions) {
|
|
39
|
+
const scriptSrc = mermaidOptions.scriptSrc;
|
|
40
|
+
if (!scriptSrc) {
|
|
41
|
+
// try to copy and load from local
|
|
42
|
+
let mp;
|
|
43
|
+
try {
|
|
44
|
+
mp = require.resolve('mermaid');
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// ignore error
|
|
47
|
+
}
|
|
48
|
+
// console.log(mp);
|
|
49
|
+
if (mp) {
|
|
50
|
+
const filename = 'mermaid.min.js';
|
|
51
|
+
const filePath = path.resolve(path.dirname(mp), filename);
|
|
52
|
+
if (fs.existsSync(filePath)) {
|
|
53
|
+
const assetPath = path.resolve(options.outputDir, 'assets', filename);
|
|
54
|
+
fs.cpSync(filePath, assetPath, {
|
|
55
|
+
recursive: true,
|
|
56
|
+
force: true
|
|
57
|
+
});
|
|
58
|
+
mermaidOptions.scriptSrc = `assets/${filename}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// console.log(mermaidOptions);
|
|
64
|
+
return mermaidOptions;
|
|
65
|
+
};
|
|
66
|
+
|
|
36
67
|
const generateData = async (results) => {
|
|
37
68
|
|
|
38
69
|
Util.logInfo('generating report data ...');
|
|
@@ -78,7 +109,7 @@ const generateData = async (results) => {
|
|
|
78
109
|
data.suiteTypes = ['project', 'file', 'describe', 'shard'];
|
|
79
110
|
data.caseTypes = ['passed', 'flaky', 'skipped', 'failed'];
|
|
80
111
|
data.traceViewerUrl = options.traceViewerUrl;
|
|
81
|
-
data.mermaid = options
|
|
112
|
+
data.mermaid = generateMermaid(options);
|
|
82
113
|
data.groupOptions = options.groupOptions;
|
|
83
114
|
|
|
84
115
|
calculateSummary(data, options);
|
package/lib/merge-data.js
CHANGED
|
@@ -4,10 +4,11 @@ const Util = require('./utils/util.js');
|
|
|
4
4
|
const generateReport = require('./generate-report.js');
|
|
5
5
|
const getDefaultOptions = require('./default/options.js');
|
|
6
6
|
const { calculateSummary, getTrends } = require('./common.js');
|
|
7
|
+
const { mergeGlobalCoverageReport } = require('./plugins/coverage/coverage.js');
|
|
7
8
|
|
|
8
9
|
const checkReportData = (item) => {
|
|
9
10
|
if (item && typeof item === 'object') {
|
|
10
|
-
if (
|
|
11
|
+
if (item.rows && item.columns && item.summary) {
|
|
11
12
|
return true;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -30,6 +31,10 @@ const initDataList = (reportDataList) => {
|
|
|
30
31
|
Util.logError(`failed to read report data file: ${item}`);
|
|
31
32
|
continue;
|
|
32
33
|
}
|
|
34
|
+
|
|
35
|
+
// for finding attachments
|
|
36
|
+
data.jsonDir = path.dirname(item);
|
|
37
|
+
|
|
33
38
|
list.push(data);
|
|
34
39
|
Util.logInfo(`report data loaded: ${item}`);
|
|
35
40
|
continue;
|
|
@@ -39,6 +44,9 @@ const initDataList = (reportDataList) => {
|
|
|
39
44
|
Util.logError(`unmatched report data format: ${item}`);
|
|
40
45
|
return;
|
|
41
46
|
}
|
|
47
|
+
|
|
48
|
+
item.jsonDir = item.outputDir;
|
|
49
|
+
|
|
42
50
|
list.push(item);
|
|
43
51
|
}
|
|
44
52
|
|
|
@@ -49,28 +57,103 @@ const initDataList = (reportDataList) => {
|
|
|
49
57
|
return list;
|
|
50
58
|
};
|
|
51
59
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
const copyTarget = (targetPath, fromDir, toDir) => {
|
|
61
|
+
const oldPath = path.resolve(fromDir, targetPath);
|
|
62
|
+
if (fs.existsSync(oldPath)) {
|
|
63
|
+
const newPath = path.resolve(toDir, targetPath);
|
|
64
|
+
if (oldPath !== newPath) {
|
|
65
|
+
fs.cpSync(oldPath, newPath, {
|
|
66
|
+
force: true,
|
|
67
|
+
recursive: true
|
|
68
|
+
});
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
55
71
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const attachmentsHandler = (item, outputDir, attachmentPathHandler) => {
|
|
75
|
+
|
|
76
|
+
const jsonDir = item.jsonDir;
|
|
77
|
+
|
|
78
|
+
const extras = Util.getAttachmentPathExtras(item);
|
|
79
|
+
Util.forEach(item.rows, (row) => {
|
|
80
|
+
if (row.type !== 'case' || !row.attachments) {
|
|
59
81
|
return;
|
|
60
82
|
}
|
|
61
|
-
|
|
83
|
+
row.attachments.forEach((attachment) => {
|
|
62
84
|
if (!attachment.path) {
|
|
63
85
|
return;
|
|
64
86
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
87
|
+
|
|
88
|
+
// copy attachment
|
|
89
|
+
const done = copyTarget(attachment.path, jsonDir, outputDir);
|
|
90
|
+
if (!done) {
|
|
91
|
+
Util.logError(`failed to copy: ${attachment.path}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (attachmentPathHandler) {
|
|
95
|
+
const newPath = attachmentPathHandler(attachment.path, extras);
|
|
96
|
+
if (newPath) {
|
|
97
|
+
attachment.path = newPath;
|
|
98
|
+
}
|
|
68
99
|
}
|
|
69
|
-
attachment.path = newPath;
|
|
70
100
|
});
|
|
71
101
|
});
|
|
72
102
|
};
|
|
73
103
|
|
|
104
|
+
const mergeArtifacts = async (artifactsList, options) => {
|
|
105
|
+
|
|
106
|
+
const outputDir = options.outputDir;
|
|
107
|
+
|
|
108
|
+
const artifacts = [];
|
|
109
|
+
const coverageRawList = [];
|
|
110
|
+
artifactsList.forEach((item) => {
|
|
111
|
+
const jsonDir = item.jsonDir;
|
|
112
|
+
let hasAssets = false;
|
|
113
|
+
item.artifacts.forEach((art) => {
|
|
114
|
+
|
|
115
|
+
const targetDirName = path.dirname(art.path);
|
|
116
|
+
// console.log(targetDir);
|
|
117
|
+
|
|
118
|
+
// merge global coverage
|
|
119
|
+
if (art.global && art.type === 'coverage') {
|
|
120
|
+
|
|
121
|
+
const rawDir = path.resolve(jsonDir, targetDirName, 'raw');
|
|
122
|
+
if (fs.existsSync(rawDir)) {
|
|
123
|
+
coverageRawList.push(rawDir);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// copy all files in art dir, like network, audit
|
|
130
|
+
copyTarget(targetDirName, jsonDir, outputDir);
|
|
131
|
+
hasAssets = true;
|
|
132
|
+
|
|
133
|
+
// update path relative to cwd/root
|
|
134
|
+
art.path = Util.relativePath(path.resolve(outputDir, art.path));
|
|
135
|
+
|
|
136
|
+
artifacts.push(art);
|
|
137
|
+
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// copy assets dir
|
|
141
|
+
if (hasAssets) {
|
|
142
|
+
copyTarget('assets', jsonDir, outputDir);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (coverageRawList.length) {
|
|
148
|
+
const coverage = await mergeGlobalCoverageReport(coverageRawList, options);
|
|
149
|
+
if (coverage) {
|
|
150
|
+
artifacts.push(coverage);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return artifacts;
|
|
155
|
+
};
|
|
156
|
+
|
|
74
157
|
const mergeDataList = async (dataList, options) => {
|
|
75
158
|
|
|
76
159
|
const trends = await getTrends(options.trend);
|
|
@@ -78,11 +161,18 @@ const mergeDataList = async (dataList, options) => {
|
|
|
78
161
|
const outputFile = await Util.resolveOutputFile(options.outputFile);
|
|
79
162
|
// init outputDir
|
|
80
163
|
const outputDir = path.dirname(outputFile);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
164
|
+
// clean
|
|
165
|
+
if (fs.existsSync(outputDir)) {
|
|
166
|
+
fs.rmSync(outputDir, {
|
|
167
|
+
recursive: true,
|
|
168
|
+
force: true,
|
|
169
|
+
maxRetries: 10
|
|
84
170
|
});
|
|
85
171
|
}
|
|
172
|
+
|
|
173
|
+
fs.mkdirSync(outputDir, {
|
|
174
|
+
recursive: true
|
|
175
|
+
});
|
|
86
176
|
// for attachment path handler
|
|
87
177
|
options.outputDir = outputDir;
|
|
88
178
|
|
|
@@ -93,10 +183,11 @@ const mergeDataList = async (dataList, options) => {
|
|
|
93
183
|
const metadata = {};
|
|
94
184
|
const system = [];
|
|
95
185
|
const rows = [];
|
|
186
|
+
const artifactsList = [];
|
|
96
187
|
|
|
97
|
-
|
|
188
|
+
for (const item of dataList) {
|
|
98
189
|
|
|
99
|
-
attachmentsHandler(item, attachmentPathHandler);
|
|
190
|
+
attachmentsHandler(item, outputDir, attachmentPathHandler);
|
|
100
191
|
|
|
101
192
|
dates.push(item.date);
|
|
102
193
|
endDates.push(item.date + item.duration);
|
|
@@ -117,7 +208,16 @@ const mergeDataList = async (dataList, options) => {
|
|
|
117
208
|
subs: item.rows
|
|
118
209
|
});
|
|
119
210
|
|
|
120
|
-
|
|
211
|
+
if (item.artifacts) {
|
|
212
|
+
artifactsList.push({
|
|
213
|
+
jsonDir: item.jsonDir,
|
|
214
|
+
artifacts: item.artifacts
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const artifacts = await mergeArtifacts(artifactsList, options);
|
|
121
221
|
|
|
122
222
|
// base on first one
|
|
123
223
|
const mergedData = {
|
|
@@ -158,7 +258,9 @@ const mergeDataList = async (dataList, options) => {
|
|
|
158
258
|
system,
|
|
159
259
|
trends,
|
|
160
260
|
|
|
161
|
-
rows
|
|
261
|
+
rows,
|
|
262
|
+
|
|
263
|
+
artifacts
|
|
162
264
|
});
|
|
163
265
|
|
|
164
266
|
// tag style can be rewrite with new options tags
|
|
@@ -185,6 +287,7 @@ module.exports = async (reportDataList, userOptions = {}) => {
|
|
|
185
287
|
};
|
|
186
288
|
|
|
187
289
|
const reportData = await mergeDataList(dataList, options);
|
|
290
|
+
// console.log(reportData.artifacts);
|
|
188
291
|
|
|
189
292
|
return generateReport(reportData, options);
|
|
190
293
|
|