diff --git a/appium/webdriver/imagelement.py b/appium/webdriver/imagelement.py deleted file mode 100644 index 14240556..00000000 --- a/appium/webdriver/imagelement.py +++ /dev/null @@ -1,53 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint: disable=no-self-use - -import math - - -class ImageElement(object): - - def __init__(self, driver, x, y, width, height): - self.driver = driver - self.center_x = math.floor(x + width / 2) - self.center_y = math.floor(y + height / 2) - self.x = x - self.y = y - self.width = width - self.height = height - - def click(self): - """ - Clicks in the middle of an image bounds - """ - return self.driver.tap([(self.center_x, self.center_y)]) - - @property - def size(self): - return {'width': self.width, 'height': self.height} - - @property - def location(self): - return {'x': self.x, 'y': self.y} - - @property - def rect(self): - return { - 'width': self.width, - 'height': self.height, - 'x': self.x, - 'y': self.y - } - - def is_displayed(self): - return True diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index 0a36163f..638a77b0 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -21,8 +21,7 @@ from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait -from selenium.common.exceptions import (TimeoutException, WebDriverException, - InvalidArgumentException, NoSuchElementException) +from selenium.common.exceptions import TimeoutException, InvalidArgumentException from selenium.webdriver.remote.command import Command as RemoteCommand @@ -35,9 +34,6 @@ from .errorhandler import MobileErrorHandler from .switch_to import MobileSwitchTo from .webelement import WebElement as MobileWebElement -from .imagelement import ImageElement - -DEFAULT_MATCH_THRESHOLD = 0.5 # From remote/webdriver.py _W3C_CAPABILITY_NAMES = frozenset([ @@ -207,6 +203,7 @@ def find_element(self, by=By.ID, value=None): :rtype: WebElement """ # if self.w3c: + # if by == By.ID: # by = By.CSS_SELECTOR # value = '[id="%s"]' % value @@ -218,8 +215,6 @@ def find_element(self, by=By.ID, value=None): # elif by == By.NAME: # by = By.CSS_SELECTOR # value = '[name="%s"]' % value - if by == By.IMAGE: - return self.find_element_by_image(value) return self.execute(RemoteCommand.FIND_ELEMENT, { 'using': by, @@ -251,9 +246,6 @@ def find_elements(self, by=By.ID, value=None): # Return empty list if driver returns null # See https://github.com/SeleniumHQ/selenium/issues/4555 - if by == By.IMAGE: - return self.find_elements_by_image(value) - return self.execute(RemoteCommand.FIND_ELEMENTS, { 'using': by, 'value': value})['value'] or [] @@ -370,51 +362,34 @@ def find_elements_by_android_viewtag(self, tag): """ return self.find_elements(by=By.ANDROID_VIEWTAG, value=tag) - def find_element_by_image(self, png_img_path, - match_threshold=DEFAULT_MATCH_THRESHOLD): + def find_element_by_image(self, img_path): """Finds a portion of a screenshot by an image. Uses driver.find_image_occurrence under the hood. :Args: - - png_img_path - a string corresponding to the path of a PNG image - - match_threshold - a double between 0 and 1 below which matches will - be rejected as element not found + - img_path - a string corresponding to the path of a image - :return: an ImageElement object + :return: an Element object """ - screenshot = self.get_screenshot_as_base64() - with open(png_img_path, 'rb') as png_file: - b64_data = base64.b64encode(png_file.read()).decode('UTF-8') - try: - res = self.find_image_occurrence(screenshot, b64_data, - threshold=match_threshold) - except WebDriverException as e: - if 'Cannot find any occurrences' in str(e): - raise NoSuchElementException(e) - raise - rect = res['rect'] - return ImageElement(self, rect['x'], rect['y'], rect['width'], - rect['height']) - - def find_elements_by_image(self, png_img_path, - match_threshold=DEFAULT_MATCH_THRESHOLD): + with open(img_path, 'rb') as i_file: + b64_data = base64.b64encode(i_file.read()).decode('UTF-8') + + return self.find_element(by=By.IMAGE, value=b64_data) + + def find_elements_by_image(self, img_path): """Finds a portion of a screenshot by an image. Uses driver.find_image_occurrence under the hood. Note that this will only ever return at most one element :Args: - - png_img_path - a string corresponding to the path of a PNG image - - match_threshold - a double between 0 and 1 below which matches will - be rejected as element not found + - img_path - a string corresponding to the path of a image - :return: possibly-empty list of ImageElements + :return: possibly-empty list of Elements """ - els = [] - try: - els.append(self.find_element_by_image(png_img_path, match_threshold)) - except NoSuchElementException: - pass - return els + with open(img_path, 'rb') as i_file: + b64_data = base64.b64encode(i_file.read()).decode('UTF-8') + + return self.find_elements(by=By.IMAGE, value=b64_data) def find_element_by_accessibility_id(self, accessibility_id): """Finds an element by accessibility id. diff --git a/test/functional/android/find_by_image_success.png b/test/functional/android/find_by_image_success.png index d7046ba8..a7f06ca9 100644 Binary files a/test/functional/android/find_by_image_success.png and b/test/functional/android/find_by_image_success.png differ diff --git a/test/functional/android/find_by_image_tests.py b/test/functional/android/find_by_image_tests.py index 9885843b..4f3b1273 100644 --- a/test/functional/android/find_by_image_tests.py +++ b/test/functional/android/find_by_image_tests.py @@ -21,6 +21,8 @@ from selenium.webdriver.support import expected_conditions as EC import desired_capabilities +import base64 + class FindByImageTests(unittest.TestCase): @@ -28,13 +30,21 @@ def setUp(self): desired_caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk') self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) + # relax template matching + self.driver.update_settings({"fixImageFindScreenshotDims": "false", + "fixImageTemplateSize": "true", + "autoUpdateImageElementPosition": "true"}) + def tearDown(self): self.driver.quit() def test_find_based_on_image_template(self): image_path = desired_capabilities.PATH('find_by_image_success.png') + with open(image_path, 'rb') as png_file: + b64_data = base64.b64encode(png_file.read()).decode('UTF-8') + el = WebDriverWait(self.driver, 3).until( - EC.presence_of_element_located((By.IMAGE, image_path)) + EC.presence_of_element_located((By.IMAGE, b64_data)) ) size = el.size self.assertIsNotNone(size['width']) @@ -62,9 +72,12 @@ def test_find_multiple_elements_by_image_just_returns_one(self): def test_find_throws_no_such_element(self): image_path = desired_capabilities.PATH('find_by_image_failure.png') + with open(image_path, 'rb') as png_file: + b64_data = base64.b64encode(png_file.read()).decode('UTF-8') + with self.assertRaises(TimeoutException): WebDriverWait(self.driver, 3).until( - EC.presence_of_element_located((By.IMAGE, image_path)) + EC.presence_of_element_located((By.IMAGE, b64_data)) ) with self.assertRaises(NoSuchElementException): self.driver.find_element_by_image(image_path)