XCTest get parent / sibling of specific element in swift

735 Views Asked by At

I would like to get parent (0x7fdc65735bd0) or better directly sibling (0x7fdc657082a0), of .staticText (0x7fdc65708190) which has no identifier, just unique label.

                ScrollView, 0x7fdc65740830, {{0.0, 94.0}, {375.0, 620.0}}
                  Other, 0x7fdc657402a0, {{0.0, 94.0}, {375.0, 689.7}}
                    Other, 0x7fdc657403b0, {{0.0, 94.0}, {375.0, 50.0}}
                      Other, 0x7fdc65740ba0, {{16.0, 110.0}, {335.0, 26.0}}
                        Other, 0x7fdc65740cb0, {{16.0, 110.0}, {335.0, 26.0}}
                          StaticText, 0x7fdc65727630, {{16.0, 110.0}, {315.0, 26.0}}, label: 'Self pickup'
                          Image, 0x7fdc65727740, {{331.0, 110.0}, {20.0, 26.0}}, identifier: 'checkbox_unchecked'
                    Other, 0x7fdc6572b860, {{0.0, 144.0}, {375.0, 1.0}}
                    Other, 0x7fdc6572b970, {{0.0, 145.0}, {375.0, 66.3}}
                      Other, 0x7fdc65735ac0, {{16.0, 161.0}, {335.0, 42.3}}
                        Other, 0x7fdc65735bd0, {{16.0, 161.0}, {335.0, 20.0}}
                          StaticText, 0x7fdc65708190, {{16.0, 161.0}, {315.0, 20.0}}, label: 'Shipping'
                          Image, 0x7fdc657082a0, {{331.0, 161.0}, {20.0, 20.0}}, identifier: 'checkbox_checked'

following code works quite good for Self pickup: app.otherElements.containing(NSPredicate(format: "label LIKE[c] 'Self pickup'")).images.firstMatch

but if I'll decide to get image of Shipping and replace NSPredicate condition: app.otherElements.containing(NSPredicate(format: "label LIKE[c] 'Shipping'")).images.firstMatch

it is still giving me the Self pickup image

Do you have any idea how to bound that to children? I tried this but can't find anything: app.otherElements.children(matching: .image).containing(NSPredicate(format: "label LIKE[c] 'Shipping'")).images.firstMatch

The reason why I need to get the sibling (.image) is to determine if the image (truly checkbox) is checked or not.

EDIT:

I would need something like this: app.otherElements.containing(NSPredicate(format: "label LIKE[c] 'Shipping'")).siblingMatching(elementType: .image)

...not sure if something like this exists, but it would fits the best to my purpose.

2

There are 2 best solutions below

0
On

Let's build this step by step. We're looking for an otherElement parent. Let's get all of them..

app.otherElements

We want one containing a staticText with an identifier (I'm rusty - I think XCUITest will identify a label as an ID as long as there isn't an explicit ID, but your predicate will work just as well!) of Shipping...

app.otherElements.containing(.staticText, identifier: "Shipping")

This gives us a query returning all of the parent otherElements of this element. If we want to access a specific one we're going to need them in an array...

app.otherElements.containing(.staticText, identifier: "Shipping").allElementsBoundByIndex

Yay! Which one do we want? The one closest to our element, which will be the last one...

app.otherElements.containing(.staticText, identifier: "Shipping").allElementsBoundByIndex.last

And then we want the image inside there? Assuming there is only one...

app.otherElements.containing(.staticText, identifier: "Shipping").allElementsBoundByIndex.last.images

If more than one, you already know how to use firstMatch or again build out the array with allElementsBoundByIndex and reference the correct index.

0
On

There is no "siblingMatching" item. However, you can still get the sibling.

Seems like the key anchor in your code is staticTexts. However , there should also be an accessibility id for it that you could used instead. For example:

Instead of app.otherElements.containing(NSPredicate(format: "label LIKE[c] 'Shipping'"))

You could have something like this app.otherElements.matching(identifier: AX_ID_EXAMPLE).

Then you could do something like this:

let elementQuery = app.otherElements.matching(identifier: AX_ID_EXAMPLE)

func getIndexForShippingElement() -> Int {
  let countMatching = elementQuery.count
  for i in 0...countMatching {
    let targetElement = elementQuery.element(boundBy: i)
    if targetElement.staticText["Shipping"].exists
    return i
    }
  }
  XCTFail("couldn't find a match for Shipping element")
  return -1 // will never be called as we would have already failed
}

Then you could call it like this:

let shippingIndex = getIndexForShippingElement()

let myTargetImage = elementQuery.element(boundBy: shippingIndex).images

As you can see both staticText["Shipping"] and the image are children of the mutual parent elementQuery.

However, I strongly recommend that you attribute an Accessibility id to the image here else it could easily break if a new image is introduced.