@arghajit/dummy 0.3.15 โ†’ 0.3.16

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arghajit/dummy",
3
3
  "author": "Arghajit Singha",
4
- "version": "0.3.15",
4
+ "version": "0.3.16",
5
5
  "description": "A Playwright reporter and dashboard for visualizing test results.",
6
6
  "homepage": "https://arghajit47.github.io/playwright-pulse/",
7
7
  "repository": {
File without changes
File without changes
File without changes
File without changes
@@ -5,6 +5,7 @@ const path = require("path");
5
5
 
6
6
  const args = process.argv.slice(2);
7
7
  let customOutputDir = null;
8
+
8
9
  for (let i = 0; i < args.length; i++) {
9
10
  if (args[i] === '--outputDir' || args[i] === '-o') {
10
11
  customOutputDir = args[i + 1];
@@ -16,17 +17,13 @@ const OUTPUT_FILE = "playwright-pulse-report.json";
16
17
 
17
18
  /**
18
19
  * Securely resolves the report directory.
19
- * Prevents Path Traversal by ensuring the output directory
20
+ * Prevents Path Traversal by ensuring the output directory
20
21
  * is contained within the current working directory.
21
22
  */
22
23
  async function getReportDir() {
23
24
  if (customOutputDir) {
24
- // 1. Resolve the absolute path
25
25
  const resolvedPath = path.resolve(process.cwd(), customOutputDir);
26
26
 
27
- // 2. Security Check: Prevent Path Traversal
28
- // We ensure the resolved path starts with the project root (process.cwd())
29
- // This blocks inputs like "../../" that try to escape the project.
30
27
  if (!resolvedPath.startsWith(process.cwd())) {
31
28
  console.error(
32
29
  "โ›” Security Error: Custom output directory must be within the current project root.",
@@ -45,21 +42,26 @@ async function getReportDir() {
45
42
  }
46
43
  }
47
44
 
48
- function getReportFiles(dir) {
49
- // Security Note: 'dir' here is now guaranteed to be safe/sanitized by getReportDir
45
+ /**
46
+ * Scans the report directory for subdirectories (shards).
47
+ * Returns an array of absolute paths to these subdirectories.
48
+ * Excludes the 'attachments' folder itself.
49
+ */
50
+ function getShardDirectories(dir) {
50
51
  if (!fs.existsSync(dir)) {
51
52
  return [];
52
53
  }
53
54
 
54
55
  return fs
55
- .readdirSync(dir)
56
- .filter(
57
- (file) =>
58
- file.startsWith("playwright-pulse-report-") && file.endsWith(".json"),
59
- );
56
+ .readdirSync(dir, { withFileTypes: true })
57
+ .filter((dirent) => dirent.isDirectory() && dirent.name !== "attachments")
58
+ .map((dirent) => path.join(dir, dirent.name));
60
59
  }
61
60
 
62
- function mergeReports(files, reportDir) {
61
+ /**
62
+ * Merges JSON reports from all shard directories.
63
+ */
64
+ function mergeReports(shardDirs) {
63
65
  let combinedRun = {
64
66
  totalTests: 0,
65
67
  passed: 0,
@@ -69,16 +71,19 @@ function mergeReports(files, reportDir) {
69
71
  };
70
72
 
71
73
  let combinedResults = [];
72
-
73
74
  let latestTimestamp = "";
74
75
  let latestGeneratedAt = "";
75
76
 
76
- for (const file of files) {
77
- // Security Note: 'file' comes from readdirSync (safe), reportDir is sanitized.
78
- const filePath = path.join(reportDir, file);
77
+ for (const shardDir of shardDirs) {
78
+ const jsonPath = path.join(shardDir, OUTPUT_FILE);
79
+
80
+ if (!fs.existsSync(jsonPath)) {
81
+ console.warn(` Warning: No ${OUTPUT_FILE} found in ${path.basename(shardDir)}`);
82
+ continue;
83
+ }
79
84
 
80
85
  try {
81
- const fileContent = fs.readFileSync(filePath, "utf-8");
86
+ const fileContent = fs.readFileSync(jsonPath, "utf-8");
82
87
  const json = JSON.parse(fileContent);
83
88
 
84
89
  const run = json.run || {};
@@ -87,7 +92,10 @@ function mergeReports(files, reportDir) {
87
92
  combinedRun.failed += run.failed || 0;
88
93
  combinedRun.skipped += run.skipped || 0;
89
94
  combinedRun.duration += run.duration || 0;
90
- combinedRun.environment = run.environment;
95
+
96
+ if (run.environment) {
97
+ combinedRun.environment = run.environment;
98
+ }
91
99
 
92
100
  if (json.results) {
93
101
  combinedResults.push(...json.results);
@@ -97,7 +105,9 @@ function mergeReports(files, reportDir) {
97
105
  if (json.metadata?.generatedAt > latestGeneratedAt)
98
106
  latestGeneratedAt = json.metadata.generatedAt;
99
107
  } catch (e) {
100
- console.warn(`Warning: Failed to process ${file}: ${e.message}`);
108
+ console.warn(
109
+ ` Warning: Failed to process JSON in ${path.basename(shardDir)}: ${e.message}`,
110
+ );
101
111
  }
102
112
  }
103
113
 
@@ -116,30 +126,104 @@ function mergeReports(files, reportDir) {
116
126
  return finalJson;
117
127
  }
118
128
 
129
+ /**
130
+ * Copies attachments from all shard directories to the main attachments folder.
131
+ */
132
+ function mergeAttachments(shardDirs, outputDir) {
133
+ const globalAttachmentsDir = path.join(outputDir, "attachments");
134
+
135
+ for (const shardDir of shardDirs) {
136
+ const shardAttachmentsDir = path.join(shardDir, "attachments");
137
+
138
+ if (!fs.existsSync(shardAttachmentsDir)) {
139
+ continue;
140
+ }
141
+
142
+ try {
143
+ if (!fs.existsSync(globalAttachmentsDir)) {
144
+ fs.mkdirSync(globalAttachmentsDir, { recursive: true });
145
+ }
146
+
147
+ // Recursively copy contents from shard attachments to global attachments
148
+ fs.cpSync(shardAttachmentsDir, globalAttachmentsDir, {
149
+ recursive: true,
150
+ });
151
+ } catch (e) {
152
+ console.warn(
153
+ ` Warning: Failed to copy attachments from ${path.basename(shardDir)}: ${e.message}`,
154
+ );
155
+ }
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Cleans up shard directories after merging.
161
+ */
162
+ function cleanupShardDirectories(shardDirs) {
163
+ console.log("\n๐Ÿงน Cleaning up shard directories...");
164
+ for (const shardDir of shardDirs) {
165
+ try {
166
+ fs.rmSync(shardDir, { recursive: true, force: true });
167
+ } catch (e) {
168
+ console.warn(
169
+ ` Warning: Could not delete ${path.basename(shardDir)}: ${e.message}`,
170
+ );
171
+ }
172
+ }
173
+ console.log("โœจ Cleanup complete.");
174
+ }
175
+
119
176
  // Main execution
120
177
  (async () => {
121
178
  const REPORT_DIR = await getReportDir();
122
179
 
123
- console.log(`Report directory set to: ${REPORT_DIR}`);
180
+ console.log(`\n๐Ÿ”„ Playwright Pulse - Merge Reports (Sharding Mode)\n`);
181
+ console.log(` Report directory: ${REPORT_DIR}`);
124
182
  if (customOutputDir) {
125
183
  console.log(` (from CLI argument)`);
126
184
  } else {
127
185
  console.log(` (auto-detected from playwright.config or using default)`);
128
186
  }
187
+ console.log();
129
188
 
130
- const reportFiles = getReportFiles(REPORT_DIR);
189
+ // 1. Get Shard Directories
190
+ const shardDirs = getShardDirectories(REPORT_DIR);
131
191
 
132
- if (reportFiles.length === 0) {
133
- console.log("No matching JSON report files found.");
134
- process.exit(1);
192
+ if (shardDirs.length === 0) {
193
+ console.log("โŒ No shard directories found.");
194
+ console.log(
195
+ " Expected structure: <report-dir>/<shard-folder>/playwright-pulse-report.json",
196
+ );
197
+ process.exit(0);
135
198
  }
136
199
 
137
- const merged = mergeReports(reportFiles, REPORT_DIR);
200
+ console.log(`๐Ÿ“‚ Found ${shardDirs.length} shard director${shardDirs.length === 1 ? 'y' : 'ies'}:`);
201
+ shardDirs.forEach((dir) => {
202
+ console.log(` - ${path.basename(dir)}`);
203
+ });
204
+ console.log();
205
+
206
+ // 2. Merge JSON Reports
207
+ console.log(`๐Ÿ”€ Merging reports...`);
208
+ const merged = mergeReports(shardDirs);
209
+ console.log(` โœ“ Merged ${shardDirs.length} report(s)`);
210
+ console.log();
211
+
212
+ // 3. Copy Attachments
213
+ console.log(`๐Ÿ“Ž Merging attachments...`);
214
+ mergeAttachments(shardDirs, REPORT_DIR);
215
+ console.log(` โœ“ Attachments merged`);
216
+
217
+ // 4. Write Final Merged JSON
218
+ const finalReportPath = path.join(REPORT_DIR, OUTPUT_FILE);
219
+ fs.writeFileSync(finalReportPath, JSON.stringify(merged, null, 2));
220
+
221
+ console.log(`\nโœ… Merged report saved as ${OUTPUT_FILE}`);
222
+ console.log(` Total tests: ${merged.run.totalTests}`);
223
+ console.log(` Passed: ${merged.run.passed} | Failed: ${merged.run.failed} | Skipped: ${merged.run.skipped}`);
224
+
225
+ // 5. Cleanup Shard Directories
226
+ cleanupShardDirectories(shardDirs);
138
227
 
139
- // Security Note: REPORT_DIR is sanitized, so writeFileSync is safe.
140
- fs.writeFileSync(
141
- path.join(REPORT_DIR, OUTPUT_FILE),
142
- JSON.stringify(merged, null, 2),
143
- );
144
- console.log(`โœ… Merged report saved as ${OUTPUT_FILE}`);
145
- })();
228
+ console.log();
229
+ })();
File without changes