appium_lib_core 4.1.0 → 6.2.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +207 -272
  3. data/README.md +41 -14
  4. data/Rakefile +4 -0
  5. data/appium_lib_core.gemspec +5 -8
  6. data/bin/console +0 -4
  7. data/lib/appium_lib_core/android/device/auth_finger_print.rb +2 -1
  8. data/lib/appium_lib_core/android/device.rb +4 -4
  9. data/lib/appium_lib_core/common/base/bridge.rb +311 -90
  10. data/lib/appium_lib_core/common/base/capabilities.rb +8 -9
  11. data/lib/appium_lib_core/common/base/device_ime.rb +49 -0
  12. data/lib/appium_lib_core/common/base/driver.rb +222 -187
  13. data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
  14. data/lib/appium_lib_core/common/base/has_location.rb +80 -0
  15. data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
  16. data/lib/appium_lib_core/common/base/http_default.rb +1 -3
  17. data/lib/appium_lib_core/{ios/uiautomation/bridge.rb → common/base/remote_status.rb} +9 -8
  18. data/lib/appium_lib_core/common/base/rotable.rb +54 -0
  19. data/lib/appium_lib_core/common/base/screenshot.rb +6 -6
  20. data/lib/appium_lib_core/common/base/search_context.rb +20 -6
  21. data/lib/appium_lib_core/common/base.rb +1 -3
  22. data/lib/appium_lib_core/common/command.rb +259 -4
  23. data/lib/appium_lib_core/common/device/app_management.rb +8 -14
  24. data/lib/appium_lib_core/common/device/image_comparison.rb +12 -4
  25. data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
  26. data/lib/appium_lib_core/common/{command/mjsonwp.rb → device/orientation.rb} +14 -11
  27. data/lib/appium_lib_core/common/device/touch_actions.rb +2 -0
  28. data/lib/appium_lib_core/common/device/value.rb +6 -8
  29. data/lib/appium_lib_core/common/error.rb +4 -5
  30. data/lib/appium_lib_core/common/log.rb +4 -1
  31. data/lib/appium_lib_core/common/touch_action/multi_touch.rb +19 -0
  32. data/lib/appium_lib_core/common/touch_action/touch_actions.rb +16 -2
  33. data/lib/appium_lib_core/common/wait.rb +38 -6
  34. data/lib/appium_lib_core/device.rb +1 -5
  35. data/lib/appium_lib_core/driver.rb +177 -102
  36. data/lib/appium_lib_core/{patch.rb → element.rb} +66 -9
  37. data/lib/appium_lib_core/{common/base/command.rb → mac2/bridge.rb} +9 -8
  38. data/lib/appium_lib_core/mac2/device/screen.rb +48 -0
  39. data/lib/appium_lib_core/mac2/device.rb +92 -0
  40. data/lib/appium_lib_core/{ios.rb → mac2.rb} +2 -5
  41. data/lib/appium_lib_core/version.rb +2 -2
  42. data/lib/appium_lib_core/windows/device/app_management.rb +38 -0
  43. data/lib/appium_lib_core/windows/device.rb +2 -0
  44. data/lib/appium_lib_core.rb +20 -10
  45. metadata +27 -83
  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/ios/uiautomation/device.rb +0 -44
  65. data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
  66. data/release_notes.md +0 -816
  67. data/script/commands.rb +0 -200
@@ -38,6 +38,8 @@ module Appium
38
38
  #
39
39
  # result = Appium::Core::Wait.until { @driver.find_element(:id, 'something') }
40
40
  #
41
+ # result = Appium::Core::Wait.until(timeout: 30, message: 'timeout') { @driver.find_element(:id, 'something') }
42
+ #
41
43
  # result = Appium::Core::Wait.until(object: 'some object') { |object|
42
44
  # @driver.find_element(:id, object)
43
45
  # }
@@ -82,6 +84,8 @@ module Appium
82
84
  #
83
85
  # Appium::Core::Wait.until_true { @driver.find_element(:id, 'something') }
84
86
  #
87
+ # Appium::Core::Wait.until_true(timeout: 30) { @driver.find_element(:id, 'something') }
88
+ #
85
89
  # Appium::Core::Wait.until_true(object: 'some object') { |object|
86
90
  # @driver.find_element(:id, object)
87
91
  # }
@@ -133,17 +137,31 @@ module Appium
133
137
  # @param [String] message Exception message if timed out.
134
138
  # @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
135
139
  #
136
- # @example
140
+ # @example With core instance
137
141
  #
138
142
  # @core.wait_true { @driver.find_element :accessibility_id, 'something' }
143
+ # @core.wait_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
144
+ #
145
+ # @core.wait_until_true { @driver.find_element :accessibility_id, 'something' }
146
+ # @core.wait_until_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
147
+ #
148
+ # @example With driver instance
149
+ #
150
+ # @driver.wait_true { |d| d.find_element :accessibility_id, 'something' }
151
+ # @driver.wait_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
139
152
  #
140
- def wait_true(timeout: nil, interval: nil, message: nil, ignored: nil)
153
+ # @driver.wait_until_true { |d| d.find_element :accessibility_id, 'something' }
154
+ # @driver.wait_until_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
155
+ #
156
+ def wait_until_true(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
141
157
  Wait.until_true(timeout: timeout || @wait_timeout,
142
158
  interval: interval || @wait_interval,
143
159
  message: message,
144
160
  ignored: ignored,
145
- object: self) { yield }
161
+ object: self,
162
+ &block)
146
163
  end
164
+ alias wait_true wait_until_true
147
165
 
148
166
  # Check every interval seconds to see if yield doesn't raise an exception.
149
167
  # Give up after timeout seconds.
@@ -155,17 +173,31 @@ module Appium
155
173
  # @param [String] message Exception message if timed out.
156
174
  # @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
157
175
  #
158
- # @example
176
+ # @example With core instance
159
177
  #
160
178
  # @core.wait { @driver.find_element :accessibility_id, 'something' }
179
+ # @core.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
180
+ #
181
+ # @core.wait_until { @driver.find_element :accessibility_id, 'something' }
182
+ # @core.wait_until(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
183
+ #
184
+ # @example With driver instance
185
+ #
186
+ # @driver.wait { @driver.find_element :accessibility_id, 'something' }
187
+ # @driver.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
188
+ #
189
+ # @driver.wait_until { |d| d.find_element :accessibility_id, 'something' }
190
+ # @driver.wait_until(timeout: 30, interval: 2) { |d| d.find_element :accessibility_id, 'something' }
161
191
  #
162
- def wait(timeout: nil, interval: nil, message: nil, ignored: nil)
192
+ def wait_until(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
163
193
  Wait.until(timeout: timeout || @wait_timeout,
164
194
  interval: interval || @wait_interval,
165
195
  message: message,
166
196
  ignored: ignored,
167
- object: self) { yield }
197
+ object: self,
198
+ &block)
168
199
  end
200
+ alias wait wait_until
169
201
  end
170
202
  end # module Core
171
203
  end # module Appium
@@ -79,11 +79,7 @@ module Appium
79
79
  end
80
80
 
81
81
  def create_bridge_command(method, &block)
82
- ::Appium::Core::Base::Bridge::MJSONWP.class_eval do
83
- undef_method method if method_defined? method
84
- block_given? ? class_eval(&block) : define_method(method) { execute method }
85
- end
86
- ::Appium::Core::Base::Bridge::W3C.class_eval do
82
+ ::Appium::Core::Base::Bridge.class_eval do
87
83
  undef_method method if method_defined? method
88
84
  block_given? ? class_eval(&block) : define_method(method) { execute method }
89
85
  end
@@ -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,10 +26,11 @@ 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>
@@ -38,18 +42,18 @@ module Appium
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
48
  # bump current session id into a particular file
45
49
  @export_session = appium_lib_opts.fetch :export_session, false
46
50
  @export_session_path = appium_lib_opts.fetch :export_session_path, default_tmp_appium_lib_session
47
51
 
48
- @direct_connect = appium_lib_opts.fetch :direct_connect, false
52
+ @direct_connect = appium_lib_opts.fetch :direct_connect, true
49
53
 
50
54
  @port = appium_lib_opts.fetch :port, Driver::DEFAULT_APPIUM_PORT
51
55
 
52
- # timeout and interval used in ::Appium::Comm.wait/wait_true
56
+ # timeout and interval used in ::Appium::Commn.wait/wait_true
53
57
  @wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
54
58
  @wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
55
59
 
@@ -74,6 +78,13 @@ module Appium
74
78
  path: 'directConnectPath'
75
79
  }.freeze
76
80
 
81
+ W3C_KEYS = {
82
+ protocol: 'appium:directConnectProtocol',
83
+ host: 'appium:directConnectHost',
84
+ port: 'appium:directConnectPort',
85
+ path: 'appium:directConnectPath'
86
+ }.freeze
87
+
77
88
  # @return [string] Returns a protocol such as http/https
78
89
  attr_reader :protocol
79
90
 
@@ -87,10 +98,10 @@ module Appium
87
98
  attr_reader :path
88
99
 
89
100
  def initialize(capabilities)
90
- @protocol = capabilities[KEYS[:protocol]]
91
- @host = capabilities[KEYS[:host]]
92
- @port = capabilities[KEYS[:port]]
93
- @path = capabilities[KEYS[:path]]
101
+ @protocol = capabilities[W3C_KEYS[:protocol]] || capabilities[KEYS[:protocol]]
102
+ @host = capabilities[W3C_KEYS[:host]] || capabilities[KEYS[:host]]
103
+ @port = capabilities[W3C_KEYS[:port]] || capabilities[KEYS[:port]]
104
+ @path = capabilities[W3C_KEYS[:path]] || capabilities[KEYS[:path]]
94
105
  end
95
106
  end
96
107
 
@@ -133,11 +144,9 @@ module Appium
133
144
  attr_reader :export_session_path
134
145
 
135
146
  # Default wait time for elements to appear in Appium server side.
136
- # Defaults to {::Appium::Core::Driver::DEFAULT_IMPLICIT_WAIT}.<br>
137
147
  # Provide <code>{ appium_lib: { wait: 30 } }</code> to {::Appium::Core.for}
138
148
  # @return [Integer]
139
149
  attr_reader :default_wait
140
- DEFAULT_IMPLICIT_WAIT = 0
141
150
 
142
151
  # Appium's server port. 4723 is by default. Defaults to {::Appium::Core::Driver::DEFAULT_APPIUM_PORT}.<br>
143
152
  # Provide <code>{ appium_lib: { port: 8080 } }</code> to {::Appium::Core.for}.
@@ -175,7 +184,8 @@ module Appium
175
184
  # - <code>directConnectPort</code>
176
185
  # - <code>directConnectPath</code>
177
186
  #
178
- # Ignore them if this parameter is <code>false</code>. Defaults to false.
187
+ # ignore them if this parameter is <code>false</code>. Defaults to true.
188
+ # These keys can have <code>appium:</code> prefix.
179
189
  #
180
190
  # @return [Bool]
181
191
  attr_reader :direct_connect
@@ -185,8 +195,6 @@ module Appium
185
195
  # @option opts [Hash] :caps Appium capabilities.
186
196
  # @option opts [Hash] :capabilities The same as :caps.
187
197
  # 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
198
  # @option opts [Appium::Core::Options] :appium_lib Capabilities affect only ruby client
191
199
  # @option opts [String] :url The same as :custom_url in :appium_lib.
192
200
  # This param is for compatibility with Selenium WebDriver format
@@ -197,10 +205,8 @@ module Appium
197
205
  #
198
206
  # # format 1
199
207
  # @core = Appium::Core.for caps: {...}, appium_lib: {...}
200
- # # format 2. 'capabilities:' or 'desired_capabilities:' is also available instead of 'caps:'.
208
+ # # format 2. 'capabilities:' is also available instead of 'caps:'.
201
209
  # @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
210
  #
205
211
  #
206
212
  # require 'rubygems'
@@ -228,7 +234,7 @@ module Appium
228
234
  # @core.start_driver # Connect to 'http://127.0.0.1:8080/wd/hub' because of 'port: 8080'
229
235
  #
230
236
  # # Start iOS driver with .zip file over HTTP
231
- # # 'desired_capabilities:' or 'capabilities:' is also available instead of 'caps:'. Either is fine.
237
+ # # 'capabilities:' is also available instead of 'caps:'. Either is fine.
232
238
  # opts = {
233
239
  # capabilities: {
234
240
  # platformName: :ios,
@@ -252,7 +258,7 @@ module Appium
252
258
  # # Start iOS driver as another format. 'url' is available like below
253
259
  # opts = {
254
260
  # url: "http://custom-host:8080/wd/hub.com",
255
- # desired_capabilities: {
261
+ # capabilities: {
256
262
  # platformName: :ios,
257
263
  # platformVersion: '11.0',
258
264
  # deviceName: 'iPhone Simulator',
@@ -271,7 +277,40 @@ module Appium
271
277
  # @core.start_driver # start driver with 'url'. Connect to 'http://custom-host:8080/wd/hub.com'
272
278
  #
273
279
  def self.for(opts = {})
274
- new(opts)
280
+ new.setup_for_new_session(opts)
281
+ end
282
+
283
+ # Attach to an existing session. The main usage of this method is to attach to
284
+ # an existing session for debugging. The generated driver instance has the capabilities which
285
+ # has the given automationName and platformName only since the W3C WebDriver spec does not provide
286
+ # an endpoint to get running session's capabilities.
287
+ #
288
+ #
289
+ # @param [String] The session id to attach to.
290
+ # @param [String] url The WebDriver URL to attach to with the session_id.
291
+ # @param [String] automation_name The platform name to keep in the dummy capabilities
292
+ # @param [String] platform_name The automation name to keep in the dummy capabilities
293
+ # @return [Selenium::WebDriver] A new driver instance with the given session id.
294
+ #
295
+ # @example
296
+ #
297
+ # new_driver = ::Appium::Core::Driver.attach_to(
298
+ # driver.session_id, # The 'driver' has an existing session id
299
+ # url: 'http://127.0.0.1:4723/wd/hub', automation_name: 'UiAutomator2', platform_name: 'Android'
300
+ # )
301
+ # new_driver.page_source # for example
302
+ #
303
+ def self.attach_to(
304
+ session_id, url: nil, automation_name: nil, platform_name: nil,
305
+ http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }
306
+ )
307
+ new.attach_to(
308
+ session_id,
309
+ automation_name: automation_name,
310
+ platform_name: platform_name,
311
+ url: url,
312
+ http_client_ops: http_client_ops
313
+ )
275
314
  end
276
315
 
277
316
  private
@@ -282,17 +321,25 @@ module Appium
282
321
  @delegate_target
283
322
  end
284
323
 
285
- public
286
-
287
324
  # @private
288
- def initialize(opts = {})
325
+ def initialize
289
326
  @delegate_target = self # for testing purpose
290
327
  @automation_name = nil # initialise before 'set_automation_name'
328
+ end
291
329
 
292
- opts = Appium.symbolize_keys opts
293
- validate_keys(opts)
330
+ public
331
+
332
+ # @private
333
+ # Set up for a new session
334
+ def setup_for_new_session(opts = {})
335
+ @custom_url = opts.delete :url # to set the custom url as :url
336
+
337
+ # TODO: Remove when we implement Options
338
+ # The symbolize_keys is to keep compatiility for the legacy code, which allows capabilities to give 'string' as the key.
339
+ # The toplevel `caps`, `capabilities` and `appium_lib` are expected to be symbol.
340
+ # FIXME: First, please try to remove `nested: true` to `nested: false`.
341
+ opts = Appium.symbolize_keys(opts, nested: true)
294
342
 
295
- @custom_url = opts.delete :url
296
343
  @caps = get_caps(opts)
297
344
 
298
345
  set_appium_lib_specific_values(get_appium_lib_opts(opts))
@@ -301,8 +348,7 @@ module Appium
301
348
  set_automation_name
302
349
 
303
350
  extend_for(device: @device, automation_name: @automation_name)
304
-
305
- self # rubocop:disable Lint/Void
351
+ self
306
352
  end
307
353
 
308
354
  # Creates a new global driver and quits the old one if it exists.
@@ -313,7 +359,7 @@ module Appium
313
359
  # @option http_client_ops [Hash] :http_client Custom HTTP Client
314
360
  # @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
315
361
  # @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
316
- # @return [Selenium::WebDriver] the new global driver
362
+ # @return [Selenium::WebDriver] A new driver instance
317
363
  #
318
364
  # @example
319
365
  #
@@ -324,7 +370,7 @@ module Appium
324
370
  #
325
371
  # # Start iOS driver
326
372
  # opts = {
327
- # caps: {
373
+ # capabilities: {
328
374
  # platformName: :ios,
329
375
  # platformVersion: '11.0',
330
376
  # deviceName: 'iPhone Simulator',
@@ -365,11 +411,12 @@ module Appium
365
411
  end
366
412
 
367
413
  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,
414
+ @driver = ::Appium::Core::Base::Driver.new(listener: @listener,
415
+ http_client: @http_client,
416
+ capabilities: @caps, # ::Appium::Core::Base::Capabilities
371
417
  url: @custom_url,
372
- listener: @listener)
418
+ wait_timeout: @wait_timeout,
419
+ wait_interval: @wait_interval)
373
420
 
374
421
  if @direct_connect
375
422
  d_c = DirectConnections.new(@driver.capabilities)
@@ -387,6 +434,8 @@ module Appium
387
434
  @http_client.additional_headers.delete Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
388
435
  end
389
436
 
437
+ # TODO: this method can be removed after releasing Appium 2.0, and after a while
438
+ # since Appium 2.0 reuqires 'automationName'. This method won't help anymore then.
390
439
  # If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
391
440
  # Since @automation_name is set only client side before start_driver is called.
392
441
  set_automation_name_if_nil
@@ -396,7 +445,47 @@ module Appium
396
445
  @driver
397
446
  end
398
447
 
399
- private
448
+ # @private
449
+ # Attach to an existing session
450
+ def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
451
+ http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
452
+
453
+ raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
454
+ raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
455
+ raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?
456
+
457
+ @custom_url = url
458
+
459
+ # use lowercase internally
460
+ @automation_name = convert_downcase(automation_name)
461
+ @device = convert_downcase(platform_name)
462
+
463
+ extend_for(device: @device, automation_name: @automation_name)
464
+
465
+ @http_client = get_http_client http_client: http_client_ops.delete(:http_client),
466
+ open_timeout: http_client_ops.delete(:open_timeout),
467
+ read_timeout: http_client_ops.delete(:read_timeout)
468
+
469
+ # Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
470
+ # the new session request, this it does not needed.
471
+
472
+ begin
473
+ # included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
474
+ @driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
475
+ url: @custom_url,
476
+ listener: @listener,
477
+ existing_session_id: session_id,
478
+ automation_name: automation_name,
479
+ platform_name: platform_name)
480
+
481
+ # export session
482
+ write_session_id(@driver.session_id, @export_session_path) if @export_session
483
+ rescue Errno::ECONNREFUSED
484
+ raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
485
+ end
486
+
487
+ @driver
488
+ end
400
489
 
401
490
  def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
402
491
  client = http_client || Appium::Core::Base::Http::Default.new
@@ -410,6 +499,8 @@ module Appium
410
499
 
411
500
  # Ignore setting default wait if the target driver has no implementation
412
501
  def set_implicit_wait_by_default(wait)
502
+ return if @default_wait.nil?
503
+
413
504
  @driver.manage.timeouts.implicit_wait = wait
414
505
  rescue ::Selenium::WebDriver::Error::UnknownError => e
415
506
  unless e.message.include?('The operation requested is not yet implemented')
@@ -420,9 +511,7 @@ module Appium
420
511
  {}
421
512
  end
422
513
 
423
- public
424
-
425
- # Quits the driver
514
+ # [Deprecated] Quits the driver. This method is the same as @driver.quit
426
515
  # @return [void]
427
516
  #
428
517
  # @example
@@ -430,12 +519,14 @@ module Appium
430
519
  # @core.quit_driver
431
520
  #
432
521
  def quit_driver
522
+ ::Appium::Logger.warn('[DEPRECATION] quit_driver will be removed. Please use @driver.quit instead.')
433
523
  @driver.quit
434
524
  rescue # rubocop:disable Style/RescueStandardError
435
525
  nil
436
526
  end
437
527
 
438
- # Returns the server's version info
528
+ # Returns the server's version info. This method calls +driver.remote_status+ internally
529
+ #
439
530
  # @return [Hash]
440
531
  #
441
532
  # @example
@@ -449,18 +540,20 @@ module Appium
449
540
  # }
450
541
  # }
451
542
  #
452
- # Returns blank hash for Selenium Grid since 'remote_status' gets 500 error
543
+ # Returns blank hash in a case +driver.remote_status+ got an error
544
+ # such as Selenium Grid. It returns 500 error against 'remote_status'.
453
545
  #
454
546
  # @example
455
547
  #
456
548
  # @core.appium_server_version #=> {}
457
549
  #
458
550
  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')
551
+ return {} if @driver.nil?
462
552
 
463
- # driver.remote_status returns 500 error for using selenium grid
553
+ @driver.remote_status
554
+ rescue StandardError
555
+ # Ignore error case in a case the target appium server
556
+ # does not support `/status` API.
464
557
  {}
465
558
  end
466
559
 
@@ -472,35 +565,35 @@ module Appium
472
565
  # @core.platform_version #=> [10,1,1]
473
566
  #
474
567
  def platform_version
568
+ ::Appium::Logger.warn(
569
+ '[DEPRECATION] platform_version method will be. ' \
570
+ 'Please check the platformVersion via @driver.capabilities["platformVersion"] instead.'
571
+ )
572
+
475
573
  p_version = @driver.capabilities['platformVersion'] || @driver.session_capabilities['platformVersion']
476
574
  p_version.split('.').map(&:to_i)
477
575
  end
478
576
 
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
492
- end
493
-
494
577
  private
495
578
 
579
+ def convert_to_symbol(value)
580
+ if value.nil?
581
+ value
582
+ else
583
+ value.to_sym
584
+ end
585
+ end
586
+
496
587
  # @private
497
588
  def extend_for(device:, automation_name:) # rubocop:disable Metrics/CyclomaticComplexity
498
589
  extend Appium::Core
499
590
  extend Appium::Core::Device
500
591
 
501
- case device
592
+ sym_automation_name = convert_to_symbol(automation_name)
593
+
594
+ case convert_to_symbol(device)
502
595
  when :android
503
- case automation_name
596
+ case sym_automation_name
504
597
  when :espresso
505
598
  ::Appium::Core::Android::Espresso::Bridge.for self
506
599
  when :uiautomator2
@@ -511,28 +604,26 @@ module Appium
511
604
  ::Appium::Core::Android::Uiautomator1::Bridge.for self
512
605
  end
513
606
  when :ios, :tvos
514
- case automation_name
607
+ case sym_automation_name
515
608
  when :safari
516
609
  ::Appium::Logger.debug('SafariDriver for iOS')
517
- when :xcuitest
610
+ else # XCUITest
518
611
  ::Appium::Core::Ios::Xcuitest::Bridge.for self
519
- else # default and UIAutomation
520
- ::Appium::Core::Ios::Uiautomation::Bridge.for self
521
612
  end
522
613
  when :mac
523
- case automation_name
614
+ case sym_automation_name
524
615
  when :safari
525
616
  ::Appium::Logger.debug('SafariDriver for macOS')
526
617
  when :gecko
527
618
  ::Appium::Logger.debug('Gecko Driver for macOS')
528
619
  when :mac2
529
- ::Appium::Logger.debug('macOS XCUITest')
620
+ ::Appium::Core::Mac2::Bridge.for self
530
621
  else
531
622
  # no Mac specific extentions
532
623
  ::Appium::Logger.debug('macOS Native')
533
624
  end
534
625
  when :windows
535
- case automation_name
626
+ case sym_automation_name
536
627
  when :gecko
537
628
  ::Appium::Logger.debug('Gecko Driver for Windows')
538
629
  else
@@ -542,7 +633,7 @@ module Appium
542
633
  # https://github.com/Samsung/appium-tizen-driver
543
634
  ::Appium::Logger.debug('tizen')
544
635
  else
545
- case automation_name
636
+ case sym_automation_name
546
637
  when :youiengine
547
638
  # https://github.com/YOU-i-Labs/appium-youiengine-driver
548
639
  ::Appium::Logger.debug('YouiEngine')
@@ -559,36 +650,9 @@ module Appium
559
650
  self
560
651
  end
561
652
 
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
653
  # @private
589
654
  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] || {})
655
+ Core::Base::Capabilities.new(opts[:caps] || opts[:capabilities] || {})
592
656
  end
593
657
 
594
658
  # @private
@@ -601,6 +665,7 @@ module Appium
601
665
  # The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
602
666
  # Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
603
667
  def set_app_path
668
+ # FIXME: maybe `:app` should check `app` as well.
604
669
  return unless @caps && @caps[:app] && !@caps[:app].empty?
605
670
  return if @caps[:app] =~ URI::DEFAULT_PARSER.make_regexp
606
671
 
@@ -639,18 +704,24 @@ module Appium
639
704
  # @private
640
705
  def set_appium_device
641
706
  # https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
642
- @device = @caps[:platformName]
707
+ # TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
708
+ @device = @caps[:platformName] || @caps['platformName']
643
709
  return @device unless @device
644
710
 
645
- @device = @device.is_a?(Symbol) ? @device.downcase : @device.downcase.strip.intern
711
+ @device = convert_downcase @device
646
712
  end
647
713
 
648
714
  # @private
649
715
  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
716
+ # TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
717
+ candidate = @caps[:automationName] || @caps['automationName']
718
+ @automation_name = candidate if candidate
719
+ @automation_name = convert_downcase @automation_name if @automation_name
720
+ end
721
+
722
+ # @private
723
+ def convert_downcase(value)
724
+ value.is_a?(Symbol) ? value.downcase : value.downcase.strip.intern
654
725
  end
655
726
 
656
727
  # @private
@@ -664,6 +735,10 @@ module Appium
664
735
 
665
736
  # @private
666
737
  def write_session_id(session_id, export_path = '/tmp/appium_lib_session')
738
+ ::Appium::Logger.warn(
739
+ '[DEPRECATION] export_session option will be removed. ' \
740
+ 'Please save the session id by yourself with #session_id method like @driver.session_id.'
741
+ )
667
742
  export_path = export_path.tr('/', '\\') if ::Appium::Core::Base.platform.windows?
668
743
  File.write(export_path, session_id)
669
744
  rescue IOError => e