2

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.

Activate card button

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.

Buttons overlapping

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.

Ivan Fateev
  • 1,032
  • 1
  • 10
  • 26

0 Answers0