@arghajit/playwright-pulse-report 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -55
- package/dist/reporter/playwright-pulse-reporter.d.ts +2 -0
- package/dist/reporter/playwright-pulse-reporter.js +229 -116
- package/dist/types/index.d.ts +17 -0
- package/package.json +12 -52
- package/scripts/generate-email-report.mjs +714 -0
- package/scripts/generate-report.mjs +2277 -0
- package/scripts/generate-static-report.mjs +1515 -1436
- package/scripts/generate-trend.mjs +165 -0
- package/scripts/merge-pulse-report.js +1 -0
- package/scripts/{sendReport.js → sendReport.mjs} +138 -71
- package/screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max-1.png +0 -0
- package/screenshots/127-0-0-1-5500-pulse-report-output-playwright-pulse-static-report-html-i-Phone-14-Pro-Max.png +0 -0
- package/screenshots/Email-report.jpg +0 -0
- package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-1.png +0 -0
- package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html-2.png +0 -0
- package/screenshots/Users-arghajitsingha-Downloads-pulse-report-1-playwright-pulse-static-report-html.png +0 -0
- package/screenshots/image.png +0 -0
- package/scripts/generate-trend-excel.mjs +0 -273
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
// generate-trend-excel.mjs
|
|
2
|
-
import * as fs from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import * as XLSX from "xlsx";
|
|
5
|
-
|
|
6
|
-
// Use dynamic import for chalk as it's ESM only for prettier console logs
|
|
7
|
-
let chalk;
|
|
8
|
-
try {
|
|
9
|
-
chalk = (await import("chalk")).default;
|
|
10
|
-
} catch (e) {
|
|
11
|
-
chalk = { green: (t) => t, red: (t) => t, yellow: (t) => t, blue: (t) => t }; // Basic fallback
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const DEFAULT_OUTPUT_DIR = "pulse-report"; // Should match reporter's outputDir
|
|
15
|
-
const DEFAULT_JSON_FILE = "playwright-pulse-report.json";
|
|
16
|
-
const TREND_EXCEL_FILE_NAME = "trend.xls";
|
|
17
|
-
|
|
18
|
-
class ExcelTrendManager {
|
|
19
|
-
_excelFilePath;
|
|
20
|
-
_maxRuns = 5;
|
|
21
|
-
|
|
22
|
-
constructor(outputDir, excelFileName = TREND_EXCEL_FILE_NAME) {
|
|
23
|
-
this._excelFilePath = path.join(outputDir, excelFileName);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getExcelFilePath() {
|
|
27
|
-
return this._excelFilePath;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async _readExistingData() {
|
|
31
|
-
try {
|
|
32
|
-
await fs.access(this._excelFilePath);
|
|
33
|
-
const buffer = await fs.readFile(this._excelFilePath);
|
|
34
|
-
return XLSX.read(buffer, { type: "buffer" });
|
|
35
|
-
} catch {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
_shiftOverallRuns(data, currentNumericRunId) {
|
|
41
|
-
const validData = Array.isArray(data) ? data : [];
|
|
42
|
-
const pastOrCurrentData = validData.filter(
|
|
43
|
-
(row) =>
|
|
44
|
-
row.hasOwnProperty("RUN_ID") &&
|
|
45
|
-
typeof row.RUN_ID === "number" &&
|
|
46
|
-
row.RUN_ID <= currentNumericRunId
|
|
47
|
-
);
|
|
48
|
-
const sortedData = [...pastOrCurrentData].sort(
|
|
49
|
-
(a, b) => a.RUN_ID - b.RUN_ID
|
|
50
|
-
);
|
|
51
|
-
if (sortedData.length > this._maxRuns) {
|
|
52
|
-
return sortedData.slice(sortedData.length - this._maxRuns);
|
|
53
|
-
}
|
|
54
|
-
return sortedData;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async updateTrendData(
|
|
58
|
-
runIdFromReport,
|
|
59
|
-
timestamp,
|
|
60
|
-
totalTests,
|
|
61
|
-
passed,
|
|
62
|
-
failed,
|
|
63
|
-
skipped,
|
|
64
|
-
duration,
|
|
65
|
-
testResultsForThisRun
|
|
66
|
-
) {
|
|
67
|
-
let workbook = await this._readExistingData();
|
|
68
|
-
const numericRunId = Math.floor(timestamp / 1000);
|
|
69
|
-
|
|
70
|
-
if (!workbook) {
|
|
71
|
-
workbook = XLSX.utils.book_new();
|
|
72
|
-
// If the workbook is new, SheetNames will be empty.
|
|
73
|
-
// We need to initialize it if it doesn't exist
|
|
74
|
-
if (!workbook.SheetNames) {
|
|
75
|
-
workbook.SheetNames = [];
|
|
76
|
-
}
|
|
77
|
-
} else {
|
|
78
|
-
// Ensure SheetNames exists even for existing workbooks (should, but defensive)
|
|
79
|
-
if (!workbook.SheetNames) {
|
|
80
|
-
workbook.SheetNames = [];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// --- Overall Data ---
|
|
85
|
-
let existingOverallData = [];
|
|
86
|
-
if (workbook.Sheets["overall"]) {
|
|
87
|
-
try {
|
|
88
|
-
existingOverallData = XLSX.utils.sheet_to_json(
|
|
89
|
-
workbook.Sheets["overall"]
|
|
90
|
-
);
|
|
91
|
-
} catch (e) {
|
|
92
|
-
console.warn(
|
|
93
|
-
chalk.yellow(
|
|
94
|
-
"Could not parse existing 'overall' sheet. Starting fresh. Error:",
|
|
95
|
-
e.message
|
|
96
|
-
)
|
|
97
|
-
);
|
|
98
|
-
existingOverallData = [];
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
existingOverallData = existingOverallData.filter(
|
|
102
|
-
(row) => row.RUN_ID !== numericRunId
|
|
103
|
-
);
|
|
104
|
-
const newOverallRow = {
|
|
105
|
-
RUN_ID: numericRunId,
|
|
106
|
-
DURATION: duration,
|
|
107
|
-
TIMESTAMP: timestamp,
|
|
108
|
-
TOTAL_TESTS: totalTests,
|
|
109
|
-
PASSED: passed,
|
|
110
|
-
FAILED: failed,
|
|
111
|
-
SKIPPED: skipped,
|
|
112
|
-
};
|
|
113
|
-
let updatedOverallData = [...existingOverallData, newOverallRow];
|
|
114
|
-
updatedOverallData = this._shiftOverallRuns(
|
|
115
|
-
updatedOverallData,
|
|
116
|
-
numericRunId
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const overallSheet = XLSX.utils.json_to_sheet(updatedOverallData);
|
|
120
|
-
|
|
121
|
-
// UPDATED: Use book_append_sheet for new sheets, or replace existing
|
|
122
|
-
if (!workbook.SheetNames.includes("overall")) {
|
|
123
|
-
XLSX.utils.book_append_sheet(workbook, overallSheet, "overall");
|
|
124
|
-
// Move "overall" to the beginning if it was just added and not already first
|
|
125
|
-
const overallIndex = workbook.SheetNames.indexOf("overall");
|
|
126
|
-
if (overallIndex > 0) {
|
|
127
|
-
const sheetName = workbook.SheetNames.splice(overallIndex, 1)[0];
|
|
128
|
-
workbook.SheetNames.unshift(sheetName);
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
workbook.Sheets["overall"] = overallSheet; // Replace existing
|
|
132
|
-
}
|
|
133
|
-
XLSX.utils.book_set_sheet_visibility(workbook, "overall", 0);
|
|
134
|
-
|
|
135
|
-
// --- Per-Test Data Sheet for the Current Run ---
|
|
136
|
-
const runKey = `test run ${numericRunId}`;
|
|
137
|
-
const currentRunTestData = testResultsForThisRun.map((test) => ({
|
|
138
|
-
TEST_NAME: test.name,
|
|
139
|
-
DURATION: test.duration,
|
|
140
|
-
STATUS: test.status,
|
|
141
|
-
TIMESTAMP: timestamp,
|
|
142
|
-
}));
|
|
143
|
-
const testRunSheet = XLSX.utils.json_to_sheet(currentRunTestData);
|
|
144
|
-
|
|
145
|
-
// UPDATED: Logic to add or replace the sheet and ensure it's in SheetNames
|
|
146
|
-
if (!workbook.SheetNames.includes(runKey)) {
|
|
147
|
-
XLSX.utils.book_append_sheet(workbook, testRunSheet, runKey); // This adds to Sheets and SheetNames
|
|
148
|
-
} else {
|
|
149
|
-
workbook.Sheets[runKey] = testRunSheet; // Just replace the sheet data
|
|
150
|
-
}
|
|
151
|
-
// Now that the sheet is guaranteed to be in SheetNames and workbook.Sheets, set visibility
|
|
152
|
-
XLSX.utils.book_set_sheet_visibility(workbook, runKey, 0);
|
|
153
|
-
|
|
154
|
-
// --- Maintain Max Sheet Count for Individual Test Runs ---
|
|
155
|
-
let testRunSheetNames = workbook.SheetNames.filter(
|
|
156
|
-
(name) => name.toLowerCase().startsWith("test run ") && name !== "overall"
|
|
157
|
-
);
|
|
158
|
-
testRunSheetNames.sort((a, b) => {
|
|
159
|
-
const matchA = a.match(/test run (\d+)$/i);
|
|
160
|
-
const matchB = b.match(/test run (\d+)$/i);
|
|
161
|
-
const idA = matchA && matchA[1] ? parseInt(matchA[1], 10) : 0;
|
|
162
|
-
const idB = matchB && matchB[1] ? parseInt(matchB[1], 10) : 0;
|
|
163
|
-
return idA - idB;
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
if (testRunSheetNames.length > this._maxRuns) {
|
|
167
|
-
const sheetsToRemoveCount = testRunSheetNames.length - this._maxRuns;
|
|
168
|
-
const removedSheetNames = [];
|
|
169
|
-
for (let i = 0; i < sheetsToRemoveCount; i++) {
|
|
170
|
-
const oldestSheetName = testRunSheetNames[i];
|
|
171
|
-
// Remove from workbook.Sheets
|
|
172
|
-
delete workbook.Sheets[oldestSheetName];
|
|
173
|
-
removedSheetNames.push(oldestSheetName);
|
|
174
|
-
}
|
|
175
|
-
// Rebuild SheetNames array without the removed sheets
|
|
176
|
-
workbook.SheetNames = workbook.SheetNames.filter(
|
|
177
|
-
(name) => !removedSheetNames.includes(name)
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// --- Write Workbook ---
|
|
182
|
-
try {
|
|
183
|
-
const buffer = XLSX.write(workbook, { bookType: "xls", type: "buffer" });
|
|
184
|
-
await fs.writeFile(this._excelFilePath, buffer);
|
|
185
|
-
console.log(
|
|
186
|
-
chalk.green(
|
|
187
|
-
`Excel trend report updated successfully at ${this._excelFilePath}`
|
|
188
|
-
)
|
|
189
|
-
);
|
|
190
|
-
} catch (writeError) {
|
|
191
|
-
console.error(
|
|
192
|
-
chalk.red(`Failed to write Excel file at ${this._excelFilePath}`)
|
|
193
|
-
);
|
|
194
|
-
console.error(chalk.red("Write Error Details:"), writeError);
|
|
195
|
-
throw writeError;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
async function generateTrendExcel() {
|
|
201
|
-
const outputDir = path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
202
|
-
const jsonReportPath = path.join(outputDir, DEFAULT_JSON_FILE);
|
|
203
|
-
|
|
204
|
-
// Ensure output directory exists before any file operations
|
|
205
|
-
try {
|
|
206
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
207
|
-
} catch (mkdirError) {
|
|
208
|
-
console.error(
|
|
209
|
-
chalk.red(`Failed to create output directory ${outputDir}:`),
|
|
210
|
-
mkdirError
|
|
211
|
-
);
|
|
212
|
-
process.exit(1);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
console.log(chalk.blue(`Reading JSON report from: ${jsonReportPath}`));
|
|
216
|
-
let reportData;
|
|
217
|
-
try {
|
|
218
|
-
const jsonData = await fs.readFile(jsonReportPath, "utf-8");
|
|
219
|
-
reportData = JSON.parse(jsonData);
|
|
220
|
-
if (!reportData || !reportData.run || !Array.isArray(reportData.results)) {
|
|
221
|
-
throw new Error(
|
|
222
|
-
"Invalid JSON report structure. Missing 'run' or 'results' data."
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
} catch (error) {
|
|
226
|
-
console.error(
|
|
227
|
-
chalk.red(`Error reading or parsing JSON report: ${error.message}`)
|
|
228
|
-
);
|
|
229
|
-
console.error(chalk.red("JSON Read/Parse Error Details:"), error);
|
|
230
|
-
process.exit(1);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const { run, results } = reportData;
|
|
234
|
-
if (!run.timestamp || isNaN(new Date(run.timestamp).getTime())) {
|
|
235
|
-
console.error(
|
|
236
|
-
chalk.red(`Invalid or missing run.timestamp in JSON: ${run.timestamp}`)
|
|
237
|
-
);
|
|
238
|
-
process.exit(1);
|
|
239
|
-
}
|
|
240
|
-
const runTimestamp = new Date(run.timestamp).getTime();
|
|
241
|
-
|
|
242
|
-
const testResultsForExcel = results.map((r) => ({
|
|
243
|
-
name: r.name,
|
|
244
|
-
duration: r.duration,
|
|
245
|
-
status: r.status,
|
|
246
|
-
}));
|
|
247
|
-
|
|
248
|
-
const excelManager = new ExcelTrendManager(outputDir);
|
|
249
|
-
try {
|
|
250
|
-
await excelManager.updateTrendData(
|
|
251
|
-
run.id,
|
|
252
|
-
runTimestamp,
|
|
253
|
-
run.totalTests,
|
|
254
|
-
run.passed,
|
|
255
|
-
run.failed,
|
|
256
|
-
run.skipped,
|
|
257
|
-
run.duration,
|
|
258
|
-
testResultsForExcel
|
|
259
|
-
);
|
|
260
|
-
} catch (excelError) {
|
|
261
|
-
console.error(chalk.red("Aborting due to error during Excel generation."));
|
|
262
|
-
console.error(chalk.red("Excel Generation Error Details:"), excelError);
|
|
263
|
-
process.exit(1);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
generateTrendExcel().catch((error) => {
|
|
268
|
-
console.error(
|
|
269
|
-
chalk.red("An unexpected error occurred in generate-trend-excel:"),
|
|
270
|
-
error
|
|
271
|
-
);
|
|
272
|
-
process.exit(1);
|
|
273
|
-
});
|