Skip to content

Enhancement appium image locating #434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AppiumLibrary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class AppiumLibrary(
| accessibility_id | Click Element `|` accessibility_id=button3 | Accessibility options utilize. | |
| xpath | Click Element `|` xpath=//UIATableView/UIATableCell/UIAButton | Matches with arbitrary XPath | |
| class | Click Element `|` class=UIAPickerWheel | Matches by class | |
| image | Click Element `|` image=icon.png | Matches by image | |
| android | Click Element `|` android=UiSelector().description('Apps') | Matches by Android UI Automator | |
| ios | Click Element `|` ios=.buttons().withName('Apps') | Matches by iOS UI Automation | |
| predicate | Click Element `|` predicate=name=="login" | Matches by iOS Predicate | Check PR: #196 |
Expand Down
13 changes: 8 additions & 5 deletions AppiumLibrary/keywords/_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def click_element(self, locator):
"""
self._info("Clicking element '%s'." % locator)
self._element_find(locator, True, True).click()

def click_button(self, index_or_name):
"""*DEPRECATED!!* in selenium v4, use `Click Element` keyword.
Click button
Expand Down Expand Up @@ -638,7 +638,10 @@ def _element_find(self, locator, first_only, required, tag=None):
_locator = locator
elements = self._element_finder.find(application, _locator, tag)
if required and len(elements) == 0:
raise ValueError("Element locator '" + locator + "' did not match any elements.")
if self._element_finder._is_image_locator(locator) :
raise ValueError("Image at '" + locator + "' did not match any elements.")
else:
raise ValueError("Element locator '" + locator + "' did not match any elements.")
if first_only:
if len(elements) == 0: return None
return elements[0]
Expand All @@ -647,9 +650,9 @@ def _element_find(self, locator, first_only, required, tag=None):
return locator
else:
elements = [locator]
# do some other stuff here like deal with list of webelements
# ... or raise locator/element specific error if required
return elements
# do some other stuff here like deal with list of webelements
# ... or raise locator/element specific error if required
return elements

def _element_find_by_text(self, text, exact_match=False):
if self._get_platform() == 'ios':
Expand Down
24 changes: 21 additions & 3 deletions AppiumLibrary/locators/elementfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from AppiumLibrary import utils
from appium.webdriver.common.appiumby import AppiumBy
from robot.api import logger

import base64
import os

class ElementFinder(object):

Expand All @@ -14,6 +15,7 @@ def __init__(self):
'name': self._find_by_name,
'xpath': self._find_by_xpath,
'class': self._find_by_class_name,
'image': self._find_by_image,
'accessibility_id': self._find_element_by_accessibility_id,
'android': self._find_by_android,
'viewtag': self._find_by_android_viewtag,
Expand All @@ -30,15 +32,18 @@ def __init__(self):
def find(self, application, locator, tag=None):
assert application is not None
assert locator is not None and len(locator) > 0

(prefix, criteria) = self._parse_locator(locator)
prefix = 'default' if prefix is None else prefix
if prefix is None:
prefix = 'image' if (self._is_image_locator(locator)) else 'default'
strategy = self._strategies.get(prefix)
if strategy is None:
raise ValueError("Element locator with prefix '" + prefix + "' is not supported")
(tag, constraints) = self._get_tag_and_constraints(tag)
return strategy(application, criteria, tag, constraints)

def _is_image_locator(self, locator):
return isinstance(locator, str) and (locator.endswith(('.png', '.jpg', '.jpeg')))

# Strategy routines, private

def _find_by_identifier(self, application, criteria, tag, constraints):
Expand Down Expand Up @@ -97,6 +102,19 @@ def _find_by_class_name(self, application, criteria, tag, constraints):
application.find_elements(by=AppiumBy.CLASS_NAME, value=criteria),
tag, constraints)

def _find_by_image(self, application, criteria, tag, constraints):
return self._filter_elements(
application.find_elements(by=AppiumBy.IMAGE, value=self._encode_image_to_base64(criteria)),
tag, constraints)

def _encode_image_to_base64(self, image_path):
if not os.path.exists(image_path):
raise FileNotFoundError(f"Image file does not exist: {image_path}")
with open(image_path, 'rb') as image_file:
image_data = image_file.read()
image_base64 = base64.b64encode(image_data).decode('utf-8')
return image_base64

def _find_element_by_accessibility_id(self, application, criteria, tag, constraints):
return self._filter_elements(
application.find_elements(by=AppiumBy.ACCESSIBILITY_ID, value=criteria),
Expand Down