UNPKG

playwright-archive

Version:

A lightweight CLI tool to archive and serve your Playwright test run history with a web interface. Useful for CI environments and local development.

168 lines (142 loc) 5.98 kB
const fs = require('fs-extra'); const path = require('path'); const RunResultsGenerator = require('./RunResultsGenerator'); class JsonReportParser { constructor(runPath = process.cwd()) { this.runPath = runPath; this.data = null; } async getData(config) { try { // Try these locations in order: // 1. metadata.json in the run directory // 2. pw-archive.json in test-results relative to cwd const metadataPath = path.join(this.runPath, "metadata.json"); const archivePath = path.resolve("test-results", "pw-archive.json"); try { if (await fs.pathExists(metadataPath)) { this.data = await fs.readJson(metadataPath); console.log("Successfully loaded metadata from:", metadataPath); } else if (await fs.pathExists(archivePath)) { this.data = await fs.readJson(archivePath); console.log("Successfully loaded archive from:", archivePath); } else { throw new Error(`No metadata found in ${metadataPath} or ${archivePath}`); } } catch (error) { console.warn(`Warning: Could not load metadata:`, error.message); this.data = { config: { metadata: {}, projects: [] }, stats: {}, suites: [] }; } // Ensure config is an object const safeConfig = config || {}; // Generate results file if configured if (safeConfig?.runResults?.enabled) { try { const generator = new RunResultsGenerator(this.data, safeConfig); await generator.generate(); } catch (error) { console.warn("Warning: Failed to generate run results:", error.message); } } const result = {}; const hideMetrics = Array.isArray(safeConfig?.display?.hideMetrics) ? safeConfig.display.hideMetrics : []; const hideTestNames = Boolean(safeConfig?.display?.hideTestNames); // Workers count if (!hideMetrics.includes('workers')) { result.actualWorkers = this.data?.config?.metadata?.actualWorkers || "Not defined"; } // Projects info if (!hideMetrics.includes('projects')) { // Ensure projects is an array const projects = Array.isArray(this.data?.config?.projects) ? this.data.config.projects : []; result.projects = { quantity: projects.length, names: projects .map(p => (p && typeof p.name === 'string') ? p.name : "Unknown") .filter(Boolean) }; // Only include projects section if we have actual data if (result.projects.quantity === 0 && result.projects.names.length === 0) { delete result.projects; } } // Test names if (!hideTestNames) { const suites = Array.isArray(this.data?.suites) ? this.data.suites : []; result.suites = { failedNames: [], passedNames: [] }; suites.forEach(suite => { if (!suite || !Array.isArray(suite.specs)) return; const title = (suite.title && typeof suite.title === 'string') ? suite.title : "Unknown Suite"; const specs = Array.isArray(suite.specs) ? suite.specs : []; const hasFailedSpecs = specs.some(spec => spec && spec.ok === false); const allSpecsPassed = specs.length > 0 && specs.every(spec => spec && spec.ok === true); if (hasFailedSpecs) { result.suites.failedNames.push(title); } if (allSpecsPassed) { result.suites.passedNames.push(title); } }); // Only include suites if we have any names if (result.suites.failedNames.length === 0 && result.suites.passedNames.length === 0) { delete result.suites; } } // Test statistics const stats = this.data?.stats || {}; result.stats = {}; if (!hideMetrics.includes('duration')) { result.stats.duration = typeof stats.duration === 'number' ? stats.duration : 0; } if (!hideMetrics.includes('totalTests')) { // Ensure we're dealing with numbers const expected = typeof stats.expected === 'number' ? stats.expected : 0; const unexpected = typeof stats.unexpected === 'number' ? stats.unexpected : 0; const skipped = typeof stats.skipped === 'number' ? stats.skipped : 0; result.stats.quantity = expected + unexpected + skipped; } if (!hideMetrics.includes('passed')) { result.stats.passedQuantity = typeof stats.expected === 'number' ? stats.expected : 0; } if (!hideMetrics.includes('failed')) { result.stats.failedQuantity = typeof stats.unexpected === 'number' ? stats.unexpected : 0; } if (!hideMetrics.includes('skipped')) { result.stats.skippedQuantity = typeof stats.skipped === 'number' ? stats.skipped : 0; } if (!hideMetrics.includes('flaky')) { result.stats.flakyQuantity = typeof stats.flaky === 'number' ? stats.flaky : 0; } // Only include stats if we have any non-zero values if (Object.values(result.stats).every(value => value === 0)) { delete result.stats; } return result; } catch (error) { console.error("Error processing test metadata:", error); // Return minimal valid structure in case of errors return { actualWorkers: "Not available", stats: { duration: 0, quantity: 0, passedQuantity: 0, failedQuantity: 0, skippedQuantity: 0, flakyQuantity: 0 } }; } } } module.exports = JsonReportParser;