Skip to content

Commit 238ca0e

Browse files
authored
add find_elements w3c for webelement (#251)
* add find_elements w3c for webelement * add tests for child elements * add todo for future work
1 parent 0e269b2 commit 238ca0e

File tree

6 files changed

+140
-35
lines changed

6 files changed

+140
-35
lines changed

appium/webdriver/webdriver.py

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -202,19 +202,19 @@ def find_element(self, by=By.ID, value=None):
202202
203203
:rtype: WebElement
204204
"""
205+
# TODO: If we need, we should enable below converter for Web context
205206
# if self.w3c:
206-
207-
# if by == By.ID:
208-
# by = By.CSS_SELECTOR
209-
# value = '[id="%s"]' % value
210-
# elif by == By.TAG_NAME:
211-
# by = By.CSS_SELECTOR
212-
# elif by == By.CLASS_NAME:
213-
# by = By.CSS_SELECTOR
214-
# value = ".%s" % value
215-
# elif by == By.NAME:
216-
# by = By.CSS_SELECTOR
217-
# value = '[name="%s"]' % value
207+
# if by == By.ID:
208+
# by = By.CSS_SELECTOR
209+
# value = '[id="%s"]' % value
210+
# elif by == By.TAG_NAME:
211+
# by = By.CSS_SELECTOR
212+
# elif by == By.CLASS_NAME:
213+
# by = By.CSS_SELECTOR
214+
# value = ".%s" % value
215+
# elif by == By.NAME:
216+
# by = By.CSS_SELECTOR
217+
# value = '[name="%s"]' % value
218218

219219
return self.execute(RemoteCommand.FIND_ELEMENT, {
220220
'using': by,
@@ -230,18 +230,19 @@ def find_elements(self, by=By.ID, value=None):
230230
231231
:rtype: list of WebElement
232232
"""
233+
# TODO: If we need, we should enable below converter for Web context
233234
# if self.w3c:
234-
# if by == By.ID:
235-
# by = By.CSS_SELECTOR
236-
# value = '[id="%s"]' % value
237-
# elif by == By.TAG_NAME:
238-
# by = By.CSS_SELECTOR
239-
# elif by == By.CLASS_NAME:
240-
# by = By.CSS_SELECTOR
241-
# value = ".%s" % value
242-
# elif by == By.NAME:
243-
# by = By.CSS_SELECTOR
244-
# value = '[name="%s"]' % value
235+
# if by == By.ID:
236+
# by = By.CSS_SELECTOR
237+
# value = '[id="%s"]' % value
238+
# elif by == By.TAG_NAME:
239+
# by = By.CSS_SELECTOR
240+
# elif by == By.CLASS_NAME:
241+
# by = By.CSS_SELECTOR
242+
# value = ".%s" % value
243+
# elif by == By.NAME:
244+
# by = By.CSS_SELECTOR
245+
# value = '[name="%s"]' % value
245246

246247
# Return empty list if driver returns null
247248
# See https://github.com/SeleniumHQ/selenium/issues/4555

appium/webdriver/webelement.py

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
from selenium.webdriver.common.by import By
1616
from selenium.webdriver.remote.webelement import WebElement as SeleniumWebElement
17+
from selenium.webdriver.remote.command import Command as RemoteCommand
18+
19+
from appium.webdriver.common.mobileby import MobileBy
1720

1821
from .mobilecommand import MobileCommand as Command
1922

@@ -28,7 +31,7 @@ def find_element_by_ios_uiautomation(self, uia_string):
2831
:Usage:
2932
driver.find_element_by_ios_uiautomation('.elements()[1].cells()[2]')
3033
"""
31-
return self.find_element(by=By.IOS_UIAUTOMATION, value=uia_string)
34+
return self.find_element(by=MobileBy.IOS_UIAUTOMATION, value=uia_string)
3235

3336
def find_elements_by_ios_uiautomation(self, uia_string):
3437
"""Finds elements by uiautomation in iOS.
@@ -39,7 +42,7 @@ def find_elements_by_ios_uiautomation(self, uia_string):
3942
:Usage:
4043
driver.find_elements_by_ios_uiautomation('.elements()[1].cells()[2]')
4144
"""
42-
return self.find_elements(by=By.IOS_UIAUTOMATION, value=uia_string)
45+
return self.find_elements(by=MobileBy.IOS_UIAUTOMATION, value=uia_string)
4346

4447
def find_element_by_ios_predicate(self, predicate_string):
4548
"""Find an element by ios predicate string.
@@ -50,7 +53,7 @@ def find_element_by_ios_predicate(self, predicate_string):
5053
:Usage:
5154
driver.find_element_by_ios_predicate('label == "myLabel"')
5255
"""
53-
return self.find_element(by=By.IOS_PREDICATE, value=predicate_string)
56+
return self.find_element(by=MobileBy.IOS_PREDICATE, value=predicate_string)
5457

5558
def find_elements_by_ios_predicate(self, predicate_string):
5659
"""Finds elements by ios predicate string.
@@ -61,7 +64,7 @@ def find_elements_by_ios_predicate(self, predicate_string):
6164
:Usage:
6265
driver.find_elements_by_ios_predicate('label == "myLabel"')
6366
"""
64-
return self.find_elements(by=By.IOS_PREDICATE, value=predicate_string)
67+
return self.find_elements(by=MobileBy.IOS_PREDICATE, value=predicate_string)
6568

6669
def find_element_by_ios_class_chain(self, class_chain_string):
6770
"""Find an element by ios class chain string.
@@ -72,7 +75,7 @@ def find_element_by_ios_class_chain(self, class_chain_string):
7275
:Usage:
7376
driver.find_element_by_ios_class_chain('XCUIElementTypeWindow/XCUIElementTypeButton[3]')
7477
"""
75-
return self.find_element(by=By.IOS_CLASS_CHAIN, value=class_chain_string)
78+
return self.find_element(by=MobileBy.IOS_CLASS_CHAIN, value=class_chain_string)
7679

7780
def find_elements_by_ios_class_chain(self, class_chain_string):
7881
"""Finds elements by ios class chain string.
@@ -83,7 +86,7 @@ def find_elements_by_ios_class_chain(self, class_chain_string):
8386
:Usage:
8487
driver.find_elements_by_ios_class_chain('XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]')
8588
"""
86-
return self.find_elements(by=By.IOS_CLASS_CHAIN, value=class_chain_string)
89+
return self.find_elements(by=MobileBy.IOS_CLASS_CHAIN, value=class_chain_string)
8790

8891
def find_element_by_android_uiautomator(self, uia_string):
8992
"""Finds element by uiautomator in Android.
@@ -94,7 +97,7 @@ def find_element_by_android_uiautomator(self, uia_string):
9497
:Usage:
9598
driver.find_element_by_android_uiautomator('.elements()[1].cells()[2]')
9699
"""
97-
return self.find_element(by=By.ANDROID_UIAUTOMATOR, value=uia_string)
100+
return self.find_element(by=MobileBy.ANDROID_UIAUTOMATOR, value=uia_string)
98101

99102
def find_elements_by_android_uiautomator(self, uia_string):
100103
"""Finds elements by uiautomator in Android.
@@ -105,7 +108,7 @@ def find_elements_by_android_uiautomator(self, uia_string):
105108
:Usage:
106109
driver.find_elements_by_android_uiautomator('.elements()[1].cells()[2]')
107110
"""
108-
return self.find_elements(by=By.ANDROID_UIAUTOMATOR, value=uia_string)
111+
return self.find_elements(by=MobileBy.ANDROID_UIAUTOMATOR, value=uia_string)
109112

110113
def find_element_by_accessibility_id(self, accessibility_id):
111114
"""Finds an element by accessibility id.
@@ -117,7 +120,7 @@ def find_element_by_accessibility_id(self, accessibility_id):
117120
:Usage:
118121
driver.find_element_by_accessibility_id()
119122
"""
120-
return self.find_element(by=By.ACCESSIBILITY_ID, value=accessibility_id)
123+
return self.find_element(by=MobileBy.ACCESSIBILITY_ID, value=accessibility_id)
121124

122125
def find_elements_by_accessibility_id(self, accessibility_id):
123126
"""Finds elements by accessibility id.
@@ -129,7 +132,57 @@ def find_elements_by_accessibility_id(self, accessibility_id):
129132
:Usage:
130133
driver.find_elements_by_accessibility_id()
131134
"""
132-
return self.find_elements(by=By.ACCESSIBILITY_ID, value=accessibility_id)
135+
return self.find_elements(by=MobileBy.ACCESSIBILITY_ID, value=accessibility_id)
136+
137+
def find_element(self, by=By.ID, value=None):
138+
"""
139+
Find an element given a By strategy and locator. Prefer the find_element_by_* methods when
140+
possible.
141+
:Usage:
142+
element = element.find_element(By.ID, 'foo')
143+
:rtype: WebElement
144+
"""
145+
# TODO: If we need, we should enable below converter for Web context
146+
# if self._w3c:
147+
# if by == By.ID:
148+
# by = By.CSS_SELECTOR
149+
# value = '[id="%s"]' % value
150+
# elif by == By.TAG_NAME:
151+
# by = By.CSS_SELECTOR
152+
# elif by == By.CLASS_NAME:
153+
# by = By.CSS_SELECTOR
154+
# value = ".%s" % value
155+
# elif by == By.NAME:
156+
# by = By.CSS_SELECTOR
157+
# value = '[name="%s"]' % value
158+
159+
return self._execute(RemoteCommand.FIND_CHILD_ELEMENT,
160+
{"using": by, "value": value})['value']
161+
162+
def find_elements(self, by=By.ID, value=None):
163+
"""
164+
Find elements given a By strategy and locator. Prefer the find_elements_by_* methods when
165+
possible.
166+
:Usage:
167+
element = element.find_elements(By.CLASS_NAME, 'foo')
168+
:rtype: list of WebElement
169+
"""
170+
# TODO: If we need, we should enable below converter for Web context
171+
# if self._w3c:
172+
# if by == By.ID:
173+
# by = By.CSS_SELECTOR
174+
# value = '[id="%s"]' % value
175+
# elif by == By.TAG_NAME:
176+
# by = By.CSS_SELECTOR
177+
# elif by == By.CLASS_NAME:
178+
# by = By.CSS_SELECTOR
179+
# value = ".%s" % value
180+
# elif by == By.NAME:
181+
# by = By.CSS_SELECTOR
182+
# value = '[name="%s"]' % value
183+
184+
return self._execute(RemoteCommand.FIND_CHILD_ELEMENTS,
185+
{"using": by, "value": value})['value']
133186

134187
def set_text(self, keys=''):
135188
"""Sends text to the element. Previous text is removed.

test/functional/android/appium_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from time import sleep
2222
from dateutil.parser import parse
2323

24-
from webdriver.applicationstate import ApplicationState
24+
from appium.webdriver.applicationstate import ApplicationState
2525
from selenium.common.exceptions import NoSuchElementException
2626

2727
from appium import webdriver

test/functional/ios/appium_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import unittest
1616
from time import sleep
1717

18-
from webdriver.applicationstate import ApplicationState
18+
from appium.webdriver.applicationstate import ApplicationState
1919
from appium import webdriver
2020
import desired_capabilities
2121

test/functional/ios/desired_capabilities.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ class PytestXdistWorker(object):
4444

4545
@staticmethod
4646
def gw(number):
47+
if PytestXdistWorker.COUNT is None:
48+
return '0'
49+
4750
if number >= PytestXdistWorker.COUNT:
4851
return 'gw0'
4952

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 unittest
16+
17+
from appium import webdriver
18+
import desired_capabilities
19+
20+
21+
class FindByElementWebelementTests(unittest.TestCase):
22+
23+
@classmethod
24+
def setUpClass(self):
25+
desired_caps = desired_capabilities.get_desired_capabilities('UICatalog.app.zip')
26+
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
27+
28+
@classmethod
29+
def tearDownClass(self):
30+
self.driver.quit()
31+
32+
def test_find_element_by_path(self):
33+
el = self.driver.find_element_by_ios_predicate('wdName == "UICatalog"')
34+
self.assertEqual('UICatalog', el.get_attribute('name'))
35+
36+
c_el = el.find_elements_by_ios_predicate('label == "TextFields"')
37+
self.assertEqual('TextFields', c_el[0].get_attribute('name'))
38+
39+
c_el = el.find_elements_by_ios_class_chain('**/XCUIElementTypeStaticText')
40+
self.assertEqual('UICatalog', c_el[0].get_attribute('name'))
41+
42+
c_el = el.find_elements_by_accessibility_id('UICatalog')
43+
self.assertEqual('UICatalog', c_el[0].get_attribute('name'))
44+
45+
46+
if __name__ == "__main__":
47+
suite = unittest.TestLoader().loadTestsFromTestCase(FindByElementWebelementTests)
48+
unittest.TextTestRunner(verbosity=2).run(suite)

0 commit comments

Comments
 (0)