@@ -27,6 +27,7 @@ extension RunnerTests {
2727 struct SelectorElementMatch {
2828 let element : XCUIElement ?
2929 let isAmbiguous : Bool
30+ let usedNonHittableFallback : Bool
3031 }
3132
3233 enum TextTypingRepairMode {
@@ -177,10 +178,15 @@ extension RunnerTests {
177178 return element. exists ? element : nil
178179 }
179180
180- func findElement( app: XCUIApplication , selectorKey: String , selectorValue: String ) -> SelectorElementMatch {
181+ func findElement(
182+ app: XCUIApplication ,
183+ selectorKey: String ,
184+ selectorValue: String ,
185+ allowNonHittableFallback: Bool = false
186+ ) -> SelectorElementMatch {
181187 let value = selectorValue. trimmingCharacters ( in: . whitespacesAndNewlines)
182188 guard !value. isEmpty else {
183- return SelectorElementMatch ( element: nil , isAmbiguous: false )
189+ return SelectorElementMatch ( element: nil , isAmbiguous: false , usedNonHittableFallback : false )
184190 }
185191 let predicate : NSPredicate
186192 switch selectorKey {
@@ -193,21 +199,44 @@ extension RunnerTests {
193199 case " text " :
194200 predicate = NSPredicate ( format: " label ==[c] %@ OR identifier ==[c] %@ OR value ==[c] %@ " , value, value, value)
195201 default :
196- return SelectorElementMatch ( element: nil , isAmbiguous: false )
202+ return SelectorElementMatch ( element: nil , isAmbiguous: false , usedNonHittableFallback : false )
197203 }
198204
199205 var matchedElement : XCUIElement ?
206+ var nonHittableElement : XCUIElement ?
200207 let matches = app. descendants ( matching: . any) . matching ( predicate) . allElementsBoundByIndex
201208 for element in matches where element. exists {
202- guard element. isHittable else {
209+ if !element. isHittable {
210+ if allowNonHittableFallback && hasTappableFrame ( app: app, element: element) {
211+ guard nonHittableElement == nil else {
212+ return SelectorElementMatch ( element: nil , isAmbiguous: true , usedNonHittableFallback: false )
213+ }
214+ nonHittableElement = element
215+ }
203216 continue
204217 }
205218 guard matchedElement == nil else {
206- return SelectorElementMatch ( element: nil , isAmbiguous: true )
219+ return SelectorElementMatch ( element: nil , isAmbiguous: true , usedNonHittableFallback : false )
207220 }
208221 matchedElement = element
209222 }
210- return SelectorElementMatch ( element: matchedElement, isAmbiguous: false )
223+ if let matchedElement {
224+ return SelectorElementMatch ( element: matchedElement, isAmbiguous: false , usedNonHittableFallback: false )
225+ }
226+ return SelectorElementMatch (
227+ element: nonHittableElement,
228+ isAmbiguous: false ,
229+ usedNonHittableFallback: nonHittableElement != nil
230+ )
231+ }
232+
233+ private func hasTappableFrame( app: XCUIApplication , element: XCUIElement ) -> Bool {
234+ let frame = element. frame
235+ if frame. isEmpty {
236+ return false
237+ }
238+ let appFrame = app. frame
239+ return appFrame. isEmpty || appFrame. intersects ( frame)
211240 }
212241
213242 func queryElement( app: XCUIApplication , selectorKey: String , selectorValue: String ) -> Response {
0 commit comments