findElementsByXPath crashes getAttribute

Hi,
I’m trying to get all “interactable” elements from an self programmed App.

I have two methods:

List<MobileElement> interactableElements = driver.findElementsByXPath(".//*[@clickable='true' or @checkable='true' or @scrollable='true']");

and:

List<MobileElement> allItems = driver.findElementsByAndroidUIAutomator("new UiSelector()");
List<MobileElement> interactableElements = allItems.stream()
            .filter(e -> Attributes.CLICKABLE.getFromBoolean(e) || Attributes.CHECKABLE.getFromBoolean(e) || Attributes.SCROLLABLE.getFromBoolean(e))
            .collect(Collectors.toList());

I figured out, that the xpath method is faster…so I favor this.

However,

interactableElements.stream().forEach(e -> System.out.println(e.getText()));

gives me a

Cached elements 'By.xpath: .//*[@clickable='true' or @checkable='true' or @scrollable='true']' do not exist in DOM anymore
For documentation on this error, please visit: https://www.seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'
System info: host: '*****', ip: '127.0.1.1', os.name: 'Linux', os.arch: 'amd64', os.version: '5.4.0-56-        generic', java.version: '11.0.9.1'
Driver info: io.appium.java_client.android.AndroidDriver
Capabilities {appActivity: MainActivity, appPackage: ****..., automationName: UiAutomator2,     clearDeviceLogsOnStart: true, databaseEnabled: false, desired: {appActivity: MainActivity, appPackage:     ****..., automationName: UiAutomator2, clearDeviceLogsOnStart: true, disableWindowAnimation: true,     newCommandTimeout: 1200, platformName: android, udid: emulator-5558}, deviceApiLevel: 30,     deviceManufacturer: Google, deviceModel: sdk_gphone_x86_arm, deviceName: emulator-5558, deviceScreenDensity: 420, deviceScreenSize: 1080x1920, deviceUDID: emulator-5558, disableWindowAnimation: true, javascriptEnabled: true, locationContextEnabled: false, networkConnectionEnabled: true, newCommandTimeout: 1200, pixelRatio: 2.625, platform: LINUX, platformName: Android, platformVersion: 11, statBarHeight: 63, takesScreenshot: true, udid: emulator-5558, viewportRect: {height: 1731, left: 0, top: 63, width: 1080}, warnings: {}, webStorageEnabled: false}
Session ID: 73114860-dd23-4709-9f43-4f3ccac05ac1
....

with the xpath method and works flawlessly with the second UIAutomator method.

Also, trying out to get any attribute from an element gives me this error.

Appium java-client 7.4.1, appium version 1.19.1

Best Regards

Even without knowing much java, I can play duck here.

e.getText() is going to assume that e is an element , which is plausible for this exercise.
However assuming that, it still just takes one element in the collection to not have all the attributes and it will go bang, you probably need to gobble that element or filter it earlier on?

Regardless , very clever technique here, thanks for sharing.

Hi duck :wink:

actually getText() always delivers a return value …at least an empty string.

However, I could also use getAttribute(“clickable”) and get the same error even though I made sure in the xpath that attribute clickable should definitely be there.

Cheers!

1 Like

Try to call getText on interactableElements right after the search is performed. The search criteria there is very wide, which means any of the found elements could become stale very fast. Also remember the internal elements cache could only keep up to 500 recent items.

1 Like

thanks for the answer. Can you provide more information about “stale” elements and why they would become stale? And why do elements not become stale using using UIAutomator method?

the 500 elements cache should be fine…definetly less.

Unfortunately, I think it is not an option for me to collect elements as needed…I need to get all elements from the page and thoroughly analyze the context. Thus, categorizing buttons into “continue”/“cancel” buttons or text fields as “name”, “email address” fields. Several different page handler types analyze the page/activity and evaluate if they can interact with it. Categorization takes quite some time so I don’t want to do it over and over again for like 10 different page handlers.

OK I fixed it…

It seems that xpath method is way too fast. I click on an element on the previous activity which transitions to the new/current activity where I start to get all elements of. However, getformxpath analyzed the previous activity even though it’s started after clicking on the button for activity transition.

thus sleeping the thread before performing findbyxpath solves the issue.

I’m amazed and frustrated of the speed…

yep, recently I’ve added several optimizations there, so now a single xpath query takes several milliseconds as opposed to, for example, iOS, where it needs thousands of milliseconds for the same.

1 Like

I am so glad I got to play duck-observer here.
I learned more than one trick out of this. This forum is full of so many duplicate threads about locators that fail, that it can get boring.

@mykola-mokhnach is there an option to let the findbyxpath method wait until the page has “settled”.

I currently have very often the case that findbyxpath returns results of the previous activity where I clicked a button. The app starts to initiate the transition, but findbyxpath is so fast that it performs the action still on the supposed to be previous page and not the page loading.

Usually, this would be done with something like WebDriverWait to wait for elements. However, since I analyze arbitrary apps, there is no element on the page that I’m expecting and I can wait for.

Any hints are appreciated!

UiAutomator has waitForIdle method and it is already called before each findElement invocation. Perhaps, there are more options in Espresso using IdlingResource.

Generally, the best approach would be to wait for some expected element to appear like you mentioned above.