@@ -1569,6 +1569,80 @@ func TestResolveRelativeSelectorContainsDescendants(t *testing.T) {
15691569 }
15701570}
15711571
1572+ // mockWDAServerForRelativeDepthTest creates a server with elements that test
1573+ // distance vs. depth selection in directional relative selectors.
1574+ // The page source has a close TextField (depth 2) and a far-but-deeply-nested
1575+ // Link (depth 5) below the anchor. The correct behavior is to select the closer one.
1576+ func mockWDAServerForRelativeDepthTest () * httptest.Server {
1577+ return httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
1578+ w .Header ().Set ("Content-Type" , "application/json" )
1579+ path := r .URL .Path
1580+
1581+ if strings .HasSuffix (path , "/source" ) {
1582+ jsonResponse (w , map [string ]interface {}{
1583+ "value" : `<?xml version="1.0" encoding="UTF-8"?>
1584+ <AppiumAUT>
1585+ <XCUIElementTypeApplication type="XCUIElementTypeApplication" name="TestApp" enabled="true" visible="true" x="0" y="0" width="390" height="844">
1586+ <XCUIElementTypeStaticText type="XCUIElementTypeStaticText" label="Email Address" enabled="true" visible="true" x="50" y="100" width="290" height="30"/>
1587+ <XCUIElementTypeTextField type="XCUIElementTypeTextField" label="email input" enabled="true" visible="true" x="50" y="140" width="290" height="40"/>
1588+ <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="50" y="300" width="290" height="100">
1589+ <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="50" y="300" width="290" height="100">
1590+ <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="50" y="300" width="290" height="100">
1591+ <XCUIElementTypeLink type="XCUIElementTypeLink" label="deep link" enabled="true" visible="true" x="50" y="350" width="290" height="30"/>
1592+ </XCUIElementTypeOther>
1593+ </XCUIElementTypeOther>
1594+ </XCUIElementTypeOther>
1595+ </XCUIElementTypeApplication>
1596+ </AppiumAUT>` ,
1597+ })
1598+ return
1599+ }
1600+
1601+ if strings .Contains (path , "/window/size" ) {
1602+ jsonResponse (w , map [string ]interface {}{
1603+ "value" : map [string ]interface {}{"width" : 390.0 , "height" : 844.0 },
1604+ })
1605+ return
1606+ }
1607+
1608+ jsonResponse (w , map [string ]interface {}{"status" : 0 })
1609+ }))
1610+ }
1611+
1612+ // TestResolveRelativeSelectorPrefersClosestOverDeepest verifies that directional
1613+ // relative selectors (below/above/leftOf/rightOf) pick the closest element by
1614+ // distance rather than the deepest in the DOM. This matches Maestro's
1615+ // .firstOrNull() behavior on the distance-sorted candidate list.
1616+ func TestResolveRelativeSelectorPrefersClosestOverDeepest (t * testing.T ) {
1617+ server := mockWDAServerForRelativeDepthTest ()
1618+ defer server .Close ()
1619+ driver := createTestDriver (server )
1620+
1621+ source , _ := driver .client .Source ()
1622+ elements , _ := ParsePageSource (source )
1623+
1624+ sel := flow.Selector {
1625+ Below : & flow.Selector {Text : "Email Address" },
1626+ }
1627+
1628+ info , err := driver .resolveRelativeSelector (sel , elements )
1629+ if err != nil {
1630+ t .Fatalf ("Expected success, got: %v" , err )
1631+ }
1632+ if info == nil {
1633+ t .Fatal ("Expected element info" )
1634+ }
1635+
1636+ // The closest element below "Email Address" (bottom at y=130) is the
1637+ // TextField at y=140, not the deeply-nested Link at y=350 (depth 5).
1638+ if info .Text != "email input" {
1639+ t .Errorf ("Expected closest element 'email input', got '%s'" , info .Text )
1640+ }
1641+ if info .Bounds .Y != 140 {
1642+ t .Errorf ("Expected element at y=140, got y=%d" , info .Bounds .Y )
1643+ }
1644+ }
1645+
15721646// TestEraseTextWithActiveElement tests eraseText with active element
15731647func TestEraseTextWithActiveElement (t * testing.T ) {
15741648 server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
0 commit comments