@@ -45,12 +45,61 @@ const ACQUIRED_USAGE_ASSERTION_TIME_REGEX =
4545const BUILD_SETTINGS_PATH_REGEX = / ^ ( { 6 } P A T H = ) .+ $ / gm;
4646const TRAILING_WHITESPACE_REGEX = / [ \t ] + $ / gm;
4747const SIMULATOR_FAILURE_TEST_PROGRESS_BLOCK_REGEX =
48- / (?: ^ R u n n i n g t e s t s \( ( \d + ) c o m p l e t e d , ( \d + ) f a i l u r e s ? , ( \d + ) s k i p p e d \) \n ) { 30 , } / gm;
48+ / (?: ^ R u n n i n g t e s t s \( \d + c o m p l e t e d , \d + f a i l u r e s ? , \d + s k i p p e d \) \n ) { 30 , } / gm;
49+ const TEST_PROGRESS_LINE_REGEX =
50+ / ^ R u n n i n g t e s t s \( ( \d + ) c o m p l e t e d , ( \d + ) f a i l u r e s ? , ( \d + ) s k i p p e d \) $ / u;
51+
52+ type TestProgress = { completed : number ; failed : number ; skipped : number } ;
4953
5054function escapeRegex ( str : string ) : string {
5155 return str . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
5256}
5357
58+ function parseTestProgressLine ( line : string ) : TestProgress | null {
59+ const match = line . match ( TEST_PROGRESS_LINE_REGEX ) ;
60+ if ( ! match ) {
61+ return null ;
62+ }
63+
64+ return {
65+ completed : Number ( match [ 1 ] ) ,
66+ failed : Number ( match [ 2 ] ) ,
67+ skipped : Number ( match [ 3 ] ) ,
68+ } ;
69+ }
70+
71+ function isMonotonicProgress ( progress : TestProgress [ ] ) : boolean {
72+ return progress . every ( ( current , index ) => {
73+ const previous = progress [ index - 1 ] ;
74+ return (
75+ previous === undefined ||
76+ ( current . completed >= previous . completed &&
77+ current . failed >= previous . failed &&
78+ current . skipped >= previous . skipped )
79+ ) ;
80+ } ) ;
81+ }
82+
83+ function normalizeSimulatorFailureTestProgressBlock ( match : string ) : string {
84+ const progress = match . trimEnd ( ) . split ( '\n' ) . map ( parseTestProgressLine ) ;
85+ const parsedProgress = progress . filter ( ( line ) : line is TestProgress => line !== null ) ;
86+ if ( parsedProgress . length !== progress . length ) {
87+ return match ;
88+ }
89+ const first = parsedProgress [ 0 ] ;
90+ const final = parsedProgress . at ( - 1 ) ;
91+ if ( ! first || ! final ) {
92+ return match ;
93+ }
94+
95+ const hasCleanStart = first . completed <= 1 && first . failed === 0 && first . skipped === 0 ;
96+ if ( ! hasCleanStart || final . failed === 0 || ! isMonotonicProgress ( parsedProgress ) ) {
97+ return match ;
98+ }
99+
100+ return `Running tests (<TEST_PROGRESS>; final: ${ final . completed } completed, ${ final . failed } failed, ${ final . skipped } skipped)\n` ;
101+ }
102+
54103export function normalizeSnapshotOutput ( text : string ) : string {
55104 let normalized = text ;
56105
@@ -142,16 +191,7 @@ export function normalizeSnapshotOutput(text: string): string {
142191
143192 normalized = normalized . replace (
144193 SIMULATOR_FAILURE_TEST_PROGRESS_BLOCK_REGEX ,
145- ( match : string , completed : string , failed : string , skipped : string ) => {
146- if (
147- ! match . startsWith ( 'Running tests (0 completed, 0 failures, 0 skipped)\n' ) ||
148- completed !== '57'
149- ) {
150- return match ;
151- }
152-
153- return `Running tests (<TEST_PROGRESS>; final: ${ completed } completed, ${ failed } failed, ${ skipped } skipped)\n` ;
154- } ,
194+ normalizeSimulatorFailureTestProgressBlock ,
155195 ) ;
156196
157197 // Normalize final test summary line (counts vary across environments)
0 commit comments