Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

add appium prefix in create session and fix find_elements for W3C #196

Merged
merged 7 commits into from
Jan 23, 2018
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
163 changes: 161 additions & 2 deletions appium/webdriver/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,61 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException

from selenium.common.exceptions import TimeoutException, InvalidArgumentException

from selenium.webdriver.remote.command import Command as RemoteCommand
import copy

# From remote/webdriver.py
_W3C_CAPABILITY_NAMES = frozenset([
'acceptInsecureCerts',
'browserName',
'browserVersion',
'platformName',
'pageLoadStrategy',
'proxy',
'setWindowRect',
'timeouts',
'unhandledPromptBehavior',
])

# From remote/webdriver.py
_OSS_W3C_CONVERSION = {
'acceptSslCerts': 'acceptInsecureCerts',
'version': 'browserVersion',
'platform': 'platformName'
}

_EXTENSION_CAPABILITY = ':'
_FORCE_MJSONWP = 'forceMjsonwp'

# override
# Add appium prefix for the non-W3C capabilities
def _make_w3c_caps(caps):
appium_prefix = 'appium:'

caps = copy.deepcopy(caps)
profile = caps.get('firefox_profile')
always_match = {}
if caps.get('proxy') and caps['proxy'].get('proxyType'):
caps['proxy']['proxyType'] = caps['proxy']['proxyType'].lower()
for k, v in caps.items():
Copy link
Contributor

Choose a reason for hiding this comment

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

-> iteritems

Copy link
Member Author

@KazuCocoa KazuCocoa Jan 4, 2018

Choose a reason for hiding this comment

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

With Python 3.x, items() looks enough as an iterator and iteritems is deprecated.

if v and k in _OSS_W3C_CONVERSION:
always_match[_OSS_W3C_CONVERSION[k]] = v.lower() if k == 'platform' else v
if k in _W3C_CAPABILITY_NAMES or _EXTENSION_CAPABILITY in k:
always_match[k] = v
else:
if not k.startswith(appium_prefix):
always_match[appium_prefix + k] = v
if profile:
moz_opts = always_match.get('moz:firefoxOptions', {})
# If it's already present, assume the caller did that intentionally.
if 'profile' not in moz_opts:
# Don't mutate the original capabilities.
new_opts = copy.deepcopy(moz_opts)
new_opts['profile'] = profile
always_match['moz:firefoxOptions'] = new_opts
return {'firstMatch': [{}], 'alwaysMatch': always_match}

class WebDriver(webdriver.Remote):
def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
Expand All @@ -48,6 +101,57 @@ def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
By.ANDROID_UIAUTOMATOR = MobileBy.ANDROID_UIAUTOMATOR
By.ACCESSIBILITY_ID = MobileBy.ACCESSIBILITY_ID

def start_session(self, capabilities, browser_profile=None):
"""
Override for Appium
Creates a new session with the desired capabilities.

:Args:
- automation_name - The name of automation engine to use.
- platform_name - The name of target platform.
- platform_version - The kind of mobile device or emulator to use
- app - The absolute local path or remote http URL to an .ipa or .apk file, or a .zip containing one of these.

Read https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md for more details.
"""
if not isinstance(capabilities, dict):
raise InvalidArgumentException('Capabilities must be a dictionary')
if browser_profile:
if 'moz:firefoxOptions' in capabilities:
capabilities['moz:firefoxOptions']['profile'] = browser_profile.encoded
else:
capabilities.update({'firefox_profile': browser_profile.encoded})

parameters = self._merge_capabilities(capabilities)

response = self.execute(RemoteCommand.NEW_SESSION, parameters)
if 'sessionId' not in response:
response = response['value']
self.session_id = response['sessionId']
self.capabilities = response.get('value')

# if capabilities is none we are probably speaking to
# a W3C endpoint
if self.capabilities is None:
self.capabilities = response.get('capabilities')

# Double check to see if we have a W3C Compliant browser
self.w3c = response.get('status') is None

def _merge_capabilities(self, capabilities):
"""
Manage capabilities whether W3C format or MJSONWP format
"""
if _FORCE_MJSONWP in capabilities:
force_mjsonwp = capabilities[_FORCE_MJSONWP]
del capabilities[_FORCE_MJSONWP]

if force_mjsonwp != False:
return {'desiredCapabilities': capabilities}

w3c_caps = _make_w3c_caps(capabilities)
return {'capabilities': w3c_caps, 'desiredCapabilities': capabilities}

@property
def contexts(self):
"""
Expand Down Expand Up @@ -78,6 +182,61 @@ def context(self):
"""
return self.current_context

def find_element(self, by=By.ID, value=None):
"""
Override for Appium
'Private' method used by the find_element_by_* methods.

:Usage:
Use the corresponding find_element_by_* instead of this.

:rtype: WebElement
"""
# if self.w3c:
# if by == By.ID:
# by = By.CSS_SELECTOR
# value = '[id="%s"]' % value
# elif by == By.TAG_NAME:
# by = By.CSS_SELECTOR
# elif by == By.CLASS_NAME:
# by = By.CSS_SELECTOR
# value = ".%s" % value
# elif by == By.NAME:
# by = By.CSS_SELECTOR
# value = '[name="%s"]' % value
return self.execute(RemoteCommand.FIND_ELEMENT, {
'using': by,
'value': value})['value']

def find_elements(self, by=By.ID, value=None):
"""
Override for Appium
'Private' method used by the find_elements_by_* methods.

:Usage:
Use the corresponding find_elements_by_* instead of this.

:rtype: list of WebElement
"""
# if self.w3c:
# if by == By.ID:
# by = By.CSS_SELECTOR
# value = '[id="%s"]' % value
# elif by == By.TAG_NAME:
# by = By.CSS_SELECTOR
# elif by == By.CLASS_NAME:
# by = By.CSS_SELECTOR
# value = ".%s" % value
# elif by == By.NAME:
# by = By.CSS_SELECTOR
# value = '[name="%s"]' % value

# Return empty list if driver returns null
# See https://github.com/SeleniumHQ/selenium/issues/4555
return self.execute(RemoteCommand.FIND_ELEMENTS, {
'using': by,
'value': value})['value'] or []

def find_element_by_ios_uiautomation(self, uia_string):
"""Finds an element by uiautomation in iOS.

Expand Down
6 changes: 3 additions & 3 deletions test/functional/ios/appium_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_toggle_touch_id_enrollment(self):
self.driver.toggle_touch_id_enrollment()

def test_hide_keyboard(self):
el = self.driver.find_element_by_name('TextFields, Uses of UITextField')
el = self.driver.find_element_by_name('Uses of UITextField')
el.click()

# get focus on text field, so keyboard comes up
Expand All @@ -66,7 +66,7 @@ def test_hide_keyboard(self):
self.assertFalse(el.is_displayed())

def test_hide_keyboard_presskey_strategy(self):
el = self.driver.find_element_by_name('TextFields, Uses of UITextField')
el = self.driver.find_element_by_name('Uses of UITextField')
el.click()

# get focus on text field, so keyboard comes up
Expand All @@ -81,7 +81,7 @@ def test_hide_keyboard_presskey_strategy(self):
self.assertFalse(el.is_displayed())

def test_hide_keyboard_no_key_name(self):
el = self.driver.find_element_by_name('TextFields, Uses of UITextField')
el = self.driver.find_element_by_name('Uses of UITextField')
el.click()

# get focus on text field, so keyboard comes up
Expand Down
6 changes: 3 additions & 3 deletions test/functional/ios/find_by_ios_class_chain_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ def tearDownClass(self):
self.driver.quit()

def test_find_element_by_path(self):
el = self.driver.find_element_by_ios_class_chain('XCUIElementTypeWindow/*/*/XCUIElementTypeStaticText')
el = self.driver.find_element_by_ios_class_chain('XCUIElementTypeWindow/**/XCUIElementTypeStaticText')
self.assertEqual('UICatalog', el.get_attribute('name'))

def test_find_multiple_elements_by_path(self):
el = self.driver.find_elements_by_ios_class_chain('XCUIElementTypeWindow/*/*')
self.assertEqual(len(el), 2)
el = self.driver.find_elements_by_ios_class_chain('XCUIElementTypeWindow/*/*/*')
self.assertEqual(6, len(el))
self.assertEqual('UICatalog', el[0].get_attribute('name'))
self.assertEqual(None, el[1].get_attribute('name'))

Expand Down