I'm writing a test that should scroll down a page until the Activate card
button is visible to the user, and then tap to it.
The problem
Appium's driver is_displayed
returns true
when an element is visible in the viewport. Since our app has floating buttons above the navigation bar, is_displayed()
returns true even though floating buttons overlap the Activate card
button.
When I call element.click()
it taps on the floating button instead, by coordinates at the center.
According to the Appium docs, an error should be thrown in such case:
Clicks element at its center point. If the element's center point is obscured by another element, an element click intercepted error is returned.
If it would be the case I could scroll a bit more on error, but this doesn't happen (a problem of the driver?).
The only way I managed to solve the issue is to scroll above some "safe area" at the bottom which is 20% of the height.
Here's the code.
def scroll_down_to(self, to: TestObject, max_scrolls: int = 10):
"""
Scrolls down until `to` element is in safe area. If maximum scrolls reached, throws an exception
"""
try:
self._check_element_in_safe_area(to)
logger.debug('Element is on the screen, no need to scroll')
return
except NoSuchElementException:
pass
size = self.driver.get_window_size()
width = size['width']
height = size['height']
start_x = width / 2
start_y = height / 3
end_x = width / 2
end_y = 0
for i in range(0, max_scrolls):
try:
self._check_element_in_safe_area(to)
logger.debug('Element found after scroll')
return
except NoSuchElementException:
self.driver.swipe(start_x, start_y, end_x, end_y)
logger.debug('Element not found after scroll, scrolling down more')
raise NoSuchElementException(f'No element found after scroll matching {to.locator}')
def _check_element_in_safe_area(self, obj: TestObject):
"""
Throws NoSuchElementException in case element is not in the safe area
"""
element: WebElement = self.driver.find_element(obj.match_type, obj.locator)
if not element.is_displayed() or not self._is_in_safe_area(element):
raise NoSuchElementException()
def _is_in_safe_area(self, element: WebElement) -> bool:
rect = element.rect
el_y = rect['y'] + rect['height'] * 0.5
return el_y < self.safe_bottom_area
The question
How do I correctly write a cross-platform (iOS/Android) test, so it would click on elements which are fully visible to the user?
I tried to find some way via UIAutomator instead, with no success.