monocart-reporter 2.6.5 → 2.7.1

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);
@@ -197,7 +197,9 @@ const generateReport = async (reportData, options) => {
197
197
  const g = report.global ? `${EC.magenta('(global)')}` : '';
198
198
  Util.logInfo(`${report.type}: ${EC.cyan(report.path)} ${report.name} ${g}`);
199
199
  // convert path to relative reporter
200
- report.path = Util.relativePath(report.path, outputDir);
200
+ if (report.path) {
201
+ report.path = Util.relativePath(report.path, outputDir);
202
+ }
201
203
  });
202
204
 
203
205
  reportData.summary.artifacts = {
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,26 +57,99 @@ 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) {
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
+ }
99
+ }
100
+ });
101
+ });
102
+ };
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
+ // merge global coverage
116
+ if (art.global && art.type === 'coverage') {
117
+
118
+ const rawDir = path.resolve(jsonDir, art.rawDir);
119
+ if (fs.existsSync(rawDir)) {
120
+ coverageRawList.push(rawDir);
121
+ }
67
122
  return;
123
+
68
124
  }
69
- attachment.path = newPath;
125
+
126
+ const targetDirName = path.dirname(art.path);
127
+ // copy all files in art dir, like network, audit
128
+ copyTarget(targetDirName, jsonDir, outputDir);
129
+ hasAssets = true;
130
+
131
+ // update path relative to cwd/root
132
+ art.path = Util.relativePath(path.resolve(outputDir, art.path));
133
+
134
+ artifacts.push(art);
135
+
70
136
  });
137
+
138
+ // copy assets dir
139
+ if (hasAssets) {
140
+ copyTarget('assets', jsonDir, outputDir);
141
+ }
142
+
71
143
  });
144
+
145
+ if (coverageRawList.length) {
146
+ const coverage = await mergeGlobalCoverageReport(coverageRawList, options);
147
+ if (coverage) {
148
+ artifacts.push(coverage);
149
+ }
150
+ }
151
+
152
+ return artifacts;
72
153
  };
73
154
 
74
155
  const mergeDataList = async (dataList, options) => {
@@ -78,11 +159,18 @@ const mergeDataList = async (dataList, options) => {
78
159
  const outputFile = await Util.resolveOutputFile(options.outputFile);
79
160
  // init outputDir
80
161
  const outputDir = path.dirname(outputFile);
81
- if (!fs.existsSync(outputDir)) {
82
- fs.mkdirSync(outputDir, {
83
- recursive: true
162
+ // clean
163
+ if (fs.existsSync(outputDir)) {
164
+ fs.rmSync(outputDir, {
165
+ recursive: true,
166
+ force: true,
167
+ maxRetries: 10
84
168
  });
85
169
  }
170
+
171
+ fs.mkdirSync(outputDir, {
172
+ recursive: true
173
+ });
86
174
  // for attachment path handler
87
175
  options.outputDir = outputDir;
88
176
 
@@ -93,10 +181,11 @@ const mergeDataList = async (dataList, options) => {
93
181
  const metadata = {};
94
182
  const system = [];
95
183
  const rows = [];
184
+ const artifactsList = [];
96
185
 
97
- dataList.forEach((item, i) => {
186
+ for (const item of dataList) {
98
187
 
99
- attachmentsHandler(item, attachmentPathHandler);
188
+ attachmentsHandler(item, outputDir, attachmentPathHandler);
100
189
 
101
190
  dates.push(item.date);
102
191
  endDates.push(item.date + item.duration);
@@ -117,7 +206,16 @@ const mergeDataList = async (dataList, options) => {
117
206
  subs: item.rows
118
207
  });
119
208
 
120
- });
209
+ if (item.artifacts) {
210
+ artifactsList.push({
211
+ jsonDir: item.jsonDir,
212
+ artifacts: item.artifacts
213
+ });
214
+ }
215
+
216
+ }
217
+
218
+ const artifacts = await mergeArtifacts(artifactsList, options);
121
219
 
122
220
  // base on first one
123
221
  const mergedData = {
@@ -158,7 +256,9 @@ const mergeDataList = async (dataList, options) => {
158
256
  system,
159
257
  trends,
160
258
 
161
- rows
259
+ rows,
260
+
261
+ artifacts
162
262
  });
163
263
 
164
264
  // tag style can be rewrite with new options tags
@@ -185,6 +285,7 @@ module.exports = async (reportDataList, userOptions = {}) => {
185
285
  };
186
286
 
187
287
  const reportData = await mergeDataList(dataList, options);
288
+ // console.log(reportData.artifacts);
188
289
 
189
290
  return generateReport(reportData, options);
190
291