tdpw
Version:
CLI tool for uploading Playwright test reports to TestDino platform with Azure storage support
268 lines • 9.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReportDiscoveryService = void 0;
const path_1 = require("path");
const fs_1 = require("../utils/fs");
const validators_1 = require("./validators");
const types_1 = require("../types");
/**
* Service to discover Playwright test report files with smart scanning.
*/
class ReportDiscoveryService {
reportDir;
constructor(reportDir) {
this.reportDir = reportDir;
}
/**
* Discover report files based on CLI options with smart scanning.
*/
async discover(options) {
const baseDir = (0, fs_1.resolvePath)(options.reportDirectory, 'Report directory');
// JSON report discovery with manual override
const jsonReportPath = options.jsonReport ?? await this.findJsonReport(baseDir);
await (0, validators_1.validateJsonReport)(jsonReportPath);
// HTML report discovery (only if upload requested)
let htmlReportDir;
if (options.uploadHtml) {
htmlReportDir = options.htmlReport ?? await this.findHtmlReport(baseDir);
if (htmlReportDir) {
await (0, validators_1.validateHtmlReportDir)(htmlReportDir);
}
}
// Trace files discovery (only if upload requested)
let traceDirectory;
if (options.uploadTraces) {
traceDirectory = options.traceDir ?? await this.findTraceDir(baseDir);
if (traceDirectory) {
await (0, validators_1.validateTraceDir)(traceDirectory);
}
}
return {
jsonReport: jsonReportPath,
htmlReport: htmlReportDir,
traceDir: traceDirectory,
};
}
/**
* Smart JSON report discovery - scan for valid Playwright JSON files
*/
async findJsonReport(baseDir) {
// Try common patterns first (faster)
const commonPatterns = [
(0, path_1.join)(baseDir, 'report.json'), // Standard Playwright
(0, path_1.join)(baseDir, 'results.json'), // Alternative name
(0, path_1.join)(baseDir, 'test-results.json'), // Another common name
(0, path_1.join)(baseDir, 'playwright-report', 'report.json'), // Nested structure
(0, path_1.join)(baseDir, 'playwright-report', 'results.json'), // Nested alternative
];
for (const candidate of commonPatterns) {
if (await (0, fs_1.exists)(candidate)) {
// Validate it's actually a Playwright report
if (await this.isValidPlaywrightJson(candidate)) {
return candidate;
}
}
}
// If no common patterns found, scan all JSON files
const jsonFiles = await this.scanForJsonFiles(baseDir);
for (const jsonFile of jsonFiles) {
if (await this.isValidPlaywrightJson(jsonFile)) {
return jsonFile;
}
}
throw new types_1.FileSystemError(`No valid Playwright JSON report found in ${baseDir}.\n` +
'💡 Looked for: report.json, results.json, test-results.json\n' +
'💡 Use --json-report <path> to specify exact location');
}
/**
* Smart HTML report discovery - find directory containing index.html
*/
async findHtmlReport(baseDir) {
// Try common patterns first
const commonPatterns = [
baseDir, // Current directory
(0, path_1.join)(baseDir, 'html-report'), // Standard name
(0, path_1.join)(baseDir, 'playwright-report'), // Standard Playwright
(0, path_1.join)(baseDir, 'report'), // Alternative
(0, path_1.join)(baseDir, 'test-report'), // Alternative
];
for (const candidate of commonPatterns) {
if (await (0, fs_1.isDirectory)(candidate)) {
const indexPath = (0, path_1.join)(candidate, 'index.html');
if (await (0, fs_1.exists)(indexPath)) {
return candidate;
}
}
}
// Scan for any directory containing index.html
const htmlDirs = await this.scanForHtmlDirectories(baseDir);
if (htmlDirs.length > 0) {
const candidateDir = htmlDirs[0];
if (candidateDir !== undefined) {
return candidateDir;
}
}
throw new types_1.FileSystemError(`No HTML report directory with index.html found in ${baseDir}.\n` +
'💡 Use --html-report <path> to specify exact location');
}
/**
* Smart trace directory discovery
*/
async findTraceDir(baseDir) {
// Try common patterns
const commonPatterns = [
(0, path_1.join)(baseDir, 'trace'),
(0, path_1.join)(baseDir, 'traces'),
(0, path_1.join)(baseDir, 'test-results'),
(0, path_1.join)(baseDir, 'playwright-report', 'trace'),
(0, path_1.join)(baseDir, 'playwright-report', 'traces'),
];
for (const candidate of commonPatterns) {
if (await (0, fs_1.isDirectory)(candidate)) {
// Check if directory contains any trace files
if (await this.containsTraceFiles(candidate)) {
return candidate;
}
}
}
// Scan for directories containing .zip files (common trace format)
const traceDirs = await this.scanForTraceDirectories(baseDir);
if (traceDirs.length > 0) {
const candidateTraceDir = traceDirs[0];
if (candidateTraceDir !== undefined) {
return candidateTraceDir;
}
}
throw new types_1.FileSystemError(`No trace directory found in ${baseDir}.\n` +
'💡 Use --trace-dir <path> to specify exact location');
}
/**
* Validate if a JSON file is a valid Playwright report
*/
async isValidPlaywrightJson(filePath) {
try {
const content = await (0, fs_1.readFile)(filePath);
const data = JSON.parse(content);
// Check for required Playwright report structure
return (data &&
typeof data === 'object' &&
'config' in data &&
'suites' in data &&
'stats' in data &&
Array.isArray(data.suites));
}
catch {
return false;
}
}
/**
* Scan directory recursively for JSON files
*/
async scanForJsonFiles(dir, maxDepth = 2) {
const jsonFiles = [];
try {
await this.scanDirectory(dir, jsonFiles, (file) => file.endsWith('.json'), 0, maxDepth);
}
catch {
// Ignore scan errors
}
return jsonFiles;
}
/**
* Scan for directories containing index.html
*/
async scanForHtmlDirectories(dir, maxDepth = 2) {
const htmlDirs = [];
try {
await this.scanDirectoryForHtml(dir, htmlDirs, 0, maxDepth);
}
catch {
// Ignore scan errors
}
return htmlDirs;
}
/**
* Scan for directories containing trace files
*/
async scanForTraceDirectories(dir, maxDepth = 2) {
const traceDirs = [];
try {
await this.scanDirectoryForTraces(dir, traceDirs, 0, maxDepth);
}
catch {
// Ignore scan errors
}
return traceDirs;
}
/**
* Generic directory scanner for files
*/
async scanDirectory(dir, results, filter, currentDepth, maxDepth) {
if (currentDepth > maxDepth)
return;
const entries = await (0, fs_1.readDir)(dir);
for (const entry of entries) {
if (await (0, fs_1.isDirectory)(entry)) {
await this.scanDirectory(entry, results, filter, currentDepth + 1, maxDepth);
}
else if (filter(entry)) {
results.push(entry);
}
}
}
/**
* Scan for HTML directories
*/
async scanDirectoryForHtml(dir, results, currentDepth, maxDepth) {
if (currentDepth > maxDepth)
return;
// Check if current directory has index.html
const indexPath = (0, path_1.join)(dir, 'index.html');
if (await (0, fs_1.exists)(indexPath)) {
results.push(dir);
return; // Don't scan subdirectories if we found one
}
// Scan subdirectories
const entries = await (0, fs_1.readDir)(dir);
for (const entry of entries) {
if (await (0, fs_1.isDirectory)(entry)) {
await this.scanDirectoryForHtml(entry, results, currentDepth + 1, maxDepth);
}
}
}
/**
* Scan for trace directories
*/
async scanDirectoryForTraces(dir, results, currentDepth, maxDepth) {
if (currentDepth > maxDepth)
return;
// Check if current directory contains trace files
if (await this.containsTraceFiles(dir)) {
results.push(dir);
return;
}
// Scan subdirectories
const entries = await (0, fs_1.readDir)(dir);
for (const entry of entries) {
if (await (0, fs_1.isDirectory)(entry)) {
await this.scanDirectoryForTraces(entry, results, currentDepth + 1, maxDepth);
}
}
}
/**
* Check if directory contains trace files
*/
async containsTraceFiles(dir) {
try {
const entries = await (0, fs_1.readDir)(dir);
return entries.some(entry => entry.endsWith('.zip') ||
entry.endsWith('.trace') ||
entry.includes('trace'));
}
catch {
return false;
}
}
}
exports.ReportDiscoveryService = ReportDiscoveryService;
//# sourceMappingURL=discovery.js.map