@@ -81,5 +81,42 @@ extension RunnerTests {
8181 . selected( index: 1 , usedNonHittableFallback: true )
8282 )
8383 }
84+
85+ #if os(iOS)
86+ func testQuerySelectorPrefersHittableMatchOverNonHittableDuplicate( ) throws {
87+ let duplicateIdentifier = " agent-device-selector-read-duplicate "
88+ app. launchArguments = [ " --agent-device-selector-read-regression " ]
89+ app. launch ( )
90+ defer {
91+ invalidateCachedTarget ( reason: " unit_test_cleanup " )
92+ app. terminate ( )
93+ }
94+ XCTAssertTrue ( app. waitForExistence ( timeout: appExistenceTimeout) )
95+
96+ let matches = app. descendants ( matching: . any)
97+ . matching ( identifier: duplicateIdentifier)
98+ . allElementsBoundByIndex
99+ . filter ( \. exists)
100+ XCTAssertEqual ( matches. count, 2 , " fixture must expose two raw identifier matches " )
101+ XCTAssertEqual ( matches. filter ( \. isHittable) . count, 1 , " fixture must expose exactly one hittable match " )
102+
103+ let command = try JSONDecoder ( ) . decode (
104+ Command . self,
105+ from: Data (
106+ #"{"command":"querySelector","commandId":"query-selector-duplicate","selectorKey":"id","selectorValue":"agent-device-selector-read-duplicate"}"# . utf8
107+ )
108+ )
109+ let response = try executeOnMainPrepared ( command: command, activeApp: app)
110+
111+ guard response. ok else {
112+ XCTFail ( String ( describing: response. error) )
113+ return
114+ }
115+ XCTAssertEqual ( response. data? . found, true )
116+ XCTAssertEqual ( response. data? . nodes? . count, 1 )
117+ XCTAssertEqual ( response. data? . nodes? . first? . identifier, duplicateIdentifier)
118+ XCTAssertEqual ( response. data? . nodes? . first? . hittable, true )
119+ }
120+ #endif
84121#endif
85122}
0 commit comments