UNPKG

pulse-dashboard

Version:

A Next.js Dashboard application for real-time monitoring and historical analysis of Playwright test executions, based on playwright-pulse-report. This component provides the UI for visualizing Playwright test results and can be run as a standalone CLI too

135 lines 7.5 kB
'use server'; import fs from 'fs/promises'; import path from 'path'; export async function getRawHistoricalReports() { console.log('[ACTIONS getRawHistoricalReports] ------------- START -------------'); const pulseUserCwdFromEnv = process.env.PULSE_USER_CWD; const pulseReportDirFromEnv = process.env.PULSE_REPORT_DIR; const currentProcessCwd = process.cwd(); console.log('[ACTIONS getRawHistoricalReports] process.env.PULSE_USER_CWD:', pulseUserCwdFromEnv); console.log("[ACTIONS getRawHistoricalReports] process.env.PULSE_REPORT_DIR:", pulseReportDirFromEnv); console.log('[ACTIONS getRawHistoricalReports] process.cwd():', currentProcessCwd); const baseDir = (pulseUserCwdFromEnv && pulseUserCwdFromEnv.trim() !== '') ? pulseUserCwdFromEnv.trim() : currentProcessCwd; const reportDir = pulseReportDirFromEnv && pulseReportDirFromEnv.trim() !== "" ? pulseReportDirFromEnv.trim() : path.join(baseDir, "pulse-report"); console.log('[ACTIONS getRawHistoricalReports] Effective baseDir determined:', baseDir); console.log("[ACTIONS getRawHistoricalReports] Effective reportDir determined:", reportDir); const historyDir = path.join(reportDir, "history"); console.log('[ACTIONS getRawHistoricalReports] Attempting to read history directory:', historyDir); try { const trendFileNames = (await fs.readdir(historyDir)).filter(file => file.startsWith('trend-') && file.endsWith('.json')); console.log(`[ACTIONS getRawHistoricalReports] Found ${trendFileNames.length} trend files in ${historyDir}`); const historicalDataArray = []; for (const fileName of trendFileNames) { const filePath = path.join(historyDir, fileName); try { const fileContent = await fs.readFile(filePath, 'utf-8'); const reportData = JSON.parse(fileContent); if (reportData.run && reportData.results) { // Ensure flakinessRate is carried over if it exists if (reportData.run.flakinessRate === undefined) { reportData.run.flakinessRate = 0; // Default if not present } historicalDataArray.push(reportData); } else { console.warn(`[ACTIONS getRawHistoricalReports] Skipping invalid historical report file (missing run or results): ${fileName}`); } } catch (fileReadError) { console.error(`[ACTIONS getRawHistoricalReports] Error reading or parsing historical file ${fileName}:`, fileReadError); } } historicalDataArray.sort((a, b) => new Date(a.run.timestamp).getTime() - new Date(b.run.timestamp).getTime()); console.log('[ACTIONS getRawHistoricalReports] ------------- END (SUCCESS) -------------'); return historicalDataArray; } catch (error) { console.error(`[ACTIONS getRawHistoricalReports] Error accessing or reading historical trends directory ${historyDir}:`, error.message); if (error instanceof Error && error.code === 'ENOENT') { console.warn(`[ACTIONS getRawHistoricalReports] History directory not found at ${historyDir}. This is normal if no historical reports exist yet.`); } console.log('[ACTIONS getRawHistoricalReports] ------------- END (ERROR) -------------'); // Re-throw or return empty to allow API route to handle HTTP response // For now, returning empty and letting API route handle it based on this logged error. return []; } } export async function getFlakyTestsAnalysis() { try { const historicalReports = await getRawHistoricalReports(); if (historicalReports.length === 0) { // Check if this is due to an actual lack of files or an error in getRawHistoricalReports // The logs from getRawHistoricalReports will indicate this. // If an error occurred there, it would have logged and returned empty. // So, here we assume if it's empty, it's genuinely no data or a handled error. console.log('[ACTIONS getFlakyTestsAnalysis] No historical reports found or an error occurred in getRawHistoricalReports. Returning empty flaky tests.'); return { success: true, flakyTests: [] }; } const testStatsMap = new Map(); for (const report of historicalReports) { if (!report.results) { // Additional safety check console.warn('[ACTIONS getFlakyTestsAnalysis] Skipping a report in history due to missing .results property.'); continue; } for (const testResult of report.results) { if (!testStatsMap.has(testResult.id)) { testStatsMap.set(testResult.id, { name: testResult.name, suiteName: testResult.suiteName, occurrences: [], }); } testStatsMap.get(testResult.id).occurrences.push({ runTimestamp: report.run.timestamp, status: testResult.status, }); } } const flakyTests = []; for (const [id, data] of testStatsMap.entries()) { const statuses = new Set(data.occurrences.map(o => o.status)); const hasPassed = statuses.has('passed'); const hasFailed = statuses.has('failed') || statuses.has('timedOut'); if (hasPassed && hasFailed) { let passedCount = 0; let failedCount = 0; let skippedCount = 0; let pendingCount = 0; data.occurrences.forEach(occ => { if (occ.status === 'passed') passedCount++; else if (occ.status === 'failed' || occ.status === 'timedOut') failedCount++; else if (occ.status === 'skipped') skippedCount++; else if (occ.status === 'pending') pendingCount++; }); const sortedOccurrences = data.occurrences.sort((a, b) => new Date(a.runTimestamp).getTime() - new Date(b.runTimestamp).getTime()); flakyTests.push({ id, name: data.name, suiteName: data.suiteName, occurrences: sortedOccurrences, passedCount, failedCount, skippedCount, pendingCount, totalRuns: data.occurrences.length, firstSeen: sortedOccurrences[0]?.runTimestamp || '', lastSeen: sortedOccurrences[sortedOccurrences.length - 1]?.runTimestamp || '', }); } } flakyTests.sort((a, b) => (b.failedCount / b.totalRuns) - (a.failedCount / a.totalRuns) || b.totalRuns - a.totalRuns); return { success: true, flakyTests }; } catch (error) { console.error('[ACTIONS getFlakyTestsAnalysis] Error analyzing flaky tests:', error); const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred during flaky test analysis.'; return { success: false, error: errorMessage }; } } //# sourceMappingURL=actions.js.map