10BC0 Add direct connect flag to be able to handle directConnectXxxxc by KazuCocoa · Pull Request #338 · appium/python-client · GitHub
[go: up one dir, main page]

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
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,35 @@ desired_caps['app'] = PATH('../../apps/UICatalog.app.zip')
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
```


## Changed or added functionality

The methods that do change are...

### Direct Connect URLs

If your Selenium/Appium server decorates the new session capabilities response with the following keys:

- `directConnectProtocol`
- `directConnectHost`
- `directConnectPort`
- `directConnectPath`

Then python client will switch its endpoint to the one specified by the values of those keys.

```python
import unittest
from appium import webdriver

desired_caps = {}
desired_caps['platformName'] = 'iOS'
desired_caps['platformVersion'] = '11.4'
desired_caps['automationName'] = 'xcuitest'
desired_caps['deviceName'] = 'iPhone Simulator'
desired_caps['app'] = PATH('../../apps/UICatalog.app.zip')

self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps, direct_connection=True)
```


### Switching between 'Native' and 'Webview'

Expand Down
28 changes: 28 additions & 0 deletions appium/common/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/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 logging
import sys


def setup_logger(level=logging.NOTSET):
logger.propagate = False
logger.setLevel(level)
handler = logging.StreamHandler(stream=sys.stderr)
logger.addHandler(handler)


# global logger
logger = logging.getLogger(__name__)
setup_logger()
40 changes: 38 additions & 2 deletions appium/webdriver/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from selenium.common.exceptions import InvalidArgumentException
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.command import Command as RemoteCommand
from selenium.webdriver.remote.remote_connection import RemoteConnection


from appium.webdriver.common.mobileby import MobileBy
from .appium_connection import AppiumConnection
Expand All @@ -43,6 +45,7 @@
from .switch_to import MobileSwitchTo
from .webelement import WebElement as MobileWebElement

from appium.common.logger import logger

# From remote/webdriver.py
_W3C_CAPABILITY_NAMES = frozenset([
Expand Down Expand Up @@ -117,7 +120,7 @@ class WebDriver(
):

def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
desired_capabilities=None, browser_profile=None, proxy=None, keep_alive=False):
desired_capabilities=None, browser_profile=None, proxy=None, keep_alive=False, direct_connection=False):

super(WebDriver, self).__init__(
AppiumConnection(command_executor, keep_alive=keep_alive),
Expand All @@ -126,12 +129,15 @@ def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
proxy
)

if self.command_executor is not None:
if hasattr(self, 'command_executor'):
self._addCommands()

self.error_handler = MobileErrorHandler()
self._switch_to = MobileSwitchTo(self)

if direct_connection:
self._update_command_executor(keep_alive=keep_alive)

# add new method to the `find_by_*` pantheon
By.IOS_UIAUTOMATION = MobileBy.IOS_UIAUTOMATION
By.IOS_PREDICATE = MobileBy.IOS_PREDICATE
Expand All @@ -142,6 +148,36 @@ def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
By.IMAGE = MobileBy.IMAGE
By.CUSTOM = MobileBy.CUSTOM

def _update_command_executor(self, keep_alive):
Copy link
Member

Choose a reason for hiding this comment

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

should probably check that these values actually exist. just because someone has set the direct_connection param doesn't mean the server supports it, right?

up to you whether you think we should throw an error if one of these values is not present, or just silently ignore the request to direct connect.

Copy link
Member Author

Choose a reason for hiding this comment

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

nice catch, I forgot it...

I'll show warning or error message rather than stopping test script since whether a server cannot handle the following requests or not depend on them.

"""Update command executor following directConnect feature"""
direct_protocol = 'directConnectProtocol'
direct_host = 'directConnectHost'
direct_port = 'directConnectPort'
direct_path = 'directConnectPath'

if (not {direct_protocol, direct_host, direct_port, direct_path}.issubset(set(self.capabilities))):
message = 'Direct connect capabilities from server were:\n'
for key in [direct_protocol, direct_host, direct_port, direct_path]:
message += '{}: \'{}\'\n'.format(key, self.capabilities.get(key, ''))
logger.warning(message)
return

protocol = self.capabilities[direct_protocol]
hostname = self.capabilities[direct_host]
port = self.capabilities[direct_port]
path = self.capabilities[direct_path]
executor = '{scheme}://{ho 802E stname}:{port}{path}'.format(
scheme=protocol,
hostname=hostname,
port=port,
path=path
)

logger.info('Updated request endpoint to %s', executor)
# Override command executor
self.command_executor = RemoteConnection(executor, keep_alive=keep_alive)
self._addCommands()

def start_session(self, capabilities, browser_profile=None):
"""
Override for Appium
Expand Down
75 changes: 75 additions & 0 deletions test/unit/webdriver/webdriver_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,78 @@ def test_find_elements_by_android_data_matcher_no_value(self):
assert d['using'] == '-android datamatcher'
assert d['value'] == '{}'
assert len(els) == 0

@httpretty.activate
def test_create_session_register_uridirect(self):
httpretty.register_uri(
httpretty.POST,
'http://localhost:4723/wd/hub/session',
body=json.dumps({'value': {
'sessionId': 'session-id',
'capabilities': {
'deviceName': 'Android Emulator',
'directConnectProtocol': 'http',
'directConnectHost': 'localhost2',
'directConnectPort': 4800,
'directConnectPath': '/special/path/wd/hub',
}
}})
)

httpretty.register_uri(
httpretty.GET,
'http://localhost2:4800/special/path/wd/hub/session/session-id/contexts',
body=json.dumps({'value': ['NATIVE_APP', 'CHROMIUM']})
)

desired_caps = {
'platformName': 'Android',
'deviceName': 'Android Emulator',
'app': 'path/to/app',
'automationName': 'UIAutomator2'
}
driver = webdriver.Remote(
'http://localhost:4723/wd/hub',
desired_caps,
direct_connection=True
)

assert 'http://localhost2:4800/special/path/wd/hub' == driver.command_executor._url
assert ['NATIVE_APP', 'CHROMIUM'] == driver.contexts

@httpretty.activate
def test_create_session_register_uridirect_no_direct_connect_path(self):
httpretty.register_uri(
httpretty.POST,
'http://localhost:4723/wd/hub/session',
body=json.dumps({'value': {
'sessionId': 'session-id',
'capabilities': {
'deviceName': 'Android Emulator',
'directConnectProtocol': 'http',
'directConnectHost': 'localhost2',
'directConnectPort': 4800
}
}})
)

httpretty.register_uri(
httpretty.GET,
'http://localhost:4723/wd/hub/session/session-id/contexts',
body=json.dumps({'value': ['NATIVE_APP', 'CHROMIUM']})
)

desired_caps = {
'platformName': 'Android',
'deviceName': 'Android Emulator',
'app': 'path/to/app',
'automationName': 'UIAutomator2'
}
driver = webdriver.Remote(
'http://localhost:4723/wd/hub',
desired_caps,
direct_connection=True
)

assert 'http://localhost:4723/wd/hub' == driver.command_executor._url
assert ['NATIVE_APP', 'CHROMIUM'] == driver.contexts
0