Skip to content

Commit 0368b5a

Browse files
authored
Add execute driver (appium#406)
* add execute driver * append docstring
1 parent b8d318c commit 0368b5a

File tree

5 files changed

+209
-2
lines changed

5 files changed

+209
-2
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
from selenium import webdriver
16+
17+
from ..mobilecommand import MobileCommand as Command
18+
19+
20+
class ExecuteDriver(webdriver.Remote):
21+
22+
def execute_driver(self, script, script_type='webdriverio', timeout_ms=None):
23+
"""Run a set of script against the current session, allowing execution of many commands in one Appium request.
24+
Please read http://appium.io/docs/en/commands/session/execute-driver for more details about the acceptable
25+
scripts and the output format.
26+
27+
Args:
28+
script (string): The string consisting of the script itself
29+
script_type (string): The name of the script type. Defaults to 'webdriverio'.
30+
timeout_ms (optional): The number of `ms` Appium should wait for the script to finish before killing it due to timeout_ms.
31+
32+
Usage:
33+
self.driver.execute_driver(script='return [];')
34+
self.driver.execute_driver(script='return [];', script_type='webdriverio')
35+
self.driver.execute_driver(script='return [];', script_type='webdriverio', timeout_ms=10000)
36+
37+
Returns:
38+
ExecuteDriver.Result: The result of the script. It has 'result' and 'logs' keys.
39+
40+
Raises:
41+
WebDriverException: If something error happenes in the script. The message has the original error message.
42+
"""
43+
44+
class Result(object):
45+
46+
def __init__(self, response):
47+
self.result = response['result']
48+
self.logs = response['logs']
49+
50+
option = {'script': script, 'type': script_type}
51+
if timeout_ms is not None:
52+
option['timeout'] = timeout_ms
53+
54+
response = self.execute(Command.EXECUTE_DRIVER, option)['value']
55+
return Result(response)
56+
57+
# pylint: disable=protected-access
58+
59+
def _addCommands(self):
60+
self.command_executor._commands[Command.EXECUTE_DRIVER] = \
61+
('POST', '/session/$sessionId/appium/execute_driver')

appium/webdriver/mobilecommand.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class MobileCommand(object):
7272
COMPARE_IMAGES = 'compareImages'
7373
IS_KEYBOARD_SHOWN = 'isKeyboardShown'
7474

75+
EXECUTE_DRIVER = 'executeDriver'
76+
7577
# Android
7678
OPEN_NOTIFICATIONS = 'openNotifications'
7779
START_ACTIVITY = 'startActivity'

appium/webdriver/webdriver.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from .extensions.clipboard import Clipboard
4141
from .extensions.context import Context
4242
from .extensions.device_time import DeviceTime
43+
from .extensions.execute_driver import ExecuteDriver
4344
from .extensions.hw_actions import HardwareActions
4445
from .extensions.images_comparison import ImagesComparison
4546
from .extensions.ime import IME
@@ -116,6 +117,7 @@ class WebDriver(
116117
Context,
117118
DeviceTime,
118119
Display,
120+
ExecuteDriver,
119121
Gsm,
120122
HardwareActions,
121123
ImagesComparison,
@@ -580,19 +582,20 @@ def find_elements_by_custom(self, selector):
580582
"""
581583
return self.find_elements(by=MobileBy.CUSTOM, value=selector)
582584

583-
def create_web_element(self, element_id):
585+
def create_web_element(self, element_id, w3c=False):
584586
"""Creates a web element with the specified element_id.
585587
586588
Overrides method in Selenium WebDriver in order to always give them
587589
Appium WebElement
588590
589591
Args:
590592
element_id (int): The element id to create a web element
593+
w3c (bool): Whether the element is W3C or MJSONWP
591594
592595
Returns:
593596
`MobileWebElement`
594597
"""
595-
return MobileWebElement(self, element_id)
598+
return MobileWebElement(self, element_id, w3c)
596599

597600
def press_button(self, button_name):
598601
"""Sends a physical button name to the device to simulate the user pressing.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 textwrap
16+
import unittest
17+
18+
from appium import webdriver
19+
from helper import desired_capabilities
20+
21+
22+
class ExecuteDriverTests(unittest.TestCase):
23+
def setUp(self):
24+
desired_caps = desired_capabilities.get_desired_capabilities('UICatalog.app.zip')
25+
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
26+
27+
def tearDown(self):
28+
self.driver.quit()
29+
30+
def test_batch(self):
31+
script = """
32+
const status = await driver.status();
33+
console.warn('warning message');
34+
return status;
35+
"""
36+
37+
response = self.driver.execute_driver(script=textwrap.dedent(script))
38+
assert(response.result['build'])
39+
assert(response.logs['warn'] == ['warning message'])
40+
41+
def test_batch_combination_python_script(self):
42+
script = """
43+
console.warn('warning message');
44+
const element = await driver.findElement('accessibility id', 'Buttons');
45+
const rect = await driver.getElementRect(element.ELEMENT);
46+
return [element, rect];
47+
"""
48+
49+
response = self.driver.execute_driver(script=textwrap.dedent(script))
50+
r = response.result[0].rect
51+
52+
assert(r == response.result[1])
53+
54+
55+
if __name__ == '__main__':
56+
suite = unittest.TestLoader().loadTestsFromTestCase(ExecuteDriverTests)
57+
unittest.TextTestRunner(verbosity=2).run(suite)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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 textwrap
16+
from test.unit.helper.test_helper import (
17+
android_w3c_driver,
18+
appium_command,
19+
get_httpretty_request_body
20+
)
21+
22+
import httpretty
23+
24+
25+
class TestWebDriverDeviceActivities(object):
26+
27+
@httpretty.activate
28+
def test_batch(self):
29+
driver = android_w3c_driver()
30+
httpretty.register_uri(
31+
httpretty.POST,
32+
appium_command('/session/1234567890/appium/execute_driver'),
33+
body='{"value": {"result":['
34+
'{"element-6066-11e4-a52e-4f735466cecf":"39000000-0000-0000-D39A-000000000000",'
35+
'"ELEMENT":"39000000-0000-0000-D39A-000000000000"},'
36+
'{"y":237,"x":18,"width":67,"height":24}],"logs":{'
37+
'"error":[],"warn":["warning message"],"log":[]}}}'
38+
)
39+
40+
script = """
41+
console.warn('warning message');
42+
const element = await driver.findElement('accessibility id', 'Buttons');
43+
const rect = await driver.getElementRect(element.ELEMENT);
44+
return [element, rect];
45+
"""
46+
response = driver.execute_driver(script=textwrap.dedent(script))
47+
# Python client convert an element item as WebElement in the result
48+
assert response.result[0].id == '39000000-0000-0000-D39A-000000000000'
49+
assert response.result[1]['y'] == 237
50+
assert response.logs['warn'] == ['warning message']
51+
52+
d = get_httpretty_request_body(httpretty.last_request())
53+
assert d['script'] == textwrap.dedent(script)
54+
assert d['type'] == 'webdriverio'
55+
assert 'timeout' not in d
56+
57+
@httpretty.activate
58+
def test_batch_with_timeout(self):
59+
driver = android_w3c_driver()
60+
httpretty.register_uri(
61+
httpretty.POST,
62+
appium_command('/session/1234567890/appium/execute_driver'),
63+
body='{"value": {"result":['
64+
'{"element-6066-11e4-a52e-4f735466cecf":"39000000-0000-0000-D39A-000000000000",'
65+
'"ELEMENT":"39000000-0000-0000-D39A-000000000000"},'
66+
'{"y":237,"x":18,"width":67,"height":24}],"logs":{'
67+
'"error":[],"warn":["warning message"],"log":[]}}}'
68+
)
69+
70+
script = """
71+
console.warn('warning message');
72+
const element = await driver.findElement('accessibility id', 'Buttons');
73+
const rect = await driver.getElementRect(element.ELEMENT);
74+
return [element, rect];
75+
"""
76+
response = driver.execute_driver(script=textwrap.dedent(script), timeout_ms=10000)
77+
assert response.result[0].id == '39000000-0000-0000-D39A-000000000000'
78+
assert response.result[1]['y'] == 237
79+
assert response.logs['error'] == []
80+
81+
d = get_httpretty_request_body(httpretty.last_request())
82+
assert d['script'] == textwrap.dedent(script)
83+
assert d['type'] == 'webdriverio'
84+
assert d['timeout'] == 10000

0 commit comments

Comments
 (0)