Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add execute driver #406

Merged
merged 7 commits into from
Jun 26, 2019
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
61 changes: 61 additions & 0 deletions appium/webdriver/extensions/execute_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/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.

from selenium import webdriver

from ..mobilecommand import MobileCommand as Command


class ExecuteDriver(webdriver.Remote):

def execute_driver(self, script, script_type='webdriverio', timeout_ms=None):
"""Run a set of script against the current session, allowing execution of many commands in one Appium request.
Please read http://appium.io/docs/en/commands/session/execute-driver for more details about the acceptable
scripts and the output format.

Args:
script (string): The string consisting of the script itself
script_type (string): The name of the script type. Defaults to 'webdriverio'.
timeout_ms (optional): The number of `ms` Appium should wait for the script to finish before killing it due to timeout_ms.

Usage:
self.driver.execute_driver(script='return [];')
self.driver.execute_driver(script='return [];', script_type='webdriverio')
self.driver.execute_driver(script='return [];', script_type='webdriverio', timeout_ms=10000)

Returns:
ExecuteDriver.Result: The result of the script. It has 'result' and 'logs' keys.

Raises:
WebDriverException: If something error happenes in the script. The message has the original error message.
"""

class Result(object):

def __init__(self, response):
self.result = response['result']
self.logs = response['logs']

option = {'script': script, 'type': script_type}
if timeout_ms is not None:
option['timeout'] = timeout_ms

response = self.execute(Command.EXECUTE_DRIVER, option)['value']
return Result(response)

# pylint: disable=protected-access

def _addCommands(self):
self.command_executor._commands[Command.EXECUTE_DRIVER] = \
('POST', '/session/$sessionId/appium/execute_driver')
2 changes: 2 additions & 0 deletions appium/webdriver/mobilecommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class MobileCommand(object):
COMPARE_IMAGES = 'compareImages'
IS_KEYBOARD_SHOWN = 'isKeyboardShown'

EXECUTE_DRIVER = 'executeDriver'

# Android
OPEN_NOTIFICATIONS = 'openNotifications'
START_ACTIVITY = 'startActivity'
Expand Down
7 changes: 5 additions & 2 deletions appium/webdriver/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from .extensions.clipboard import Clipboard
from .extensions.context import Context
from .extensions.device_time import DeviceTime
from .extensions.execute_driver import ExecuteDriver
from .extensions.hw_actions import HardwareActions
from .extensions.images_comparison import ImagesComparison
from .extensions.ime import IME
Expand Down Expand Up @@ -116,6 +117,7 @@ class WebDriver(
Context,
DeviceTime,
Display,
ExecuteDriver,
Gsm,
HardwareActions,
ImagesComparison,
Expand Down Expand Up @@ -580,19 +582,20 @@ def find_elements_by_custom(self, selector):
"""
return self.find_elements(by=MobileBy.CUSTOM, value=selector)

def create_web_element(self, element_id):
def create_web_element(self, element_id, w3c=False):
"""Creates a web element with the specified element_id.

Overrides method in Selenium WebDriver in order to always give them
Appium WebElement

Args:
element_id (int): The element id to create a web element
w3c (bool): Whether the element is W3C or MJSONWP

Returns:
`MobileWebElement`
"""
return MobileWebElement(self, element_id)
return MobileWebElement(self, element_id, w3c)

def press_button(self, button_name):
"""Sends a physical button name to the device to simulate the user pressing.
Expand Down
57 changes: 57 additions & 0 deletions test/functional/ios/execute_driver_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/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 textwrap
import unittest

from appium import webdriver
from helper import desired_capabilities


class ExecuteDriverTests(unittest.TestCase):
def setUp(self):
desired_caps = desired_capabilities.get_desired_capabilities('UICatalog.app.zip')
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

def tearDown(self):
self.driver.quit()

def test_batch(self):
script = """
Copy link
Contributor

Choose a reason for hiding this comment

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

const status = await driver.status();
console.warn('warning message');
return status;
"""

response = self.driver.execute_driver(script=textwrap.dedent(script))
assert(response.result['build'])
assert(response.logs['warn'] == ['warning message'])

def test_batch_combination_python_script(self):
script = """
console.warn('warning message');
const element = await driver.findElement('accessibility id', 'Buttons');
const rect = await driver.getElementRect(element.ELEMENT);
return [element, rect];
"""

response = self.driver.execute_driver(script=textwrap.dedent(script))
r = response.result[0].rect

assert(r == response.result[1])


if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(ExecuteDriverTests)
unittest.TextTestRunner(verbosity=2).run(suite)
84 changes: 84 additions & 0 deletions test/unit/webdriver/device/execute_driver_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/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 textwrap
from test.unit.helper.test_helper import (
android_w3c_driver,
appium_command,
get_httpretty_request_body
)

import httpretty


class TestWebDriverDeviceActivities(object):

@httpretty.activate
def test_batch(self):
driver = android_w3c_driver()
httpretty.register_uri(
httpretty.POST,
appium_command('/session/1234567890/appium/execute_driver'),
body='{"value": {"result":['
'{"element-6066-11e4-a52e-4f735466cecf":"39000000-0000-0000-D39A-000000000000",'
'"ELEMENT":"39000000-0000-0000-D39A-000000000000"},'
'{"y":237,"x":18,"width":67,"height":24}],"logs":{'
'"error":[],"warn":["warning message"],"log":[]}}}'
)

script = """
console.warn('warning message');
const element = await driver.findElement('accessibility id', 'Buttons');
const rect = await driver.getElementRect(element.ELEMENT);
return [element, rect];
"""
response = driver.execute_driver(script=textwrap.dedent(script))
# Python client convert an element item as WebElement in the result
assert response.result[0].id == '39000000-0000-0000-D39A-000000000000'
assert response.result[1]['y'] == 237
assert response.logs['warn'] == ['warning message']

d = get_httpretty_request_body(httpretty.last_request())
assert d['script'] == textwrap.dedent(script)
assert d['type'] == 'webdriverio'
assert 'timeout' not in d
Copy link
Collaborator

Choose a reason for hiding this comment

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

'timeout' not in d.keys() ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Current way is fine

>>> d = {'timeout': 1}
>>> 'timeout' in d
True
>>> 'timeout' in d.keys()
True


@httpretty.activate
def test_batch_with_timeout(self):
driver = android_w3c_driver()
httpretty.register_uri(
httpretty.POST,
appium_command('/session/1234567890/appium/execute_driver'),
body='{"value": {"result":['
'{"element-6066-11e4-a52e-4f735466cecf":"39000000-0000-0000-D39A-000000000000",'
'"ELEMENT":"39000000-0000-0000-D39A-000000000000"},'
'{"y":237,"x":18,"width":67,"height":24}],"logs":{'
'"error":[],"warn":["warning message"],"log":[]}}}'
)

script = """
console.warn('warning message');
const element = await driver.findElement('accessibility id', 'Buttons');
const rect = await driver.getElementRect(element.ELEMENT);
return [element, rect];
"""
response = driver.execute_driver(script=textwrap.dedent(script), timeout_ms=10000)
assert response.result[0].id == '39000000-0000-0000-D39A-000000000000'
assert response.result[1]['y'] == 237
assert response.logs['error'] == []

d = get_httpretty_request_body(httpretty.last_request())
assert d['script'] == textwrap.dedent(script)
assert d['type'] == 'webdriverio'
assert d['timeout'] == 10000