@@ -14,7 +14,7 @@ export type InteractionSurfaceSignature = NonNullable<
1414
1515export type InteractionSurfaceChange = 'changed' | 'unchanged' | 'ambiguous' ;
1616
17- export function shouldRetryTouchOnNoChange ( flags : CommandFlags | undefined ) : boolean {
17+ function shouldRetryTouchOnNoChange ( flags : CommandFlags | undefined ) : boolean {
1818 return flags ?. interactionOutcome ?. retryOnNoChange === true ;
1919}
2020
@@ -28,13 +28,14 @@ export function markPendingInteractionOutcome(params: {
2828 const { session, command, positionals, flags, preSnapshot } = params ;
2929 if ( ! shouldRetryTouchOnNoChange ( flags ) ) return ;
3030 if ( ! supportsInteractionOutcomePolicy ( session ) ) return ;
31- if ( ! isRetryableTapCommand ( command ) ) return ;
31+ const retryCommand = retryCommandForTap ( command ) ;
32+ if ( ! retryCommand ) return ;
3233 if ( ! isCoordinatePair ( positionals ) ) return ;
3334 const preSignature = buildInteractionSurfaceSignature ( preSnapshot ?. nodes ?? [ ] ) ;
3435 if ( preSignature . length === 0 ) return ;
3536 session . pendingInteractionOutcome = {
3637 action : command ,
37- command,
38+ command : retryCommand ,
3839 positionals,
3940 flags : stripInternalInteractionOutcomeFlags ( flags ) ,
4041 markedAt : Date . now ( ) ,
@@ -67,11 +68,10 @@ export function clearPendingInteractionOutcome(session: SessionState | undefined
6768export async function retryPendingInteractionOutcome ( params : {
6869 session : SessionState ;
6970 pending : NonNullable < SessionState [ 'pendingInteractionOutcome' ] > ;
70- requestFlags : CommandFlags | undefined ;
7171 logPath : string ;
7272 snapshot : SnapshotState ;
7373} ) : Promise < { retried : boolean ; change : InteractionSurfaceChange } > {
74- const { session, pending, requestFlags , snapshot } = params ;
74+ const { session, pending, snapshot } = params ;
7575 const change = classifyInteractionSurfaceChange (
7676 pending . preSignature ,
7777 buildInteractionSurfaceSignature ( snapshot . nodes ) ,
@@ -92,12 +92,7 @@ export async function retryPendingInteractionOutcome(params: {
9292 } ,
9393 } ) ;
9494 await dispatchCommand ( session . device , pending . command , pending . positionals , pending . flags ?. out , {
95- ...contextFromFlags (
96- params . logPath ,
97- mergeRetryDispatchFlags ( pending . flags , requestFlags ) ,
98- session . appBundleId ,
99- session . trace ?. outPath ,
100- ) ,
95+ ...contextFromFlags ( params . logPath , pending . flags , session . appBundleId , session . trace ?. outPath ) ,
10196 surface : session . surface ,
10297 } ) ;
10398 return { retried : true , change } ;
@@ -156,31 +151,8 @@ export function buildInteractionSurfaceSignature(nodes: SnapshotNode[]): Array<{
156151 const entries : InteractionSurfaceSignature = [ ] ;
157152
158153 for ( const node of nodes ) {
159- if ( ! node . rect ) continue ;
160- if ( ! isFiniteRect ( node . rect ) ) continue ;
161- if ( isScrollIndicator ( node ) ) continue ;
162- const semanticKey = [
163- node . identifier ,
164- node . label ,
165- node . value ,
166- node . type ,
167- node . role ,
168- node . enabled === false ? 'disabled' : 'enabled' ,
169- node . selected === true ? 'selected' : 'unselected' ,
170- node . hittable === true ? 'hittable' : 'not-hittable' ,
171- ]
172- . map ( ( value ) => ( typeof value === 'string' ? value . trim ( ) : '' ) )
173- . join ( '|' ) ;
174- if ( ! semanticKey . replaceAll ( '|' , '' ) ) continue ;
175- const occurrence = occurrenceCounts . get ( semanticKey ) ?? 0 ;
176- occurrenceCounts . set ( semanticKey , occurrence + 1 ) ;
177- entries . push ( {
178- key : `${ semanticKey } |#${ occurrence } ` ,
179- x : Math . round ( node . rect . x ) ,
180- y : Math . round ( node . rect . y ) ,
181- width : Math . round ( node . rect . width ) ,
182- height : Math . round ( node . rect . height ) ,
183- } ) ;
154+ const entry = buildInteractionSurfaceEntry ( node , occurrenceCounts ) ;
155+ if ( entry ) entries . push ( entry ) ;
184156 }
185157
186158 return entries ;
@@ -212,26 +184,50 @@ export function areInteractionSurfaceSignaturesStable(
212184 return true ;
213185}
214186
215- function mergeRetryDispatchFlags (
216- pendingFlags : CommandFlags | undefined ,
217- requestFlags : CommandFlags | undefined ,
218- ) : CommandFlags | undefined {
219- return {
220- ...( pendingFlags ?? { } ) ,
221- platform : requestFlags ?. platform ?? pendingFlags ?. platform ,
222- target : requestFlags ?. target ?? pendingFlags ?. target ,
223- device : requestFlags ?. device ?? pendingFlags ?. device ,
224- udid : requestFlags ?. udid ?? pendingFlags ?. udid ,
225- serial : requestFlags ?. serial ?? pendingFlags ?. serial ,
226- } ;
227- }
228-
229187function supportsInteractionOutcomePolicy ( session : SessionState ) : boolean {
230188 return session . device . platform === 'ios' || session . device . platform === 'android' ;
231189}
232190
233- function isRetryableTapCommand ( command : string ) : boolean {
234- return command === 'click' || command === 'press' ;
191+ function retryCommandForTap ( command : string ) : string | undefined {
192+ if ( command === 'click' ) return 'press' ;
193+ if ( command === 'press' ) return 'press' ;
194+ return undefined ;
195+ }
196+
197+ function buildInteractionSurfaceEntry (
198+ node : SnapshotNode ,
199+ occurrenceCounts : Map < string , number > ,
200+ ) : InteractionSurfaceSignature [ number ] | undefined {
201+ if ( ! node . rect ) return undefined ;
202+ if ( ! isFiniteRect ( node . rect ) ) return undefined ;
203+ if ( isScrollIndicator ( node ) ) return undefined ;
204+ const semanticKey = interactionSurfaceSemanticKey ( node ) ;
205+ if ( ! semanticKey ) return undefined ;
206+ const occurrence = occurrenceCounts . get ( semanticKey ) ?? 0 ;
207+ occurrenceCounts . set ( semanticKey , occurrence + 1 ) ;
208+ return {
209+ key : `${ semanticKey } |#${ occurrence } ` ,
210+ x : Math . round ( node . rect . x ) ,
211+ y : Math . round ( node . rect . y ) ,
212+ width : Math . round ( node . rect . width ) ,
213+ height : Math . round ( node . rect . height ) ,
214+ } ;
215+ }
216+
217+ function interactionSurfaceSemanticKey ( node : SnapshotNode ) : string | undefined {
218+ const semanticKey = [
219+ node . identifier ,
220+ node . label ,
221+ node . value ,
222+ node . type ,
223+ node . role ,
224+ node . enabled === false ? 'disabled' : 'enabled' ,
225+ node . selected === true ? 'selected' : 'unselected' ,
226+ node . hittable === true ? 'hittable' : 'not-hittable' ,
227+ ]
228+ . map ( ( value ) => ( typeof value === 'string' ? value . trim ( ) : '' ) )
229+ . join ( '|' ) ;
230+ return semanticKey . replaceAll ( '|' , '' ) ? semanticKey : undefined ;
235231}
236232
237233function isCoordinatePair ( positionals : string [ ] ) : boolean {
0 commit comments