1

I'm using the AppiumDriver and MobileElement java packages (I'm writing in Kotlin) to UI test an iOS app that already has tests written using XCUITest.

I need a function that behaves the same as XCUIElement.exists() acts in XCUITest. This would be a function that returns true if the UIElement in question is present on the screen regardless of whether it is visible/clickable. An example of this is a UI element embedded in a scrollview that's out of sight (not scrolled to). It's on the page, but it is not visible nor is it hittable.

Right now I have a scrollview with UI elements out of sight, that the program will hang waiting for their visibility, until I manually scroll the scrollview to bring them into view, which at that point will pass the tests.

I can confirm that MobileElement.->

isDisplayed() (this behaves more like isHittable() than exists() from what I've seen)

isEnabled()

isSelected()

All do not do what I want, neither does anything along the lines of:

driver.findElements(By.xpath("value")).size() != 0

The above line seems to return true in the exact same cases as isDisplayed if I'm not mistaken.

EDIT: Some users have suggested that the elements may not exist yet due to lazy loading, and that being the reason why the driver.findElements(By.xpath("value")).size() != 0 line does not work. In response to this I have added an image of the div representing the scrollview in the iOS app. The highlighted UI Element still cannot be found using until the user manually scrolls down to make it visible before the request times out.

enter image description here

  • My guess is that the elements may not actually exist until you scroll. Look up "lazy loading" to get more info. Are you able to verify in the dev console that they do in fact exist before scrolling? – JeffC Jan 06 '20 at 16:14
  • They seem to exist though. I looked at my app on the "App Source" section of the attribute inspector in appium and can see that the elements are in fact there, embedded in the scrollview, even when they are not currently scrolled to. I'm not sure if this proves that the elements are in fact in the DOM but I feel it does. I have edited my original question to include an image of this. – HumanZero HumanZero Jan 08 '20 at 09:54

2 Answers2

0
driver.findElements(By.xpath("value")).size() != 0

Is the correct way to test if an element exists. This doesn't check viability, it just fetch all the elements currently in the DOM that match the locator. Even if you define implicit wait (if necessary) to the existence of the elements, nothing else.

Guy
  • 46,488
  • 10
  • 44
  • 88
  • 1
    What do you mean by "viability" in this context? I have an element that I know is on the page, because if I scroll down then mobileElement.isDisplayed returns true but using the code you outlined does the same thing. If I put an `Assert.assertTrue( . )` around it, it will wait about 30 seconds and fail. During this 30 seconds it is doing an implicit wait for the condition to be met (it never is). – HumanZero HumanZero Jan 06 '20 at 14:43
  • @HumanZeroHumanZero If the page has lazy loading and the element is not added to the DOM before scrolling to it than there is no difference, the loaded element will be visible by default. It fails because the element doesn't yet exists. But if the element always exists in the DOM `driver.findElements` will locate it and return it even if it isn't visible. – Guy Jan 06 '20 at 15:32
  • Well then the issue would be that XCUITest is able to recognise that a UI element is present even if it isn't on screen currently. It seems that this isn't something that the selenium webdriver is able to do? (tried on both iOS and Android). Is this perhaps because of how the tests are carried out from within the device itself whereas with appium the commands are from an external source? Not sure if I understand completely. – HumanZero HumanZero Jan 06 '20 at 17:57
  • @HumanZeroHumanZero Selenium is able to locate elements not currently on the screen. But they have to be in the DOM. If the element doesn't exists until the scrolling (lazy loading) it just doesn't exists. The best example is an element with `style="visibility: hidden;"` `findElements()` will locate it while `isDisplayed()` will return false. – Guy Jan 07 '20 at 05:11
  • Sorry, I don't think I made myself clear. If XCUITest is able to locate an element without it being visible, and Appium uses XCUITest as the UI Automation framework, then surely the ui element should be present in the DOM regardless of it's visibility? Am I misunderstanding something? – HumanZero HumanZero Jan 07 '20 at 09:33
  • @HumanZeroHumanZero there are many applications where scrolling to the bottom of the page trigger appearance of new content. This called lazy loading, the elements are added by javascript, until then those elements are not in the DOM. – Guy Jan 07 '20 at 09:45
  • My point is that it is the same app that, using XCUITest, finds elements off the page but the appium libraries does not find them. For your point about lazy loading and the elements not being in the DOM, I looked at my app on the "App Source" section of the attribute inspector in appium and can see that the elements are in fact there, embedded in the scrollview, even when they are not currently scrolled to. I have edited my original question to include an image of this. – HumanZero HumanZero Jan 08 '20 at 09:50
0

To check the existance i.e. the presence of element regardless of the element being visible / hittable you have to induce WebDriverWait for the presenceOfElementLocated() and you can use the following Locator Strategy:

new WebDriverWait(driver, 20).until(ExpectedConditions.presenceOfElementLocated(By.xpath("value"))).size() != 0
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • 1
    Hi thanks for the answer, but unfortunately this seems to do the same thing - only return true when the element is displayed on screen. If not it times out. The way I've come to this conclusion as when I give the id of an element I know is visible - it finds it. When I give it the id of an element that isn't visible, it waits the full 20 seconds and times out. – HumanZero HumanZero Jan 06 '20 at 14:53