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 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. You may need to transfer the attachments by yourself and update the path of the attachments with `attachmentPath` API.
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
- // or JSON data
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
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
@@ -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.mermaid;
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 (Util.isList(item.rows) && Util.isList(item.columns) && item.summary) {
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 attachmentsHandler = (data, attachmentPathHandler) => {
53
- if (!attachmentPathHandler) {
54
- return;
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
- const extras = Util.getAttachmentPathExtras(data);
57
- Util.forEach(data.rows, (item) => {
58
- if (item.type !== 'case' || !item.attachments) {
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
- item.attachments.forEach((attachment) => {
83
+ row.attachments.forEach((attachment) => {
62
84
  if (!attachment.path) {
63
85
  return;
64
86
  }
65
- const newPath = attachmentPathHandler(attachment.path, extras);
66
- if (!newPath) {
67
- return;
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
- if (!fs.existsSync(outputDir)) {
82
- fs.mkdirSync(outputDir, {
83
- recursive: true
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
- dataList.forEach((item, i) => {
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