@@ -133,6 +133,38 @@ test('runtime click keeps distinct tab button centers when iOS reports the tab b
133133 assert . equal ( selectorResult . node ?. label , 'Settings' ) ;
134134} ) ;
135135
136+ test ( 'runtime click rejects refs covered by floating overlays' , async ( ) => {
137+ const calls : Point [ ] = [ ] ;
138+ const device = createInteractionDevice ( coveredByTabBarSnapshot ( ) , {
139+ tap : async ( _context , point ) => {
140+ calls . push ( point ) ;
141+ } ,
142+ } ) ;
143+
144+ await assert . rejects (
145+ ( ) => device . interactions . click ( ref ( '@e2' ) , { session : 'default' } ) ,
146+ / R e f @ e 2 i s c o v e r e d b y a n o t h e r v i s i b l e e l e m e n t / ,
147+ ) ;
148+ assert . deepEqual ( calls , [ ] ) ;
149+ } ) ;
150+
151+ test ( 'runtime selector interactions skip covered matches when an uncovered duplicate exists' , async ( ) => {
152+ const calls : Point [ ] = [ ] ;
153+ const device = createInteractionDevice ( duplicateCoveredLabelSnapshot ( ) , {
154+ tap : async ( _context , point ) => {
155+ calls . push ( point ) ;
156+ } ,
157+ } ) ;
158+
159+ const result = await device . interactions . click ( selector ( 'label="Save draft"' ) , {
160+ session : 'default' ,
161+ } ) ;
162+
163+ assert . equal ( result . kind , 'selector' ) ;
164+ assert . equal ( result . node ?. ref , 'e2' ) ;
165+ assert . deepEqual ( calls , [ { x : 86 , y : 142 } ] ) ;
166+ } ) ;
167+
136168test ( 'runtime click keeps non-button semantic targets at their own center' , async ( ) => {
137169 const calls : Point [ ] = [ ] ;
138170 const device = createInteractionDevice ( nonHittableCellSnapshot ( ) , {
@@ -875,6 +907,77 @@ function iosTabBarSnapshot(): SnapshotState {
875907 ] ) ;
876908}
877909
910+ function coveredByTabBarSnapshot ( ) : SnapshotState {
911+ return makeSnapshotState ( [
912+ {
913+ index : 0 ,
914+ depth : 0 ,
915+ type : 'Application' ,
916+ label : 'Example' ,
917+ rect : { x : 0 , y : 0 , width : 390 , height : 844 } ,
918+ } ,
919+ {
920+ index : 1 ,
921+ depth : 1 ,
922+ parentIndex : 0 ,
923+ type : 'Button' ,
924+ label : 'Save draft' ,
925+ rect : { x : 16 , y : 790 , width : 140 , height : 44 } ,
926+ hittable : false ,
927+ interactionBlocked : 'covered' ,
928+ presentationHints : [ 'covered' ] ,
929+ } ,
930+ {
931+ index : 2 ,
932+ depth : 1 ,
933+ parentIndex : 0 ,
934+ type : 'TabBar' ,
935+ rect : { x : 0 , y : 760 , width : 390 , height : 84 } ,
936+ hittable : true ,
937+ } ,
938+ ] ) ;
939+ }
940+
941+ function duplicateCoveredLabelSnapshot ( ) : SnapshotState {
942+ return makeSnapshotState ( [
943+ {
944+ index : 0 ,
945+ depth : 0 ,
946+ type : 'Application' ,
947+ label : 'Example' ,
948+ rect : { x : 0 , y : 0 , width : 390 , height : 844 } ,
949+ } ,
950+ {
951+ index : 1 ,
952+ depth : 1 ,
953+ parentIndex : 0 ,
954+ type : 'Button' ,
955+ label : 'Save draft' ,
956+ rect : { x : 16 , y : 120 , width : 140 , height : 44 } ,
957+ hittable : true ,
958+ } ,
959+ {
960+ index : 2 ,
961+ depth : 1 ,
962+ parentIndex : 0 ,
963+ type : 'Button' ,
964+ label : 'Save draft' ,
965+ rect : { x : 16 , y : 790 , width : 140 , height : 44 } ,
966+ hittable : false ,
967+ interactionBlocked : 'covered' ,
968+ presentationHints : [ 'covered' ] ,
969+ } ,
970+ {
971+ index : 3 ,
972+ depth : 1 ,
973+ parentIndex : 0 ,
974+ type : 'TabBar' ,
975+ rect : { x : 0 , y : 760 , width : 390 , height : 84 } ,
976+ hittable : true ,
977+ } ,
978+ ] ) ;
979+ }
980+
878981function nonHittableCellSnapshot ( ) : SnapshotState {
879982 return makeSnapshotState ( [
880983 {
0 commit comments