@arghajit/dummy 0.1.0-beta-5 → 0.1.0-beta-7
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,107 @@ 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
|
|
151
|
-
// Changed return type to string
|
|
153
|
+
var _a, _b, _c;
|
|
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();
|
|
154
|
-
const userAgentString = (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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();
|
|
156
|
+
const userAgentString = test.info().project.use.userAgent;
|
|
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 browserDisplayInfo = this.getBrowserInfo(test);
|
|
241
|
+
// const browserDisplayInfo = this.getBrowserInfo(test);
|
|
242
|
+
const ua = test.info().project.use.userAgent;
|
|
243
|
+
const parser = new ua_parser_js_1.UAParser(ua);
|
|
244
|
+
const res = parser.getResult();
|
|
245
|
+
const browserDisplayInfo = res.browser.name || "";
|
|
256
246
|
const testStatus = convertStatus(result.status, test);
|
|
257
247
|
const startTime = new Date(result.startTime);
|
|
258
248
|
const endTime = new Date(startTime.getTime() + result.duration);
|
|
259
|
-
const testIdForFiles = test.id ||
|
|
249
|
+
const testIdForFiles = test.id || // Playwright's internal unique ID for the test case
|
|
260
250
|
`${test
|
|
261
251
|
.titlePath()
|
|
262
252
|
.join("_")
|
|
@@ -264,60 +254,54 @@ class PlaywrightPulseReporter {
|
|
|
264
254
|
const processAllSteps = async (steps) => {
|
|
265
255
|
let processed = [];
|
|
266
256
|
for (const step of steps) {
|
|
267
|
-
const processedStep = await this.processStep(step, testIdForFiles, browserDisplayInfo,
|
|
257
|
+
const processedStep = await this.processStep(step, testIdForFiles, browserDisplayInfo, // Pass the detailed browser info string
|
|
258
|
+
test);
|
|
268
259
|
processed.push(processedStep);
|
|
269
260
|
if (step.steps && step.steps.length > 0) {
|
|
270
|
-
processedStep.steps = await processAllSteps(step.steps);
|
|
261
|
+
processedStep.steps = await processAllSteps(step.steps); // Recursive call
|
|
271
262
|
}
|
|
272
263
|
}
|
|
273
264
|
return processed;
|
|
274
265
|
};
|
|
275
266
|
let codeSnippet = undefined;
|
|
276
267
|
try {
|
|
277
|
-
if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) &&
|
|
268
|
+
if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) &&
|
|
269
|
+
((_c = test.location) === null || _c === void 0 ? void 0 : _c.line) !== undefined &&
|
|
270
|
+
((_d = test.location) === null || _d === void 0 ? void 0 : _d.column) !== undefined) {
|
|
278
271
|
const relativePath = path.relative(this.config.rootDir, test.location.file);
|
|
279
272
|
codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
|
|
280
273
|
}
|
|
281
274
|
}
|
|
282
275
|
catch (e) {
|
|
283
|
-
console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
|
|
284
|
-
}
|
|
285
|
-
const stdoutMessages = [];
|
|
286
|
-
if (result.stdout && result.stdout.length > 0) {
|
|
287
|
-
result.stdout.forEach((item) => {
|
|
288
|
-
stdoutMessages.push(typeof item === "string" ? item : item.toString());
|
|
289
|
-
});
|
|
276
|
+
// console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
|
|
290
277
|
}
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
stderrMessages.push(typeof item === "string" ? item : item.toString());
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
const uniqueTestId = test.id;
|
|
278
|
+
const stdoutMessages = ((_e = result.stdout) === null || _e === void 0 ? void 0 : _e.map((item) => typeof item === "string" ? item : item.toString())) || [];
|
|
279
|
+
const stderrMessages = ((_f = result.stderr) === null || _f === void 0 ? void 0 : _f.map((item) => typeof item === "string" ? item : item.toString())) || [];
|
|
280
|
+
const uniqueTestId = test.id; // test.id is Playwright's unique ID for a test case instance
|
|
298
281
|
const pulseResult = {
|
|
299
282
|
id: uniqueTestId,
|
|
300
|
-
runId: "TBD",
|
|
283
|
+
runId: "TBD", // Will be set during final report generation
|
|
301
284
|
name: test.titlePath().join(" > "),
|
|
302
|
-
suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((
|
|
285
|
+
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
286
|
status: testStatus,
|
|
304
287
|
duration: result.duration,
|
|
305
288
|
startTime: startTime,
|
|
306
289
|
endTime: endTime,
|
|
307
|
-
browser: browserDisplayInfo,
|
|
290
|
+
browser: browserDisplayInfo, // Use the detailed browser string
|
|
308
291
|
retries: result.retry,
|
|
309
|
-
steps: ((
|
|
310
|
-
errorMessage: (
|
|
311
|
-
stackTrace: (
|
|
292
|
+
steps: ((_h = result.steps) === null || _h === void 0 ? void 0 : _h.length) ? await processAllSteps(result.steps) : [],
|
|
293
|
+
errorMessage: (_j = result.error) === null || _j === void 0 ? void 0 : _j.message,
|
|
294
|
+
stackTrace: (_k = result.error) === null || _k === void 0 ? void 0 : _k.stack,
|
|
312
295
|
codeSnippet: codeSnippet,
|
|
313
296
|
tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
|
|
314
|
-
screenshots: [],
|
|
315
|
-
videoPath: undefined,
|
|
316
|
-
tracePath: undefined,
|
|
297
|
+
screenshots: [], // To be populated by attachFiles
|
|
298
|
+
videoPath: undefined, // To be populated by attachFiles
|
|
299
|
+
tracePath: undefined, // To be populated by attachFiles
|
|
317
300
|
stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
|
|
318
301
|
stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
|
|
319
302
|
};
|
|
320
303
|
try {
|
|
304
|
+
// IMPORTANT: attachFiles logic
|
|
321
305
|
(0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
|
|
322
306
|
}
|
|
323
307
|
catch (attachError) {
|
|
@@ -352,27 +336,28 @@ class PlaywrightPulseReporter {
|
|
|
352
336
|
console.error(`Pulse Reporter: Shard ${this.shardIndex} failed to write temporary results to ${tempFilePath}`, error);
|
|
353
337
|
}
|
|
354
338
|
}
|
|
355
|
-
async _mergeShardResults(finalRunData
|
|
339
|
+
async _mergeShardResults(finalRunData // Pass the TestRun object to populate
|
|
340
|
+
) {
|
|
341
|
+
var _a, _b;
|
|
356
342
|
let allShardProcessedResults = [];
|
|
357
|
-
const totalShards = this.config.shard ?
|
|
343
|
+
const totalShards = (_b = (_a = this.config.shard) === null || _a === void 0 ? void 0 : _a.total) !== null && _b !== void 0 ? _b : 1;
|
|
358
344
|
for (let i = 0; i < totalShards; i++) {
|
|
359
345
|
const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
|
|
360
346
|
try {
|
|
361
347
|
const content = await fs.readFile(tempFilePath, "utf-8");
|
|
362
|
-
const shardResults = JSON.parse(content);
|
|
363
|
-
allShardProcessedResults
|
|
364
|
-
allShardProcessedResults.concat(shardResults);
|
|
348
|
+
const shardResults = JSON.parse(content); // Dates are already ISO strings
|
|
349
|
+
allShardProcessedResults.push(...shardResults);
|
|
365
350
|
}
|
|
366
351
|
catch (error) {
|
|
367
352
|
if ((error === null || error === void 0 ? void 0 : error.code) === "ENOENT") {
|
|
368
|
-
console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}
|
|
353
|
+
// console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}.`);
|
|
369
354
|
}
|
|
370
355
|
else {
|
|
371
356
|
console.error(`Pulse Reporter: Could not read/parse results from shard ${i} (${tempFilePath}). Error:`, error);
|
|
372
357
|
}
|
|
373
358
|
}
|
|
374
359
|
}
|
|
375
|
-
|
|
360
|
+
const finalUniqueResultsMap = new Map();
|
|
376
361
|
for (const result of allShardProcessedResults) {
|
|
377
362
|
const existing = finalUniqueResultsMap.get(result.id);
|
|
378
363
|
if (!existing || result.retries >= existing.retries) {
|
|
@@ -380,23 +365,15 @@ class PlaywrightPulseReporter {
|
|
|
380
365
|
}
|
|
381
366
|
}
|
|
382
367
|
const finalResultsList = Array.from(finalUniqueResultsMap.values());
|
|
383
|
-
finalResultsList.forEach((r) => (r.runId = finalRunData.id));
|
|
368
|
+
finalResultsList.forEach((r) => (r.runId = finalRunData.id)); // Assign runId to each test result
|
|
369
|
+
// Update the passed finalRunData object with aggregated stats
|
|
384
370
|
finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
|
|
385
371
|
finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
|
|
386
372
|
finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
|
|
387
373
|
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
374
|
return {
|
|
398
|
-
run: finalRunData,
|
|
399
|
-
results:
|
|
375
|
+
run: finalRunData, // Contains Date object for timestamp
|
|
376
|
+
results: finalResultsList, // Contains ISO strings for dates from shards
|
|
400
377
|
metadata: { generatedAt: new Date().toISOString() },
|
|
401
378
|
};
|
|
402
379
|
}
|
|
@@ -410,7 +387,7 @@ class PlaywrightPulseReporter {
|
|
|
410
387
|
}
|
|
411
388
|
catch (error) {
|
|
412
389
|
if ((error === null || error === void 0 ? void 0 : error.code) !== "ENOENT") {
|
|
413
|
-
console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
|
|
390
|
+
// console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
|
|
414
391
|
}
|
|
415
392
|
}
|
|
416
393
|
}
|
|
@@ -426,7 +403,7 @@ class PlaywrightPulseReporter {
|
|
|
426
403
|
}
|
|
427
404
|
}
|
|
428
405
|
async onEnd(result) {
|
|
429
|
-
var _a, _b
|
|
406
|
+
var _a, _b;
|
|
430
407
|
if (this.shardIndex !== undefined) {
|
|
431
408
|
await this._writeShardResults();
|
|
432
409
|
return;
|
|
@@ -435,16 +412,18 @@ class PlaywrightPulseReporter {
|
|
|
435
412
|
const duration = runEndTime - this.runStartTime;
|
|
436
413
|
const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
|
|
437
414
|
const runData = {
|
|
415
|
+
// This is the single source of truth for current run's data
|
|
438
416
|
id: runId,
|
|
439
|
-
timestamp: new Date(this.runStartTime),
|
|
417
|
+
timestamp: new Date(this.runStartTime), // Stored as Date object
|
|
440
418
|
totalTests: 0,
|
|
441
419
|
passed: 0,
|
|
442
420
|
failed: 0,
|
|
443
421
|
skipped: 0,
|
|
444
422
|
duration,
|
|
445
423
|
};
|
|
446
|
-
let finalReport
|
|
424
|
+
let finalReport;
|
|
447
425
|
if (this.isSharded) {
|
|
426
|
+
// _mergeShardResults will populate the runData object passed to it
|
|
448
427
|
finalReport = await this._mergeShardResults(runData);
|
|
449
428
|
}
|
|
450
429
|
else {
|
|
@@ -453,37 +432,18 @@ class PlaywrightPulseReporter {
|
|
|
453
432
|
runData.failed = this.results.filter((r) => r.status === "failed").length;
|
|
454
433
|
runData.skipped = this.results.filter((r) => r.status === "skipped").length;
|
|
455
434
|
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
435
|
finalReport = {
|
|
466
|
-
run: runData,
|
|
467
|
-
results:
|
|
436
|
+
run: runData, // runData contains a Date object for timestamp
|
|
437
|
+
results: this.results, // results contain Date objects for startTime, endTime
|
|
468
438
|
metadata: { generatedAt: new Date().toISOString() },
|
|
469
439
|
};
|
|
470
440
|
}
|
|
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 = {
|
|
441
|
+
// This check should be robust now
|
|
442
|
+
if (!finalReport ||
|
|
443
|
+
!finalReport.run ||
|
|
444
|
+
typeof finalReport.run.totalTests !== "number") {
|
|
445
|
+
console.error("PlaywrightPulseReporter: CRITICAL - finalReport object or its run data was malformed. Cannot create summary.");
|
|
446
|
+
const errorReportMinimal = {
|
|
487
447
|
run: {
|
|
488
448
|
id: runId,
|
|
489
449
|
timestamp: new Date(this.runStartTime),
|
|
@@ -491,28 +451,30 @@ PlaywrightPulseReporter: Run Finished
|
|
|
491
451
|
passed: 0,
|
|
492
452
|
failed: 0,
|
|
493
453
|
skipped: 0,
|
|
494
|
-
duration
|
|
454
|
+
duration,
|
|
495
455
|
},
|
|
496
456
|
results: [],
|
|
497
457
|
metadata: {
|
|
498
458
|
generatedAt: new Date().toISOString(),
|
|
499
459
|
},
|
|
500
460
|
};
|
|
501
|
-
const finalOutputPathOnError = path.join(this.outputDir, this.baseOutputFile);
|
|
502
461
|
try {
|
|
462
|
+
const errorPath = path.join(this.outputDir, this.baseOutputFile);
|
|
503
463
|
await this._ensureDirExists(this.outputDir);
|
|
504
|
-
|
|
505
|
-
|
|
464
|
+
// Stringify with Date conversion for the minimal error report
|
|
465
|
+
await fs.writeFile(errorPath, JSON.stringify(errorReportMinimal, (key, value) => value instanceof Date ? value.toISOString() : value, 2));
|
|
466
|
+
console.warn(`PlaywrightPulseReporter: Wrote a minimal error report to ${errorPath}.`);
|
|
506
467
|
}
|
|
507
|
-
catch (
|
|
508
|
-
console.error(
|
|
468
|
+
catch (e) {
|
|
469
|
+
console.error("PlaywrightPulseReporter: Failed to write minimal error report.", e);
|
|
509
470
|
}
|
|
510
471
|
return;
|
|
511
472
|
}
|
|
473
|
+
// At this point, finalReport.run is guaranteed to be populated by either _mergeShardResults or the non-sharded path.
|
|
512
474
|
const reportRunData = finalReport.run;
|
|
513
|
-
const finalRunStatus = ((_a = reportRunData
|
|
475
|
+
const finalRunStatus = ((_a = reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
|
|
514
476
|
? "failed"
|
|
515
|
-
: ((_b = reportRunData
|
|
477
|
+
: ((_b = reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
|
|
516
478
|
? result.status === "interrupted"
|
|
517
479
|
? "interrupted"
|
|
518
480
|
: "no tests or error"
|
|
@@ -521,11 +483,11 @@ PlaywrightPulseReporter: Run Finished
|
|
|
521
483
|
PlaywrightPulseReporter: Run Finished
|
|
522
484
|
-----------------------------------------
|
|
523
485
|
Overall Status: ${finalRunStatus.toUpperCase()}
|
|
524
|
-
Total Tests: ${
|
|
525
|
-
Passed: ${reportRunData
|
|
526
|
-
Failed: ${reportRunData
|
|
527
|
-
Skipped: ${reportRunData
|
|
528
|
-
Duration: ${(
|
|
486
|
+
Total Tests: ${reportRunData.totalTests}
|
|
487
|
+
Passed: ${reportRunData.passed}
|
|
488
|
+
Failed: ${reportRunData.failed}
|
|
489
|
+
Skipped: ${reportRunData.skipped}
|
|
490
|
+
Duration: ${(reportRunData.duration / 1000).toFixed(2)}s
|
|
529
491
|
-----------------------------------------`;
|
|
530
492
|
if (this.printsToStdio()) {
|
|
531
493
|
console.log(summary);
|
|
@@ -533,11 +495,11 @@ PlaywrightPulseReporter: Run Finished
|
|
|
533
495
|
const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
|
|
534
496
|
try {
|
|
535
497
|
await this._ensureDirExists(this.outputDir);
|
|
498
|
+
// Custom replacer for JSON.stringify to handle Date objects correctly
|
|
536
499
|
await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
|
|
537
|
-
if (value instanceof Date)
|
|
500
|
+
if (value instanceof Date) {
|
|
538
501
|
return value.toISOString();
|
|
539
|
-
|
|
540
|
-
return value.toString();
|
|
502
|
+
}
|
|
541
503
|
return value;
|
|
542
504
|
}, 2));
|
|
543
505
|
if (this.printsToStdio()) {
|
package/package.json
CHANGED