@arghajit/dummy 0.1.0-beta-4 → 0.1.0-beta-6
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.
|
@@ -87,13 +87,14 @@ class PlaywrightPulseReporter {
|
|
|
87
87
|
: configDir;
|
|
88
88
|
this.outputDir = path.resolve(configFileDir, (_a = this.options.outputDir) !== null && _a !== void 0 ? _a : "pulse-report");
|
|
89
89
|
this.attachmentsDir = path.resolve(this.outputDir, ATTACHMENTS_SUBDIR);
|
|
90
|
-
this.options.outputDir = this.outputDir;
|
|
90
|
+
this.options.outputDir = this.outputDir; // Ensure options has the resolved path
|
|
91
91
|
const totalShards = this.config.shard ? this.config.shard.total : 1;
|
|
92
92
|
this.isSharded = totalShards > 1;
|
|
93
93
|
this.shardIndex = this.config.shard
|
|
94
94
|
? this.config.shard.current - 1
|
|
95
95
|
: undefined;
|
|
96
96
|
this._ensureDirExists(this.outputDir)
|
|
97
|
+
.then(() => this._ensureDirExists(this.attachmentsDir)) // Also ensure attachmentsDir exists
|
|
97
98
|
.then(() => {
|
|
98
99
|
if (this.shardIndex === undefined || this.shardIndex === 0) {
|
|
99
100
|
console.log(`PlaywrightPulseReporter: Starting test run with ${suite.allTests().length} tests${this.isSharded ? ` across ${totalShards} shards` : ""}. Pulse outputting to ${this.outputDir}`);
|
|
@@ -106,10 +107,11 @@ class PlaywrightPulseReporter {
|
|
|
106
107
|
.catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
|
|
107
108
|
}
|
|
108
109
|
onTestBegin(test) {
|
|
109
|
-
// console.log(`Starting test: ${test.
|
|
110
|
+
// Optional: console.log(`Starting test: ${test.titlePath().join(' > ')} for project ${test.parent?.project()?.name}`);
|
|
110
111
|
}
|
|
111
|
-
async processStep(step, testId, browserName,
|
|
112
|
-
|
|
112
|
+
async processStep(step, testId, browserName, // This will be the detailed browser info string
|
|
113
|
+
testCase) {
|
|
114
|
+
var _a, _b, _c, _d, _e;
|
|
113
115
|
let stepStatus = "passed";
|
|
114
116
|
let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
|
|
115
117
|
if ((_c = (_b = step.error) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.startsWith("Test is skipped:")) {
|
|
@@ -122,7 +124,8 @@ class PlaywrightPulseReporter {
|
|
|
122
124
|
const startTime = new Date(step.startTime);
|
|
123
125
|
const endTime = new Date(startTime.getTime() + Math.max(0, duration));
|
|
124
126
|
let codeLocation = "";
|
|
125
|
-
if (step.location) {
|
|
127
|
+
if ((_d = step.location) === null || _d === void 0 ? void 0 : _d.file) {
|
|
128
|
+
// Check if file path exists
|
|
126
129
|
codeLocation = `${path.relative(this.config.rootDir, step.location.file)}:${step.location.line}:${step.location.column}`;
|
|
127
130
|
}
|
|
128
131
|
let stepTitle = step.title;
|
|
@@ -133,9 +136,9 @@ class PlaywrightPulseReporter {
|
|
|
133
136
|
duration: duration,
|
|
134
137
|
startTime: startTime,
|
|
135
138
|
endTime: endTime,
|
|
136
|
-
browser: browserName,
|
|
139
|
+
browser: browserName, // Store the detailed browser string for the step
|
|
137
140
|
errorMessage: errorMessage,
|
|
138
|
-
stackTrace: ((
|
|
141
|
+
stackTrace: ((_e = step.error) === null || _e === void 0 ? void 0 : _e.stack) || undefined,
|
|
139
142
|
codeLocation: codeLocation || undefined,
|
|
140
143
|
isHook: step.category === "hook",
|
|
141
144
|
hookType: step.category === "hook"
|
|
@@ -143,120 +146,103 @@ class PlaywrightPulseReporter {
|
|
|
143
146
|
? "before"
|
|
144
147
|
: "after"
|
|
145
148
|
: undefined,
|
|
146
|
-
steps: [],
|
|
149
|
+
steps: [], // Will be populated by recursive calls in onTestEnd
|
|
147
150
|
};
|
|
148
151
|
}
|
|
149
152
|
getBrowserInfo(test) {
|
|
150
|
-
var _a, _b, _c, _d
|
|
151
|
-
// Changed return type to string
|
|
153
|
+
var _a, _b, _c, _d;
|
|
152
154
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
153
|
-
const configuredBrowserType = (_c = (_b = project === null || project === void 0 ? void 0 : project.use) === null || _b === void 0 ? void 0 : _b.defaultBrowserType) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
155
|
+
const configuredBrowserType = (_c = (_b = project === null || project === void 0 ? void 0 : project.use) === null || _b === void 0 ? void 0 : _b.defaultBrowserType) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
154
156
|
const userAgentString = (_d = project === null || project === void 0 ? void 0 : project.use) === null || _d === void 0 ? void 0 : _d.userAgent;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
// --- DEBUG LOGS (IMPORTANT! Check these in your console output) ---
|
|
158
|
+
console.log(`[PulseReporter DEBUG] Project: ${(project === null || project === void 0 ? void 0 : project.name) || "N/A"}`);
|
|
159
|
+
console.log(`[PulseReporter DEBUG] Configured Browser Type: "${configuredBrowserType}"`);
|
|
160
|
+
console.log(`[PulseReporter DEBUG] User Agent String for UAParser: "${userAgentString}"`);
|
|
161
|
+
// --- END DEBUG LOGS ---
|
|
162
|
+
let parsedBrowserName;
|
|
163
|
+
let parsedVersion;
|
|
164
|
+
let parsedOsName;
|
|
165
|
+
let parsedOsVersion;
|
|
166
|
+
let deviceModel;
|
|
167
|
+
let deviceType;
|
|
160
168
|
if (userAgentString) {
|
|
161
169
|
try {
|
|
162
170
|
const parser = new ua_parser_js_1.UAParser(userAgentString);
|
|
163
171
|
const uaResult = parser.getResult();
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// If UAParser didn't find a browser name, but we have an engine,
|
|
174
|
-
// it might be more informative than just the configuredBrowserType.
|
|
175
|
-
else if (uaResult.engine.name &&
|
|
176
|
-
configuredBrowserType !== uaResult.engine.name.toLowerCase()) {
|
|
177
|
-
// Prefer engine name if it's more specific and different from default (e.g. WebKit for a generic device)
|
|
178
|
-
finalBrowserName = uaResult.engine.name;
|
|
179
|
-
}
|
|
180
|
-
// 2. Specific Overrides / Refinements
|
|
181
|
-
// Handling for mobile devices, especially if UAParser provides generic browser names
|
|
172
|
+
// --- DEBUG LOGS (IMPORTANT! Check these in your console output) ---
|
|
173
|
+
console.log("[PulseReporter DEBUG] UAParser Result:", JSON.stringify(uaResult, null, 2));
|
|
174
|
+
// --- END DEBUG LOGS ---
|
|
175
|
+
parsedBrowserName = uaResult.browser.name;
|
|
176
|
+
parsedVersion = uaResult.browser.version;
|
|
177
|
+
parsedOsName = uaResult.os.name;
|
|
178
|
+
parsedOsVersion = uaResult.os.version;
|
|
179
|
+
deviceModel = uaResult.device.model;
|
|
180
|
+
deviceType = uaResult.device.type;
|
|
182
181
|
if (deviceType === "mobile" || deviceType === "tablet") {
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
else if ((_f = uaResult.os.name) === null || _f === void 0 ? void 0 : _f.toLowerCase().includes("android")) {
|
|
188
|
-
if ((_g = uaResult.browser.name) === null || _g === void 0 ? void 0 : _g.toLowerCase().includes("chrome")) {
|
|
189
|
-
finalBrowserName = "Chrome Mobile";
|
|
182
|
+
if (parsedOsName === null || parsedOsName === void 0 ? void 0 : parsedOsName.toLowerCase().includes("android")) {
|
|
183
|
+
if (parsedBrowserName === null || parsedBrowserName === void 0 ? void 0 : parsedBrowserName.toLowerCase().includes("chrome")) {
|
|
184
|
+
parsedBrowserName = "Chrome Mobile";
|
|
190
185
|
}
|
|
191
|
-
else if (
|
|
192
|
-
|
|
186
|
+
else if (parsedBrowserName === null || parsedBrowserName === void 0 ? void 0 : parsedBrowserName.toLowerCase().includes("firefox")) {
|
|
187
|
+
parsedBrowserName = "Firefox Mobile";
|
|
193
188
|
}
|
|
194
|
-
else if (uaResult.engine.name === "Blink" &&
|
|
195
|
-
|
|
196
|
-
// Generic Android Webview
|
|
197
|
-
finalBrowserName = "Android WebView";
|
|
189
|
+
else if (uaResult.engine.name === "Blink" && !parsedBrowserName) {
|
|
190
|
+
parsedBrowserName = "Android WebView";
|
|
198
191
|
}
|
|
199
|
-
else if (
|
|
200
|
-
|
|
192
|
+
else if (parsedBrowserName) {
|
|
193
|
+
// Parsed name is likely okay
|
|
201
194
|
}
|
|
202
195
|
else {
|
|
203
|
-
|
|
196
|
+
parsedBrowserName = "Android Browser";
|
|
204
197
|
}
|
|
205
198
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
finalBrowserName = "Electron App"; // More descriptive for Electron
|
|
209
|
-
// For Electron, version might be app's version, not Chromium's.
|
|
210
|
-
// You might need custom logic if you want the underlying Chromium version.
|
|
211
|
-
}
|
|
212
|
-
// 3. OS Information
|
|
213
|
-
if (uaResult.os.name) {
|
|
214
|
-
osName = ` on ${uaResult.os.name}`;
|
|
215
|
-
if (uaResult.os.version) {
|
|
216
|
-
osVersion = ` ${uaResult.os.version.split(".")[0]}`; // Major OS version
|
|
199
|
+
else if (parsedOsName === null || parsedOsName === void 0 ? void 0 : parsedOsName.toLowerCase().includes("ios")) {
|
|
200
|
+
parsedBrowserName = "Mobile Safari";
|
|
217
201
|
}
|
|
218
202
|
}
|
|
203
|
+
else if (parsedBrowserName === "Electron") {
|
|
204
|
+
parsedBrowserName = "Electron App";
|
|
205
|
+
}
|
|
219
206
|
}
|
|
220
207
|
catch (error) {
|
|
221
208
|
console.warn(`Pulse Reporter: Error parsing User-Agent string "${userAgentString}":`, error);
|
|
222
|
-
// Fallback to configuredBrowserType already set in finalBrowserName
|
|
223
209
|
}
|
|
224
210
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
211
|
+
let finalDisplayName;
|
|
212
|
+
if (parsedBrowserName) {
|
|
213
|
+
finalDisplayName = parsedBrowserName;
|
|
214
|
+
if (parsedVersion) {
|
|
215
|
+
finalDisplayName += ` v${parsedVersion.split(".")[0]}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else if (configuredBrowserType && configuredBrowserType !== "unknown") {
|
|
219
|
+
finalDisplayName =
|
|
233
220
|
configuredBrowserType.charAt(0).toUpperCase() +
|
|
234
|
-
configuredBrowserType.slice(1);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return displayString.trim();
|
|
221
|
+
configuredBrowserType.slice(1);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
finalDisplayName = "Unknown Browser";
|
|
225
|
+
}
|
|
226
|
+
if (parsedOsName) {
|
|
227
|
+
finalDisplayName += ` on ${parsedOsName}`;
|
|
228
|
+
if (parsedOsVersion) {
|
|
229
|
+
finalDisplayName += ` ${parsedOsVersion.split(".")[0]}`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Example: Append device model if it's a mobile/tablet and model exists
|
|
233
|
+
// if ((deviceType === "mobile" || deviceType === "tablet") && deviceModel && !finalDisplayName.includes(deviceModel)) {
|
|
234
|
+
// finalDisplayName += ` (${deviceModel})`;
|
|
235
|
+
// }
|
|
236
|
+
return finalDisplayName.trim();
|
|
251
237
|
}
|
|
252
238
|
async onTestEnd(test, result) {
|
|
253
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
239
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
254
240
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
255
|
-
const
|
|
241
|
+
const browserDisplayInfo = this.getBrowserInfo(test);
|
|
256
242
|
const testStatus = convertStatus(result.status, test);
|
|
257
243
|
const startTime = new Date(result.startTime);
|
|
258
244
|
const endTime = new Date(startTime.getTime() + result.duration);
|
|
259
|
-
const testIdForFiles = test.id ||
|
|
245
|
+
const testIdForFiles = test.id || // Playwright's internal unique ID for the test case
|
|
260
246
|
`${test
|
|
261
247
|
.titlePath()
|
|
262
248
|
.join("_")
|
|
@@ -264,60 +250,54 @@ class PlaywrightPulseReporter {
|
|
|
264
250
|
const processAllSteps = async (steps) => {
|
|
265
251
|
let processed = [];
|
|
266
252
|
for (const step of steps) {
|
|
267
|
-
const processedStep = await this.processStep(step, testIdForFiles,
|
|
253
|
+
const processedStep = await this.processStep(step, testIdForFiles, browserDisplayInfo, // Pass the detailed browser info string
|
|
254
|
+
test);
|
|
268
255
|
processed.push(processedStep);
|
|
269
256
|
if (step.steps && step.steps.length > 0) {
|
|
270
|
-
processedStep.steps = await processAllSteps(step.steps);
|
|
257
|
+
processedStep.steps = await processAllSteps(step.steps); // Recursive call
|
|
271
258
|
}
|
|
272
259
|
}
|
|
273
260
|
return processed;
|
|
274
261
|
};
|
|
275
262
|
let codeSnippet = undefined;
|
|
276
263
|
try {
|
|
277
|
-
if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) &&
|
|
264
|
+
if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) &&
|
|
265
|
+
((_c = test.location) === null || _c === void 0 ? void 0 : _c.line) !== undefined &&
|
|
266
|
+
((_d = test.location) === null || _d === void 0 ? void 0 : _d.column) !== undefined) {
|
|
278
267
|
const relativePath = path.relative(this.config.rootDir, test.location.file);
|
|
279
268
|
codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
|
|
280
269
|
}
|
|
281
270
|
}
|
|
282
271
|
catch (e) {
|
|
283
|
-
console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
|
|
272
|
+
// console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
|
|
284
273
|
}
|
|
285
|
-
const stdoutMessages = [];
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
stdoutMessages.push(typeof item === "string" ? item : item.toString());
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
const stderrMessages = [];
|
|
292
|
-
if (result.stderr && result.stderr.length > 0) {
|
|
293
|
-
result.stderr.forEach((item) => {
|
|
294
|
-
stderrMessages.push(typeof item === "string" ? item : item.toString());
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
const uniqueTestId = test.id;
|
|
274
|
+
const stdoutMessages = ((_e = result.stdout) === null || _e === void 0 ? void 0 : _e.map((item) => typeof item === "string" ? item : item.toString())) || [];
|
|
275
|
+
const stderrMessages = ((_f = result.stderr) === null || _f === void 0 ? void 0 : _f.map((item) => typeof item === "string" ? item : item.toString())) || [];
|
|
276
|
+
const uniqueTestId = test.id; // test.id is Playwright's unique ID for a test case instance
|
|
298
277
|
const pulseResult = {
|
|
299
278
|
id: uniqueTestId,
|
|
300
|
-
runId: "TBD",
|
|
279
|
+
runId: "TBD", // Will be set during final report generation
|
|
301
280
|
name: test.titlePath().join(" > "),
|
|
302
|
-
suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((
|
|
281
|
+
suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((_g = this.config.projects[0]) === null || _g === void 0 ? void 0 : _g.name) || "Default Suite",
|
|
303
282
|
status: testStatus,
|
|
304
283
|
duration: result.duration,
|
|
305
284
|
startTime: startTime,
|
|
306
285
|
endTime: endTime,
|
|
307
|
-
browser:
|
|
286
|
+
browser: browserDisplayInfo, // Use the detailed browser string
|
|
308
287
|
retries: result.retry,
|
|
309
|
-
steps: ((
|
|
310
|
-
errorMessage: (
|
|
311
|
-
stackTrace: (
|
|
288
|
+
steps: ((_h = result.steps) === null || _h === void 0 ? void 0 : _h.length) ? await processAllSteps(result.steps) : [],
|
|
289
|
+
errorMessage: (_j = result.error) === null || _j === void 0 ? void 0 : _j.message,
|
|
290
|
+
stackTrace: (_k = result.error) === null || _k === void 0 ? void 0 : _k.stack,
|
|
312
291
|
codeSnippet: codeSnippet,
|
|
313
292
|
tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
|
|
314
|
-
screenshots: [],
|
|
315
|
-
videoPath: undefined,
|
|
316
|
-
tracePath: undefined,
|
|
293
|
+
screenshots: [], // To be populated by attachFiles
|
|
294
|
+
videoPath: undefined, // To be populated by attachFiles
|
|
295
|
+
tracePath: undefined, // To be populated by attachFiles
|
|
317
296
|
stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
|
|
318
297
|
stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
|
|
319
298
|
};
|
|
320
299
|
try {
|
|
300
|
+
// IMPORTANT: attachFiles logic
|
|
321
301
|
(0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
|
|
322
302
|
}
|
|
323
303
|
catch (attachError) {
|
|
@@ -352,27 +332,28 @@ class PlaywrightPulseReporter {
|
|
|
352
332
|
console.error(`Pulse Reporter: Shard ${this.shardIndex} failed to write temporary results to ${tempFilePath}`, error);
|
|
353
333
|
}
|
|
354
334
|
}
|
|
355
|
-
async _mergeShardResults(finalRunData
|
|
335
|
+
async _mergeShardResults(finalRunData // Pass the TestRun object to populate
|
|
336
|
+
) {
|
|
337
|
+
var _a, _b;
|
|
356
338
|
let allShardProcessedResults = [];
|
|
357
|
-
const totalShards = this.config.shard ?
|
|
339
|
+
const totalShards = (_b = (_a = this.config.shard) === null || _a === void 0 ? void 0 : _a.total) !== null && _b !== void 0 ? _b : 1;
|
|
358
340
|
for (let i = 0; i < totalShards; i++) {
|
|
359
341
|
const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
|
|
360
342
|
try {
|
|
361
343
|
const content = await fs.readFile(tempFilePath, "utf-8");
|
|
362
|
-
const shardResults = JSON.parse(content);
|
|
363
|
-
allShardProcessedResults
|
|
364
|
-
allShardProcessedResults.concat(shardResults);
|
|
344
|
+
const shardResults = JSON.parse(content); // Dates are already ISO strings
|
|
345
|
+
allShardProcessedResults.push(...shardResults);
|
|
365
346
|
}
|
|
366
347
|
catch (error) {
|
|
367
348
|
if ((error === null || error === void 0 ? void 0 : error.code) === "ENOENT") {
|
|
368
|
-
console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}
|
|
349
|
+
// console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}.`);
|
|
369
350
|
}
|
|
370
351
|
else {
|
|
371
352
|
console.error(`Pulse Reporter: Could not read/parse results from shard ${i} (${tempFilePath}). Error:`, error);
|
|
372
353
|
}
|
|
373
354
|
}
|
|
374
355
|
}
|
|
375
|
-
|
|
356
|
+
const finalUniqueResultsMap = new Map();
|
|
376
357
|
for (const result of allShardProcessedResults) {
|
|
377
358
|
const existing = finalUniqueResultsMap.get(result.id);
|
|
378
359
|
if (!existing || result.retries >= existing.retries) {
|
|
@@ -380,23 +361,15 @@ class PlaywrightPulseReporter {
|
|
|
380
361
|
}
|
|
381
362
|
}
|
|
382
363
|
const finalResultsList = Array.from(finalUniqueResultsMap.values());
|
|
383
|
-
finalResultsList.forEach((r) => (r.runId = finalRunData.id));
|
|
364
|
+
finalResultsList.forEach((r) => (r.runId = finalRunData.id)); // Assign runId to each test result
|
|
365
|
+
// Update the passed finalRunData object with aggregated stats
|
|
384
366
|
finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
|
|
385
367
|
finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
|
|
386
368
|
finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
|
|
387
369
|
finalRunData.totalTests = finalResultsList.length;
|
|
388
|
-
const reviveDates = (key, value) => {
|
|
389
|
-
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
390
|
-
if (typeof value === "string" && isoDateRegex.test(value)) {
|
|
391
|
-
const date = new Date(value);
|
|
392
|
-
return !isNaN(date.getTime()) ? date : value;
|
|
393
|
-
}
|
|
394
|
-
return value;
|
|
395
|
-
};
|
|
396
|
-
const properlyTypedResults = JSON.parse(JSON.stringify(finalResultsList), reviveDates);
|
|
397
370
|
return {
|
|
398
|
-
run: finalRunData,
|
|
399
|
-
results:
|
|
371
|
+
run: finalRunData, // Contains Date object for timestamp
|
|
372
|
+
results: finalResultsList, // Contains ISO strings for dates from shards
|
|
400
373
|
metadata: { generatedAt: new Date().toISOString() },
|
|
401
374
|
};
|
|
402
375
|
}
|
|
@@ -410,7 +383,7 @@ class PlaywrightPulseReporter {
|
|
|
410
383
|
}
|
|
411
384
|
catch (error) {
|
|
412
385
|
if ((error === null || error === void 0 ? void 0 : error.code) !== "ENOENT") {
|
|
413
|
-
console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
|
|
386
|
+
// console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
|
|
414
387
|
}
|
|
415
388
|
}
|
|
416
389
|
}
|
|
@@ -426,7 +399,7 @@ class PlaywrightPulseReporter {
|
|
|
426
399
|
}
|
|
427
400
|
}
|
|
428
401
|
async onEnd(result) {
|
|
429
|
-
var _a, _b
|
|
402
|
+
var _a, _b;
|
|
430
403
|
if (this.shardIndex !== undefined) {
|
|
431
404
|
await this._writeShardResults();
|
|
432
405
|
return;
|
|
@@ -435,16 +408,18 @@ class PlaywrightPulseReporter {
|
|
|
435
408
|
const duration = runEndTime - this.runStartTime;
|
|
436
409
|
const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
|
|
437
410
|
const runData = {
|
|
411
|
+
// This is the single source of truth for current run's data
|
|
438
412
|
id: runId,
|
|
439
|
-
timestamp: new Date(this.runStartTime),
|
|
413
|
+
timestamp: new Date(this.runStartTime), // Stored as Date object
|
|
440
414
|
totalTests: 0,
|
|
441
415
|
passed: 0,
|
|
442
416
|
failed: 0,
|
|
443
417
|
skipped: 0,
|
|
444
418
|
duration,
|
|
445
419
|
};
|
|
446
|
-
let finalReport
|
|
420
|
+
let finalReport;
|
|
447
421
|
if (this.isSharded) {
|
|
422
|
+
// _mergeShardResults will populate the runData object passed to it
|
|
448
423
|
finalReport = await this._mergeShardResults(runData);
|
|
449
424
|
}
|
|
450
425
|
else {
|
|
@@ -453,37 +428,18 @@ class PlaywrightPulseReporter {
|
|
|
453
428
|
runData.failed = this.results.filter((r) => r.status === "failed").length;
|
|
454
429
|
runData.skipped = this.results.filter((r) => r.status === "skipped").length;
|
|
455
430
|
runData.totalTests = this.results.length;
|
|
456
|
-
const reviveDates = (key, value) => {
|
|
457
|
-
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
458
|
-
if (typeof value === "string" && isoDateRegex.test(value)) {
|
|
459
|
-
const date = new Date(value);
|
|
460
|
-
return !isNaN(date.getTime()) ? date : value;
|
|
461
|
-
}
|
|
462
|
-
return value;
|
|
463
|
-
};
|
|
464
|
-
const properlyTypedResults = JSON.parse(JSON.stringify(this.results), reviveDates);
|
|
465
431
|
finalReport = {
|
|
466
|
-
run: runData,
|
|
467
|
-
results:
|
|
432
|
+
run: runData, // runData contains a Date object for timestamp
|
|
433
|
+
results: this.results, // results contain Date objects for startTime, endTime
|
|
468
434
|
metadata: { generatedAt: new Date().toISOString() },
|
|
469
435
|
};
|
|
470
436
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
Total Tests: N/A
|
|
478
|
-
Passed: N/A
|
|
479
|
-
Failed: N/A
|
|
480
|
-
Skipped: N/A
|
|
481
|
-
Duration: N/A
|
|
482
|
-
-----------------------------------------`;
|
|
483
|
-
if (this.printsToStdio()) {
|
|
484
|
-
console.log(errorSummary);
|
|
485
|
-
}
|
|
486
|
-
const errorReport = {
|
|
437
|
+
// This check should be robust now
|
|
438
|
+
if (!finalReport ||
|
|
439
|
+
!finalReport.run ||
|
|
440
|
+
typeof finalReport.run.totalTests !== "number") {
|
|
441
|
+
console.error("PlaywrightPulseReporter: CRITICAL - finalReport object or its run data was malformed. Cannot create summary.");
|
|
442
|
+
const errorReportMinimal = {
|
|
487
443
|
run: {
|
|
488
444
|
id: runId,
|
|
489
445
|
timestamp: new Date(this.runStartTime),
|
|
@@ -491,28 +447,30 @@ PlaywrightPulseReporter: Run Finished
|
|
|
491
447
|
passed: 0,
|
|
492
448
|
failed: 0,
|
|
493
449
|
skipped: 0,
|
|
494
|
-
duration
|
|
450
|
+
duration,
|
|
495
451
|
},
|
|
496
452
|
results: [],
|
|
497
453
|
metadata: {
|
|
498
454
|
generatedAt: new Date().toISOString(),
|
|
499
455
|
},
|
|
500
456
|
};
|
|
501
|
-
const finalOutputPathOnError = path.join(this.outputDir, this.baseOutputFile);
|
|
502
457
|
try {
|
|
458
|
+
const errorPath = path.join(this.outputDir, this.baseOutputFile);
|
|
503
459
|
await this._ensureDirExists(this.outputDir);
|
|
504
|
-
|
|
505
|
-
|
|
460
|
+
// Stringify with Date conversion for the minimal error report
|
|
461
|
+
await fs.writeFile(errorPath, JSON.stringify(errorReportMinimal, (key, value) => value instanceof Date ? value.toISOString() : value, 2));
|
|
462
|
+
console.warn(`PlaywrightPulseReporter: Wrote a minimal error report to ${errorPath}.`);
|
|
506
463
|
}
|
|
507
|
-
catch (
|
|
508
|
-
console.error(
|
|
464
|
+
catch (e) {
|
|
465
|
+
console.error("PlaywrightPulseReporter: Failed to write minimal error report.", e);
|
|
509
466
|
}
|
|
510
467
|
return;
|
|
511
468
|
}
|
|
469
|
+
// At this point, finalReport.run is guaranteed to be populated by either _mergeShardResults or the non-sharded path.
|
|
512
470
|
const reportRunData = finalReport.run;
|
|
513
|
-
const finalRunStatus = ((_a = reportRunData
|
|
471
|
+
const finalRunStatus = ((_a = reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
|
|
514
472
|
? "failed"
|
|
515
|
-
: ((_b = reportRunData
|
|
473
|
+
: ((_b = reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
|
|
516
474
|
? result.status === "interrupted"
|
|
517
475
|
? "interrupted"
|
|
518
476
|
: "no tests or error"
|
|
@@ -521,11 +479,11 @@ PlaywrightPulseReporter: Run Finished
|
|
|
521
479
|
PlaywrightPulseReporter: Run Finished
|
|
522
480
|
-----------------------------------------
|
|
523
481
|
Overall Status: ${finalRunStatus.toUpperCase()}
|
|
524
|
-
Total Tests: ${
|
|
525
|
-
Passed: ${reportRunData
|
|
526
|
-
Failed: ${reportRunData
|
|
527
|
-
Skipped: ${reportRunData
|
|
528
|
-
Duration: ${(
|
|
482
|
+
Total Tests: ${reportRunData.totalTests}
|
|
483
|
+
Passed: ${reportRunData.passed}
|
|
484
|
+
Failed: ${reportRunData.failed}
|
|
485
|
+
Skipped: ${reportRunData.skipped}
|
|
486
|
+
Duration: ${(reportRunData.duration / 1000).toFixed(2)}s
|
|
529
487
|
-----------------------------------------`;
|
|
530
488
|
if (this.printsToStdio()) {
|
|
531
489
|
console.log(summary);
|
|
@@ -533,11 +491,11 @@ PlaywrightPulseReporter: Run Finished
|
|
|
533
491
|
const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
|
|
534
492
|
try {
|
|
535
493
|
await this._ensureDirExists(this.outputDir);
|
|
494
|
+
// Custom replacer for JSON.stringify to handle Date objects correctly
|
|
536
495
|
await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
|
|
537
|
-
if (value instanceof Date)
|
|
496
|
+
if (value instanceof Date) {
|
|
538
497
|
return value.toISOString();
|
|
539
|
-
|
|
540
|
-
return value.toString();
|
|
498
|
+
}
|
|
541
499
|
return value;
|
|
542
500
|
}, 2));
|
|
543
501
|
if (this.printsToStdio()) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arghajit/dummy",
|
|
3
3
|
"author": "Arghajit Singha",
|
|
4
|
-
"version": "0.1.0-beta-
|
|
4
|
+
"version": "0.1.0-beta-6",
|
|
5
5
|
"description": "A Playwright reporter and dashboard for visualizing test results.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"playwright",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/node": "^20",
|
|
65
|
+
"@types/ua-parser-js": "^0.7.39",
|
|
65
66
|
"eslint": "9.25.1",
|
|
66
67
|
"typescript": "^5"
|
|
67
68
|
},
|