Skip to content

Commit 9376bf2

Browse files
authored
add execuite_driver (#222)
* add execuite_driver * add element class converter * append docstring, fix test code style
1 parent 8b70c86 commit 9376bf2

File tree

15 files changed

+234
-8
lines changed

15 files changed

+234
-8
lines changed

.rubocop.yml

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Metrics/ParameterLists:
1818
Max: 6
1919
Lint/NestedMethodDefinition:
2020
Enabled: false
21+
# TODO: Replace <<- with <<~ after dropping Ruby 2.2
22+
Layout/IndentHeredoc:
23+
Enabled: false
2124
Style/Documentation:
2225
Enabled: false
2326
Style/CommentedKeyword:

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Read `release_notes.md` for commit level details.
55
## [Unreleased]
66

77
### Enhancements
8+
- Add `execute_driver` to run a batch script on Appium 1.14.0+
89

910
### Bug fixes
1011

lib/appium_lib_core/common/base.rb

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
require_relative 'device/clipboard_content_type'
2929
require_relative 'device/device'
3030
require_relative 'device/touch_actions'
31+
require_relative 'device/execute_driver'
3132

3233
# The following files have selenium-webdriver related stuff.
3334
require_relative 'base/driver'

lib/appium_lib_core/common/base/bridge/mjsonwp.rb

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class MJSONWP < ::Selenium::WebDriver::Remote::OSS::Bridge
3131
include Device::ScreenRecord::Command
3232
include Device::Device
3333
include Device::TouchActions
34+
include Device::ExecuteDriver
3435

3536
def commands(command)
3637
::Appium::Core::Commands::MJSONWP::COMMANDS[command]
@@ -53,6 +54,13 @@ def take_viewport_screenshot
5354
def send_actions(_data)
5455
raise Error::UnsupportedOperationError, '#send_actions has not been supported in MJSONWP'
5556
end
57+
58+
# For Appium
59+
# @param [Hash] id The id which can get as a response from server
60+
# @return [::Selenium::WebDriver::Element]
61+
def convert_to_element(id)
62+
::Selenium::WebDriver::Element.new self, element_id_from(id)
63+
end
5664
end # class MJSONWP
5765
end # class Bridge
5866
end # class Base

lib/appium_lib_core/common/base/bridge/w3c.rb

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class W3C < ::Selenium::WebDriver::Remote::W3C::Bridge
3131
include Device::ScreenRecord::Command
3232
include Device::Device
3333
include Device::TouchActions
34+
include Device::ExecuteDriver
3435

3536
def commands(command)
3637
::Appium::Core::Commands::W3C::COMMANDS[command]
@@ -133,6 +134,13 @@ def find_elements_by(how, what, parent = nil)
133134
ids.map { |id| ::Selenium::WebDriver::Element.new self, element_id_from(id) }
134135
end
135136

137+
# For Appium
138+
# @param [Hash] id The id which can get as a response from server
139+
# @return [::Selenium::WebDriver::Element]
140+
def convert_to_element(id)
141+
::Selenium::WebDriver::Element.new self, element_id_from(id)
142+
end
143+
136144
# For Appium
137145
# override
138146
# called in `extend DriverExtensions::HasNetworkConnection`

lib/appium_lib_core/common/base/driver.rb

+48
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,54 @@ def find_elements_by_image(img_path)
10421042
template = Base64.strict_encode64 File.read img_path
10431043
find_elements :image, template
10441044
end
1045+
1046+
# @since Appium 1.14.0
1047+
#
1048+
# Run a set of script against the current session, allowing execution of many commands in one Appium request.
1049+
# Supports {https://webdriver.io/docs/api.html WebdriverIO} API so far.
1050+
# Please read {http://appium.io/docs/en/commands/session/execute-driver command API} for more details
1051+
# about acceptable scripts and the output.
1052+
#
1053+
# @param [String] script The string consisting of the script itself
1054+
# @param [String] type The name of the script type.
1055+
# Defaults to 'webdriverio'. Depends on server implementation which type is supported.
1056+
# @param [Integer] timeout_ms The number of `ms` Appium should wait for the script to finish
1057+
# before killing it due to timeout.
1058+
#
1059+
# @return [Appium::Core::Base::Device::ExecuteDriver::Result] The script result parsed by
1060+
# Appium::Core::Base::Device::ExecuteDriver::Result.
1061+
#
1062+
# @raise [::Selenium::WebDriver::Error::UnknownError] If something error happens in the script.
1063+
# It has the original message.
1064+
#
1065+
# @example
1066+
# script = <<~SCRIPT
1067+
# const status = await driver.status();
1068+
# console.warn('warning message');
1069+
# return [status];
1070+
# SCRIPT
1071+
# r = @@driver.execute_driver(script: script, type: 'webdriverio', timeout: 10_000)
1072+
# r #=> An instance of Appium::Core::Base::Device::ExecuteDriver::Result
1073+
# r.result #=> The `result` key part as the result of the script
1074+
# r.logs #=> The `logs` key part as `{'log' => [], 'warn' => [], 'error' => []}`
1075+
#
1076+
def execute_driver(script: '', type: 'webdriverio', timeout_ms: nil)
1077+
@bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
1078+
end
1079+
1080+
# Convert vanilla element response to ::Selenium::WebDriver::Element
1081+
#
1082+
# @param [Hash] id The id which can get as a response from server
1083+
# @return [::Selenium::WebDriver::Element]
1084+
#
1085+
# @example
1086+
# response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
1087+
# ele = @driver.convert_to_element(response) #=> ::Selenium::WebDriver::Element
1088+
# ele.rect #=> Can get the rect of the element
1089+
#
1090+
def convert_to_element(id)
1091+
@bridge.convert_to_element id
1092+
end
10451093
end # class Driver
10461094
end # class Base
10471095
end # module Core

lib/appium_lib_core/common/command/common.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ module Commands
6464
stop_recording_screen: [:post, 'session/:session_id/appium/stop_recording_screen'],
6565
start_recording_screen: [:post, 'session/:session_id/appium/start_recording_screen'],
6666
compare_images: [:post, 'session/:session_id/appium/compare_images'],
67-
is_keyboard_shown: [:get, 'session/:session_id/appium/device/is_keyboard_shown']
67+
is_keyboard_shown: [:get, 'session/:session_id/appium/device/is_keyboard_shown'],
68+
execute_driver: [:post, 'session/:session_id/appium/execute_driver']
6869
}.freeze
6970

7071
COMMAND_ANDROID = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
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+
module Appium
16+
module Core
17+
class Base
18+
module Device
19+
module ExecuteDriver
20+
class Result
21+
attr_reader :result, :logs
22+
23+
def initialize(response)
24+
@result = response['result']
25+
@logs = response['logs']
26+
end
27+
end
28+
29+
def execute_driver(script: '', type: 'webdriverio', timeout_ms: nil)
30+
option = { script: script, type: type }
31+
32+
option[:timeout] = timeout_ms if timeout_ms
33+
34+
response = execute :execute_driver, {}, option
35+
Result.new(response)
36+
end
37+
end # module Execute
38+
end # module Device
39+
end # class Base
40+
end # module Core
41+
end # module Appium

lib/appium_lib_core/device.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ def extended(_mod)
3939
:app_state,
4040
:stop_recording_screen, :stop_and_save_recording_screen,
4141
:shake, :device_time,
42-
:touch_actions, :multi_touch
42+
:touch_actions, :multi_touch,
43+
:execute_driver
4344
].each(&method(:delegate_from_appium_driver))
4445
end
4546

test/functional/ios/driver_test.rb

+41-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ def test_click_back
7272

7373
# @since Appium 1.14.0
7474
def test_default_keyboard_pref
75-
skip 'til WDA 0.0.6 will be released'
76-
7775
bundle_id = @@driver.session_capabilities['CFBundleIdentifier']
7876
begin
7977
@@driver.activate_app('com.apple.Preferences')
@@ -89,6 +87,47 @@ def test_default_keyboard_pref
8987
assert_equal '0', switches_status['Predictive']
9088
end
9189

90+
# @since Appium 1.14.0
91+
def test_batch
92+
script = <<-SCRIPT
93+
const status = await driver.status();
94+
console.warn('warning message');
95+
return [status];
96+
SCRIPT
97+
98+
r = @@driver.execute_driver(script: script, type: 'webdriverio', timeout_ms: 10_000)
99+
assert(r.result.first['build'])
100+
assert('warning message', r.logs['warn'].first)
101+
end
102+
103+
# @since Appium 1.14.0
104+
def test_batch_only_return
105+
script = <<-SCRIPT
106+
SCRIPT
107+
108+
r = @@driver.execute_driver(script: script, type: 'webdriverio')
109+
assert(r.result.nil?)
110+
assert([], r.logs['warn'])
111+
end
112+
113+
# @since Appium 1.14.0
114+
def test_batch_combination_ruby_script
115+
script = <<-SCRIPT
116+
console.warn('warning message');
117+
const element = await driver.findElement('accessibility id', 'Buttons');
118+
const rect = await driver.getElementRect(element.ELEMENT);
119+
return [element, rect];
120+
SCRIPT
121+
122+
r = @@driver.execute_driver(script: script)
123+
ele = @@driver.convert_to_element(r.result.first)
124+
rect = ele.rect
125+
assert_equal(rect.x, r.result[1]['x'])
126+
assert_equal(rect.y, r.result[1]['y'])
127+
assert_equal(rect.width, r.result[1]['width'])
128+
assert_equal(rect.height, r.result[1]['height'])
129+
end
130+
92131
# TODO: call @driver.quit after tests
93132
end
94133
end

test/unit/android/device/mjsonwp/definition_test.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ def test_with_arg_definitions
9191
:set_network_connection,
9292
:get_performance_data,
9393
:get_clipboard,
94-
:set_clipboard])
94+
:set_clipboard,
95+
:execute_driver])
9596
end
9697
end
9798
end # module MJSONWP

test/unit/android/device/w3c/definition_test.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ def test_with_arg_definitions
9191
:set_network_connection,
9292
:get_performance_data,
9393
:get_clipboard,
94-
:set_clipboard])
94+
:set_clipboard,
95+
:execute_driver])
9596
end
9697
end # class DefinitionTest
9798
end # module W3C
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# frozen_string_literal: true
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+
require 'test_helper'
16+
require 'webmock/minitest'
17+
18+
# $ rake test:unit TEST=test/unit/android/device/w3c/commands_test.rb
19+
class AppiumLibCoreTest
20+
module Android
21+
module Device
22+
module W3C
23+
class ExecuteDriverTest < Minitest::Test
24+
include AppiumLibCoreTest::Mock
25+
26+
def setup
27+
@core ||= ::Appium::Core.for(Caps.android)
28+
@driver ||= android_mock_create_session_w3c
29+
end
30+
31+
def test_batch_no_timeout
32+
script = <<-SCRIPT
33+
const status = await driver.status();
34+
return status;
35+
SCRIPT
36+
37+
stub_request(:post, "#{SESSION}/appium/execute_driver")
38+
.with(body: { script: script, type: 'webdriverio' }.to_json)
39+
.to_return(headers: HEADER, status: 200, body: {
40+
value: { result: { build: 'version' }, logs: { log: [], warn: [], error: [] } }
41+
}.to_json)
42+
43+
r = @driver.execute_driver(script: script, type: 'webdriverio')
44+
45+
assert_requested(:post, "#{SESSION}/appium/execute_driver", times: 1)
46+
assert_equal({ 'build' => 'version' }, r.result)
47+
assert_equal({ 'log' => [], 'warn' => [], 'error' => [] }, r.logs)
48+
end
49+
50+
def test_batch
51+
script = <<-SCRIPT
52+
console.warn('warning message');
53+
SCRIPT
54+
55+
stub_request(:post, "#{SESSION}/appium/execute_driver")
56+
.with(body: { script: script, type: 'webdriverio', timeout: 1000 }.to_json)
57+
.to_return(headers: HEADER, status: 200, body: {
58+
value: { result: nil, logs: { log: [], warn: ['warning message'], error: [] } }
59+
}.to_json)
60+
61+
r = @driver.execute_driver(script: script, type: 'webdriverio', timeout_ms: 1000)
62+
63+
assert_requested(:post, "#{SESSION}/appium/execute_driver", times: 1)
64+
assert_nil(r.result)
65+
assert_equal({ 'log' => [], 'warn' => ['warning message'], 'error' => [] }, r.logs)
66+
end
67+
end # class DeviceLockTest
68+
end # module W3C
69+
end # module Device
70+
end # module Android
71+
end # class AppiumLibCoreTest

test/unit/ios/device/mjsonwp/definition_test.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ def test_with_arg_definitions
7979
:touch_actions,
8080
:multi_touch,
8181
:touch_id,
82-
:toggle_touch_id_enrollment])
82+
:toggle_touch_id_enrollment,
83+
:execute_driver])
8384
end
8485
end # class DefinitionTest
8586
end # module MJSONWP

test/unit/ios/device/w3c/definition_test.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ def test_with_arg_definitions
8282
:touch_actions,
8383
:multi_touch,
8484
:touch_id,
85-
:toggle_touch_id_enrollment])
85+
:toggle_touch_id_enrollment,
86+
:execute_driver])
8687
end
8788
end # class DefinitionTest
8889
end # module W3C

0 commit comments

Comments
 (0)