Skip to content

Commit 8b4b7df

Browse files
committed
Add accessibility id locator strategy
1 parent e76c39e commit 8b4b7df

File tree

5 files changed

+126
-1
lines changed

5 files changed

+126
-1
lines changed

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ As a base for the following code examples, the following sets up the [UnitTest](
2828
environment:
2929

3030
```python
31+
# Android environment
32+
import unittest
3133
from appium import webdriver
3234

3335
desired_caps = {}
@@ -41,6 +43,18 @@ desired_caps['app-activity'] = '.HomeScreenActivity'
4143
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
4244
```
4345

46+
```python
47+
# iOS environment
48+
import unittest
49+
from appium import webdriver
50+
51+
desired_caps = {}
52+
desired_caps['app'] = PATH('../../apps/UICatalog.app.zip')
53+
54+
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
55+
```
56+
57+
4458
## Changed or added functionality
4559

4660
The methods that do change are...
@@ -81,9 +95,46 @@ This allows elements in iOS applications to be found using recursive element
8195
search using the UIAutomation library. Adds the methods `driver.find_element_by_ios_uiautomation`
8296
and `driver.find_elements_by_ios_uiautomation`.
8397

98+
```python
99+
el = self.driver.find_element_by_ios_uiautomation('.elements()[0]')
100+
self.assertEqual('UICatalog', el.get_attribute('name'))
101+
```
102+
103+
```python
104+
els = self.driver.find_elements_by_ios_uiautomation('elements()')
105+
self.assertIsInstance(els, list)
106+
```
107+
84108

85109
### Finding elements by Android UIAutomator search
86110

87111
This allows elements in an Android application to be found using recursive element
88112
search using the UIAutomator library. Adds the methods `driver.find_element_by_android_uiautomator`
89113
and `driver.find_elements_by_android_uiautomator`.
114+
115+
```python
116+
el = self.driver.find_element_by_android_uiautomator('new UiSelector().description("Animation")')
117+
self.assertIsNotNone(el)
118+
```
119+
120+
```python
121+
els = self.driver.find_elements_by_android_uiautomator('new UiSelector().clickable(true)')
122+
self.assertIsInstance(els, list)
123+
```
124+
125+
126+
### Finding elements by Accessibility ID
127+
128+
Allows for elements to be found using the "Accessibility ID". The methods take a
129+
string representing the accessibility id or label attached to a given element, e.g., for iOS the accessibility identifier and for Android the content-description. Adds the methods
130+
`driver.find_element_by_accessibility_id` and `find_elements_by_accessibility_id`.
131+
132+
```python
133+
el = self.driver.find_element_by_accessibility_id('Animation')
134+
self.assertIsNotNone(el)
135+
```
136+
137+
```python
138+
els = self.driver.find_elements_by_accessibility_id('Animation')
139+
self.assertIsInstance(els, list)
140+
```

appium/webdriver/common/mobileby.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
class MobileBy(By):
1818
IOS_UIAUTOMATION = '-ios uiautomation'
1919
ANDROID_UIAUTOMATOR = '-android uiautomator'
20+
ACCESSIBILITY_ID = 'accessibility id'

appium/webdriver/webdriver.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
3636
# add new method to the `find_by_*` pantheon
3737
By.IOS_UIAUTOMATION = MobileBy.IOS_UIAUTOMATION
3838
By.ANDROID_UIAUTOMATOR = MobileBy.ANDROID_UIAUTOMATOR
39+
By.ACCESSIBILITY_ID = MobileBy.ACCESSIBILITY_ID
3940

4041
@property
4142
def contexts(self):
@@ -101,6 +102,28 @@ def find_elements_by_android_uiautomator(self, uia_string):
101102
"""
102103
return self.find_elements(by=By.ANDROID_UIAUTOMATOR, value=uia_string)
103104

105+
def find_element_by_accessibility_id(self, id):
106+
"""Finds an element by accessibility id.
107+
108+
:Args:
109+
- id - a string corresponding to a recursive element search using the Id/Name that the native Accessibility options utilize
110+
111+
:Usage:
112+
driver.find_element_by_accessibility_id()
113+
"""
114+
return self.find_element(by=By.ACCESSIBILITY_ID, value=id)
115+
116+
def find_elements_by_accessibility_id(self, id):
117+
"""Finds elements by accessibility id.
118+
119+
:Args:
120+
- id - a string corresponding to a recursive element search using the Id/Name that the native Accessibility options utilize
121+
122+
:Usage:
123+
driver.find_elements_by_accessibility_id()
124+
"""
125+
return self.find_elements(by=By.ACCESSIBILITY_ID, value=id)
126+
104127
def _addCommands(self):
105128
self.command_executor._commands[Command.CONTEXTS] = \
106129
('GET', '/session/$sessionId/contexts')
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
from time import sleep
17+
18+
from appium import webdriver
19+
20+
import unittest
21+
22+
# Returns abs path relative to this file and not cwd
23+
PATH = lambda p: os.path.abspath(
24+
os.path.join(os.path.dirname(__file__), p)
25+
)
26+
27+
class FindByAccessibilityIDTests(unittest.TestCase):
28+
def setUp(self):
29+
desired_caps = {}
30+
desired_caps['device'] = 'Android'
31+
desired_caps['browserName'] = ''
32+
desired_caps['version'] = '4.2'
33+
desired_caps['app'] = PATH('../../apps/ApiDemos-debug.apk')
34+
desired_caps['app-package'] = 'com.example.android.apis'
35+
desired_caps['app-activity'] = '.ApiDemos'
36+
37+
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
38+
39+
def tearDown(self):
40+
self.driver.quit()
41+
42+
def test_find_single_element(self):
43+
el = self.driver.find_element_by_accessibility_id('Animation')
44+
self.assertIsNotNone(el)
45+
46+
def test_find_multiple_elements(self):
47+
els = self.driver.find_elements_by_accessibility_id('Animation')
48+
self.assertIsInstance(els, list)
49+
50+
if __name__ == "__main__":
51+
unittest.main()

test/functional/android/find_by_uiautomator_tests.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from time import sleep
1717

1818
from appium import webdriver
19-
from appium.common.exceptions import NoSuchContextException
2019

2120
import unittest
2221

0 commit comments

Comments
 (0)