Skip to content
Merged
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 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='withText', args=['Accessibility'], className='ViewMatchers')

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