@arghajit/dummy 0.1.0-beta-7 → 0.1.0-beta-8
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.
|
@@ -15,8 +15,8 @@ export declare class PlaywrightPulseReporter implements Reporter {
|
|
|
15
15
|
printsToStdio(): boolean;
|
|
16
16
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
17
17
|
onTestBegin(test: TestCase): void;
|
|
18
|
+
private getBrowserDetails;
|
|
18
19
|
private processStep;
|
|
19
|
-
getBrowserInfo(test: TestCase): string;
|
|
20
20
|
onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
|
|
21
21
|
onError(error: any): void;
|
|
22
22
|
private _writeShardResults;
|
|
@@ -39,7 +39,7 @@ const fs = __importStar(require("fs/promises"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const crypto_1 = require("crypto");
|
|
41
41
|
const attachment_utils_1 = require("./attachment-utils"); // Use relative path
|
|
42
|
-
const ua_parser_js_1 = require("ua-parser-js");
|
|
42
|
+
const ua_parser_js_1 = require("ua-parser-js"); // Added UAParser import
|
|
43
43
|
const convertStatus = (status, testCase) => {
|
|
44
44
|
if ((testCase === null || testCase === void 0 ? void 0 : testCase.expectedStatus) === "failed") {
|
|
45
45
|
return "failed";
|
|
@@ -87,14 +87,13 @@ 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;
|
|
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
|
|
98
97
|
.then(() => {
|
|
99
98
|
if (this.shardIndex === undefined || this.shardIndex === 0) {
|
|
100
99
|
console.log(`PlaywrightPulseReporter: Starting test run with ${suite.allTests().length} tests${this.isSharded ? ` across ${totalShards} shards` : ""}. Pulse outputting to ${this.outputDir}`);
|
|
@@ -107,11 +106,89 @@ class PlaywrightPulseReporter {
|
|
|
107
106
|
.catch((err) => console.error("Pulse Reporter: Error during initialization:", err));
|
|
108
107
|
}
|
|
109
108
|
onTestBegin(test) {
|
|
110
|
-
|
|
109
|
+
console.log(`Starting test: ${test.title}`);
|
|
111
110
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
getBrowserDetails(test) {
|
|
112
|
+
var _a, _b, _c, _d;
|
|
113
|
+
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project(); // project() can return undefined if not in a project context
|
|
114
|
+
const projectConfig = project === null || project === void 0 ? void 0 : project.use; // This is where options like userAgent, defaultBrowserType are
|
|
115
|
+
const userAgent = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.userAgent;
|
|
116
|
+
const configuredBrowserType = (_b = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.defaultBrowserType) === null || _b === void 0 ? void 0 : _b.toLowerCase();
|
|
117
|
+
// --- DEBUG LOGS (Uncomment if needed for diagnosing) ---
|
|
118
|
+
// console.log(`[PulseReporter DEBUG] Project: ${test.info().project.name}`);
|
|
119
|
+
// console.log(`[PulseReporter DEBUG] Configured Browser Type: "${configuredBrowserType}"`);
|
|
120
|
+
// console.log(`[PulseReporter DEBUG] User Agent for UAParser: "${userAgent}"`);
|
|
121
|
+
// --- END DEBUG LOGS ---
|
|
122
|
+
// if (!userAgent) {
|
|
123
|
+
// // Fallback if no user agent is available
|
|
124
|
+
// return configuredBrowserType
|
|
125
|
+
// ? configuredBrowserType.charAt(0).toUpperCase() +
|
|
126
|
+
// configuredBrowserType.slice(1)
|
|
127
|
+
// : "Unknown Browser";
|
|
128
|
+
// }
|
|
129
|
+
// try {
|
|
130
|
+
const parser = new ua_parser_js_1.UAParser(userAgent);
|
|
131
|
+
const result = parser.getResult();
|
|
132
|
+
// --- DEBUG LOGS (Uncomment if needed for diagnosing) ---
|
|
133
|
+
// console.log("[PulseReporter DEBUG] UAParser Result:", JSON.stringify(result, null, 2));
|
|
134
|
+
// --- END DEBUG LOGS ---
|
|
135
|
+
let browserName = result.browser.name;
|
|
136
|
+
const browserVersion = result.browser.version
|
|
137
|
+
? ` v${result.browser.version.split(".")[0]}`
|
|
138
|
+
: ""; // Major version
|
|
139
|
+
const osName = result.os.name ? ` on ${result.os.name}` : "";
|
|
140
|
+
const osVersion = result.os.version
|
|
141
|
+
? ` ${result.os.version.split(".")[0]}`
|
|
142
|
+
: ""; // Major version
|
|
143
|
+
const deviceType = result.device.type; // "mobile", "tablet", etc.
|
|
144
|
+
// If UAParser couldn't determine browser name, fallback to configured type
|
|
145
|
+
if (!browserName) {
|
|
146
|
+
browserName = configuredBrowserType
|
|
147
|
+
? configuredBrowserType.charAt(0).toUpperCase() +
|
|
148
|
+
configuredBrowserType.slice(1)
|
|
149
|
+
: "Unknown";
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// Specific refinements for mobile based on parsed OS and device type
|
|
153
|
+
if (deviceType === "mobile" || deviceType === "tablet") {
|
|
154
|
+
if ((_c = result.os.name) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes("android")) {
|
|
155
|
+
if (browserName.toLowerCase().includes("chrome"))
|
|
156
|
+
browserName = "Chrome Mobile";
|
|
157
|
+
else if (browserName.toLowerCase().includes("firefox"))
|
|
158
|
+
browserName = "Firefox Mobile";
|
|
159
|
+
else if (result.engine.name === "Blink" && !result.browser.name)
|
|
160
|
+
browserName = "Android WebView";
|
|
161
|
+
else if (browserName &&
|
|
162
|
+
!browserName.toLowerCase().includes("mobile")) {
|
|
163
|
+
// Keep it as is, e.g. "Samsung Browser" is specific enough
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
browserName = "Android Browser"; // default for android if not specific
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if ((_d = result.os.name) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes("ios")) {
|
|
170
|
+
browserName = "Mobile Safari";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (browserName === "Electron") {
|
|
174
|
+
browserName = "Electron App";
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
let finalString = `${browserName}${browserVersion}${osName}${osVersion}`;
|
|
178
|
+
return finalString.trim() || "Unknown Browser";
|
|
179
|
+
// } catch (error) {
|
|
180
|
+
// console.warn(
|
|
181
|
+
// `Pulse Reporter: Error parsing User-Agent string "${userAgent}":`,
|
|
182
|
+
// error
|
|
183
|
+
// );
|
|
184
|
+
// return configuredBrowserType
|
|
185
|
+
// ? configuredBrowserType.charAt(0).toUpperCase() +
|
|
186
|
+
// configuredBrowserType.slice(1)
|
|
187
|
+
// : "Unknown Browser";
|
|
188
|
+
// }
|
|
189
|
+
}
|
|
190
|
+
async processStep(step, testId, browserDetails, testCase) {
|
|
191
|
+
var _a, _b, _c, _d;
|
|
115
192
|
let stepStatus = "passed";
|
|
116
193
|
let errorMessage = ((_a = step.error) === null || _a === void 0 ? void 0 : _a.message) || undefined;
|
|
117
194
|
if ((_c = (_b = step.error) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.startsWith("Test is skipped:")) {
|
|
@@ -124,8 +201,7 @@ class PlaywrightPulseReporter {
|
|
|
124
201
|
const startTime = new Date(step.startTime);
|
|
125
202
|
const endTime = new Date(startTime.getTime() + Math.max(0, duration));
|
|
126
203
|
let codeLocation = "";
|
|
127
|
-
if (
|
|
128
|
-
// Check if file path exists
|
|
204
|
+
if (step.location) {
|
|
129
205
|
codeLocation = `${path.relative(this.config.rootDir, step.location.file)}:${step.location.line}:${step.location.column}`;
|
|
130
206
|
}
|
|
131
207
|
let stepTitle = step.title;
|
|
@@ -136,9 +212,9 @@ class PlaywrightPulseReporter {
|
|
|
136
212
|
duration: duration,
|
|
137
213
|
startTime: startTime,
|
|
138
214
|
endTime: endTime,
|
|
139
|
-
browser:
|
|
215
|
+
browser: browserDetails,
|
|
140
216
|
errorMessage: errorMessage,
|
|
141
|
-
stackTrace: ((
|
|
217
|
+
stackTrace: ((_d = step.error) === null || _d === void 0 ? void 0 : _d.stack) || undefined,
|
|
142
218
|
codeLocation: codeLocation || undefined,
|
|
143
219
|
isHook: step.category === "hook",
|
|
144
220
|
hookType: step.category === "hook"
|
|
@@ -146,107 +222,17 @@ class PlaywrightPulseReporter {
|
|
|
146
222
|
? "before"
|
|
147
223
|
: "after"
|
|
148
224
|
: undefined,
|
|
149
|
-
steps: [],
|
|
225
|
+
steps: [],
|
|
150
226
|
};
|
|
151
227
|
}
|
|
152
|
-
getBrowserInfo(test) {
|
|
153
|
-
var _a, _b, _c;
|
|
154
|
-
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
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;
|
|
168
|
-
if (userAgentString) {
|
|
169
|
-
try {
|
|
170
|
-
const parser = new ua_parser_js_1.UAParser(userAgentString);
|
|
171
|
-
const uaResult = parser.getResult();
|
|
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;
|
|
181
|
-
if (deviceType === "mobile" || deviceType === "tablet") {
|
|
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";
|
|
185
|
-
}
|
|
186
|
-
else if (parsedBrowserName === null || parsedBrowserName === void 0 ? void 0 : parsedBrowserName.toLowerCase().includes("firefox")) {
|
|
187
|
-
parsedBrowserName = "Firefox Mobile";
|
|
188
|
-
}
|
|
189
|
-
else if (uaResult.engine.name === "Blink" && !parsedBrowserName) {
|
|
190
|
-
parsedBrowserName = "Android WebView";
|
|
191
|
-
}
|
|
192
|
-
else if (parsedBrowserName) {
|
|
193
|
-
// Parsed name is likely okay
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
parsedBrowserName = "Android Browser";
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
else if (parsedOsName === null || parsedOsName === void 0 ? void 0 : parsedOsName.toLowerCase().includes("ios")) {
|
|
200
|
-
parsedBrowserName = "Mobile Safari";
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
else if (parsedBrowserName === "Electron") {
|
|
204
|
-
parsedBrowserName = "Electron App";
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
console.warn(`Pulse Reporter: Error parsing User-Agent string "${userAgentString}":`, error);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
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 =
|
|
220
|
-
configuredBrowserType.charAt(0).toUpperCase() +
|
|
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();
|
|
237
|
-
}
|
|
238
228
|
async onTestEnd(test, result) {
|
|
239
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
229
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
240
230
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
241
|
-
|
|
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 || "";
|
|
231
|
+
const browserDetails = this.getBrowserDetails(test);
|
|
246
232
|
const testStatus = convertStatus(result.status, test);
|
|
247
233
|
const startTime = new Date(result.startTime);
|
|
248
234
|
const endTime = new Date(startTime.getTime() + result.duration);
|
|
249
|
-
const testIdForFiles = test.id ||
|
|
235
|
+
const testIdForFiles = test.id ||
|
|
250
236
|
`${test
|
|
251
237
|
.titlePath()
|
|
252
238
|
.join("_")
|
|
@@ -254,54 +240,60 @@ class PlaywrightPulseReporter {
|
|
|
254
240
|
const processAllSteps = async (steps) => {
|
|
255
241
|
let processed = [];
|
|
256
242
|
for (const step of steps) {
|
|
257
|
-
const processedStep = await this.processStep(step, testIdForFiles,
|
|
258
|
-
test);
|
|
243
|
+
const processedStep = await this.processStep(step, testIdForFiles, browserDetails, test);
|
|
259
244
|
processed.push(processedStep);
|
|
260
245
|
if (step.steps && step.steps.length > 0) {
|
|
261
|
-
processedStep.steps = await processAllSteps(step.steps);
|
|
246
|
+
processedStep.steps = await processAllSteps(step.steps);
|
|
262
247
|
}
|
|
263
248
|
}
|
|
264
249
|
return processed;
|
|
265
250
|
};
|
|
266
251
|
let codeSnippet = undefined;
|
|
267
252
|
try {
|
|
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) {
|
|
253
|
+
if (((_b = test.location) === null || _b === void 0 ? void 0 : _b.file) && ((_c = test.location) === null || _c === void 0 ? void 0 : _c.line) && ((_d = test.location) === null || _d === void 0 ? void 0 : _d.column)) {
|
|
271
254
|
const relativePath = path.relative(this.config.rootDir, test.location.file);
|
|
272
255
|
codeSnippet = `Test defined at: ${relativePath}:${test.location.line}:${test.location.column}`;
|
|
273
256
|
}
|
|
274
257
|
}
|
|
275
258
|
catch (e) {
|
|
276
|
-
|
|
259
|
+
console.warn(`Pulse Reporter: Could not extract code snippet for ${test.title}`, e);
|
|
260
|
+
}
|
|
261
|
+
const stdoutMessages = [];
|
|
262
|
+
if (result.stdout && result.stdout.length > 0) {
|
|
263
|
+
result.stdout.forEach((item) => {
|
|
264
|
+
stdoutMessages.push(typeof item === "string" ? item : item.toString());
|
|
265
|
+
});
|
|
277
266
|
}
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
267
|
+
const stderrMessages = [];
|
|
268
|
+
if (result.stderr && result.stderr.length > 0) {
|
|
269
|
+
result.stderr.forEach((item) => {
|
|
270
|
+
stderrMessages.push(typeof item === "string" ? item : item.toString());
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
const uniqueTestId = test.id;
|
|
281
274
|
const pulseResult = {
|
|
282
275
|
id: uniqueTestId,
|
|
283
|
-
runId: "TBD",
|
|
276
|
+
runId: "TBD",
|
|
284
277
|
name: test.titlePath().join(" > "),
|
|
285
|
-
suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((
|
|
278
|
+
suiteName: (project === null || project === void 0 ? void 0 : project.name) || ((_e = this.config.projects[0]) === null || _e === void 0 ? void 0 : _e.name) || "Default Suite",
|
|
286
279
|
status: testStatus,
|
|
287
280
|
duration: result.duration,
|
|
288
281
|
startTime: startTime,
|
|
289
282
|
endTime: endTime,
|
|
290
|
-
browser:
|
|
283
|
+
browser: browserDetails,
|
|
291
284
|
retries: result.retry,
|
|
292
|
-
steps: ((
|
|
293
|
-
errorMessage: (
|
|
294
|
-
stackTrace: (
|
|
285
|
+
steps: ((_f = result.steps) === null || _f === void 0 ? void 0 : _f.length) ? await processAllSteps(result.steps) : [],
|
|
286
|
+
errorMessage: (_g = result.error) === null || _g === void 0 ? void 0 : _g.message,
|
|
287
|
+
stackTrace: (_h = result.error) === null || _h === void 0 ? void 0 : _h.stack,
|
|
295
288
|
codeSnippet: codeSnippet,
|
|
296
289
|
tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
|
|
297
|
-
screenshots: [],
|
|
298
|
-
videoPath: undefined,
|
|
299
|
-
tracePath: undefined,
|
|
290
|
+
screenshots: [],
|
|
291
|
+
videoPath: undefined,
|
|
292
|
+
tracePath: undefined,
|
|
300
293
|
stdout: stdoutMessages.length > 0 ? stdoutMessages : undefined,
|
|
301
294
|
stderr: stderrMessages.length > 0 ? stderrMessages : undefined,
|
|
302
295
|
};
|
|
303
296
|
try {
|
|
304
|
-
// IMPORTANT: attachFiles logic
|
|
305
297
|
(0, attachment_utils_1.attachFiles)(testIdForFiles, result, pulseResult, this.options);
|
|
306
298
|
}
|
|
307
299
|
catch (attachError) {
|
|
@@ -336,28 +328,27 @@ class PlaywrightPulseReporter {
|
|
|
336
328
|
console.error(`Pulse Reporter: Shard ${this.shardIndex} failed to write temporary results to ${tempFilePath}`, error);
|
|
337
329
|
}
|
|
338
330
|
}
|
|
339
|
-
async _mergeShardResults(finalRunData
|
|
340
|
-
) {
|
|
341
|
-
var _a, _b;
|
|
331
|
+
async _mergeShardResults(finalRunData) {
|
|
342
332
|
let allShardProcessedResults = [];
|
|
343
|
-
const totalShards =
|
|
333
|
+
const totalShards = this.config.shard ? this.config.shard.total : 1;
|
|
344
334
|
for (let i = 0; i < totalShards; i++) {
|
|
345
335
|
const tempFilePath = path.join(this.outputDir, `${TEMP_SHARD_FILE_PREFIX}${i}.json`);
|
|
346
336
|
try {
|
|
347
337
|
const content = await fs.readFile(tempFilePath, "utf-8");
|
|
348
|
-
const shardResults = JSON.parse(content);
|
|
349
|
-
allShardProcessedResults
|
|
338
|
+
const shardResults = JSON.parse(content);
|
|
339
|
+
allShardProcessedResults =
|
|
340
|
+
allShardProcessedResults.concat(shardResults);
|
|
350
341
|
}
|
|
351
342
|
catch (error) {
|
|
352
343
|
if ((error === null || error === void 0 ? void 0 : error.code) === "ENOENT") {
|
|
353
|
-
|
|
344
|
+
console.warn(`Pulse Reporter: Shard results file not found: ${tempFilePath}. This might be normal if a shard had no tests or failed early.`);
|
|
354
345
|
}
|
|
355
346
|
else {
|
|
356
347
|
console.error(`Pulse Reporter: Could not read/parse results from shard ${i} (${tempFilePath}). Error:`, error);
|
|
357
348
|
}
|
|
358
349
|
}
|
|
359
350
|
}
|
|
360
|
-
|
|
351
|
+
let finalUniqueResultsMap = new Map();
|
|
361
352
|
for (const result of allShardProcessedResults) {
|
|
362
353
|
const existing = finalUniqueResultsMap.get(result.id);
|
|
363
354
|
if (!existing || result.retries >= existing.retries) {
|
|
@@ -365,15 +356,23 @@ class PlaywrightPulseReporter {
|
|
|
365
356
|
}
|
|
366
357
|
}
|
|
367
358
|
const finalResultsList = Array.from(finalUniqueResultsMap.values());
|
|
368
|
-
finalResultsList.forEach((r) => (r.runId = finalRunData.id));
|
|
369
|
-
// Update the passed finalRunData object with aggregated stats
|
|
359
|
+
finalResultsList.forEach((r) => (r.runId = finalRunData.id));
|
|
370
360
|
finalRunData.passed = finalResultsList.filter((r) => r.status === "passed").length;
|
|
371
361
|
finalRunData.failed = finalResultsList.filter((r) => r.status === "failed").length;
|
|
372
362
|
finalRunData.skipped = finalResultsList.filter((r) => r.status === "skipped").length;
|
|
373
363
|
finalRunData.totalTests = finalResultsList.length;
|
|
364
|
+
const reviveDates = (key, value) => {
|
|
365
|
+
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
366
|
+
if (typeof value === "string" && isoDateRegex.test(value)) {
|
|
367
|
+
const date = new Date(value);
|
|
368
|
+
return !isNaN(date.getTime()) ? date : value;
|
|
369
|
+
}
|
|
370
|
+
return value;
|
|
371
|
+
};
|
|
372
|
+
const properlyTypedResults = JSON.parse(JSON.stringify(finalResultsList), reviveDates);
|
|
374
373
|
return {
|
|
375
|
-
run: finalRunData,
|
|
376
|
-
results:
|
|
374
|
+
run: finalRunData,
|
|
375
|
+
results: properlyTypedResults,
|
|
377
376
|
metadata: { generatedAt: new Date().toISOString() },
|
|
378
377
|
};
|
|
379
378
|
}
|
|
@@ -387,7 +386,7 @@ class PlaywrightPulseReporter {
|
|
|
387
386
|
}
|
|
388
387
|
catch (error) {
|
|
389
388
|
if ((error === null || error === void 0 ? void 0 : error.code) !== "ENOENT") {
|
|
390
|
-
|
|
389
|
+
console.warn("Pulse Reporter: Warning during cleanup of temporary files:", error.message);
|
|
391
390
|
}
|
|
392
391
|
}
|
|
393
392
|
}
|
|
@@ -403,7 +402,7 @@ class PlaywrightPulseReporter {
|
|
|
403
402
|
}
|
|
404
403
|
}
|
|
405
404
|
async onEnd(result) {
|
|
406
|
-
var _a, _b;
|
|
405
|
+
var _a, _b, _c;
|
|
407
406
|
if (this.shardIndex !== undefined) {
|
|
408
407
|
await this._writeShardResults();
|
|
409
408
|
return;
|
|
@@ -412,18 +411,16 @@ class PlaywrightPulseReporter {
|
|
|
412
411
|
const duration = runEndTime - this.runStartTime;
|
|
413
412
|
const runId = `run-${this.runStartTime}-${(0, crypto_1.randomUUID)()}`;
|
|
414
413
|
const runData = {
|
|
415
|
-
// This is the single source of truth for current run's data
|
|
416
414
|
id: runId,
|
|
417
|
-
timestamp: new Date(this.runStartTime),
|
|
415
|
+
timestamp: new Date(this.runStartTime),
|
|
418
416
|
totalTests: 0,
|
|
419
417
|
passed: 0,
|
|
420
418
|
failed: 0,
|
|
421
419
|
skipped: 0,
|
|
422
420
|
duration,
|
|
423
421
|
};
|
|
424
|
-
let finalReport;
|
|
422
|
+
let finalReport = undefined; // Initialize as undefined
|
|
425
423
|
if (this.isSharded) {
|
|
426
|
-
// _mergeShardResults will populate the runData object passed to it
|
|
427
424
|
finalReport = await this._mergeShardResults(runData);
|
|
428
425
|
}
|
|
429
426
|
else {
|
|
@@ -432,18 +429,37 @@ class PlaywrightPulseReporter {
|
|
|
432
429
|
runData.failed = this.results.filter((r) => r.status === "failed").length;
|
|
433
430
|
runData.skipped = this.results.filter((r) => r.status === "skipped").length;
|
|
434
431
|
runData.totalTests = this.results.length;
|
|
432
|
+
const reviveDates = (key, value) => {
|
|
433
|
+
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
434
|
+
if (typeof value === "string" && isoDateRegex.test(value)) {
|
|
435
|
+
const date = new Date(value);
|
|
436
|
+
return !isNaN(date.getTime()) ? date : value;
|
|
437
|
+
}
|
|
438
|
+
return value;
|
|
439
|
+
};
|
|
440
|
+
const properlyTypedResults = JSON.parse(JSON.stringify(this.results), reviveDates);
|
|
435
441
|
finalReport = {
|
|
436
|
-
run: runData,
|
|
437
|
-
results:
|
|
442
|
+
run: runData,
|
|
443
|
+
results: properlyTypedResults,
|
|
438
444
|
metadata: { generatedAt: new Date().toISOString() },
|
|
439
445
|
};
|
|
440
446
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
+
if (!finalReport) {
|
|
448
|
+
console.error("PlaywrightPulseReporter: CRITICAL - finalReport object was not generated. Cannot create summary.");
|
|
449
|
+
const errorSummary = `
|
|
450
|
+
PlaywrightPulseReporter: Run Finished
|
|
451
|
+
-----------------------------------------
|
|
452
|
+
Overall Status: ERROR (Report data missing)
|
|
453
|
+
Total Tests: N/A
|
|
454
|
+
Passed: N/A
|
|
455
|
+
Failed: N/A
|
|
456
|
+
Skipped: N/A
|
|
457
|
+
Duration: N/A
|
|
458
|
+
-----------------------------------------`;
|
|
459
|
+
if (this.printsToStdio()) {
|
|
460
|
+
console.log(errorSummary);
|
|
461
|
+
}
|
|
462
|
+
const errorReport = {
|
|
447
463
|
run: {
|
|
448
464
|
id: runId,
|
|
449
465
|
timestamp: new Date(this.runStartTime),
|
|
@@ -451,30 +467,28 @@ class PlaywrightPulseReporter {
|
|
|
451
467
|
passed: 0,
|
|
452
468
|
failed: 0,
|
|
453
469
|
skipped: 0,
|
|
454
|
-
duration,
|
|
470
|
+
duration: duration,
|
|
455
471
|
},
|
|
456
472
|
results: [],
|
|
457
473
|
metadata: {
|
|
458
474
|
generatedAt: new Date().toISOString(),
|
|
459
475
|
},
|
|
460
476
|
};
|
|
477
|
+
const finalOutputPathOnError = path.join(this.outputDir, this.baseOutputFile);
|
|
461
478
|
try {
|
|
462
|
-
const errorPath = path.join(this.outputDir, this.baseOutputFile);
|
|
463
479
|
await this._ensureDirExists(this.outputDir);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
console.warn(`PlaywrightPulseReporter: Wrote a minimal error report to ${errorPath}.`);
|
|
480
|
+
await fs.writeFile(finalOutputPathOnError, JSON.stringify(errorReport, null, 2));
|
|
481
|
+
console.warn(`PlaywrightPulseReporter: Wrote an error report to ${finalOutputPathOnError} as finalReport was missing.`);
|
|
467
482
|
}
|
|
468
|
-
catch (
|
|
469
|
-
console.error(
|
|
483
|
+
catch (writeError) {
|
|
484
|
+
console.error(`PlaywrightPulseReporter: Failed to write error report: ${writeError.message}`);
|
|
470
485
|
}
|
|
471
486
|
return;
|
|
472
487
|
}
|
|
473
|
-
// At this point, finalReport.run is guaranteed to be populated by either _mergeShardResults or the non-sharded path.
|
|
474
488
|
const reportRunData = finalReport.run;
|
|
475
|
-
const finalRunStatus = ((_a = reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
|
|
489
|
+
const finalRunStatus = ((_a = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed) !== null && _a !== void 0 ? _a : 0) > 0
|
|
476
490
|
? "failed"
|
|
477
|
-
: ((_b = reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
|
|
491
|
+
: ((_b = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) !== null && _b !== void 0 ? _b : 0) === 0 && result.status !== "passed"
|
|
478
492
|
? result.status === "interrupted"
|
|
479
493
|
? "interrupted"
|
|
480
494
|
: "no tests or error"
|
|
@@ -483,11 +497,11 @@ class PlaywrightPulseReporter {
|
|
|
483
497
|
PlaywrightPulseReporter: Run Finished
|
|
484
498
|
-----------------------------------------
|
|
485
499
|
Overall Status: ${finalRunStatus.toUpperCase()}
|
|
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
|
|
500
|
+
Total Tests: ${(reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.totalTests) || 0}
|
|
501
|
+
Passed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.passed}
|
|
502
|
+
Failed: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.failed}
|
|
503
|
+
Skipped: ${reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.skipped}
|
|
504
|
+
Duration: ${(((_c = reportRunData === null || reportRunData === void 0 ? void 0 : reportRunData.duration) !== null && _c !== void 0 ? _c : 0) / 1000).toFixed(2)}s
|
|
491
505
|
-----------------------------------------`;
|
|
492
506
|
if (this.printsToStdio()) {
|
|
493
507
|
console.log(summary);
|
|
@@ -495,11 +509,11 @@ PlaywrightPulseReporter: Run Finished
|
|
|
495
509
|
const finalOutputPath = path.join(this.outputDir, this.baseOutputFile);
|
|
496
510
|
try {
|
|
497
511
|
await this._ensureDirExists(this.outputDir);
|
|
498
|
-
// Custom replacer for JSON.stringify to handle Date objects correctly
|
|
499
512
|
await fs.writeFile(finalOutputPath, JSON.stringify(finalReport, (key, value) => {
|
|
500
|
-
if (value instanceof Date)
|
|
513
|
+
if (value instanceof Date)
|
|
501
514
|
return value.toISOString();
|
|
502
|
-
|
|
515
|
+
if (typeof value === "bigint")
|
|
516
|
+
return value.toString();
|
|
503
517
|
return value;
|
|
504
518
|
}, 2));
|
|
505
519
|
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-8",
|
|
5
5
|
"description": "A Playwright reporter and dashboard for visualizing test results.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"playwright",
|
|
@@ -45,25 +45,65 @@
|
|
|
45
45
|
"report:email": "node ./scripts/sendReport.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"@genkit-ai/googleai": "^1.6.2",
|
|
49
|
+
"@genkit-ai/next": "^1.6.2",
|
|
50
|
+
"@hookform/resolvers": "^4.1.3",
|
|
51
|
+
"@radix-ui/react-accordion": "^1.2.3",
|
|
52
|
+
"@radix-ui/react-alert-dialog": "^1.1.6",
|
|
53
|
+
"@radix-ui/react-avatar": "^1.1.3",
|
|
54
|
+
"@radix-ui/react-checkbox": "^1.1.4",
|
|
55
|
+
"@radix-ui/react-dialog": "^1.1.6",
|
|
56
|
+
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
|
57
|
+
"@radix-ui/react-label": "^2.1.2",
|
|
58
|
+
"@radix-ui/react-menubar": "^1.1.6",
|
|
59
|
+
"@radix-ui/react-popover": "^1.1.6",
|
|
60
|
+
"@radix-ui/react-progress": "^1.1.2",
|
|
61
|
+
"@radix-ui/react-radio-group": "^1.2.3",
|
|
62
|
+
"@radix-ui/react-scroll-area": "^1.2.3",
|
|
63
|
+
"@radix-ui/react-select": "^2.1.6",
|
|
64
|
+
"@radix-ui/react-separator": "^1.1.2",
|
|
65
|
+
"@radix-ui/react-slider": "^1.2.3",
|
|
66
|
+
"@radix-ui/react-slot": "^1.1.2",
|
|
67
|
+
"@radix-ui/react-switch": "^1.1.3",
|
|
68
|
+
"@radix-ui/react-tabs": "^1.1.3",
|
|
69
|
+
"@radix-ui/react-toast": "^1.2.6",
|
|
70
|
+
"@radix-ui/react-tooltip": "^1.1.8",
|
|
71
|
+
"@tanstack-query-firebase/react": "^1.0.5",
|
|
72
|
+
"@tanstack/react-query": "^5.66.0",
|
|
48
73
|
"archiver": "^7.0.1",
|
|
49
74
|
"class-variance-authority": "^0.7.1",
|
|
50
75
|
"clsx": "^2.1.1",
|
|
51
76
|
"d3": "^7.9.0",
|
|
52
77
|
"date-fns": "^3.6.0",
|
|
53
78
|
"dotenv": "^16.5.0",
|
|
79
|
+
"firebase": "^11.3.0",
|
|
80
|
+
"genkit": "^1.6.2",
|
|
54
81
|
"highcharts": "^12.2.0",
|
|
55
82
|
"jsdom": "^26.1.0",
|
|
56
83
|
"lucide-react": "^0.475.0",
|
|
84
|
+
"next": "15.2.3",
|
|
57
85
|
"nodemailer": "^7.0.3",
|
|
58
86
|
"patch-package": "^8.0.0",
|
|
87
|
+
"react": "^18.3.1",
|
|
88
|
+
"react-day-picker": "^8.10.1",
|
|
89
|
+
"react-dom": "^18.3.1",
|
|
90
|
+
"react-hook-form": "^7.54.2",
|
|
59
91
|
"recharts": "^2.15.1",
|
|
92
|
+
"tailwind-merge": "^3.0.1",
|
|
93
|
+
"tailwindcss-animate": "^1.0.7",
|
|
60
94
|
"ua-parser-js": "^2.0.3",
|
|
61
95
|
"zod": "^3.24.2"
|
|
62
96
|
},
|
|
63
97
|
"devDependencies": {
|
|
64
98
|
"@types/node": "^20",
|
|
99
|
+
"@types/react": "^18",
|
|
100
|
+
"@types/react-dom": "^18",
|
|
65
101
|
"@types/ua-parser-js": "^0.7.39",
|
|
66
102
|
"eslint": "9.25.1",
|
|
103
|
+
"eslint-config-next": "15.3.1",
|
|
104
|
+
"genkit-cli": "^1.6.1",
|
|
105
|
+
"postcss": "^8",
|
|
106
|
+
"tailwindcss": "^3.4.1",
|
|
67
107
|
"typescript": "^5"
|
|
68
108
|
},
|
|
69
109
|
"engines": {
|