@arghajit/playwright-pulse-report
Version:
A Playwright reporter and dashboard for visualizing test results.
166 lines (153 loc) • 5.57 kB
JavaScript
import * as fs from "fs/promises";
import path from "path";
// Use dynamic import for chalk as it's ESM only for prettier console logs
let chalk;
try {
chalk = (await import("chalk")).default;
} catch (e) {
chalk = {
green: (t) => t,
red: (t) => t,
yellow: (t) => t,
blue: (t) => t,
bold: (t) => t,
};
}
const DEFAULT_OUTPUT_DIR = "pulse-report";
const CURRENT_RUN_JSON_FILE = "playwright-pulse-report.json"; // Source of the current run data
const HISTORY_SUBDIR = "history"; // Subdirectory for historical JSON files
const HISTORY_FILE_PREFIX = "trend-";
const MAX_HISTORY_FILES = 15; // Store last 15 runs
async function archiveCurrentRunData() {
const outputDir = path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
const currentRunJsonPath = path.join(outputDir, CURRENT_RUN_JSON_FILE);
const historyDir = path.join(outputDir, HISTORY_SUBDIR);
try {
// 1. Ensure history directory exists
await fs.mkdir(historyDir, { recursive: true });
// console.log(chalk.blue(`History directory ensured at: ${historyDir}`));
// 2. Read the current run's JSON data
// console.log(chalk.blue(`Reading current run data from: ${currentRunJsonPath}`));
let currentReportData;
try {
const jsonData = await fs.readFile(currentRunJsonPath, "utf-8");
currentReportData = JSON.parse(jsonData);
if (
!currentReportData ||
!currentReportData.run ||
!currentReportData.run.timestamp
) {
throw new Error(
"Invalid current run JSON report structure. Missing 'run' or 'run.timestamp' data."
);
}
} catch (error) {
console.error(
chalk.red(
`Error reading or parsing current run JSON report at ${currentRunJsonPath}: ${error.message}`
)
);
process.exit(1); // Exit if we can't read the source file
}
// 3. Determine the filename for the new history file
// Ensure timestamp is a valid number before using getTime()
let runTimestampMs;
try {
runTimestampMs = new Date(currentReportData.run.timestamp).getTime();
if (isNaN(runTimestampMs)) {
throw new Error(
`Invalid timestamp value: ${currentReportData.run.timestamp}`
);
}
} catch (dateError) {
console.error(
chalk.red(
`Failed to parse timestamp '${currentReportData.run.timestamp}': ${dateError.message}`
)
);
process.exit(1);
}
const newHistoryFileName = `${HISTORY_FILE_PREFIX}${runTimestampMs}.json`;
const newHistoryFilePath = path.join(historyDir, newHistoryFileName);
// 4. Write the current run's data to the new history file
// console.log(chalk.blue(`Saving current run data to: ${newHistoryFilePath}`));
await fs.writeFile(
newHistoryFilePath,
JSON.stringify(currentReportData, null, 2),
"utf-8"
);
console.log(chalk.green(`Archived current run to: ${newHistoryFilePath}`));
// 5. Prune old history files
await pruneOldHistoryFiles(historyDir);
} catch (error) {
console.error(
chalk.red(`Error in archiveCurrentRunData: ${error.message}`)
);
// console.error(error.stack); // Uncomment for more detailed stack trace
process.exit(1);
}
}
async function pruneOldHistoryFiles(historyDir) {
// console.log(chalk.blue(`Pruning old history files in ${historyDir} (keeping last ${MAX_HISTORY_FILES})...`));
try {
const files = await fs.readdir(historyDir);
const historyJsonFiles = files
.filter(
(file) => file.startsWith(HISTORY_FILE_PREFIX) && file.endsWith(".json")
)
.map((file) => {
const timestampPart = file
.replace(HISTORY_FILE_PREFIX, "")
.replace(".json", "");
return { name: file, timestamp: parseInt(timestampPart, 10) };
})
.filter((file) => !isNaN(file.timestamp))
.sort((a, b) => a.timestamp - b.timestamp); // Sort ascending (oldest first)
if (historyJsonFiles.length > MAX_HISTORY_FILES) {
const filesToDelete = historyJsonFiles.slice(
0,
historyJsonFiles.length - MAX_HISTORY_FILES
);
console.log(
chalk.yellow(
`Found ${historyJsonFiles.length} history files. Pruning ${filesToDelete.length} oldest file(s)...`
)
);
for (const fileMeta of filesToDelete) {
const filePathToDelete = path.join(historyDir, fileMeta.name);
try {
await fs.unlink(filePathToDelete);
// console.log(chalk.gray(`Deleted old history file: ${fileMeta.name}`));
} catch (deleteError) {
console.warn(
chalk.yellow(
`Could not delete old history file ${fileMeta.name}: ${deleteError.message}`
)
);
}
}
} else {
// console.log(chalk.green(`Found ${historyJsonFiles.length} history files. No pruning needed.`));
}
} catch (error) {
console.warn(
chalk.yellow(
`Warning during history pruning in ${historyDir}: ${error.message}`
)
);
// Don't exit for pruning errors, as saving the current run is more critical
}
}
// Main execution
archiveCurrentRunData().catch((error) => {
// Fallback catch, though critical errors in archiveCurrentRunData should exit
if (process.exitCode === undefined || process.exitCode === 0) {
// check if not already exited
console.error(
chalk.red.bold("An unexpected error occurred in history archiving:"),
error
);
process.exit(1);
}
});