appium_lib_core 4.1.0 → 8.0.2

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +316 -270
  3. data/README.md +65 -15
  4. data/Rakefile +5 -22
  5. data/appium_lib_core.gemspec +6 -9
  6. data/bin/console +0 -4
  7. data/lib/appium_lib_core/android/device/auth_finger_print.rb +4 -1
  8. data/lib/appium_lib_core/android/device/network.rb +10 -0
  9. data/lib/appium_lib_core/android/device/performance.rb +3 -0
  10. data/lib/appium_lib_core/android/device/screen.rb +2 -0
  11. data/lib/appium_lib_core/android/device.rb +80 -17
  12. data/lib/appium_lib_core/common/base/bridge.rb +309 -93
  13. data/lib/appium_lib_core/common/base/capabilities.rb +8 -9
  14. data/lib/appium_lib_core/common/{command/mjsonwp.rb → base/device_ime.rb} +33 -12
  15. data/lib/appium_lib_core/common/base/driver.rb +258 -331
  16. data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
  17. data/lib/appium_lib_core/common/base/has_location.rb +80 -0
  18. data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
  19. data/lib/appium_lib_core/common/base/http_default.rb +15 -38
  20. data/lib/appium_lib_core/{ios/uiautomation/bridge.rb → common/base/remote_status.rb} +9 -8
  21. data/lib/appium_lib_core/common/base/rotable.rb +62 -0
  22. data/lib/appium_lib_core/common/base/screenshot.rb +8 -8
  23. data/lib/appium_lib_core/common/base/search_context.rb +13 -17
  24. data/lib/appium_lib_core/common/base.rb +1 -5
  25. data/lib/appium_lib_core/common/command.rb +244 -4
  26. data/lib/appium_lib_core/common/device/app_management.rb +2 -26
  27. data/lib/appium_lib_core/common/device/context.rb +1 -5
  28. data/lib/appium_lib_core/common/device/image_comparison.rb +12 -4
  29. data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
  30. data/lib/appium_lib_core/common/device/{touch_actions.rb → orientation.rb} +6 -10
  31. data/lib/appium_lib_core/common/error.rb +4 -5
  32. data/lib/appium_lib_core/common/log.rb +5 -4
  33. data/lib/appium_lib_core/common/wait.rb +38 -6
  34. data/lib/appium_lib_core/device.rb +3 -9
  35. data/lib/appium_lib_core/driver.rb +182 -148
  36. data/lib/appium_lib_core/{patch.rb → element.rb} +62 -25
  37. data/lib/appium_lib_core/ios/xcuitest/device.rb +2 -0
  38. data/lib/appium_lib_core/{common/base/command.rb → mac2/bridge.rb} +9 -8
  39. data/lib/appium_lib_core/mac2/device/screen.rb +48 -0
  40. data/lib/appium_lib_core/mac2/device.rb +92 -0
  41. data/lib/appium_lib_core/{ios.rb → mac2.rb} +2 -5
  42. data/lib/appium_lib_core/support/event_firing_bridge.rb +57 -0
  43. data/lib/appium_lib_core/version.rb +2 -2
  44. data/lib/appium_lib_core.rb +21 -10
  45. metadata +28 -94
  46. data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
  47. data/.github/contributing.md +0 -26
  48. data/.github/issue_template.md +0 -20
  49. data/.github/workflows/unittest.yml +0 -68
  50. data/.gitignore +0 -18
  51. data/.rubocop.yml +0 -58
  52. data/azure-pipelines.yml +0 -15
  53. data/ci-jobs/functional/android_setup.yml +0 -3
  54. data/ci-jobs/functional/ios_setup.yml +0 -7
  55. data/ci-jobs/functional/publish_test_result.yml +0 -18
  56. data/ci-jobs/functional/run_appium.yml +0 -25
  57. data/ci-jobs/functional/start-emulator.sh +0 -26
  58. data/ci-jobs/functional_test.yml +0 -298
  59. data/docs/mobile_command.md +0 -34
  60. data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
  61. data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
  62. data/lib/appium_lib_core/common/command/common.rb +0 -110
  63. data/lib/appium_lib_core/common/command/w3c.rb +0 -56
  64. data/lib/appium_lib_core/common/device/value.rb +0 -52
  65. data/lib/appium_lib_core/common/touch_action/multi_touch.rb +0 -56
  66. data/lib/appium_lib_core/common/touch_action/touch_actions.rb +0 -203
  67. data/lib/appium_lib_core/ios/uiautomation/device.rb +0 -44
  68. data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
  69. data/release_notes.md +0 -816
  70. 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, :export_session, :export_session_path,
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, Driver::DEFAULT_IMPLICIT_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
- # bump current session id into a particular file
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::Comm.wait/wait_true
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
- # Ignore them if this parameter is <code>false</code>. Defaults to false.
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:' or 'desired_capabilities:' is also available instead of 'caps:'.
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
- # # 'desired_capabilities:' or 'capabilities:' is also available instead of 'caps:'. Either is fine.
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
- # desired_capabilities: {
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,25 @@ module Appium
282
325
  @delegate_target
283
326
  end
284
327
 
285
- public
286
-
287
328
  # @private
288
- def initialize(opts = {})
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
- opts = Appium.symbolize_keys opts
293
- validate_keys(opts)
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
340
+
341
+ # TODO: Remove when we implement Options
342
+ # The symbolize_keys is to keep compatiility for the legacy code, which allows capabilities to give 'string' as the key.
343
+ # The toplevel `caps`, `capabilities` and `appium_lib` are expected to be symbol.
344
+ # FIXME: First, please try to remove `nested: true` to `nested: false`.
345
+ opts = Appium.symbolize_keys(opts, nested: true)
294
346
 
295
- @custom_url = opts.delete :url
296
347
  @caps = get_caps(opts)
297
348
 
298
349
  set_appium_lib_specific_values(get_appium_lib_opts(opts))
@@ -301,8 +352,7 @@ module Appium
301
352
  set_automation_name
302
353
 
303
354
  extend_for(device: @device, automation_name: @automation_name)
304
-
305
- self # rubocop:disable Lint/Void
355
+ self
306
356
  end
307
357
 
308
358
  # Creates a new global driver and quits the old one if it exists.
@@ -313,7 +363,7 @@ module Appium
313
363
  # @option http_client_ops [Hash] :http_client Custom HTTP Client
314
364
  # @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
315
365
  # @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
316
- # @return [Selenium::WebDriver] the new global driver
366
+ # @return [Selenium::WebDriver] A new driver instance
317
367
  #
318
368
  # @example
319
369
  #
@@ -324,7 +374,7 @@ module Appium
324
374
  #
325
375
  # # Start iOS driver
326
376
  # opts = {
327
- # caps: {
377
+ # capabilities: {
328
378
  # platformName: :ios,
329
379
  # platformVersion: '11.0',
330
380
  # deviceName: 'iPhone Simulator',
@@ -365,19 +415,17 @@ module Appium
365
415
  end
366
416
 
367
417
  begin
368
- # included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
369
- @driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
370
- desired_capabilities: @caps,
418
+ @driver = ::Appium::Core::Base::Driver.new(listener: @listener,
419
+ http_client: @http_client,
420
+ capabilities: @caps, # ::Appium::Core::Base::Capabilities
371
421
  url: @custom_url,
372
- listener: @listener)
422
+ wait_timeout: @wait_timeout,
423
+ wait_interval: @wait_interval)
373
424
 
374
425
  if @direct_connect
375
426
  d_c = DirectConnections.new(@driver.capabilities)
376
427
  @driver.update_sending_request_to(protocol: d_c.protocol, host: d_c.host, port: d_c.port, path: d_c.path)
377
428
  end
378
-
379
- # export session
380
- write_session_id(@driver.session_id, @export_session_path) if @export_session
381
429
  rescue Errno::ECONNREFUSED
382
430
  raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
383
431
  end
@@ -387,6 +435,8 @@ module Appium
387
435
  @http_client.additional_headers.delete Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
388
436
  end
389
437
 
438
+ # TODO: this method can be removed after releasing Appium 2.0, and after a while
439
+ # since Appium 2.0 reuqires 'automationName'. This method won't help anymore then.
390
440
  # If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
391
441
  # Since @automation_name is set only client side before start_driver is called.
392
442
  set_automation_name_if_nil
@@ -396,20 +446,53 @@ module Appium
396
446
  @driver
397
447
  end
398
448
 
399
- private
449
+ # @private
450
+ # Attach to an existing session
451
+ def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
452
+ http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
400
453
 
401
- def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
402
- client = http_client || Appium::Core::Base::Http::Default.new
454
+ raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
455
+ raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
456
+ raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?
457
+
458
+ @custom_url = url
459
+
460
+ # use lowercase internally
461
+ @automation_name = convert_downcase(automation_name)
462
+ @device = convert_downcase(platform_name)
463
+
464
+ extend_for(device: @device, automation_name: @automation_name)
403
465
 
404
- # open_timeout and read_timeout are explicit wait.
405
- client.open_timeout = open_timeout if open_timeout
406
- client.read_timeout = read_timeout if read_timeout
466
+ @http_client = get_http_client http_client: http_client_ops.delete(:http_client),
467
+ open_timeout: http_client_ops.delete(:open_timeout),
468
+ read_timeout: http_client_ops.delete(:read_timeout)
469
+
470
+ # Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
471
+ # the new session request, this it does not needed.
472
+
473
+ begin
474
+ # included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
475
+ @driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
476
+ url: @custom_url,
477
+ listener: @listener,
478
+ existing_session_id: session_id,
479
+ automation_name: automation_name,
480
+ platform_name: platform_name)
481
+ rescue Errno::ECONNREFUSED
482
+ raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
483
+ end
407
484
 
408
- client
485
+ @driver
486
+ end
487
+
488
+ def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
489
+ http_client || Appium::Core::Base::Http::Default.new(open_timeout: open_timeout, read_timeout: read_timeout)
409
490
  end
410
491
 
411
492
  # Ignore setting default wait if the target driver has no implementation
412
493
  def set_implicit_wait_by_default(wait)
494
+ return if @default_wait.nil?
495
+
413
496
  @driver.manage.timeouts.implicit_wait = wait
414
497
  rescue ::Selenium::WebDriver::Error::UnknownError => e
415
498
  unless e.message.include?('The operation requested is not yet implemented')
@@ -420,9 +503,7 @@ module Appium
420
503
  {}
421
504
  end
422
505
 
423
- public
424
-
425
- # Quits the driver
506
+ # [Deprecated] Quits the driver. This method is the same as @driver.quit
426
507
  # @return [void]
427
508
  #
428
509
  # @example
@@ -430,12 +511,14 @@ module Appium
430
511
  # @core.quit_driver
431
512
  #
432
513
  def quit_driver
514
+ ::Appium::Logger.warn('[DEPRECATION] quit_driver will be removed. Please use @driver.quit instead.')
433
515
  @driver.quit
434
516
  rescue # rubocop:disable Style/RescueStandardError
435
517
  nil
436
518
  end
437
519
 
438
- # Returns the server's version info
520
+ # Returns the server's version info. This method calls +driver.remote_status+ internally
521
+ #
439
522
  # @return [Hash]
440
523
  #
441
524
  # @example
@@ -449,58 +532,43 @@ module Appium
449
532
  # }
450
533
  # }
451
534
  #
452
- # Returns blank hash for Selenium Grid since 'remote_status' gets 500 error
535
+ # Returns blank hash in a case +driver.remote_status+ got an error
536
+ # such as Selenium Grid. It returns 500 error against 'remote_status'.
453
537
  #
454
538
  # @example
455
539
  #
456
540
  # @core.appium_server_version #=> {}
457
541
  #
458
542
  def appium_server_version
459
- @driver.remote_status
460
- rescue Selenium::WebDriver::Error::ServerError => e
461
- raise ::Appium::Core::Error::ServerError unless e.message.include?('status code 500')
543
+ return {} if @driver.nil?
462
544
 
463
- # driver.remote_status returns 500 error for using selenium grid
545
+ @driver.remote_status
546
+ rescue StandardError
547
+ # Ignore error case in a case the target appium server
548
+ # does not support `/status` API.
464
549
  {}
465
550
  end
466
551
 
467
- # Return the platform version as an array of integers
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
552
+ private
478
553
 
479
- # Takes a png screenshot and saves to the target path.
480
- #
481
- # @param png_save_path [String] the full path to save the png
482
- # @return [File]
483
- #
484
- # @example
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
554
+ def convert_to_symbol(value)
555
+ if value.nil?
556
+ value
557
+ else
558
+ value.to_sym
559
+ end
492
560
  end
493
561
 
494
- private
495
-
496
562
  # @private
497
563
  def extend_for(device:, automation_name:) # rubocop:disable Metrics/CyclomaticComplexity
498
564
  extend Appium::Core
499
565
  extend Appium::Core::Device
500
566
 
501
- case device
567
+ sym_automation_name = convert_to_symbol(automation_name)
568
+
569
+ case convert_to_symbol(device)
502
570
  when :android
503
- case automation_name
571
+ case sym_automation_name
504
572
  when :espresso
505
573
  ::Appium::Core::Android::Espresso::Bridge.for self
506
574
  when :uiautomator2
@@ -511,28 +579,26 @@ module Appium
511
579
  ::Appium::Core::Android::Uiautomator1::Bridge.for self
512
580
  end
513
581
  when :ios, :tvos
514
- case automation_name
582
+ case sym_automation_name
515
583
  when :safari
516
584
  ::Appium::Logger.debug('SafariDriver for iOS')
517
- when :xcuitest
585
+ else # XCUITest
518
586
  ::Appium::Core::Ios::Xcuitest::Bridge.for self
519
- else # default and UIAutomation
520
- ::Appium::Core::Ios::Uiautomation::Bridge.for self
521
587
  end
522
588
  when :mac
523
- case automation_name
589
+ case sym_automation_name
524
590
  when :safari
525
591
  ::Appium::Logger.debug('SafariDriver for macOS')
526
592
  when :gecko
527
593
  ::Appium::Logger.debug('Gecko Driver for macOS')
528
594
  when :mac2
529
- ::Appium::Logger.debug('macOS XCUITest')
595
+ ::Appium::Core::Mac2::Bridge.for self
530
596
  else
531
597
  # no Mac specific extentions
532
598
  ::Appium::Logger.debug('macOS Native')
533
599
  end
534
600
  when :windows
535
- case automation_name
601
+ case sym_automation_name
536
602
  when :gecko
537
603
  ::Appium::Logger.debug('Gecko Driver for Windows')
538
604
  else
@@ -542,7 +608,7 @@ module Appium
542
608
  # https://github.com/Samsung/appium-tizen-driver
543
609
  ::Appium::Logger.debug('tizen')
544
610
  else
545
- case automation_name
611
+ case sym_automation_name
546
612
  when :youiengine
547
613
  # https://github.com/YOU-i-Labs/appium-youiengine-driver
548
614
  ::Appium::Logger.debug('YouiEngine')
@@ -559,36 +625,9 @@ module Appium
559
625
  self
560
626
  end
561
627
 
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
628
  # @private
589
629
  def get_caps(opts)
590
- # FIXME: Remove 'desired_capabilities' in the next major Selenium update
591
- Core::Base::Capabilities.create_capabilities(opts[:caps] || opts[:capabilities] || opts[:desired_capabilities] || {})
630
+ Core::Base::Capabilities.new(opts[:caps] || opts[:capabilities] || {})
592
631
  end
593
632
 
594
633
  # @private
@@ -601,6 +640,7 @@ module Appium
601
640
  # The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
602
641
  # Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
603
642
  def set_app_path
643
+ # FIXME: maybe `:app` should check `app` as well.
604
644
  return unless @caps && @caps[:app] && !@caps[:app].empty?
605
645
  return if @caps[:app] =~ URI::DEFAULT_PARSER.make_regexp
606
646
 
@@ -623,9 +663,6 @@ module Appium
623
663
 
624
664
  @default_wait = opts.default_wait
625
665
 
626
- @export_session = opts.export_session
627
- @export_session_path = opts.export_session_path
628
-
629
666
  @port = opts.port
630
667
 
631
668
  @wait_timeout = opts.wait_timeout
@@ -639,18 +676,24 @@ module Appium
639
676
  # @private
640
677
  def set_appium_device
641
678
  # https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
642
- @device = @caps[:platformName]
679
+ # TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
680
+ @device = @caps[:platformName] || @caps['platformName']
643
681
  return @device unless @device
644
682
 
645
- @device = @device.is_a?(Symbol) ? @device.downcase : @device.downcase.strip.intern
683
+ @device = convert_downcase @device
646
684
  end
647
685
 
648
686
  # @private
649
687
  def set_automation_name
650
- @automation_name = @caps[:automationName] if @caps[:automationName]
651
- @automation_name = if @automation_name
652
- @automation_name.is_a?(Symbol) ? @automation_name.downcase : @automation_name.downcase.strip.intern
653
- end
688
+ # TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
689
+ candidate = @caps[:automationName] || @caps['automationName']
690
+ @automation_name = candidate if candidate
691
+ @automation_name = convert_downcase @automation_name if @automation_name
692
+ end
693
+
694
+ # @private
695
+ def convert_downcase(value)
696
+ value.is_a?(Symbol) ? value.downcase : value.downcase.strip.intern
654
697
  end
655
698
 
656
699
  # @private
@@ -661,15 +704,6 @@ module Appium
661
704
  @driver.capabilities['automationName'].downcase.strip.intern
662
705
  end
663
706
  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
707
  end # class Driver
674
708
  end # module Core
675
709
  end # module Appium