mirror of
https://github.com/hanzo-studio/studio-frontend.git
synced 2026-10-30 18:55:56 +00:00
* feat: add test count display to Playwright PR comments - Add extract-playwright-counts.mjs script to parse test results from Playwright reports - Update pr-playwright-deploy-and-comment.sh to extract and display test counts - Show overall summary with passed/failed/flaky/skipped counts - Display per-browser test counts inline with report links - Use dynamic status icons based on test results (✅/❌/⚠️) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * feat: include skipped test count in per-browser display - Add skipped test extraction for individual browser reports - Update per-browser display format to show all four counts: (✅ passed / ❌ failed / ⚠️ flaky / ⏭️ skipped) - Provides complete test result visibility at a glance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: improve test count extraction reliability in CI - Use absolute paths for script and report directories - Add debug logging to help diagnose extraction issues - Move counts display after View Report link as requested - Format: [View Report](url) • ✅ passed / ❌ failed / ⚠️ flaky / ⏭️ skipped 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: generate JSON reports alongside HTML for test count extraction - Add JSON reporter to Playwright test runs - Generate report.json alongside HTML reports - Store JSON report in playwright-report directory - This enables accurate test count extraction from CI artifacts The HTML reports alone don't contain easily extractable test statistics as they use a React app with dynamically loaded data. JSON reports provide direct access to test counts. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: correct JSON reporter syntax for Playwright tests - Use proper syntax for JSON reporter with outputFile option - Run separate commands for HTML and JSON report merging - Specify output path directly in reporter configuration - Ensures report.json is created in playwright-report directory This fixes the "No such file or directory" error when trying to move report.json file, as it wasn't being created in the first place. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Revert "fix: correct JSON reporter syntax for Playwright tests" This reverts commit 605d7cc1e2c33a2f40eaf27ff023b0b8ac4ed87d. * fix: use correct Playwright reporter syntax with comma-separated list - Use --reporter=html,json syntax (comma-separated, not space) - Move test-results.json to playwright-report/report.json after generation - Remove incorrect PLAYWRIGHT_JSON_OUTPUT_NAME env variable - Add || true to prevent failure if JSON file doesn't exist The JSON reporter outputs to test-results.json by default when using the comma-separated reporter list syntax. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: improve test count extraction reliability in CI - Use separate --reporter flags for list, html, and json - Set PLAYWRIGHT_JSON_OUTPUT_NAME env var to specify JSON output path - Run HTML and JSON report generation separately for merged reports - Ensures report.json is created in playwright-report directory The combined reporter syntax wasn't creating the JSON file properly. Using separate reporter flags with env var ensures JSON is generated. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Update scripts/cicd/pr-playwright-deploy-and-comment.sh Co-authored-by: Alexander Brown <[email protected]> * refactor: convert extraction script to TypeScript and use tsx - Convert extract-playwright-counts.mjs to TypeScript (.ts) - Add proper TypeScript types for better type safety - Use tsx for execution instead of node - Auto-install tsx in CI if not available - Better alignment with the TypeScript codebase This provides better type safety and consistency with the rest of the codebase while maintaining the same functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * chore(pr-playwright-deploy-and-comment.sh): move tsx installation check to the beginning of the script for better organization and efficiency * [auto-fix] Apply ESLint and Prettier fixes --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Alexander Brown <[email protected]> Co-authored-by: GitHub Action <[email protected]>
184 lines
5.5 KiB
TypeScript
Executable File
184 lines
5.5 KiB
TypeScript
Executable File
#!/usr/bin/env tsx
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
|
|
interface TestStats {
|
|
expected?: number
|
|
unexpected?: number
|
|
flaky?: number
|
|
skipped?: number
|
|
finished?: number
|
|
}
|
|
|
|
interface ReportData {
|
|
stats?: TestStats
|
|
}
|
|
|
|
interface TestCounts {
|
|
passed: number
|
|
failed: number
|
|
flaky: number
|
|
skipped: number
|
|
total: number
|
|
}
|
|
|
|
/**
|
|
* Extract test counts from Playwright HTML report
|
|
* @param reportDir - Path to the playwright-report directory
|
|
* @returns Test counts { passed, failed, flaky, skipped, total }
|
|
*/
|
|
function extractTestCounts(reportDir: string): TestCounts {
|
|
const counts: TestCounts = {
|
|
passed: 0,
|
|
failed: 0,
|
|
flaky: 0,
|
|
skipped: 0,
|
|
total: 0
|
|
}
|
|
|
|
try {
|
|
// First, try to find report.json which Playwright generates with JSON reporter
|
|
const jsonReportFile = path.join(reportDir, 'report.json')
|
|
if (fs.existsSync(jsonReportFile)) {
|
|
const reportJson: ReportData = JSON.parse(
|
|
fs.readFileSync(jsonReportFile, 'utf-8')
|
|
)
|
|
if (reportJson.stats) {
|
|
const stats = reportJson.stats
|
|
counts.total = stats.expected || 0
|
|
counts.passed =
|
|
(stats.expected || 0) -
|
|
(stats.unexpected || 0) -
|
|
(stats.flaky || 0) -
|
|
(stats.skipped || 0)
|
|
counts.failed = stats.unexpected || 0
|
|
counts.flaky = stats.flaky || 0
|
|
counts.skipped = stats.skipped || 0
|
|
return counts
|
|
}
|
|
}
|
|
|
|
// Try index.html - Playwright HTML report embeds data in a script tag
|
|
const indexFile = path.join(reportDir, 'index.html')
|
|
if (fs.existsSync(indexFile)) {
|
|
const content = fs.readFileSync(indexFile, 'utf-8')
|
|
|
|
// Look for the embedded report data in various formats
|
|
// Format 1: window.playwrightReportBase64
|
|
let dataMatch = content.match(
|
|
/window\.playwrightReportBase64\s*=\s*["']([^"']+)["']/
|
|
)
|
|
if (dataMatch) {
|
|
try {
|
|
const decodedData = Buffer.from(dataMatch[1], 'base64').toString(
|
|
'utf-8'
|
|
)
|
|
const reportData: ReportData = JSON.parse(decodedData)
|
|
|
|
if (reportData.stats) {
|
|
const stats = reportData.stats
|
|
counts.total = stats.expected || 0
|
|
counts.passed =
|
|
(stats.expected || 0) -
|
|
(stats.unexpected || 0) -
|
|
(stats.flaky || 0) -
|
|
(stats.skipped || 0)
|
|
counts.failed = stats.unexpected || 0
|
|
counts.flaky = stats.flaky || 0
|
|
counts.skipped = stats.skipped || 0
|
|
return counts
|
|
}
|
|
} catch (e) {
|
|
// Continue to try other formats
|
|
}
|
|
}
|
|
|
|
// Format 2: window.playwrightReport
|
|
dataMatch = content.match(/window\.playwrightReport\s*=\s*({[\s\S]*?});/)
|
|
if (dataMatch) {
|
|
try {
|
|
// Use Function constructor instead of eval for safety
|
|
const reportData = new Function(
|
|
'return ' + dataMatch[1]
|
|
)() as ReportData
|
|
|
|
if (reportData.stats) {
|
|
const stats = reportData.stats
|
|
counts.total = stats.expected || 0
|
|
counts.passed =
|
|
(stats.expected || 0) -
|
|
(stats.unexpected || 0) -
|
|
(stats.flaky || 0) -
|
|
(stats.skipped || 0)
|
|
counts.failed = stats.unexpected || 0
|
|
counts.flaky = stats.flaky || 0
|
|
counts.skipped = stats.skipped || 0
|
|
return counts
|
|
}
|
|
} catch (e) {
|
|
// Continue to try other formats
|
|
}
|
|
}
|
|
|
|
// Format 3: Look for stats in the HTML content directly
|
|
// Playwright sometimes renders stats in the UI
|
|
const statsMatch = content.match(
|
|
/(\d+)\s+passed[^0-9]*(\d+)\s+failed[^0-9]*(\d+)\s+flaky[^0-9]*(\d+)\s+skipped/i
|
|
)
|
|
if (statsMatch) {
|
|
counts.passed = parseInt(statsMatch[1]) || 0
|
|
counts.failed = parseInt(statsMatch[2]) || 0
|
|
counts.flaky = parseInt(statsMatch[3]) || 0
|
|
counts.skipped = parseInt(statsMatch[4]) || 0
|
|
counts.total =
|
|
counts.passed + counts.failed + counts.flaky + counts.skipped
|
|
return counts
|
|
}
|
|
|
|
// Format 4: Try to extract from summary text patterns
|
|
const passedMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+passed/i)
|
|
const failedMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+failed/i)
|
|
const flakyMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+flaky/i)
|
|
const skippedMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+skipped/i)
|
|
const totalMatch = content.match(
|
|
/(\d+)\s+(?:tests?|specs?)\s+(?:total|ran)/i
|
|
)
|
|
|
|
if (passedMatch) counts.passed = parseInt(passedMatch[1]) || 0
|
|
if (failedMatch) counts.failed = parseInt(failedMatch[1]) || 0
|
|
if (flakyMatch) counts.flaky = parseInt(flakyMatch[1]) || 0
|
|
if (skippedMatch) counts.skipped = parseInt(skippedMatch[1]) || 0
|
|
if (totalMatch) {
|
|
counts.total = parseInt(totalMatch[1]) || 0
|
|
} else if (
|
|
counts.passed ||
|
|
counts.failed ||
|
|
counts.flaky ||
|
|
counts.skipped
|
|
) {
|
|
counts.total =
|
|
counts.passed + counts.failed + counts.flaky + counts.skipped
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error reading report from ${reportDir}:`, error)
|
|
}
|
|
|
|
return counts
|
|
}
|
|
|
|
// Main execution
|
|
const reportDir = process.argv[2]
|
|
|
|
if (!reportDir) {
|
|
console.error('Usage: extract-playwright-counts.ts <report-directory>')
|
|
process.exit(1)
|
|
}
|
|
|
|
const counts = extractTestCounts(reportDir)
|
|
|
|
// Output as JSON for easy parsing in shell script
|
|
console.log(JSON.stringify(counts))
|
|
|
|
export { extractTestCounts }
|