Skip to content
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

use normal element #236

Merged
merged 6 commits into from
Aug 19, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
51 changes: 0 additions & 51 deletions appium/webdriver/imagelement.py

This file was deleted.

50 changes: 12 additions & 38 deletions appium/webdriver/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from .errorhandler import MobileErrorHandler
from .switch_to import MobileSwitchTo
from .webelement import WebElement as MobileWebElement
from .imagelement import ImageElement

from appium.webdriver.clipboard_content_type import ClipboardContentType
from appium.webdriver.common.mobileby import MobileBy
Expand All @@ -27,16 +26,13 @@

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

import base64
import copy

DEFAULT_MATCH_THRESHOLD = 0.5

# From remote/webdriver.py
_W3C_CAPABILITY_NAMES = frozenset([
'acceptInsecureCerts',
Expand Down Expand Up @@ -213,8 +209,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,
Expand Down Expand Up @@ -246,9 +240,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 []
Expand Down Expand Up @@ -341,51 +332,34 @@ def find_elements_by_android_uiautomator(self, uia_string):
"""
return self.find_elements(by=By.ANDROID_UIAUTOMATOR, value=uia_string)

def find_element_by_image(self, png_img_path,
match_threshold=DEFAULT_MATCH_THRESHOLD):
def find_element_by_image(self, png_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

: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):

return self.find_element(by=By.IMAGE, value=b64_data)

def find_elements_by_image(self, png_img_path):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is no necessarily a png file. The format might be anything support by OpenCV itself

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡

"""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

: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(png_img_path, 'rb') as png_file:
b64_data = base64.b64encode(png_file.read()).decode('UTF-8')

return self.find_elements(by=By.IMAGE, value=b64_data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in java I call this locator type By.imageTemplate. How do you think which one is more appropriate?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah =>

By.IMAGE = MobileBy.IMAGE


  1. As a user, if users should imagine what logic is used behind the method, it's better to use template since it help users clalify the behaviour, I think. Then, I'd like to define -image template as the selector though.

  2. We defined the selector as -image. Then, using image in both By and Selector is more appropriate, I felt.

Comparing 1 and 2, so far, 2 is enough, I think. (By.IMAGE is much enough.)

(I considered the naming, too 🤔 )


def find_element_by_accessibility_id(self, id):
"""Finds an element by accessibility id.
Expand Down
Binary file modified test/functional/android/find_by_image_success.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 14 additions & 2 deletions test/functional/android/find_by_image_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,29 @@
from selenium.webdriver.support import expected_conditions as EC
import desired_capabilities

import base64

class FindByImageTests(unittest.TestCase):

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'])
Expand Down Expand Up @@ -62,9 +71,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)
Expand Down