Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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 appium/webdriver/common/mobileby.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class MobileBy(By):
ANDROID_UIAUTOMATOR = '-android uiautomator'
ANDROID_VIEWTAG = '-android viewtag'
ANDROID_DATA_MATCHER = '-android datamatcher'
ANDROID_VIEW_MATCHER = '-android viewmatcher'
WINDOWS_UI_AUTOMATION = '-windows uiautomation'
ACCESSIBILITY_ID = 'accessibility id'
IMAGE = '-image'
Expand Down
34 changes: 34 additions & 0 deletions appium/webdriver/extensions/search_context/android.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,39 @@
class AndroidSearchContext(BaseSearchContext):
"""Define search context for Android"""

def find_element_by_android_view_matcher(self, name=None, args=None, className=None):
"""Finds element by [onView](https://developer.android.com/training/testing/espresso/basics) in Android

It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver).

Args:
name (:obj:`str`, optional): The name of a method to invoke.
The method must return a Hamcrest
[Matcher](http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html)
args (:obj:`str`, optional): The args provided to the method
className (:obj:`str`, optional): The class name that the method is part of (defaults to `org.hamcrest.Matchers`).
Can be fully qualified by having the androidx.test.espresso.matcher. prefix.
If the prefix is not provided then it is going to be added implicitly.
(e.g.: `class=CursorMatchers` fully qualified is `class=androidx.test.espresso.matcher.CursorMatchers`

Returns:
`appium.webdriver.webelement.WebElement`: The found element

Raises:
TypeError - Raises a TypeError if the arguments are not validated for JSON format

Usage:
driver.find_element_by_android_view_matcher(name='hasEntry', args=['title', 'Animation'])

# To enable auto completion in PyCharm(IDE)
:rtype: `appium.webdriver.webelement.WebElement`
"""

return self.find_element(
by=MobileBy.ANDROID_VIEW_MATCHER,
value=self._build_data_matcher(name=name, args=args, className=className)
)

def find_element_by_android_data_matcher(self, name=None, args=None, className=None):
"""Finds element by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android

Expand Down Expand Up @@ -58,6 +91,7 @@ def find_element_by_android_data_matcher(self, name=None, args=None, className=N

def find_elements_by_android_data_matcher(self, name=None, args=None, className=None):
"""Finds elements by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android

It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver).

Args:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python

# 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.

import os
import unittest

import pytest
from selenium.common.exceptions import WebDriverException

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.extensions.search_context.android import (
AndroidSearchContext
)
from test.functional.android.helper.test_helper import (
desired_capabilities,
is_ci
)


class FindByViewMatcherTests(unittest.TestCase):

def setUp(self):
desired_caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk.zip')
desired_caps['automationName'] = 'Espresso'
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

def tearDown(self):
if is_ci():
# Take the screenshot to investigate when tests failed only on CI
img_path = os.path.join(os.getcwd(), self._testMethodName + '.png')
self.driver.get_screenshot_as_file(img_path)
self.driver.quit()

def test_find_single_element(self):
el = self.driver.find_element_by_android_view_matcher(
name='withText', args=['Accessibility'], className='ViewMatchers')
assert el.text == 'Accessibility'

def test_find_single_element_ful_class_name(self):
el = self.driver.find_element_by_android_view_matcher(
name='withText', args=['Accessibility'], className='androidx.test.espresso.matcher.ViewMatchers')
assert el.text == 'Accessibility'

def test_find_single_element_using_hamcrest_matcher(self):
el = self.driver.find_element_by_android_view_matcher(
name='withText',
args={
'name': 'containsString',
'args': 'Animati',
'class': 'org.hamcrest.Matchers'},
className='ViewMatchers')
assert el.text == 'Animation'

# androidx.test.espresso.AmbiguousViewMatcherException:
# 'with text: a string containing "Access"' matches multiple views in the hierarchy.
def test_find_multiple_elements(self):
Copy link
Collaborator Author

@ki4070ma ki4070ma Feb 9, 2020

Choose a reason for hiding this comment

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

[Notes] This is reason not to have find_elements_by_android_view_matcher.

value = AndroidSearchContext()._build_data_matcher(
name='withSubstring', args=['Access'], className='ViewMatchers')
with pytest.raises(WebDriverException):
self.driver.find_elements(by=MobileBy.ANDROID_VIEW_MATCHER, value=value)


if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(FindByViewMatcherTests)
unittest.TextTestRunner(verbosity=2).run(suite)