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.
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:
containing(_:identifier:)
let parentElement = app.otherElements.containing(.textField, identifier: "test").firstMatch
let parentElement = app.otherElements.containing(.textField, identifier: "test").element(boundBy: 0)
containing(_ predicate: NSPredicate)
let parentElement = app.otherElements.containing(NSPredicate(format: "label CONTAINS[c] 'test'").firstMatch
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:
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)
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.