monocart-reporter 2.9.3 → 2.9.5

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
@@ -1060,13 +1060,14 @@ see example [merge.js](https://github.com/cenfun/monocart-reporter-examples/blob
1060
1060
 
1061
1061
  ### Using `merge` CLI
1062
1062
  ```sh
1063
- npx monocart merge <glob-patterns>
1063
+ # NOTE: The asterisk(*) is a special character which is interpreted by some operating systems (Mac and Linux), please put it in quotes
1064
+ npx monocart merge '<glob-patterns>'
1064
1065
 
1065
1066
  # -o --outputFile
1066
- npx monocart merge path-to/shard*/index.json -o merged-reports/index.html
1067
+ npx monocart merge 'path-to/shard*/index.json' -o merged-reports/index.html
1067
1068
 
1068
1069
  # -c --config
1069
- npx monocart merge path-to/shard*/index.json -c mr.config.js
1070
+ npx monocart merge 'path-to/shard*/my-report.zip' -c mr.config.js
1070
1071
  ```
1071
1072
  The default config files (In order of priority)
1072
1073
  - mr.config.js
@@ -18,64 +18,85 @@ const generateJson = (outputDir, filename, reportData, options) => {
18
18
  reportData.jsonPath = Util.relativePath(jsonPath);
19
19
  };
20
20
 
21
- const forEachFile = (dir, callback) => {
22
- if (!fs.existsSync(dir)) {
23
- return;
21
+ const initZipOptions = (outputDir, filename, zipOptions) => {
22
+ let clean = false;
23
+ let outputFile;
24
+ if (typeof zipOptions === 'string') {
25
+ outputFile = zipOptions;
26
+ } else if (typeof zipOptions === 'object') {
27
+ if (zipOptions.outputFile) {
28
+ outputFile = zipOptions.outputFile;
29
+ }
30
+ if (zipOptions.clean) {
31
+ clean = true;
32
+ }
24
33
  }
25
- const dirs = [];
26
- fs.readdirSync(dir, {
27
- withFileTypes: true
28
- }).forEach((it) => {
29
34
 
30
- if (it.isFile()) {
31
- callback(it.name, dir);
32
- return;
35
+ if (outputFile) {
36
+ if (!outputFile.endsWith('.zip')) {
37
+ outputFile += '.zip';
33
38
  }
34
-
35
- if (it.isDirectory()) {
36
- dirs.push(path.resolve(dir, it.name));
39
+ const zipDir = path.dirname(outputFile);
40
+ if (!fs.existsSync(zipDir)) {
41
+ fs.mkdirSync(zipDir, {
42
+ recursive: true
43
+ });
37
44
  }
38
- });
39
45
 
40
- for (const subDir of dirs) {
41
- forEachFile(subDir, callback);
46
+ } else {
47
+ outputFile = path.resolve(outputDir, `${filename}.zip`);
42
48
  }
43
49
 
50
+
51
+ return {
52
+ outputFile,
53
+ clean
54
+ };
44
55
  };
45
56
 
46
57
  const generateZip = (outputDir, filename, reportData, options) => {
47
58
 
48
- if (!options.zip) {
59
+ const zipOptions = options.zip;
60
+ if (!zipOptions) {
49
61
  return;
50
62
  }
51
63
 
52
- let zipPath = path.resolve(outputDir, `${filename}.zip`);
53
- if (typeof options.zip === 'string') {
54
- zipPath = options.zip;
55
- if (!zipPath.endsWith('.zip')) {
56
- zipPath += '.zip';
57
- }
58
- const zipDir = path.dirname(zipPath);
59
- if (!fs.existsSync(zipDir)) {
60
- fs.mkdirSync(zipDir, {
61
- recursive: true
62
- });
63
- }
64
- }
64
+ const { outputFile, clean } = initZipOptions(outputDir, filename, zipOptions);
65
65
 
66
66
  const reportFiles = [];
67
67
  return new Promise((resolve) => {
68
68
  const zipFile = new ZipFile();
69
- zipFile.outputStream.pipe(fs.createWriteStream(zipPath)).on('close', function() {
69
+ zipFile.outputStream.pipe(fs.createWriteStream(outputFile)).on('close', function() {
70
70
 
71
- // remove files after zip
72
71
  reportData.reportFiles = reportFiles;
73
- reportData.zipPath = Util.relativePath(zipPath);
72
+
73
+ // whether to clean the files after zipped
74
+ if (clean) {
75
+ // console.log('clean', reportFiles);
76
+ const dirSet = new Set();
77
+ reportFiles.forEach((f) => {
78
+ const fp = path.resolve(outputDir, f);
79
+ dirSet.add(path.dirname(fp));
80
+ Util.rmSync(fp);
81
+ });
82
+
83
+ // clean empty dirs
84
+ const dirList = Array.from(dirSet).reverse();
85
+ dirList.forEach((dir) => {
86
+ const files = fs.readdirSync(dir);
87
+ if (files.length === 0) {
88
+ Util.rmSync(dir);
89
+ }
90
+ });
91
+
92
+ }
93
+
94
+ reportData.zipPath = Util.relativePath(outputFile);
74
95
 
75
96
  resolve();
76
97
  });
77
98
 
78
- forEachFile(outputDir, (name, dir) => {
99
+ Util.forEachFile(outputDir, (name, dir) => {
79
100
  const absPath = path.resolve(dir, name);
80
101
  const relPath = Util.relativePath(absPath, outputDir);
81
102
  reportFiles.push(relPath);
package/lib/index.d.ts CHANGED
@@ -61,7 +61,12 @@ export type MonocartReporterOptions = {
61
61
  * {boolean} using default path
62
62
  * {string} zip file path
63
63
  */
64
- zip?: boolean | string;
64
+ zip?: boolean | string | {
65
+ /** the zip file path */
66
+ outputFile?: string;
67
+ /** whether to clean the files after zipped */
68
+ clean?: boolean;
69
+ };
65
70
 
66
71
  /**
67
72
  * whether to copy attachments to the reporter output dir, defaults to true
package/lib/merge-data.js CHANGED
@@ -37,7 +37,31 @@ const unzipDataFile = async (item, num, options) => {
37
37
  await zip.close();
38
38
 
39
39
  const filename = path.basename(item, '.zip');
40
- return path.resolve(unzipDir, `${filename}.json`);
40
+ let jsonPath = path.resolve(unzipDir, `${filename}.json`);
41
+ if (fs.existsSync(jsonPath)) {
42
+ return jsonPath;
43
+ }
44
+
45
+ // filename for both html and json
46
+ const htmlMap = {};
47
+ const jsonMap = {};
48
+ Util.forEachFile(unzipDir, (name, dir) => {
49
+ if (name.endsWith('.html')) {
50
+ htmlMap[path.basename(name, '.html')] = true;
51
+ } else if (name.endsWith('.json')) {
52
+ jsonMap[path.basename(name, '.json')] = true;
53
+ }
54
+ }, true);
55
+
56
+ const keys = Object.keys(htmlMap);
57
+ for (const fn of keys) {
58
+ if (jsonMap[fn]) {
59
+ jsonPath = path.resolve(unzipDir, `${fn}.json`);
60
+ break;
61
+ }
62
+ }
63
+
64
+ return jsonPath;
41
65
  };
42
66
 
43
67
  const getReportData = async (item, num, options) => {
package/lib/utils/util.js CHANGED
@@ -277,6 +277,50 @@ const Util = {
277
277
  }
278
278
  },
279
279
 
280
+ // eslint-disable-next-line complexity
281
+ forEachFile: (dir, callback, shallow) => {
282
+ if (!fs.existsSync(dir)) {
283
+ return;
284
+ }
285
+
286
+ const isBreak = (res) => {
287
+ return res === 'break' || res === false;
288
+ };
289
+
290
+ const dirs = [];
291
+ const list = fs.readdirSync(dir, {
292
+ withFileTypes: true
293
+ });
294
+
295
+ for (const item of list) {
296
+
297
+ if (item.isFile()) {
298
+ const res = callback(item.name, dir);
299
+ if (isBreak(res)) {
300
+ return res;
301
+ }
302
+ continue;
303
+ }
304
+
305
+ if (item.isDirectory()) {
306
+ dirs.push(path.resolve(dir, item.name));
307
+ }
308
+
309
+ }
310
+
311
+ if (shallow) {
312
+ return;
313
+ }
314
+
315
+ for (const subDir of dirs) {
316
+ const res = Util.forEachFile(subDir, callback, shallow);
317
+ if (isBreak(res)) {
318
+ return res;
319
+ }
320
+ }
321
+
322
+ },
323
+
280
324
  getTemplate: function(templatePath) {
281
325
  if (!Util.templateCache) {
282
326
  Util.templateCache = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monocart-reporter",
3
- "version": "2.9.3",
3
+ "version": "2.9.5",
4
4
  "description": "A playwright test reporter. Shows suites/cases/steps with tree style, markdown annotations, custom columns/formatters/data collection visitors, console logs, style tags, send email.",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@babel/code-frame": "^7.25.7",
56
- "@playwright/test": "^1.47.2",
56
+ "@playwright/test": "^1.48.0",
57
57
  "ansi-to-html": "^0.7.2",
58
58
  "async-tick": "^1.0.0",
59
59
  "autolinker": "^4.0.0",