@@ -40,16 +40,15 @@ function renderReplayTestSummary(
4040 for ( const entry of data . failures ) {
4141 renderFailedTestResult ( entry ) ;
4242 }
43- for ( const entry of flaky ) {
44- renderFlakyTestResult ( entry ) ;
45- }
4643 }
4744
4845 const durationMs = typeof data . durationMs === 'number' ? data . durationMs : undefined ;
4946 const flakySuffix = flaky . length > 0 ? `, ${ flaky . length } flaky` : '' ;
47+ const durationSuffix = durationMs !== undefined ? ` in ${ formatDurationSeconds ( durationMs ) } ` : '' ;
5048 process . stdout . write (
51- `Test summary: ${ data . passed } passed, ${ data . failed } failed${ flakySuffix } ${ durationMs !== undefined ? ` in ${ durationMs } ms` : '' } \n` ,
49+ `Test summary: ${ data . passed } passed, ${ data . failed } failed${ flakySuffix } ${ durationSuffix } \n` ,
5250 ) ;
51+ renderFlakyTestSummary ( flaky ) ;
5352 return getReplayTestExitCode ( data ) ;
5453}
5554
@@ -59,11 +58,10 @@ function renderVerboseTestResult(result: ReplaySuiteTestResult): void {
5958 return ;
6059 }
6160
62- const prefix = replayResultPrefix ( result ) ;
63- const attemptSuffix =
64- 'attempts' in result && result . attempts > 1 ? ` after ${ result . attempts } attempts` : '' ;
65- const durationSuffix = result . durationMs > 0 ? ` (${ result . durationMs } ms)` : '' ;
66- process . stdout . write ( `${ prefix } ${ result . file } ${ attemptSuffix } ${ durationSuffix } \n` ) ;
61+ const durationSuffix = formatReplayTestDurationSuffix ( result ) ;
62+ process . stdout . write (
63+ `${ replayResultPrefix ( result ) } ${ replayTestDisplayName ( result ) } ${ durationSuffix } \n` ,
64+ ) ;
6765 if ( result . status === 'skipped' ) {
6866 process . stdout . write ( ` ${ result . message ?? 'skipped' } \n` ) ;
6967 }
@@ -73,16 +71,16 @@ function renderFailedTestResult(
7371 result : Extract < ReplaySuiteTestResult , { status : 'failed' } > ,
7472) : void {
7573 const attemptSuffix = result . attempts > 1 ? ` after ${ result . attempts } attempts` : '' ;
76- const durationSuffix = result . durationMs > 0 ? ` ( ${ result . durationMs } ms)` : '' ;
77- process . stdout . write ( `FAIL ${ result . file } ${ attemptSuffix } ${ durationSuffix } \n` ) ;
74+ const durationSuffix = formatReplayTestDurationSuffix ( result ) ;
75+ process . stdout . write ( `FAIL ${ replayTestDisplayName ( result ) } ${ attemptSuffix } ${ durationSuffix } \n` ) ;
7876 process . stdout . write ( ` ${ result . error ?. message ?? 'Unknown test failure' } \n` ) ;
7977 for ( const line of replayFailureConsoleLines ( result ) ) {
8078 process . stdout . write ( ` ${ line } \n` ) ;
8179 }
8280}
8381
8482function replayResultPrefix ( result : ReplaySuiteTestResult ) : string {
85- if ( result . status === 'passed' ) return result . attempts > 1 ? 'FLAKY' : 'PASS' ;
83+ if ( result . status === 'passed' ) return 'PASS' ;
8684 if ( result . status === 'skipped' ) return 'SKIP' ;
8785 return 'INFO' ;
8886}
@@ -98,17 +96,48 @@ function replayFailureConsoleLines(
9896 ] . filter ( Boolean ) ;
9997}
10098
101- function renderFlakyTestResult ( result : Extract < ReplaySuiteTestResult , { status : 'passed' } > ) : void {
102- const durationSuffix = result . durationMs > 0 ? ` (${ result . durationMs } ms)` : '' ;
103- process . stdout . write ( `FLAKY ${ result . file } after ${ result . attempts } attempts${ durationSuffix } \n` ) ;
104- }
105-
10699function isFlakyReplayTestResult (
107100 result : ReplaySuiteTestResult ,
108101) : result is Extract < ReplaySuiteTestResult , { status : 'passed' } > {
109102 return result . status === 'passed' && result . attempts > 1 ;
110103}
111104
105+ function renderFlakyTestSummary (
106+ results : Array < Extract < ReplaySuiteTestResult , { status : 'passed' } > > ,
107+ ) : void {
108+ if ( results . length === 0 ) return ;
109+ process . stdout . write ( 'Flaky tests:\n' ) ;
110+ for ( const result of results ) {
111+ const durationSuffix =
112+ result . durationMs > 0 ? `, total ${ formatDurationSeconds ( result . durationMs ) } ` : '' ;
113+ process . stdout . write (
114+ ` PASS ${ replayTestDisplayName ( result ) } after ${ result . attempts } attempts${ durationSuffix } \n` ,
115+ ) ;
116+ for ( const failure of result . attemptFailures ?? [ ] ) {
117+ const attemptDuration =
118+ typeof failure . durationMs === 'number'
119+ ? ` (${ formatDurationSeconds ( failure . durationMs ) } )`
120+ : '' ;
121+ process . stdout . write (
122+ ` attempt ${ failure . attempt } failed${ attemptDuration } : ${ failure . message } \n` ,
123+ ) ;
124+ }
125+ }
126+ }
127+
128+ function replayTestDisplayName ( result : ReplaySuiteTestResult ) : string {
129+ const title = result . title ?. trim ( ) ;
130+ return title && title . length > 0 ? title : path . basename ( result . file ) ;
131+ }
132+
133+ function formatReplayTestDurationSuffix ( result : ReplaySuiteTestResult ) : string {
134+ const durationMs =
135+ result . status === 'passed' && typeof result . finalAttemptDurationMs === 'number'
136+ ? result . finalAttemptDurationMs
137+ : result . durationMs ;
138+ return durationMs > 0 ? ` (${ formatDurationSeconds ( durationMs ) } )` : '' ;
139+ }
140+
112141function getReplayTestExitCode ( data : ReplaySuiteResult ) : number {
113142 return data . failed > 0 ? 1 : 0 ;
114143}
@@ -144,7 +173,7 @@ function buildReplayJunitXml(suite: ReplaySuiteResult): string {
144173}
145174
146175function renderJUnitTestCase ( test : ReplaySuiteTestResult ) : string [ ] {
147- const name = xmlEscape ( path . basename ( test . file ) ) ;
176+ const name = xmlEscape ( replayTestDisplayName ( test ) ) ;
148177 const className = xmlEscape (
149178 path . dirname ( test . file ) === '.' ? test . file : path . dirname ( test . file ) ,
150179 ) ;
@@ -239,6 +268,13 @@ function formatJUnitSeconds(durationMs: number): string {
239268 return ( Math . max ( 0 , durationMs ) / 1000 ) . toFixed ( 3 ) ;
240269}
241270
271+ function formatDurationSeconds ( durationMs : number ) : string {
272+ const seconds = Math . max ( 0 , durationMs ) / 1000 ;
273+ if ( seconds >= 10 ) return `${ seconds . toFixed ( 1 ) } s` ;
274+ if ( seconds >= 1 ) return `${ seconds . toFixed ( 2 ) } s` ;
275+ return `${ seconds . toFixed ( 3 ) . replace ( / 0 + $ / , '' ) . replace ( / \. $ / , '' ) } s` ;
276+ }
277+
242278function xmlEscape ( value : string ) : string {
243279 return value
244280 . replaceAll ( '&' , '&' )
0 commit comments