@@ -4,6 +4,7 @@ import { Octokit } from '@octokit/rest';
44import { downloadArtifactZip , findFilesInZip } from './lib/artifact-download.mjs' ;
55import { parsePlaywrightJsonReport } from './lib/parse-playwright-json.mjs' ;
66import { createSlackBlocks , sendSlackBatched } from './lib/slack-test-health-blocks.mjs' ;
7+ import { partitionSummary } from './lib/classify-report-buckets.mjs' ;
78import { summarizeTestHealth } from './lib/summarize-test-health.mjs' ;
89import { getDateRange , getWorkflowRuns } from './lib/workflow-runs.mjs' ;
910
@@ -12,15 +13,24 @@ if (!githubToken) {
1213 throw new Error ( 'Missing GITHUB_TOKEN env var' ) ;
1314}
1415
16+ const parsePositiveInt = ( value , fallback ) => {
17+ const trimmed = value ?. trim ( ) ;
18+ if ( ! trimmed ) {
19+ return fallback ;
20+ }
21+ const parsed = parseInt ( trimmed , 10 ) ;
22+ return Number . isFinite ( parsed ) && parsed > 0 ? parsed : fallback ;
23+ } ;
24+
1525const env = {
1626 OWNER : process . env . OWNER || 'MetaMask' ,
1727 REPOSITORY : process . env . REPOSITORY ,
1828 WORKFLOW_IDS : process . env . WORKFLOW_IDS ,
1929 BRANCH : process . env . BRANCH || 'main' ,
20- LOOKBACK_DAYS : parseInt ( process . env . LOOKBACK_DAYS ?? '1' ) ,
30+ LOOKBACK_DAYS : parsePositiveInt ( process . env . LOOKBACK_DAYS , 1 ) ,
2131 ARTIFACT_NAME_PREFIX : process . env . ARTIFACT_NAME_PREFIX || 'playwright-json-report' ,
2232 RESULTS_FILE_PATTERN : process . env . RESULTS_FILE_PATTERN || 'playwright-report' ,
23- TOP_N : parseInt ( process . env . TOP_N ?? '10' ) ,
33+ TOP_N : parsePositiveInt ( process . env . TOP_N , 15 ) ,
2434 REPORT_TITLE : process . env . REPORT_TITLE || 'Playwright Test Health Report' ,
2535 SLACK_WEBHOOK : process . env . SLACK_WEBHOOK || '' ,
2636 GITHUB_TOKEN : githubToken ,
@@ -39,6 +49,14 @@ function getWorkflowIds() {
3949 . filter ( Boolean ) ;
4050}
4151
52+ function isTestFailureFinding ( finding ) {
53+ return finding . classification === 'broken' || finding . classification === 'flaky' || finding . classification === 'infra' ;
54+ }
55+
56+ function countTestFailureRuns ( findings ) {
57+ return new Set ( findings . filter ( isTestFailureFinding ) . map ( finding => finding . runId ) ) . size ;
58+ }
59+
4260async function getMergedWorkflowRuns ( github , dateRange ) {
4361 const workflowIds = getWorkflowIds ( ) ;
4462 const runs = [ ] ;
@@ -107,6 +125,7 @@ async function collectFindings(github, runs) {
107125 runId : run . id ,
108126 runUrl : run . html_url || `https://github.com/${ env . OWNER } /${ env . REPOSITORY } /actions/runs/${ run . id } ` ,
109127 date : run . created_at ,
128+ artifactName : artifact . name ,
110129 } ) ,
111130 ) ;
112131 } catch ( error ) {
@@ -135,35 +154,37 @@ async function sendSlackReport(summary, dateDisplay, metadata) {
135154 reportTitle : env . REPORT_TITLE ,
136155 topN : env . TOP_N ,
137156 workflowsScanned : metadata . workflowsScanned ,
138- failedRunCount : metadata . failedRunCount ,
139157 workflowCount : metadata . workflowCount ,
158+ testFailureRunCount : metadata . testFailureRunCount ,
159+ otherFailedRunCount : metadata . otherFailedRunCount ,
160+ lookbackDays : env . LOOKBACK_DAYS ,
140161 } ) ;
141162 await sendSlackBatched ( env . SLACK_WEBHOOK , blocks ) ;
142163 console . log ( '✅ Report sent to Slack successfully' ) ;
143164}
144165
145- function logClassificationDiagnostics ( summary ) {
146- const totalUniqueTests = summary . length ;
147- const currentlyBroken = summary . filter ( test => test . brokenCount > 0 ) ;
148- const currentlyFlaky = summary . filter ( test => test . brokenCount === 0 && test . flakyCount > 0 ) ;
149- const latestPassed = summary . filter ( test => test . latestClassification === 'passed' ) ;
150- const resolvedFromFailure = summary . filter (
151- test =>
152- test . latestClassification === 'passed' &&
153- ( test . historicalBrokenCount ?? 0 ) > 0 ,
154- ) ;
166+ function logClassificationDiagnostics ( summary , metadata ) {
167+ const { brokenItems, flakyItems, watchItems, infraItems } = partitionSummary ( summary ) ;
155168
156169 console . log ( '\n🧾 Classification diagnostics' ) ;
157- console . log ( ` Unique tests observed: ${ totalUniqueTests } ` ) ;
158- console . log ( ` Latest state -> broken: ${ currentlyBroken . length } , flaky: ${ currentlyFlaky . length } , passed: ${ latestPassed . length } ` ) ;
159- console . log ( ` Resolved since earlier runs (had broken history, latest passed): ${ resolvedFromFailure . length } ` ) ;
170+ console . log ( ` Lookback: ${ env . LOOKBACK_DAYS } day(s)` ) ;
171+ console . log ( ` Unique tests observed: ${ summary . length } ` ) ;
172+ console . log (
173+ ` Buckets -> broken: ${ brokenItems . length } , flaky: ${ flakyItems . length } , watch: ${ watchItems . length } , infra: ${ infraItems . length } ` ,
174+ ) ;
175+ console . log ( ` CI runs: ${ metadata . workflowCount } | Test-failure runs: ${ metadata . testFailureRunCount } ` ) ;
176+ console . log ( ` Other CI failures: ${ metadata . otherFailedRunCount } ` ) ;
160177
161- if ( resolvedFromFailure . length > 0 ) {
162- const preview = resolvedFromFailure
178+ if ( watchItems . length > 0 ) {
179+ const preview = watchItems
163180 . slice ( 0 , 5 )
164- . map ( test => `${ test . name } (${ test . projectName } )` )
181+ . map ( test => {
182+ const broken = test . historicalBrokenCount ?? 0 ;
183+ const flaky = test . historicalFlakyCount ?? 0 ;
184+ return `${ test . name } (${ test . projectName } , broken ${ broken } , flaky ${ flaky } )` ;
185+ } )
165186 . join ( '; ' ) ;
166- console . log ( ` Sample resolved (broken→passed) : ${ preview } ` ) ;
187+ console . log ( ` Sample watch : ${ preview } ` ) ;
167188 }
168189}
169190
@@ -173,6 +194,7 @@ async function main() {
173194 const workflowsScanned = getWorkflowIds ( ) ;
174195
175196 console . log ( '🧪 Playwright Test Health Report\n' ) ;
197+ console . log ( `Lookback: ${ env . LOOKBACK_DAYS } day(s)` ) ;
176198 console . log ( `Time range: ${ dateRange . from } to ${ dateRange . to } ` ) ;
177199 console . log ( `Workflows: ${ workflowsScanned . join ( ', ' ) } \n` ) ;
178200
@@ -192,11 +214,20 @@ async function main() {
192214 return ;
193215 }
194216
217+ const testFailureRunCount = countTestFailureRuns ( findings ) ;
218+ const otherFailedRunCount = Math . max ( 0 , failedRunCount - testFailureRunCount ) ;
195219 const summary = summarizeTestHealth ( findings ) ;
196- logClassificationDiagnostics ( summary ) ;
220+
221+ logClassificationDiagnostics ( summary , {
222+ workflowCount : workflowRuns . length ,
223+ testFailureRunCount,
224+ otherFailedRunCount,
225+ } ) ;
226+
197227 await sendSlackReport ( summary , dateRange . display , {
198228 workflowCount : workflowRuns . length ,
199- failedRunCount,
229+ testFailureRunCount,
230+ otherFailedRunCount,
200231 workflowsScanned,
201232 } ) ;
202233 } catch ( error ) {
0 commit comments