Skip to content

Commit 48d0174

Browse files
snomiaoclaudeDrJKLactions-user
authored
feat: add test count display to Playwright PR comments (#5458)
* 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 605d7cc. * 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]>
1 parent 90b1b47 commit 48d0174

File tree

3 files changed

+340
-10
lines changed

3 files changed

+340
-10
lines changed

.github/workflows/test-ui.yaml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,13 @@ jobs:
229229

230230
- name: Run Playwright tests (${{ matrix.browser }})
231231
id: playwright
232-
run: npx playwright test --project=${{ matrix.browser }} --reporter=html
232+
run: |
233+
# Run tests with both HTML and JSON reporters
234+
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
235+
npx playwright test --project=${{ matrix.browser }} \
236+
--reporter=list \
237+
--reporter=html \
238+
--reporter=json
233239
working-directory: ComfyUI_frontend
234240

235241
- uses: actions/upload-artifact@v4
@@ -275,7 +281,12 @@ jobs:
275281
merge-multiple: true
276282

277283
- name: Merge into HTML Report
278-
run: npx playwright merge-reports --reporter html ./all-blob-reports
284+
run: |
285+
# Generate HTML report
286+
npx playwright merge-reports --reporter=html ./all-blob-reports
287+
# Generate JSON report separately with explicit output path
288+
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
289+
npx playwright merge-reports --reporter=json ./all-blob-reports
279290
working-directory: ComfyUI_frontend
280291

281292
- name: Upload HTML report
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#!/usr/bin/env tsx
2+
import fs from 'fs'
3+
import path from 'path'
4+
5+
interface TestStats {
6+
expected?: number
7+
unexpected?: number
8+
flaky?: number
9+
skipped?: number
10+
finished?: number
11+
}
12+
13+
interface ReportData {
14+
stats?: TestStats
15+
}
16+
17+
interface TestCounts {
18+
passed: number
19+
failed: number
20+
flaky: number
21+
skipped: number
22+
total: number
23+
}
24+
25+
/**
26+
* Extract test counts from Playwright HTML report
27+
* @param reportDir - Path to the playwright-report directory
28+
* @returns Test counts { passed, failed, flaky, skipped, total }
29+
*/
30+
function extractTestCounts(reportDir: string): TestCounts {
31+
const counts: TestCounts = {
32+
passed: 0,
33+
failed: 0,
34+
flaky: 0,
35+
skipped: 0,
36+
total: 0
37+
}
38+
39+
try {
40+
// First, try to find report.json which Playwright generates with JSON reporter
41+
const jsonReportFile = path.join(reportDir, 'report.json')
42+
if (fs.existsSync(jsonReportFile)) {
43+
const reportJson: ReportData = JSON.parse(
44+
fs.readFileSync(jsonReportFile, 'utf-8')
45+
)
46+
if (reportJson.stats) {
47+
const stats = reportJson.stats
48+
counts.total = stats.expected || 0
49+
counts.passed =
50+
(stats.expected || 0) -
51+
(stats.unexpected || 0) -
52+
(stats.flaky || 0) -
53+
(stats.skipped || 0)
54+
counts.failed = stats.unexpected || 0
55+
counts.flaky = stats.flaky || 0
56+
counts.skipped = stats.skipped || 0
57+
return counts
58+
}
59+
}
60+
61+
// Try index.html - Playwright HTML report embeds data in a script tag
62+
const indexFile = path.join(reportDir, 'index.html')
63+
if (fs.existsSync(indexFile)) {
64+
const content = fs.readFileSync(indexFile, 'utf-8')
65+
66+
// Look for the embedded report data in various formats
67+
// Format 1: window.playwrightReportBase64
68+
let dataMatch = content.match(
69+
/window\.playwrightReportBase64\s*=\s*["']([^"']+)["']/
70+
)
71+
if (dataMatch) {
72+
try {
73+
const decodedData = Buffer.from(dataMatch[1], 'base64').toString(
74+
'utf-8'
75+
)
76+
const reportData: ReportData = JSON.parse(decodedData)
77+
78+
if (reportData.stats) {
79+
const stats = reportData.stats
80+
counts.total = stats.expected || 0
81+
counts.passed =
82+
(stats.expected || 0) -
83+
(stats.unexpected || 0) -
84+
(stats.flaky || 0) -
85+
(stats.skipped || 0)
86+
counts.failed = stats.unexpected || 0
87+
counts.flaky = stats.flaky || 0
88+
counts.skipped = stats.skipped || 0
89+
return counts
90+
}
91+
} catch (e) {
92+
// Continue to try other formats
93+
}
94+
}
95+
96+
// Format 2: window.playwrightReport
97+
dataMatch = content.match(/window\.playwrightReport\s*=\s*({[\s\S]*?});/)
98+
if (dataMatch) {
99+
try {
100+
// Use Function constructor instead of eval for safety
101+
const reportData = new Function(
102+
'return ' + dataMatch[1]
103+
)() as ReportData
104+
105+
if (reportData.stats) {
106+
const stats = reportData.stats
107+
counts.total = stats.expected || 0
108+
counts.passed =
109+
(stats.expected || 0) -
110+
(stats.unexpected || 0) -
111+
(stats.flaky || 0) -
112+
(stats.skipped || 0)
113+
counts.failed = stats.unexpected || 0
114+
counts.flaky = stats.flaky || 0
115+
counts.skipped = stats.skipped || 0
116+
return counts
117+
}
118+
} catch (e) {
119+
// Continue to try other formats
120+
}
121+
}
122+
123+
// Format 3: Look for stats in the HTML content directly
124+
// Playwright sometimes renders stats in the UI
125+
const statsMatch = content.match(
126+
/(\d+)\s+passed[^0-9]*(\d+)\s+failed[^0-9]*(\d+)\s+flaky[^0-9]*(\d+)\s+skipped/i
127+
)
128+
if (statsMatch) {
129+
counts.passed = parseInt(statsMatch[1]) || 0
130+
counts.failed = parseInt(statsMatch[2]) || 0
131+
counts.flaky = parseInt(statsMatch[3]) || 0
132+
counts.skipped = parseInt(statsMatch[4]) || 0
133+
counts.total =
134+
counts.passed + counts.failed + counts.flaky + counts.skipped
135+
return counts
136+
}
137+
138+
// Format 4: Try to extract from summary text patterns
139+
const passedMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+passed/i)
140+
const failedMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+failed/i)
141+
const flakyMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+flaky/i)
142+
const skippedMatch = content.match(/(\d+)\s+(?:tests?|specs?)\s+skipped/i)
143+
const totalMatch = content.match(
144+
/(\d+)\s+(?:tests?|specs?)\s+(?:total|ran)/i
145+
)
146+
147+
if (passedMatch) counts.passed = parseInt(passedMatch[1]) || 0
148+
if (failedMatch) counts.failed = parseInt(failedMatch[1]) || 0
149+
if (flakyMatch) counts.flaky = parseInt(flakyMatch[1]) || 0
150+
if (skippedMatch) counts.skipped = parseInt(skippedMatch[1]) || 0
151+
if (totalMatch) {
152+
counts.total = parseInt(totalMatch[1]) || 0
153+
} else if (
154+
counts.passed ||
155+
counts.failed ||
156+
counts.flaky ||
157+
counts.skipped
158+
) {
159+
counts.total =
160+
counts.passed + counts.failed + counts.flaky + counts.skipped
161+
}
162+
}
163+
} catch (error) {
164+
console.error(`Error reading report from ${reportDir}:`, error)
165+
}
166+
167+
return counts
168+
}
169+
170+
// Main execution
171+
const reportDir = process.argv[2]
172+
173+
if (!reportDir) {
174+
console.error('Usage: extract-playwright-counts.ts <report-directory>')
175+
process.exit(1)
176+
}
177+
178+
const counts = extractTestCounts(reportDir)
179+
180+
// Output as JSON for easy parsing in shell script
181+
console.log(JSON.stringify(counts))
182+
183+
export { extractTestCounts }

0 commit comments

Comments
 (0)