appium_lib_core 4.1.0 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +357 -270
- data/README.md +68 -16
- data/Rakefile +8 -20
- data/Steepfile +11 -0
- data/appium_lib_core.gemspec +13 -15
- data/bin/console +0 -4
- data/lib/appium_lib_core/android/device/auth_finger_print.rb +4 -1
- data/lib/appium_lib_core/android/device/clipboard.rb +4 -2
- data/lib/appium_lib_core/android/device/emulator.rb +11 -5
- data/lib/appium_lib_core/android/device/network.rb +10 -0
- data/lib/appium_lib_core/android/device/performance.rb +3 -0
- data/lib/appium_lib_core/android/device/screen.rb +5 -1
- data/lib/appium_lib_core/android/device.rb +83 -20
- data/lib/appium_lib_core/common/base/bridge.rb +238 -95
- data/lib/appium_lib_core/common/base/capabilities.rb +21 -8
- data/lib/appium_lib_core/common/{command/mjsonwp.rb → base/device_ime.rb} +33 -12
- data/lib/appium_lib_core/common/base/driver.rb +260 -334
- data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
- data/lib/appium_lib_core/common/base/has_location.rb +80 -0
- data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
- data/lib/appium_lib_core/common/base/http_default.rb +22 -38
- data/lib/appium_lib_core/{ios/uiautomation/bridge.rb → common/base/remote_status.rb} +9 -8
- data/lib/appium_lib_core/common/base/rotable.rb +62 -0
- data/lib/appium_lib_core/common/base/screenshot.rb +10 -10
- data/lib/appium_lib_core/common/base/search_context.rb +98 -172
- data/lib/appium_lib_core/common/base.rb +1 -5
- data/lib/appium_lib_core/common/command.rb +244 -4
- data/lib/appium_lib_core/common/device/app_management.rb +2 -26
- data/lib/appium_lib_core/common/device/context.rb +1 -5
- data/lib/appium_lib_core/common/device/image_comparison.rb +27 -10
- data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
- data/lib/appium_lib_core/common/device/{touch_actions.rb → orientation.rb} +6 -10
- data/lib/appium_lib_core/common/device/screen_record.rb +8 -2
- data/lib/appium_lib_core/common/error.rb +5 -5
- data/lib/appium_lib_core/common/log.rb +5 -4
- data/lib/appium_lib_core/common/wait.rb +38 -6
- data/lib/appium_lib_core/device.rb +3 -9
- data/lib/appium_lib_core/driver.rb +193 -182
- data/lib/appium_lib_core/{patch.rb → element.rb} +64 -26
- data/lib/appium_lib_core/ios/device/clipboard.rb +4 -2
- data/lib/appium_lib_core/ios/xcuitest/device.rb +2 -0
- data/lib/appium_lib_core/{common/base/command.rb → mac2/bridge.rb} +9 -8
- data/lib/appium_lib_core/mac2/device/screen.rb +48 -0
- data/lib/appium_lib_core/mac2/device.rb +92 -0
- data/lib/appium_lib_core/{ios.rb → mac2.rb} +2 -5
- data/lib/appium_lib_core/support/event_firing_bridge.rb +57 -0
- data/lib/appium_lib_core/version.rb +2 -2
- data/lib/appium_lib_core.rb +23 -10
- data/rbs_collection.lock.yaml +252 -0
- data/rbs_collection.yaml +15 -0
- data/sig/gems/selenium/abstract_event_listener.rbs +8 -0
- data/sig/gems/selenium/capabilities.rbs +8 -0
- data/sig/gems/selenium/common.rbs +10 -0
- data/sig/gems/selenium/default.rbs +10 -0
- data/sig/gems/selenium/driver.rbs +7 -0
- data/sig/gems/selenium/has_session_id.rbs +8 -0
- data/sig/gems/selenium/has_web_storage.rbs +8 -0
- data/sig/gems/selenium/uploads_files.rbs +8 -0
- data/sig/lib/appium_lib_core/common/base/capabilities.rbs +9 -0
- data/sig/lib/appium_lib_core/common/base/driver.rbs +167 -0
- data/sig/lib/appium_lib_core/common/base/driver_settings.rbs +15 -0
- data/sig/lib/appium_lib_core/common/base/has_location.rbs +13 -0
- data/sig/lib/appium_lib_core/common/base/has_network_connection.rbs +19 -0
- data/sig/lib/appium_lib_core/common/base/http_default.rbs +38 -0
- data/sig/lib/appium_lib_core/common/base/platform.rbs +7 -0
- data/sig/lib/appium_lib_core/common/base/remote_status.rbs +9 -0
- data/sig/lib/appium_lib_core/common/base/rotable.rbs +17 -0
- data/sig/lib/appium_lib_core/common/base/screenshot.rbs +19 -0
- data/sig/lib/appium_lib_core/common/device/battery_status.rbs +13 -0
- data/sig/lib/appium_lib_core/common/wait.rbs +31 -0
- data/sig/lib/appium_lib_core/device.rbs +21 -0
- data/sig/lib/appium_lib_core/driver.rbs +196 -0
- data/sig/lib/appium_lib_core/ios/xcuitest/device/battery.rbs +15 -0
- data/sig/lib/appium_lib_core/version.rbs +7 -0
- data/sig/lib/appium_lib_core.rbs +8 -0
- metadata +88 -111
- data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
- data/.github/contributing.md +0 -26
- data/.github/issue_template.md +0 -20
- data/.github/workflows/unittest.yml +0 -68
- data/.gitignore +0 -18
- data/.rubocop.yml +0 -58
- data/azure-pipelines.yml +0 -15
- data/ci-jobs/functional/android_setup.yml +0 -3
- data/ci-jobs/functional/ios_setup.yml +0 -7
- data/ci-jobs/functional/publish_test_result.yml +0 -18
- data/ci-jobs/functional/run_appium.yml +0 -25
- data/ci-jobs/functional/start-emulator.sh +0 -26
- data/ci-jobs/functional_test.yml +0 -298
- data/docs/mobile_command.md +0 -34
- data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
- data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
- data/lib/appium_lib_core/common/command/common.rb +0 -110
- data/lib/appium_lib_core/common/command/w3c.rb +0 -56
- data/lib/appium_lib_core/common/device/value.rb +0 -52
- data/lib/appium_lib_core/common/touch_action/multi_touch.rb +0 -56
- data/lib/appium_lib_core/common/touch_action/touch_actions.rb +0 -203
- data/lib/appium_lib_core/ios/uiautomation/device.rb +0 -44
- data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
- data/release_notes.md +0 -816
- data/script/commands.rb +0 -200
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
require 'uri'
|
|
16
16
|
|
|
17
17
|
module Appium
|
|
18
|
+
# The struct for 'location'
|
|
19
|
+
Location = Struct.new(:latitude, :longitude, :altitude)
|
|
20
|
+
|
|
18
21
|
module Core
|
|
19
22
|
module Android
|
|
20
23
|
autoload :Uiautomator1, 'appium_lib_core/android'
|
|
@@ -23,33 +26,30 @@ module Appium
|
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
module Ios
|
|
26
|
-
autoload :Uiautomation, 'appium_lib_core/ios'
|
|
27
29
|
autoload :Xcuitest, 'appium_lib_core/ios_xcuitest'
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
autoload :Mac2, 'appium_lib_core/mac2'
|
|
33
|
+
|
|
30
34
|
autoload :Windows, 'appium_lib_core/windows'
|
|
31
35
|
|
|
32
36
|
# This options affects only client side as <code>:appium_lib</code> key.<br>
|
|
33
37
|
# Read {::Appium::Core::Driver} about each attribute
|
|
34
38
|
class Options
|
|
35
|
-
attr_reader :custom_url, :default_wait,
|
|
39
|
+
attr_reader :custom_url, :default_wait,
|
|
36
40
|
:port, :wait_timeout, :wait_interval, :listener,
|
|
37
41
|
:direct_connect, :enable_idempotency_header
|
|
38
42
|
|
|
39
43
|
def initialize(appium_lib_opts)
|
|
40
44
|
@custom_url = appium_lib_opts.fetch :server_url, nil
|
|
41
|
-
@default_wait = appium_lib_opts.fetch :wait,
|
|
45
|
+
@default_wait = appium_lib_opts.fetch :wait, nil
|
|
42
46
|
@enable_idempotency_header = appium_lib_opts.fetch :enable_idempotency_header, true
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
@export_session = appium_lib_opts.fetch :export_session, false
|
|
46
|
-
@export_session_path = appium_lib_opts.fetch :export_session_path, default_tmp_appium_lib_session
|
|
47
|
-
|
|
48
|
-
@direct_connect = appium_lib_opts.fetch :direct_connect, false
|
|
48
|
+
@direct_connect = appium_lib_opts.fetch :direct_connect, true
|
|
49
49
|
|
|
50
50
|
@port = appium_lib_opts.fetch :port, Driver::DEFAULT_APPIUM_PORT
|
|
51
51
|
|
|
52
|
-
# timeout and interval used in ::Appium::
|
|
52
|
+
# timeout and interval used in ::Appium::Commn.wait/wait_true
|
|
53
53
|
@wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
|
|
54
54
|
@wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
|
|
55
55
|
|
|
@@ -74,6 +74,13 @@ module Appium
|
|
|
74
74
|
path: 'directConnectPath'
|
|
75
75
|
}.freeze
|
|
76
76
|
|
|
77
|
+
W3C_KEYS = {
|
|
78
|
+
protocol: 'appium:directConnectProtocol',
|
|
79
|
+
host: 'appium:directConnectHost',
|
|
80
|
+
port: 'appium:directConnectPort',
|
|
81
|
+
path: 'appium:directConnectPath'
|
|
82
|
+
}.freeze
|
|
83
|
+
|
|
77
84
|
# @return [string] Returns a protocol such as http/https
|
|
78
85
|
attr_reader :protocol
|
|
79
86
|
|
|
@@ -87,17 +94,17 @@ module Appium
|
|
|
87
94
|
attr_reader :path
|
|
88
95
|
|
|
89
96
|
def initialize(capabilities)
|
|
90
|
-
@protocol = capabilities[KEYS[:protocol]]
|
|
91
|
-
@host = capabilities[KEYS[:host]]
|
|
92
|
-
@port = capabilities[KEYS[:port]]
|
|
93
|
-
@path = capabilities[KEYS[:path]]
|
|
97
|
+
@protocol = capabilities[W3C_KEYS[:protocol]] || capabilities[KEYS[:protocol]]
|
|
98
|
+
@host = capabilities[W3C_KEYS[:host]] || capabilities[KEYS[:host]]
|
|
99
|
+
@port = capabilities[W3C_KEYS[:port]] || capabilities[KEYS[:port]]
|
|
100
|
+
@path = capabilities[W3C_KEYS[:path]] || capabilities[KEYS[:path]]
|
|
94
101
|
end
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
class Driver
|
|
98
105
|
include Waitable
|
|
99
106
|
|
|
100
|
-
# Selenium webdriver capabilities
|
|
107
|
+
# Selenium webdriver capabilities, but the value is provided capabilities basis.
|
|
101
108
|
# @return [Core::Base::Capabilities]
|
|
102
109
|
attr_reader :caps
|
|
103
110
|
|
|
@@ -118,7 +125,7 @@ module Appium
|
|
|
118
125
|
|
|
119
126
|
# Automation name sent to appium server or received by server.<br>
|
|
120
127
|
# If automation_name is <code>nil</code>, it is not set both client side and server side.
|
|
121
|
-
# @return [
|
|
128
|
+
# @return [Symbol]
|
|
122
129
|
attr_reader :automation_name
|
|
123
130
|
|
|
124
131
|
# Custom URL for the selenium server. If set this attribute, ruby_lib_core try to handshake to the custom url.<br>
|
|
@@ -126,18 +133,10 @@ module Appium
|
|
|
126
133
|
# @return [String]
|
|
127
134
|
attr_reader :custom_url
|
|
128
135
|
|
|
129
|
-
# Export session id to textfile in /tmp for 3rd party tools. False by default.
|
|
130
|
-
# @return [Boolean]
|
|
131
|
-
attr_reader :export_session
|
|
132
|
-
# @return [String] By default, session id is exported in '/tmp/appium_lib_session'
|
|
133
|
-
attr_reader :export_session_path
|
|
134
|
-
|
|
135
136
|
# Default wait time for elements to appear in Appium server side.
|
|
136
|
-
# Defaults to {::Appium::Core::Driver::DEFAULT_IMPLICIT_WAIT}.<br>
|
|
137
137
|
# Provide <code>{ appium_lib: { wait: 30 } }</code> to {::Appium::Core.for}
|
|
138
138
|
# @return [Integer]
|
|
139
139
|
attr_reader :default_wait
|
|
140
|
-
DEFAULT_IMPLICIT_WAIT = 0
|
|
141
140
|
|
|
142
141
|
# Appium's server port. 4723 is by default. Defaults to {::Appium::Core::Driver::DEFAULT_APPIUM_PORT}.<br>
|
|
143
142
|
# Provide <code>{ appium_lib: { port: 8080 } }</code> to {::Appium::Core.for}.
|
|
@@ -175,7 +174,8 @@ module Appium
|
|
|
175
174
|
# - <code>directConnectPort</code>
|
|
176
175
|
# - <code>directConnectPath</code>
|
|
177
176
|
#
|
|
178
|
-
#
|
|
177
|
+
# ignore them if this parameter is <code>false</code>. Defaults to true.
|
|
178
|
+
# These keys can have <code>appium:</code> prefix.
|
|
179
179
|
#
|
|
180
180
|
# @return [Bool]
|
|
181
181
|
attr_reader :direct_connect
|
|
@@ -185,8 +185,6 @@ module Appium
|
|
|
185
185
|
# @option opts [Hash] :caps Appium capabilities.
|
|
186
186
|
# @option opts [Hash] :capabilities The same as :caps.
|
|
187
187
|
# This param is for compatibility with Selenium WebDriver format
|
|
188
|
-
# @option opts [Hash] :desired_capabilities The same as :caps.
|
|
189
|
-
# This param is for compatibility with Selenium WebDriver format
|
|
190
188
|
# @option opts [Appium::Core::Options] :appium_lib Capabilities affect only ruby client
|
|
191
189
|
# @option opts [String] :url The same as :custom_url in :appium_lib.
|
|
192
190
|
# This param is for compatibility with Selenium WebDriver format
|
|
@@ -197,10 +195,8 @@ module Appium
|
|
|
197
195
|
#
|
|
198
196
|
# # format 1
|
|
199
197
|
# @core = Appium::Core.for caps: {...}, appium_lib: {...}
|
|
200
|
-
# # format 2. 'capabilities:'
|
|
198
|
+
# # format 2. 'capabilities:' is also available instead of 'caps:'.
|
|
201
199
|
# @core = Appium::Core.for url: "http://127.0.0.1:8080/wd/hub", capabilities: {...}, appium_lib: {...}
|
|
202
|
-
# # format 3. 'appium_lib: {...}' can be blank
|
|
203
|
-
# @core = Appium::Core.for url: "http://127.0.0.1:8080/wd/hub", desired_capabilities: {...}
|
|
204
200
|
#
|
|
205
201
|
#
|
|
206
202
|
# require 'rubygems'
|
|
@@ -216,7 +212,6 @@ module Appium
|
|
|
216
212
|
# app: '/path/to/MyiOS.app'
|
|
217
213
|
# },
|
|
218
214
|
# appium_lib: {
|
|
219
|
-
# export_session: false,
|
|
220
215
|
# port: 8080,
|
|
221
216
|
# wait: 0,
|
|
222
217
|
# wait_timeout: 20,
|
|
@@ -228,7 +223,7 @@ module Appium
|
|
|
228
223
|
# @core.start_driver # Connect to 'http://127.0.0.1:8080/wd/hub' because of 'port: 8080'
|
|
229
224
|
#
|
|
230
225
|
# # Start iOS driver with .zip file over HTTP
|
|
231
|
-
# #
|
|
226
|
+
# # 'capabilities:' is also available instead of 'caps:'. Either is fine.
|
|
232
227
|
# opts = {
|
|
233
228
|
# capabilities: {
|
|
234
229
|
# platformName: :ios,
|
|
@@ -239,7 +234,6 @@ module Appium
|
|
|
239
234
|
# },
|
|
240
235
|
# appium_lib: {
|
|
241
236
|
# server_url: 'http://custom-host:8080/wd/hub.com',
|
|
242
|
-
# export_session: false,
|
|
243
237
|
# wait: 0,
|
|
244
238
|
# wait_timeout: 20,
|
|
245
239
|
# wait_interval: 0.3,
|
|
@@ -252,7 +246,7 @@ module Appium
|
|
|
252
246
|
# # Start iOS driver as another format. 'url' is available like below
|
|
253
247
|
# opts = {
|
|
254
248
|
# url: "http://custom-host:8080/wd/hub.com",
|
|
255
|
-
#
|
|
249
|
+
# capabilities: {
|
|
256
250
|
# platformName: :ios,
|
|
257
251
|
# platformVersion: '11.0',
|
|
258
252
|
# deviceName: 'iPhone Simulator',
|
|
@@ -260,7 +254,6 @@ module Appium
|
|
|
260
254
|
# app: '/path/to/MyiOS.app'
|
|
261
255
|
# },
|
|
262
256
|
# appium_lib: {
|
|
263
|
-
# export_session: false,
|
|
264
257
|
# wait: 0,
|
|
265
258
|
# wait_timeout: 20,
|
|
266
259
|
# wait_interval: 0.3,
|
|
@@ -270,8 +263,58 @@ module Appium
|
|
|
270
263
|
# @core = Appium::Core.for(opts) # create a core driver with 'opts' and extend methods into 'self'
|
|
271
264
|
# @core.start_driver # start driver with 'url'. Connect to 'http://custom-host:8080/wd/hub.com'
|
|
272
265
|
#
|
|
266
|
+
# # With a custom listener
|
|
267
|
+
# class CustomListener < ::Selenium::WebDriver::Support::AbstractEventListener
|
|
268
|
+
# // something
|
|
269
|
+
# end
|
|
270
|
+
# capabilities: {
|
|
271
|
+
# platformName: :ios,
|
|
272
|
+
# platformVersion: '11.0',
|
|
273
|
+
# deviceName: 'iPhone Simulator',
|
|
274
|
+
# automationName: 'XCUITest',
|
|
275
|
+
# app: '/path/to/MyiOS.app'
|
|
276
|
+
# },
|
|
277
|
+
# appium_lib: {
|
|
278
|
+
# listener: CustomListener.new,
|
|
279
|
+
# }
|
|
280
|
+
# @core = Appium::Core.for capabilities: capabilities, appium_lib: appium_lib
|
|
281
|
+
# @core.start_driver
|
|
282
|
+
#
|
|
273
283
|
def self.for(opts = {})
|
|
274
|
-
new(opts)
|
|
284
|
+
new.setup_for_new_session(opts)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Attach to an existing session. The main usage of this method is to attach to
|
|
288
|
+
# an existing session for debugging. The generated driver instance has the capabilities which
|
|
289
|
+
# has the given automationName and platformName only since the W3C WebDriver spec does not provide
|
|
290
|
+
# an endpoint to get running session's capabilities.
|
|
291
|
+
#
|
|
292
|
+
#
|
|
293
|
+
# @param [String] The session id to attach to.
|
|
294
|
+
# @param [String] url The WebDriver URL to attach to with the session_id.
|
|
295
|
+
# @param [String] automation_name The platform name to keep in the dummy capabilities
|
|
296
|
+
# @param [String] platform_name The automation name to keep in the dummy capabilities
|
|
297
|
+
# @return [Selenium::WebDriver] A new driver instance with the given session id.
|
|
298
|
+
#
|
|
299
|
+
# @example
|
|
300
|
+
#
|
|
301
|
+
# new_driver = ::Appium::Core::Driver.attach_to(
|
|
302
|
+
# driver.session_id, # The 'driver' has an existing session id
|
|
303
|
+
# url: 'http://127.0.0.1:4723/wd/hub', automation_name: 'UiAutomator2', platform_name: 'Android'
|
|
304
|
+
# )
|
|
305
|
+
# new_driver.page_source # for example
|
|
306
|
+
#
|
|
307
|
+
def self.attach_to(
|
|
308
|
+
session_id, url: nil, automation_name: nil, platform_name: nil,
|
|
309
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }
|
|
310
|
+
)
|
|
311
|
+
new.attach_to(
|
|
312
|
+
session_id,
|
|
313
|
+
automation_name: automation_name,
|
|
314
|
+
platform_name: platform_name,
|
|
315
|
+
url: url,
|
|
316
|
+
http_client_ops: http_client_ops
|
|
317
|
+
)
|
|
275
318
|
end
|
|
276
319
|
|
|
277
320
|
private
|
|
@@ -282,17 +325,19 @@ module Appium
|
|
|
282
325
|
@delegate_target
|
|
283
326
|
end
|
|
284
327
|
|
|
285
|
-
public
|
|
286
|
-
|
|
287
328
|
# @private
|
|
288
|
-
def initialize
|
|
329
|
+
def initialize
|
|
289
330
|
@delegate_target = self # for testing purpose
|
|
290
331
|
@automation_name = nil # initialise before 'set_automation_name'
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
public
|
|
291
335
|
|
|
292
|
-
|
|
293
|
-
|
|
336
|
+
# @private
|
|
337
|
+
# Set up for a new session
|
|
338
|
+
def setup_for_new_session(opts = {})
|
|
339
|
+
@custom_url = opts.delete :url # to set the custom url as :url
|
|
294
340
|
|
|
295
|
-
@custom_url = opts.delete :url
|
|
296
341
|
@caps = get_caps(opts)
|
|
297
342
|
|
|
298
343
|
set_appium_lib_specific_values(get_appium_lib_opts(opts))
|
|
@@ -301,8 +346,7 @@ module Appium
|
|
|
301
346
|
set_automation_name
|
|
302
347
|
|
|
303
348
|
extend_for(device: @device, automation_name: @automation_name)
|
|
304
|
-
|
|
305
|
-
self # rubocop:disable Lint/Void
|
|
349
|
+
self
|
|
306
350
|
end
|
|
307
351
|
|
|
308
352
|
# Creates a new global driver and quits the old one if it exists.
|
|
@@ -313,7 +357,7 @@ module Appium
|
|
|
313
357
|
# @option http_client_ops [Hash] :http_client Custom HTTP Client
|
|
314
358
|
# @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
|
|
315
359
|
# @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
|
|
316
|
-
# @return [Selenium::WebDriver]
|
|
360
|
+
# @return [Selenium::WebDriver] A new driver instance
|
|
317
361
|
#
|
|
318
362
|
# @example
|
|
319
363
|
#
|
|
@@ -324,7 +368,7 @@ module Appium
|
|
|
324
368
|
#
|
|
325
369
|
# # Start iOS driver
|
|
326
370
|
# opts = {
|
|
327
|
-
#
|
|
371
|
+
# capabilities: {
|
|
328
372
|
# platformName: :ios,
|
|
329
373
|
# platformVersion: '11.0',
|
|
330
374
|
# deviceName: 'iPhone Simulator',
|
|
@@ -350,7 +394,8 @@ module Appium
|
|
|
350
394
|
|
|
351
395
|
def start_driver(server_url: nil,
|
|
352
396
|
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
|
|
353
|
-
@custom_url ||=
|
|
397
|
+
@custom_url ||= "http://127.0.0.1:#{@port}/wd/hub"
|
|
398
|
+
@custom_url = server_url unless server_url.nil?
|
|
354
399
|
|
|
355
400
|
@http_client = get_http_client http_client: http_client_ops.delete(:http_client),
|
|
356
401
|
open_timeout: http_client_ops.delete(:open_timeout),
|
|
@@ -358,58 +403,85 @@ module Appium
|
|
|
358
403
|
|
|
359
404
|
if @enable_idempotency_header
|
|
360
405
|
if @http_client.instance_variable_defined? :@additional_headers
|
|
361
|
-
@http_client.
|
|
406
|
+
@http_client.set_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency], SecureRandom.uuid
|
|
362
407
|
else
|
|
363
408
|
::Appium::Logger.warn 'No additional_headers attribute in this http client instance'
|
|
364
409
|
end
|
|
365
410
|
end
|
|
366
411
|
|
|
367
412
|
begin
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
413
|
+
@driver = ::Appium::Core::Base::Driver.new(listener: @listener,
|
|
414
|
+
http_client: @http_client,
|
|
415
|
+
capabilities: @caps, # ::Appium::Core::Base::Capabilities
|
|
371
416
|
url: @custom_url,
|
|
372
|
-
|
|
417
|
+
wait_timeout: @wait_timeout,
|
|
418
|
+
wait_interval: @wait_interval)
|
|
373
419
|
|
|
374
420
|
if @direct_connect
|
|
375
421
|
d_c = DirectConnections.new(@driver.capabilities)
|
|
376
422
|
@driver.update_sending_request_to(protocol: d_c.protocol, host: d_c.host, port: d_c.port, path: d_c.path)
|
|
377
423
|
end
|
|
378
|
-
|
|
379
|
-
# export session
|
|
380
|
-
write_session_id(@driver.session_id, @export_session_path) if @export_session
|
|
381
424
|
rescue Errno::ECONNREFUSED
|
|
382
425
|
raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
|
383
426
|
end
|
|
384
427
|
|
|
385
428
|
if @http_client.instance_variable_defined? :@additional_headers
|
|
386
429
|
# We only need the key for a new session request. Should remove it for other following commands.
|
|
387
|
-
@http_client.
|
|
430
|
+
@http_client.delete_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
|
|
388
431
|
end
|
|
389
432
|
|
|
390
|
-
# If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
|
|
391
|
-
# Since @automation_name is set only client side before start_driver is called.
|
|
392
|
-
set_automation_name_if_nil
|
|
393
|
-
|
|
394
433
|
set_implicit_wait_by_default(@default_wait)
|
|
395
434
|
|
|
396
435
|
@driver
|
|
397
436
|
end
|
|
398
437
|
|
|
399
|
-
private
|
|
438
|
+
# @private
|
|
439
|
+
# Attach to an existing session
|
|
440
|
+
def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
|
|
441
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
|
|
442
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
|
|
443
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
|
|
444
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?
|
|
400
445
|
|
|
401
|
-
|
|
402
|
-
|
|
446
|
+
@custom_url = url
|
|
447
|
+
|
|
448
|
+
# use lowercase internally
|
|
449
|
+
@automation_name = convert_downcase(automation_name)
|
|
450
|
+
@device = convert_downcase(platform_name)
|
|
451
|
+
|
|
452
|
+
extend_for(device: @device, automation_name: @automation_name)
|
|
403
453
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
454
|
+
@http_client = get_http_client http_client: http_client_ops.delete(:http_client),
|
|
455
|
+
open_timeout: http_client_ops.delete(:open_timeout),
|
|
456
|
+
read_timeout: http_client_ops.delete(:read_timeout)
|
|
457
|
+
|
|
458
|
+
# Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
|
|
459
|
+
# the new session request, this it does not needed.
|
|
460
|
+
|
|
461
|
+
begin
|
|
462
|
+
# included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
|
|
463
|
+
@driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
|
|
464
|
+
url: @custom_url,
|
|
465
|
+
listener: @listener,
|
|
466
|
+
existing_session_id: session_id,
|
|
467
|
+
automation_name: automation_name,
|
|
468
|
+
platform_name: platform_name)
|
|
469
|
+
rescue Errno::ECONNREFUSED
|
|
470
|
+
raise ::Appium::Core::Error::SessionNotCreatedError,
|
|
471
|
+
"ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
|
472
|
+
end
|
|
407
473
|
|
|
408
|
-
|
|
474
|
+
@driver
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
|
|
478
|
+
http_client || Appium::Core::Base::Http::Default.new(open_timeout: open_timeout, read_timeout: read_timeout)
|
|
409
479
|
end
|
|
410
480
|
|
|
411
481
|
# Ignore setting default wait if the target driver has no implementation
|
|
412
482
|
def set_implicit_wait_by_default(wait)
|
|
483
|
+
return if @default_wait.nil?
|
|
484
|
+
|
|
413
485
|
@driver.manage.timeouts.implicit_wait = wait
|
|
414
486
|
rescue ::Selenium::WebDriver::Error::UnknownError => e
|
|
415
487
|
unless e.message.include?('The operation requested is not yet implemented')
|
|
@@ -420,22 +492,8 @@ module Appium
|
|
|
420
492
|
{}
|
|
421
493
|
end
|
|
422
494
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
# Quits the driver
|
|
426
|
-
# @return [void]
|
|
495
|
+
# Returns the server's version info. This method calls +driver.remote_status+ internally
|
|
427
496
|
#
|
|
428
|
-
# @example
|
|
429
|
-
#
|
|
430
|
-
# @core.quit_driver
|
|
431
|
-
#
|
|
432
|
-
def quit_driver
|
|
433
|
-
@driver.quit
|
|
434
|
-
rescue # rubocop:disable Style/RescueStandardError
|
|
435
|
-
nil
|
|
436
|
-
end
|
|
437
|
-
|
|
438
|
-
# Returns the server's version info
|
|
439
497
|
# @return [Hash]
|
|
440
498
|
#
|
|
441
499
|
# @example
|
|
@@ -449,58 +507,43 @@ module Appium
|
|
|
449
507
|
# }
|
|
450
508
|
# }
|
|
451
509
|
#
|
|
452
|
-
# Returns blank hash
|
|
510
|
+
# Returns blank hash in a case +driver.remote_status+ got an error
|
|
511
|
+
# such as Selenium Grid. It returns 500 error against 'remote_status'.
|
|
453
512
|
#
|
|
454
513
|
# @example
|
|
455
514
|
#
|
|
456
515
|
# @core.appium_server_version #=> {}
|
|
457
516
|
#
|
|
458
517
|
def appium_server_version
|
|
459
|
-
@driver.
|
|
460
|
-
rescue Selenium::WebDriver::Error::ServerError => e
|
|
461
|
-
raise ::Appium::Core::Error::ServerError unless e.message.include?('status code 500')
|
|
518
|
+
return {} if @driver.nil?
|
|
462
519
|
|
|
463
|
-
|
|
520
|
+
@driver.remote_status
|
|
521
|
+
rescue StandardError
|
|
522
|
+
# Ignore error case in a case the target appium server
|
|
523
|
+
# does not support `/status` API.
|
|
464
524
|
{}
|
|
465
525
|
end
|
|
466
526
|
|
|
467
|
-
|
|
468
|
-
# @return [Array<Integer>]
|
|
469
|
-
#
|
|
470
|
-
# @example
|
|
471
|
-
#
|
|
472
|
-
# @core.platform_version #=> [10,1,1]
|
|
473
|
-
#
|
|
474
|
-
def platform_version
|
|
475
|
-
p_version = @driver.capabilities['platformVersion'] || @driver.session_capabilities['platformVersion']
|
|
476
|
-
p_version.split('.').map(&:to_i)
|
|
477
|
-
end
|
|
527
|
+
private
|
|
478
528
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
#
|
|
486
|
-
# @core.screenshot '/tmp/hi.png' #=> nil
|
|
487
|
-
# # same as '@driver.save_screenshot png_save_path'
|
|
488
|
-
#
|
|
489
|
-
def screenshot(png_save_path)
|
|
490
|
-
::Appium::Logger.warn '[DEPRECATION] screenshot will be removed. Please use driver.save_screenshot instead.'
|
|
491
|
-
@driver.save_screenshot png_save_path
|
|
529
|
+
def convert_to_symbol(value)
|
|
530
|
+
if value.nil?
|
|
531
|
+
value
|
|
532
|
+
else
|
|
533
|
+
value.to_sym
|
|
534
|
+
end
|
|
492
535
|
end
|
|
493
536
|
|
|
494
|
-
private
|
|
495
|
-
|
|
496
537
|
# @private
|
|
497
538
|
def extend_for(device:, automation_name:) # rubocop:disable Metrics/CyclomaticComplexity
|
|
498
539
|
extend Appium::Core
|
|
499
540
|
extend Appium::Core::Device
|
|
500
541
|
|
|
501
|
-
|
|
542
|
+
sym_automation_name = convert_to_symbol(automation_name)
|
|
543
|
+
|
|
544
|
+
case convert_to_symbol(device)
|
|
502
545
|
when :android
|
|
503
|
-
case
|
|
546
|
+
case sym_automation_name
|
|
504
547
|
when :espresso
|
|
505
548
|
::Appium::Core::Android::Espresso::Bridge.for self
|
|
506
549
|
when :uiautomator2
|
|
@@ -511,28 +554,26 @@ module Appium
|
|
|
511
554
|
::Appium::Core::Android::Uiautomator1::Bridge.for self
|
|
512
555
|
end
|
|
513
556
|
when :ios, :tvos
|
|
514
|
-
case
|
|
557
|
+
case sym_automation_name
|
|
515
558
|
when :safari
|
|
516
559
|
::Appium::Logger.debug('SafariDriver for iOS')
|
|
517
|
-
|
|
560
|
+
else # XCUITest
|
|
518
561
|
::Appium::Core::Ios::Xcuitest::Bridge.for self
|
|
519
|
-
else # default and UIAutomation
|
|
520
|
-
::Appium::Core::Ios::Uiautomation::Bridge.for self
|
|
521
562
|
end
|
|
522
563
|
when :mac
|
|
523
|
-
case
|
|
564
|
+
case sym_automation_name
|
|
524
565
|
when :safari
|
|
525
566
|
::Appium::Logger.debug('SafariDriver for macOS')
|
|
526
567
|
when :gecko
|
|
527
568
|
::Appium::Logger.debug('Gecko Driver for macOS')
|
|
528
569
|
when :mac2
|
|
529
|
-
::Appium::
|
|
570
|
+
::Appium::Core::Mac2::Bridge.for self
|
|
530
571
|
else
|
|
531
572
|
# no Mac specific extentions
|
|
532
573
|
::Appium::Logger.debug('macOS Native')
|
|
533
574
|
end
|
|
534
575
|
when :windows
|
|
535
|
-
case
|
|
576
|
+
case sym_automation_name
|
|
536
577
|
when :gecko
|
|
537
578
|
::Appium::Logger.debug('Gecko Driver for Windows')
|
|
538
579
|
else
|
|
@@ -542,7 +583,7 @@ module Appium
|
|
|
542
583
|
# https://github.com/Samsung/appium-tizen-driver
|
|
543
584
|
::Appium::Logger.debug('tizen')
|
|
544
585
|
else
|
|
545
|
-
case
|
|
586
|
+
case sym_automation_name
|
|
546
587
|
when :youiengine
|
|
547
588
|
# https://github.com/YOU-i-Labs/appium-youiengine-driver
|
|
548
589
|
::Appium::Logger.debug('YouiEngine')
|
|
@@ -559,36 +600,9 @@ module Appium
|
|
|
559
600
|
self
|
|
560
601
|
end
|
|
561
602
|
|
|
562
|
-
# @private
|
|
563
|
-
def validate_keys(opts)
|
|
564
|
-
flatten_ops = flatten_hash_keys(opts)
|
|
565
|
-
|
|
566
|
-
# FIXME: Remove 'desired_capabilities' in the next major Selenium update
|
|
567
|
-
unless opts.member?(:caps) || opts.member?(:capabilities) || opts.member?(:desired_capabilities)
|
|
568
|
-
raise Error::NoCapabilityError
|
|
569
|
-
end
|
|
570
|
-
|
|
571
|
-
if !opts.member?(:appium_lib) && flatten_ops.member?(:appium_lib)
|
|
572
|
-
raise Error::CapabilityStructureError, 'Please check the value of appium_lib in the capability'
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
true
|
|
576
|
-
end
|
|
577
|
-
|
|
578
|
-
# @private
|
|
579
|
-
def flatten_hash_keys(hash, flatten_keys_result = [])
|
|
580
|
-
hash.each do |key, value|
|
|
581
|
-
flatten_keys_result << key
|
|
582
|
-
flatten_hash_keys(value, flatten_keys_result) if value.is_a?(Hash)
|
|
583
|
-
end
|
|
584
|
-
|
|
585
|
-
flatten_keys_result
|
|
586
|
-
end
|
|
587
|
-
|
|
588
603
|
# @private
|
|
589
604
|
def get_caps(opts)
|
|
590
|
-
|
|
591
|
-
Core::Base::Capabilities.create_capabilities(opts[:caps] || opts[:capabilities] || opts[:desired_capabilities] || {})
|
|
605
|
+
Core::Base::Capabilities.new(opts[:caps] || opts[:capabilities] || {})
|
|
592
606
|
end
|
|
593
607
|
|
|
594
608
|
# @private
|
|
@@ -596,21 +610,27 @@ module Appium
|
|
|
596
610
|
opts[:appium_lib] || {}
|
|
597
611
|
end
|
|
598
612
|
|
|
613
|
+
# @private
|
|
614
|
+
def get_app
|
|
615
|
+
get_cap 'app'
|
|
616
|
+
end
|
|
617
|
+
|
|
599
618
|
# @private
|
|
600
619
|
# Path to the .apk, .app or .app.zip.
|
|
601
620
|
# The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
|
|
602
621
|
# Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
|
|
603
622
|
def set_app_path
|
|
604
|
-
|
|
605
|
-
return
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
623
|
+
# FIXME: maybe `:app` should check `app` as well.
|
|
624
|
+
return unless @caps && get_app && !get_app.empty?
|
|
625
|
+
return if get_app =~ URI::DEFAULT_PARSER.make_regexp
|
|
626
|
+
|
|
627
|
+
app_path = File.expand_path(get_app)
|
|
628
|
+
@caps['app'] = if File.exist? app_path
|
|
629
|
+
app_path
|
|
630
|
+
else
|
|
631
|
+
::Appium::Logger.warn("Use #{get_app} directly since #{app_path} does not exist.")
|
|
632
|
+
get_app
|
|
633
|
+
end
|
|
614
634
|
end
|
|
615
635
|
|
|
616
636
|
# @private
|
|
@@ -623,9 +643,6 @@ module Appium
|
|
|
623
643
|
|
|
624
644
|
@default_wait = opts.default_wait
|
|
625
645
|
|
|
626
|
-
@export_session = opts.export_session
|
|
627
|
-
@export_session_path = opts.export_session_path
|
|
628
|
-
|
|
629
646
|
@port = opts.port
|
|
630
647
|
|
|
631
648
|
@wait_timeout = opts.wait_timeout
|
|
@@ -639,36 +656,30 @@ module Appium
|
|
|
639
656
|
# @private
|
|
640
657
|
def set_appium_device
|
|
641
658
|
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
|
642
|
-
@device =
|
|
659
|
+
@device = get_cap 'platformName'
|
|
643
660
|
return @device unless @device
|
|
644
661
|
|
|
645
|
-
@device = @device
|
|
662
|
+
@device = convert_to_symbol(convert_downcase(@device))
|
|
646
663
|
end
|
|
647
664
|
|
|
648
665
|
# @private
|
|
649
666
|
def set_automation_name
|
|
650
|
-
|
|
651
|
-
@automation_name = if
|
|
652
|
-
|
|
653
|
-
end
|
|
667
|
+
candidate = get_cap 'automationName'
|
|
668
|
+
@automation_name = candidate if candidate
|
|
669
|
+
@automation_name = convert_to_symbol(convert_downcase(@automation_name)) if @automation_name
|
|
654
670
|
end
|
|
655
671
|
|
|
656
672
|
# @private
|
|
657
|
-
def
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
@automation_name = if @driver.capabilities['automationName']
|
|
661
|
-
@driver.capabilities['automationName'].downcase.strip.intern
|
|
662
|
-
end
|
|
673
|
+
def convert_downcase(value)
|
|
674
|
+
value.is_a?(Symbol) ? value.downcase : value.downcase.strip.intern
|
|
663
675
|
end
|
|
664
676
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
nil
|
|
677
|
+
def get_cap(name)
|
|
678
|
+
name_with_prefix = "#{::Appium::Core::Base::Bridge::APPIUM_PREFIX}#{name}"
|
|
679
|
+
@caps[convert_to_symbol name] ||
|
|
680
|
+
@caps[name] ||
|
|
681
|
+
@caps[convert_to_symbol name_with_prefix] ||
|
|
682
|
+
@caps[name_with_prefix]
|
|
672
683
|
end
|
|
673
684
|
end # class Driver
|
|
674
685
|
end # module Core
|