Search code examples
xcuitest

Select parent of XCUIElement


How can I select the parent element of an XCUIElement in XCUITest? According to the documentation, the class has children() and descendants() but nothing to select parents or siblings. It seems to me I must be missing something - how can Apple have an element tree without navigation in both directions???

I know there is a method containing() on XCUIElementQuery but that is not the same. I also know that an accessibilityIdentifier might help but I am thinking of writing a generic method for testing any view with a given Navbar label. Passing in all the identifiers of all the elements I would like to access does not seem like a good option.


Solution

  • Unfortunately there is no direct named method to access parent elements similar to children() and descendants() provided by Apple but you were actually on the right track with containing(). There are two ways I usually approach it when I need to locate a parent element based on children/descendants:

    1. Using containing(_:identifier:)
      let parentElement = app.otherElements.containing(.textField, identifier: "test").firstMatch
      or
      let parentElement = app.otherElements.containing(.textField, identifier: "test").element(boundBy: 0)
    2. Using containing(_ predicate: NSPredicate)
      let parentElement = app.otherElements.containing(NSPredicate(format: "label CONTAINS[c] 'test'").firstMatch
      or
      let parentElement = app.otherElements.containing(NSPredicate(format: "label CONTAINS[c] 'test'").element(boundBy: 0)

    These are just examples with random data/element types because you didn't mention exactly what you want to achieve but you can go from there.

    Update:
    As usual the Apple documentation doesn't do a good service. They say 'descendants' but what they actually mean is both direct descendants(children) and non-direct descendants(descendants). Unfortunately there is no guarantee and there is no generic solution. It should be based on your current needs and the application implementation. More examples that could be useful:

    1. If you don't want the first element from the query you are better off using element(boundBy: index). So if you know that XCUIElementQuery will give you 5 elements and you know you need the 3rd one:
      let parentElement = app.otherElements.containing(.textField, identifier: "test").element(boundBy: 2)
    2. Fine graining of your element locators. Lets say you have 3 views with identifier "SomeView", these 3 views each contain 2 other subviews and the subviews have a button with identifier "SomeButton".
    let parentViews = app.otherElements.matching(identifier: "SomeView")
    let subView = parentViews.element(boundBy: 2).otherElements.containing(.button, identifier: "SomeButton").element(boundBy: 1)
    

    This will give you the second subview containing a button with identifier "SomeButton" from the third parent view with identifier "SomeView". Using such an approach you can fine tune until you get exactly what you need and not all parents, grandparents, great-grandparents etc.

    I wish Apple provided a bit more flexibility for the locators with XCTest like Xpath does for Appium but even these tools can be sufficient most of the time.