appium_lib_core 4.1.0 → 9.1.1
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 +337 -270
- data/README.md +65 -15
- data/Rakefile +5 -22
- data/appium_lib_core.gemspec +12 -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/network.rb +10 -0
- data/lib/appium_lib_core/android/device/performance.rb +3 -0
- data/lib/appium_lib_core/android/device/screen.rb +2 -0
- data/lib/appium_lib_core/android/device.rb +80 -17
- 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 +263 -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 +8 -8
- 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 +12 -4
- 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/error.rb +4 -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 +191 -160
- data/lib/appium_lib_core/{patch.rb → element.rb} +64 -26
- 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
- metadata +53 -118
- 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,10 +94,10 @@ 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
|
|
@@ -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
|
291
333
|
|
292
|
-
|
293
|
-
|
334
|
+
public
|
335
|
+
|
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',
|
@@ -358,35 +402,35 @@ module Appium
|
|
358
402
|
|
359
403
|
if @enable_idempotency_header
|
360
404
|
if @http_client.instance_variable_defined? :@additional_headers
|
361
|
-
@http_client.
|
405
|
+
@http_client.set_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency], SecureRandom.uuid
|
362
406
|
else
|
363
407
|
::Appium::Logger.warn 'No additional_headers attribute in this http client instance'
|
364
408
|
end
|
365
409
|
end
|
366
410
|
|
367
411
|
begin
|
368
|
-
|
369
|
-
|
370
|
-
|
412
|
+
@driver = ::Appium::Core::Base::Driver.new(listener: @listener,
|
413
|
+
http_client: @http_client,
|
414
|
+
capabilities: @caps, # ::Appium::Core::Base::Capabilities
|
371
415
|
url: @custom_url,
|
372
|
-
|
416
|
+
wait_timeout: @wait_timeout,
|
417
|
+
wait_interval: @wait_interval)
|
373
418
|
|
374
419
|
if @direct_connect
|
375
420
|
d_c = DirectConnections.new(@driver.capabilities)
|
376
421
|
@driver.update_sending_request_to(protocol: d_c.protocol, host: d_c.host, port: d_c.port, path: d_c.path)
|
377
422
|
end
|
378
|
-
|
379
|
-
# export session
|
380
|
-
write_session_id(@driver.session_id, @export_session_path) if @export_session
|
381
423
|
rescue Errno::ECONNREFUSED
|
382
424
|
raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
383
425
|
end
|
384
426
|
|
385
427
|
if @http_client.instance_variable_defined? :@additional_headers
|
386
428
|
# We only need the key for a new session request. Should remove it for other following commands.
|
387
|
-
@http_client.
|
429
|
+
@http_client.delete_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
|
388
430
|
end
|
389
431
|
|
432
|
+
# TODO: this method can be removed after releasing Appium 2.0, and after a while
|
433
|
+
# since Appium 2.0 reuqires 'automationName'. This method won't help anymore then.
|
390
434
|
# If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
|
391
435
|
# Since @automation_name is set only client side before start_driver is called.
|
392
436
|
set_automation_name_if_nil
|
@@ -396,20 +440,53 @@ module Appium
|
|
396
440
|
@driver
|
397
441
|
end
|
398
442
|
|
399
|
-
private
|
443
|
+
# @private
|
444
|
+
# Attach to an existing session
|
445
|
+
def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
|
446
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
|
400
447
|
|
401
|
-
|
402
|
-
|
448
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
|
449
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
|
450
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?
|
403
451
|
|
404
|
-
|
405
|
-
client.open_timeout = open_timeout if open_timeout
|
406
|
-
client.read_timeout = read_timeout if read_timeout
|
452
|
+
@custom_url = url
|
407
453
|
|
408
|
-
|
454
|
+
# use lowercase internally
|
455
|
+
@automation_name = convert_downcase(automation_name)
|
456
|
+
@device = convert_downcase(platform_name)
|
457
|
+
|
458
|
+
extend_for(device: @device, automation_name: @automation_name)
|
459
|
+
|
460
|
+
@http_client = get_http_client http_client: http_client_ops.delete(:http_client),
|
461
|
+
open_timeout: http_client_ops.delete(:open_timeout),
|
462
|
+
read_timeout: http_client_ops.delete(:read_timeout)
|
463
|
+
|
464
|
+
# Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
|
465
|
+
# the new session request, this it does not needed.
|
466
|
+
|
467
|
+
begin
|
468
|
+
# included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
|
469
|
+
@driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
|
470
|
+
url: @custom_url,
|
471
|
+
listener: @listener,
|
472
|
+
existing_session_id: session_id,
|
473
|
+
automation_name: automation_name,
|
474
|
+
platform_name: platform_name)
|
475
|
+
rescue Errno::ECONNREFUSED
|
476
|
+
raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
477
|
+
end
|
478
|
+
|
479
|
+
@driver
|
480
|
+
end
|
481
|
+
|
482
|
+
def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
|
483
|
+
http_client || Appium::Core::Base::Http::Default.new(open_timeout: open_timeout, read_timeout: read_timeout)
|
409
484
|
end
|
410
485
|
|
411
486
|
# Ignore setting default wait if the target driver has no implementation
|
412
487
|
def set_implicit_wait_by_default(wait)
|
488
|
+
return if @default_wait.nil?
|
489
|
+
|
413
490
|
@driver.manage.timeouts.implicit_wait = wait
|
414
491
|
rescue ::Selenium::WebDriver::Error::UnknownError => e
|
415
492
|
unless e.message.include?('The operation requested is not yet implemented')
|
@@ -420,9 +497,7 @@ module Appium
|
|
420
497
|
{}
|
421
498
|
end
|
422
499
|
|
423
|
-
|
424
|
-
|
425
|
-
# Quits the driver
|
500
|
+
# [Deprecated] Quits the driver. This method is the same as @driver.quit
|
426
501
|
# @return [void]
|
427
502
|
#
|
428
503
|
# @example
|
@@ -430,12 +505,14 @@ module Appium
|
|
430
505
|
# @core.quit_driver
|
431
506
|
#
|
432
507
|
def quit_driver
|
508
|
+
::Appium::Logger.warn('[DEPRECATION] quit_driver will be removed. Please use @driver.quit instead.')
|
433
509
|
@driver.quit
|
434
510
|
rescue # rubocop:disable Style/RescueStandardError
|
435
511
|
nil
|
436
512
|
end
|
437
513
|
|
438
|
-
# Returns the server's version info
|
514
|
+
# Returns the server's version info. This method calls +driver.remote_status+ internally
|
515
|
+
#
|
439
516
|
# @return [Hash]
|
440
517
|
#
|
441
518
|
# @example
|
@@ -449,58 +526,43 @@ module Appium
|
|
449
526
|
# }
|
450
527
|
# }
|
451
528
|
#
|
452
|
-
# Returns blank hash
|
529
|
+
# Returns blank hash in a case +driver.remote_status+ got an error
|
530
|
+
# such as Selenium Grid. It returns 500 error against 'remote_status'.
|
453
531
|
#
|
454
532
|
# @example
|
455
533
|
#
|
456
534
|
# @core.appium_server_version #=> {}
|
457
535
|
#
|
458
536
|
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')
|
537
|
+
return {} if @driver.nil?
|
462
538
|
|
463
|
-
|
539
|
+
@driver.remote_status
|
540
|
+
rescue StandardError
|
541
|
+
# Ignore error case in a case the target appium server
|
542
|
+
# does not support `/status` API.
|
464
543
|
{}
|
465
544
|
end
|
466
545
|
|
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
|
546
|
+
private
|
478
547
|
|
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
|
548
|
+
def convert_to_symbol(value)
|
549
|
+
if value.nil?
|
550
|
+
value
|
551
|
+
else
|
552
|
+
value.to_sym
|
553
|
+
end
|
492
554
|
end
|
493
555
|
|
494
|
-
private
|
495
|
-
|
496
556
|
# @private
|
497
557
|
def extend_for(device:, automation_name:) # rubocop:disable Metrics/CyclomaticComplexity
|
498
558
|
extend Appium::Core
|
499
559
|
extend Appium::Core::Device
|
500
560
|
|
501
|
-
|
561
|
+
sym_automation_name = convert_to_symbol(automation_name)
|
562
|
+
|
563
|
+
case convert_to_symbol(device)
|
502
564
|
when :android
|
503
|
-
case
|
565
|
+
case sym_automation_name
|
504
566
|
when :espresso
|
505
567
|
::Appium::Core::Android::Espresso::Bridge.for self
|
506
568
|
when :uiautomator2
|
@@ -511,28 +573,26 @@ module Appium
|
|
511
573
|
::Appium::Core::Android::Uiautomator1::Bridge.for self
|
512
574
|
end
|
513
575
|
when :ios, :tvos
|
514
|
-
case
|
576
|
+
case sym_automation_name
|
515
577
|
when :safari
|
516
578
|
::Appium::Logger.debug('SafariDriver for iOS')
|
517
|
-
|
579
|
+
else # XCUITest
|
518
580
|
::Appium::Core::Ios::Xcuitest::Bridge.for self
|
519
|
-
else # default and UIAutomation
|
520
|
-
::Appium::Core::Ios::Uiautomation::Bridge.for self
|
521
581
|
end
|
522
582
|
when :mac
|
523
|
-
case
|
583
|
+
case sym_automation_name
|
524
584
|
when :safari
|
525
585
|
::Appium::Logger.debug('SafariDriver for macOS')
|
526
586
|
when :gecko
|
527
587
|
::Appium::Logger.debug('Gecko Driver for macOS')
|
528
588
|
when :mac2
|
529
|
-
::Appium::
|
589
|
+
::Appium::Core::Mac2::Bridge.for self
|
530
590
|
else
|
531
591
|
# no Mac specific extentions
|
532
592
|
::Appium::Logger.debug('macOS Native')
|
533
593
|
end
|
534
594
|
when :windows
|
535
|
-
case
|
595
|
+
case sym_automation_name
|
536
596
|
when :gecko
|
537
597
|
::Appium::Logger.debug('Gecko Driver for Windows')
|
538
598
|
else
|
@@ -542,7 +602,7 @@ module Appium
|
|
542
602
|
# https://github.com/Samsung/appium-tizen-driver
|
543
603
|
::Appium::Logger.debug('tizen')
|
544
604
|
else
|
545
|
-
case
|
605
|
+
case sym_automation_name
|
546
606
|
when :youiengine
|
547
607
|
# https://github.com/YOU-i-Labs/appium-youiengine-driver
|
548
608
|
::Appium::Logger.debug('YouiEngine')
|
@@ -559,36 +619,9 @@ module Appium
|
|
559
619
|
self
|
560
620
|
end
|
561
621
|
|
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
622
|
# @private
|
589
623
|
def get_caps(opts)
|
590
|
-
|
591
|
-
Core::Base::Capabilities.create_capabilities(opts[:caps] || opts[:capabilities] || opts[:desired_capabilities] || {})
|
624
|
+
Core::Base::Capabilities.new(opts[:caps] || opts[:capabilities] || {})
|
592
625
|
end
|
593
626
|
|
594
627
|
# @private
|
@@ -596,21 +629,27 @@ module Appium
|
|
596
629
|
opts[:appium_lib] || {}
|
597
630
|
end
|
598
631
|
|
632
|
+
# @private
|
633
|
+
def get_app
|
634
|
+
@caps[:app] || @caps['app']
|
635
|
+
end
|
636
|
+
|
599
637
|
# @private
|
600
638
|
# Path to the .apk, .app or .app.zip.
|
601
639
|
# The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
|
602
640
|
# Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
|
603
641
|
def set_app_path
|
604
|
-
|
605
|
-
return
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
642
|
+
# FIXME: maybe `:app` should check `app` as well.
|
643
|
+
return unless @caps && get_app && !get_app.empty?
|
644
|
+
return if get_app =~ URI::DEFAULT_PARSER.make_regexp
|
645
|
+
|
646
|
+
app_path = File.expand_path(get_app)
|
647
|
+
@caps['app'] = if File.exist? app_path
|
648
|
+
app_path
|
649
|
+
else
|
650
|
+
::Appium::Logger.warn("Use #{get_app} directly since #{app_path} does not exist.")
|
651
|
+
get_app
|
652
|
+
end
|
614
653
|
end
|
615
654
|
|
616
655
|
# @private
|
@@ -623,9 +662,6 @@ module Appium
|
|
623
662
|
|
624
663
|
@default_wait = opts.default_wait
|
625
664
|
|
626
|
-
@export_session = opts.export_session
|
627
|
-
@export_session_path = opts.export_session_path
|
628
|
-
|
629
665
|
@port = opts.port
|
630
666
|
|
631
667
|
@wait_timeout = opts.wait_timeout
|
@@ -639,18 +675,22 @@ module Appium
|
|
639
675
|
# @private
|
640
676
|
def set_appium_device
|
641
677
|
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
642
|
-
@device = @caps[:platformName]
|
678
|
+
@device = @caps[:platformName] || @caps['platformName']
|
643
679
|
return @device unless @device
|
644
680
|
|
645
|
-
@device =
|
681
|
+
@device = convert_downcase @device
|
646
682
|
end
|
647
683
|
|
648
684
|
# @private
|
649
685
|
def set_automation_name
|
650
|
-
|
651
|
-
@automation_name = if
|
652
|
-
|
653
|
-
|
686
|
+
candidate = @caps[:automationName] || @caps['automationName']
|
687
|
+
@automation_name = candidate if candidate
|
688
|
+
@automation_name = convert_downcase @automation_name if @automation_name
|
689
|
+
end
|
690
|
+
|
691
|
+
# @private
|
692
|
+
def convert_downcase(value)
|
693
|
+
value.is_a?(Symbol) ? value.downcase : value.downcase.strip.intern
|
654
694
|
end
|
655
695
|
|
656
696
|
# @private
|
@@ -661,15 +701,6 @@ module Appium
|
|
661
701
|
@driver.capabilities['automationName'].downcase.strip.intern
|
662
702
|
end
|
663
703
|
end
|
664
|
-
|
665
|
-
# @private
|
666
|
-
def write_session_id(session_id, export_path = '/tmp/appium_lib_session')
|
667
|
-
export_path = export_path.tr('/', '\\') if ::Appium::Core::Base.platform.windows?
|
668
|
-
File.write(export_path, session_id)
|
669
|
-
rescue IOError => e
|
670
|
-
::Appium::Logger.warn e
|
671
|
-
nil
|
672
|
-
end
|
673
704
|
end # class Driver
|
674
705
|
end # module Core
|
675
706
|
end # module Appium
|