@arghajit/playwright-pulse-report 0.1.6 → 0.2.1
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 +23 -6
- package/dist/reporter/playwright-pulse-reporter.js +148 -118
- package/dist/types/index.d.ts +3 -1
- package/package.json +5 -2
- 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-static-report.mjs +1848 -1262
- package/scripts/generate-trend.mjs +165 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
// XLSX is NO LONGER NEEDED here
|
|
4
|
+
|
|
5
|
+
// Use dynamic import for chalk as it's ESM only for prettier console logs
|
|
6
|
+
let chalk;
|
|
7
|
+
try {
|
|
8
|
+
chalk = (await import("chalk")).default;
|
|
9
|
+
} catch (e) {
|
|
10
|
+
chalk = {
|
|
11
|
+
green: (t) => t,
|
|
12
|
+
red: (t) => t,
|
|
13
|
+
yellow: (t) => t,
|
|
14
|
+
blue: (t) => t,
|
|
15
|
+
bold: (t) => t,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DEFAULT_OUTPUT_DIR = "pulse-report";
|
|
20
|
+
const CURRENT_RUN_JSON_FILE = "playwright-pulse-report.json"; // Source of the current run data
|
|
21
|
+
const HISTORY_SUBDIR = "history"; // Subdirectory for historical JSON files
|
|
22
|
+
const HISTORY_FILE_PREFIX = "trend-";
|
|
23
|
+
const MAX_HISTORY_FILES = 15; // Store last 15 runs
|
|
24
|
+
|
|
25
|
+
async function archiveCurrentRunData() {
|
|
26
|
+
const outputDir = path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
|
|
27
|
+
const currentRunJsonPath = path.join(outputDir, CURRENT_RUN_JSON_FILE);
|
|
28
|
+
const historyDir = path.join(outputDir, HISTORY_SUBDIR);
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// 1. Ensure history directory exists
|
|
32
|
+
await fs.mkdir(historyDir, { recursive: true });
|
|
33
|
+
// console.log(chalk.blue(`History directory ensured at: ${historyDir}`));
|
|
34
|
+
|
|
35
|
+
// 2. Read the current run's JSON data
|
|
36
|
+
// console.log(chalk.blue(`Reading current run data from: ${currentRunJsonPath}`));
|
|
37
|
+
let currentReportData;
|
|
38
|
+
try {
|
|
39
|
+
const jsonData = await fs.readFile(currentRunJsonPath, "utf-8");
|
|
40
|
+
currentReportData = JSON.parse(jsonData);
|
|
41
|
+
if (
|
|
42
|
+
!currentReportData ||
|
|
43
|
+
!currentReportData.run ||
|
|
44
|
+
!currentReportData.run.timestamp
|
|
45
|
+
) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"Invalid current run JSON report structure. Missing 'run' or 'run.timestamp' data."
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(
|
|
52
|
+
chalk.red(
|
|
53
|
+
`Error reading or parsing current run JSON report at ${currentRunJsonPath}: ${error.message}`
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
process.exit(1); // Exit if we can't read the source file
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 3. Determine the filename for the new history file
|
|
60
|
+
// Ensure timestamp is a valid number before using getTime()
|
|
61
|
+
let runTimestampMs;
|
|
62
|
+
try {
|
|
63
|
+
runTimestampMs = new Date(currentReportData.run.timestamp).getTime();
|
|
64
|
+
if (isNaN(runTimestampMs)) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Invalid timestamp value: ${currentReportData.run.timestamp}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
} catch (dateError) {
|
|
70
|
+
console.error(
|
|
71
|
+
chalk.red(
|
|
72
|
+
`Failed to parse timestamp '${currentReportData.run.timestamp}': ${dateError.message}`
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const newHistoryFileName = `${HISTORY_FILE_PREFIX}${runTimestampMs}.json`;
|
|
79
|
+
const newHistoryFilePath = path.join(historyDir, newHistoryFileName);
|
|
80
|
+
|
|
81
|
+
// 4. Write the current run's data to the new history file
|
|
82
|
+
// console.log(chalk.blue(`Saving current run data to: ${newHistoryFilePath}`));
|
|
83
|
+
await fs.writeFile(
|
|
84
|
+
newHistoryFilePath,
|
|
85
|
+
JSON.stringify(currentReportData, null, 2),
|
|
86
|
+
"utf-8"
|
|
87
|
+
);
|
|
88
|
+
console.log(chalk.green(`Archived current run to: ${newHistoryFilePath}`));
|
|
89
|
+
|
|
90
|
+
// 5. Prune old history files
|
|
91
|
+
await pruneOldHistoryFiles(historyDir);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(
|
|
94
|
+
chalk.red(`Error in archiveCurrentRunData: ${error.message}`)
|
|
95
|
+
);
|
|
96
|
+
// console.error(error.stack); // Uncomment for more detailed stack trace
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function pruneOldHistoryFiles(historyDir) {
|
|
102
|
+
// console.log(chalk.blue(`Pruning old history files in ${historyDir} (keeping last ${MAX_HISTORY_FILES})...`));
|
|
103
|
+
try {
|
|
104
|
+
const files = await fs.readdir(historyDir);
|
|
105
|
+
const historyJsonFiles = files
|
|
106
|
+
.filter(
|
|
107
|
+
(file) => file.startsWith(HISTORY_FILE_PREFIX) && file.endsWith(".json")
|
|
108
|
+
)
|
|
109
|
+
.map((file) => {
|
|
110
|
+
const timestampPart = file
|
|
111
|
+
.replace(HISTORY_FILE_PREFIX, "")
|
|
112
|
+
.replace(".json", "");
|
|
113
|
+
return { name: file, timestamp: parseInt(timestampPart, 10) };
|
|
114
|
+
})
|
|
115
|
+
.filter((file) => !isNaN(file.timestamp))
|
|
116
|
+
.sort((a, b) => a.timestamp - b.timestamp); // Sort ascending (oldest first)
|
|
117
|
+
|
|
118
|
+
if (historyJsonFiles.length > MAX_HISTORY_FILES) {
|
|
119
|
+
const filesToDelete = historyJsonFiles.slice(
|
|
120
|
+
0,
|
|
121
|
+
historyJsonFiles.length - MAX_HISTORY_FILES
|
|
122
|
+
);
|
|
123
|
+
console.log(
|
|
124
|
+
chalk.yellow(
|
|
125
|
+
`Found ${historyJsonFiles.length} history files. Pruning ${filesToDelete.length} oldest file(s)...`
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
for (const fileMeta of filesToDelete) {
|
|
129
|
+
const filePathToDelete = path.join(historyDir, fileMeta.name);
|
|
130
|
+
try {
|
|
131
|
+
await fs.unlink(filePathToDelete);
|
|
132
|
+
// console.log(chalk.gray(`Deleted old history file: ${fileMeta.name}`));
|
|
133
|
+
} catch (deleteError) {
|
|
134
|
+
console.warn(
|
|
135
|
+
chalk.yellow(
|
|
136
|
+
`Could not delete old history file ${fileMeta.name}: ${deleteError.message}`
|
|
137
|
+
)
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
// console.log(chalk.green(`Found ${historyJsonFiles.length} history files. No pruning needed.`));
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.warn(
|
|
146
|
+
chalk.yellow(
|
|
147
|
+
`Warning during history pruning in ${historyDir}: ${error.message}`
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
// Don't exit for pruning errors, as saving the current run is more critical
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Main execution
|
|
155
|
+
archiveCurrentRunData().catch((error) => {
|
|
156
|
+
// Fallback catch, though critical errors in archiveCurrentRunData should exit
|
|
157
|
+
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
158
|
+
// check if not already exited
|
|
159
|
+
console.error(
|
|
160
|
+
chalk.red.bold("An unexpected error occurred in history archiving:"),
|
|
161
|
+
error
|
|
162
|
+
);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
});
|