appium_lib_core 4.1.0 → 5.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +129 -275
  3. data/README.md +12 -7
  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 +291 -90
  10. data/lib/appium_lib_core/common/base/capabilities.rb +10 -3
  11. data/lib/appium_lib_core/common/base/device_ime.rb +49 -0
  12. data/lib/appium_lib_core/common/base/driver.rb +223 -184
  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/common/base/remote_status.rb +31 -0
  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 +19 -4
  21. data/lib/appium_lib_core/common/base.rb +1 -3
  22. data/lib/appium_lib_core/common/command.rb +255 -4
  23. data/lib/appium_lib_core/common/device/app_management.rb +2 -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 -6
  29. data/lib/appium_lib_core/common/error.rb +4 -1
  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 +42 -50
  36. data/lib/appium_lib_core/{patch.rb → element.rb} +66 -9
  37. data/lib/appium_lib_core/ios/uiautomation/patch.rb +1 -1
  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/mac2.rb +17 -0
  42. data/lib/appium_lib_core/version.rb +2 -2
  43. data/lib/appium_lib_core.rb +2 -5
  44. metadata +25 -84
  45. data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
  46. data/.github/contributing.md +0 -26
  47. data/.github/issue_template.md +0 -20
  48. data/.github/workflows/unittest.yml +0 -68
  49. data/.gitignore +0 -18
  50. data/.rubocop.yml +0 -58
  51. data/azure-pipelines.yml +0 -15
  52. data/ci-jobs/functional/android_setup.yml +0 -3
  53. data/ci-jobs/functional/ios_setup.yml +0 -7
  54. data/ci-jobs/functional/publish_test_result.yml +0 -18
  55. data/ci-jobs/functional/run_appium.yml +0 -25
  56. data/ci-jobs/functional/start-emulator.sh +0 -26
  57. data/ci-jobs/functional_test.yml +0 -298
  58. data/docs/mobile_command.md +0 -34
  59. data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
  60. data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
  61. data/lib/appium_lib_core/common/command/common.rb +0 -110
  62. data/lib/appium_lib_core/common/command/w3c.rb +0 -56
  63. data/release_notes.md +0 -816
  64. data/script/commands.rb +0 -200
@@ -13,8 +13,15 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require 'base64'
16
+ require_relative 'device_ime'
17
+ require_relative 'driver_settings'
16
18
  require_relative 'search_context'
17
19
  require_relative 'screenshot'
20
+ require_relative 'rotable'
21
+ require_relative 'remote_status'
22
+ require_relative 'has_location'
23
+ require_relative 'has_network_connection'
24
+ require_relative '../wait'
18
25
 
19
26
  module Appium
20
27
  module Core
@@ -22,42 +29,57 @@ module Appium
22
29
  class Driver < ::Selenium::WebDriver::Driver
23
30
  include ::Selenium::WebDriver::DriverExtensions::UploadsFiles
24
31
  include ::Selenium::WebDriver::DriverExtensions::HasSessionId
25
- include ::Selenium::WebDriver::DriverExtensions::Rotatable
26
32
  include ::Selenium::WebDriver::DriverExtensions::HasRemoteStatus
27
33
  include ::Selenium::WebDriver::DriverExtensions::HasWebStorage
28
34
 
35
+ include ::Appium::Core::Base::Rotatable
29
36
  include ::Appium::Core::Base::SearchContext
30
- include ::Appium::Core::Base::TakeScreenshot
37
+ include ::Appium::Core::Base::TakesScreenshot
38
+ include ::Appium::Core::Base::HasRemoteStatus
39
+ include ::Appium::Core::Base::HasLocation
40
+ include ::Appium::Core::Base::HasNetworkConnection
41
+
42
+ include ::Appium::Core::Waitable
31
43
 
32
44
  # Private API.
33
45
  # Do not use this for general use. Used by flutter driver to get bridge for creating a new element
34
46
  attr_reader :bridge
35
47
 
36
- def initialize(opts = {})
37
- listener = opts.delete(:listener)
38
- @bridge = ::Appium::Core::Base::Bridge.handshake(**opts)
39
- if @bridge.dialect == :oss # MJSONWP
40
- extend ::Selenium::WebDriver::DriverExtensions::HasTouchScreen
41
- extend ::Selenium::WebDriver::DriverExtensions::HasLocation
42
- extend ::Selenium::WebDriver::DriverExtensions::HasNetworkConnection
43
- elsif @bridge.dialect == :w3c
44
- # TODO: Only for Appium. Ideally, we'd like to remove the below like selenium-webdriver
45
- extend ::Selenium::WebDriver::DriverExtensions::HasTouchScreen
46
- extend ::Selenium::WebDriver::DriverExtensions::HasLocation
47
- extend ::Selenium::WebDriver::DriverExtensions::HasNetworkConnection
48
- end
49
- super(@bridge, listener: listener)
48
+ private
49
+
50
+ def initialize(bridge: nil, listener: nil, **opts)
51
+ # For ::Appium::Core::Waitable
52
+ @wait_timeout = opts.delete(:wait_timeout)
53
+ @wait_interval = opts.delete(:wait_interval)
54
+
55
+ # For logging.
56
+ # TODO: Remove when appium core no longer uses this in this bridge.
57
+ @automation_name = opts.delete(:automation_name)
58
+
59
+ super
50
60
  end
51
61
 
52
- # Get the dialect value
53
- # @return [:oss|:w3c]
54
- def dialect
55
- @bridge.dialect
62
+ # Create a proper bridge instance.
63
+ #
64
+ # @return [::Appium::Core::Base::Bridge]
65
+ #
66
+ def create_bridge(**opts)
67
+ capabilities = opts.delete(:capabilities)
68
+ bridge_opts = { http_client: opts.delete(:http_client), url: opts.delete(:url) }
69
+ raise ::Appium::Core::Error::ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
70
+
71
+ bridge = ::Appium::Core::Base::Bridge.new(**bridge_opts)
72
+
73
+ bridge.create_session(capabilities)
74
+ bridge
56
75
  end
57
76
 
77
+ public
78
+
58
79
  # Update +server_url+ and HTTP clients following this arguments, protocol, host, port and path.
59
80
  # After this method, +@bridge.http+ will be a new instance following them instead of +server_url+ which is
60
81
  # set before creating session.
82
+ # If +@bridge.http+ did not have +update_sending_request_to+ method, this method returns immediately.
61
83
  #
62
84
  # @example
63
85
  #
@@ -66,14 +88,124 @@ module Appium
66
88
  # driver.manage.timeouts.implicit_wait = 10 # @bridge.http is for 'https://example2.com:9000/wd/hub/'
67
89
  #
68
90
  def update_sending_request_to(protocol:, host:, port:, path:)
69
- @bridge.http.update_sending_request_to(scheme: protocol,
70
- host: host,
71
- port: port,
72
- path: path)
91
+ unless @bridge.http&.class&.method_defined? :update_sending_request_to
92
+ ::Appium::Logger.warn "#{@bridge.http&.class} has no 'update_sending_request_to'. " \
93
+ 'It keeps current connection target.'
94
+ return
95
+ end
96
+
97
+ @bridge.http&.update_sending_request_to(scheme: protocol,
98
+ host: host,
99
+ port: port,
100
+ path: path)
101
+ end
102
+
103
+ AVAILABLE_METHODS = [
104
+ :get, :head, :post, :put, :delete,
105
+ :connect, :options, :trace, :patch
106
+ ].freeze
107
+ # Define a new custom method to the driver so that you can define your own method for
108
+ # drivers/plugins in Appium 2.0. Appium 2.0 and its custom drivers/plugins allow you
109
+ # to define custom commands that are not part of W3C spec.
110
+ #
111
+ # @param [Symbol] method HTTP request method as https://www.w3.org/TR/webdriver/#endpoints
112
+ # @param [string] url The url to URL template as https://www.w3.org/TR/webdriver/#endpoints.
113
+ # +:session_id+ is the placeholder of 'session id'.
114
+ # Other place holders can be specified with +:+ prefix like +:id+.
115
+ # Then, the +:id+ will be replaced with a given value as the seconds argument of +execute+
116
+ # @param [Symbol] name The name of method that is called as the driver instance method.
117
+ # @param [Proc] block The block to involve as the method.
118
+ # Please define a method that has the same +name+ with arguments you want.
119
+ # The method must has +execute+ method. tHe +execute+ is calls the +url+
120
+ # with the given parameters.
121
+ # The first argument should be +name+ as symbol.
122
+ # The second argument should be hash. If keys in the hash matches +:+ prefix
123
+ # string in the given url, the matched string in the given url will be
124
+ # values in the hash.
125
+ # The third argument should be hash. The hash will be the request body.
126
+ # Please read examples below for more details.
127
+ # @raise [ArgumentError] If the given +method+ is invalid value.
128
+ #
129
+ # @example
130
+ #
131
+ # @driver.add_command(
132
+ # method: :get,
133
+ # url: 'session/:session_id/path/to/custom/url',
134
+ # name: :test_command
135
+ # )
136
+ # # Send a GET request to 'session/<session id>/path/to/custom/url'
137
+ # @driver.test_command
138
+ #
139
+ #
140
+ # @driver.add_command(
141
+ # method: :post,
142
+ # url: 'session/:session_id/path/to/custom/url',
143
+ # name: :test_command
144
+ # ) do
145
+ # def test_command(argument)
146
+ # execute(:test_command, {}, { dummy: argument })
147
+ # end
148
+ # end
149
+ # # Send a POST request to 'session/<session id>/path/to/custom/url'
150
+ # # with body "{ dummy: 1 }" as JSON object. "1" is the argument.
151
+ # # ':session_id' in the given 'url' is replaced with current 'session id'.
152
+ # @driver.test_command(1)
153
+ #
154
+ #
155
+ # @driver.add_command(
156
+ # method: :post,
157
+ # url: 'session/:session_id/element/:id/custom/action',
158
+ # name: :test_action_command
159
+ # ) do
160
+ # def test_action_command(element_id, action)
161
+ # execute(:test_action_command, {id: element_id}, { dummy_action: action })
162
+ # end
163
+ # end
164
+ # # Send a POST request to 'session/<session id>/element/<element id>/custom/action'
165
+ # # with body "{ dummy_action: #{action} }" as JSON object. "action" is the seconds argument.
166
+ # # ':session_id' in the given url is replaced with current 'session id'.
167
+ # # ':id' in the given url is replaced with the given 'element_id'.
168
+ # e = @driver.find_element :accessibility_id, 'an element'
169
+ # @driver.test_action_command(e.id, 'action')
170
+ #
171
+ def add_command(method:, url:, name:, &block)
172
+ unless AVAILABLE_METHODS.include? method
173
+ raise ::Appium::Core::Error::ArgumentError, "Available method is either #{AVAILABLE_METHODS}"
174
+ end
175
+
176
+ # TODO: Remove this logger before Appium 2.0 release
177
+ ::Appium::Logger.info '[Experimental] this method is experimental for Appium 2.0. This interface may change.'
178
+
179
+ @bridge.add_command method: method, url: url, name: name, &block
73
180
  end
74
181
 
75
182
  ### Methods for Appium
76
183
 
184
+ # Perform 'key' actions for W3C module.
185
+ # Generate +key+ pointer action here and users can use this via +driver.key_action+
186
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/W3CActionBuilder.html
187
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/KeyActions.html
188
+ #
189
+ # The pointer type is 'key' by default in the Appium Ruby client.
190
+ # +driver.action+ in Appium Ruby client has 'pointer' action by default.
191
+ # This method is a shortcut to set 'key' type.
192
+ # Hense this method is equal to +driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')])+
193
+ # as below example.
194
+ #
195
+ # @example
196
+ #
197
+ # element = @driver.find_element(:id, "some id")
198
+ # @driver.key_action.send_key('hiあ').perform # The 'send_key' is a part of 'KeyActions'
199
+ # # is equal to:
200
+ # # @driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')]).send_keys('hiあ').perform
201
+ #
202
+ def key_action(async: false)
203
+ @bridge.action(
204
+ async: async,
205
+ devices: [::Selenium::WebDriver::Interactions.key('keyboard')]
206
+ )
207
+ end
208
+
77
209
  # Lock the device
78
210
  # @return [String]
79
211
  #
@@ -138,38 +270,6 @@ module Appium
138
270
  end
139
271
  alias is_keyboard_shown keyboard_shown?
140
272
 
141
- # [DEPRECATION]
142
- # Send keys for a current active element
143
- # @param [String] key Input text
144
- #
145
- # @example
146
- #
147
- # @driver.send_keys 'happy testing!'
148
- #
149
- def send_keys(*key)
150
- ::Appium::Logger.warn(
151
- '[DEPRECATION] Driver#send_keys is deprecated in W3C spec. Use driver.action.<command>.perform instead'
152
- )
153
- @bridge.send_keys_to_active_element(key)
154
- end
155
- alias type send_keys
156
-
157
- class DriverSettings
158
- # @private this class is private
159
- def initialize(bridge)
160
- @bridge = bridge
161
- end
162
-
163
- def get
164
- @bridge.get_settings
165
- end
166
-
167
- def update(settings)
168
- @bridge.update_settings(settings)
169
- end
170
- end
171
- private_constant :DriverSettings
172
-
173
273
  # Returns an instance of DriverSettings to call get/update.
174
274
  #
175
275
  # @example
@@ -178,7 +278,7 @@ module Appium
178
278
  # @driver.settings.update('allowInvisibleElements': true)
179
279
  #
180
280
  def settings
181
- @driver_settings ||= DriverSettings.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
281
+ @settings ||= DriverSettings.new(@bridge)
182
282
  end
183
283
 
184
284
  # Get appium Settings for current test session.
@@ -200,8 +300,8 @@ module Appium
200
300
  #
201
301
  # @example
202
302
  #
203
- # @driver.update_settings('allowInvisibleElements': true)
204
- # @driver.settings.update('allowInvisibleElements': true)
303
+ # @driver.update_settings({ 'allowInvisibleElements': true })
304
+ # @driver.settings.update({ 'allowInvisibleElements': true })
205
305
  # @driver.settings = { 'allowInvisibleElements': true }
206
306
  #
207
307
  def settings=(value)
@@ -209,36 +309,10 @@ module Appium
209
309
  end
210
310
  alias update_settings settings=
211
311
 
212
- class DeviceIME
213
- # @private this class is private
214
- def initialize(bridge)
215
- @bridge = bridge
216
- end
217
-
218
- def activate(ime_name)
219
- @bridge.ime_activate(ime_name)
220
- end
221
-
222
- def available_engines
223
- @bridge.ime_available_engines
224
- end
225
-
226
- def active_engine
227
- @bridge.ime_active_engine
228
- end
229
-
230
- def activated?
231
- @bridge.ime_activated
232
- end
233
-
234
- def deactivate
235
- @bridge.ime_deactivate
236
- end
237
- end
238
- private_constant :DeviceIME
239
-
240
312
  # Returns an instance of DeviceIME
241
313
  #
314
+ # @return [Appium::Core::Base::Driver::DeviceIME]
315
+ #
242
316
  # @example
243
317
  #
244
318
  # @driver.ime.activate engine: 'com.android.inputmethod.latin/.LatinIME'
@@ -248,7 +322,7 @@ module Appium
248
322
  # @driver.ime.deactivate #=> Deactivate current IME engine
249
323
  #
250
324
  def ime
251
- @device_ime ||= DeviceIME.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
325
+ @ime ||= DeviceIME.new(@bridge)
252
326
  end
253
327
 
254
328
  # Android only. Make an engine that is available active.
@@ -289,6 +363,8 @@ module Appium
289
363
  # @!method ime_activated
290
364
  # Android only. Indicates whether IME input is active at the moment (not if it is available).
291
365
  #
366
+ # @return [Boolean]
367
+ #
292
368
  # @example
293
369
  #
294
370
  # @driver.ime_activated #=> True if IME is activated
@@ -366,42 +442,10 @@ module Appium
366
442
  end
367
443
  alias set_context context=
368
444
 
369
- # Set the value to element directly
370
- #
371
- # @example
372
- #
373
- # @driver.set_immediate_value element, 'hello'
374
- #
375
- def set_immediate_value(element, *value)
376
- ::Appium::Logger.warn '[DEPRECATION] driver#set_immediate_value(element, *value) is deprecated. ' \
377
- 'Use Element#immediate_value(*value) instead'
378
- @bridge.set_immediate_value(element, *value)
379
- end
380
-
381
- # Replace the value to element directly
382
- #
383
- # @example
384
- #
385
- # @driver.replace_value element, 'hello'
386
- #
387
- def replace_value(element, *value)
388
- ::Appium::Logger.warn '[DEPRECATION] driver#replace_value(element, *value) is deprecated. ' \
389
- 'Use Element#replace_value(*value) instead'
390
- @bridge.replace_value(element, *value)
391
- end
392
-
393
445
  # Place a file in a specific location on the device.
394
- # On iOS, the server should have ifuse libraries installed and configured properly for this feature to work on
395
- # real devices.
396
446
  # On Android, the application under test should be built with debuggable flag enabled in order to get access to
397
447
  # its container on the internal file system.
398
448
  #
399
- # {https://github.com/libimobiledevice/ifuse iFuse GitHub page6}
400
- #
401
- # {https://github.com/osxfuse/osxfuse/wiki/FAQ osxFuse FAQ}
402
- #
403
- # {https://developer.android.com/studio/debug 'Debug Your App' developer article}
404
- #
405
449
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
406
450
  # If the path starts with application id prefix, then the file will be pushed to the root of
407
451
  # the corresponding application container.
@@ -418,18 +462,10 @@ module Appium
418
462
  @bridge.push_file(path, filedata)
419
463
  end
420
464
 
421
- # Pull a file from the simulator/device.
422
- # On iOS the server should have ifuse
423
- # libraries installed and configured properly for this feature to work on real devices.
465
+ # Pull a file from the remote device.
424
466
  # On Android the application under test should be built with debuggable flag enabled in order to get access
425
467
  # to its container on the internal file system.
426
468
  #
427
- # {https://github.com/libimobiledevice/ifuse iFuse GitHub page6}
428
- #
429
- # {https://github.com/osxfuse/osxfuse/wiki/FAQ osxFuse FAQ}
430
- #
431
- # {https://developer.android.com/studio/debug 'Debug Your App' developer article}
432
- #
433
469
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
434
470
  # If the path starts with application id prefix, then the file will be pulled from the root
435
471
  # of the corresponding application container.
@@ -438,7 +474,6 @@ module Appium
438
474
  # Only pulling files from application containers is supported for iOS Simulator.
439
475
  # Provide the remote path in format
440
476
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
441
- # (Make sure this in ifuse doc)
442
477
  #
443
478
  # @return [Base64-decoded] Base64 decoded data
444
479
  #
@@ -455,18 +490,10 @@ module Appium
455
490
  @bridge.pull_file(path)
456
491
  end
457
492
 
458
- # Pull a folder content from the simulator/device.
459
- # On iOS the server should have ifuse libraries installed and configured properly for this feature to work
460
- # on real devices.
493
+ # Pull a folder content from the remote device.
461
494
  # On Android the application under test should be built with debuggable flag enabled in order to get access to
462
495
  # its container on the internal file system.
463
496
  #
464
- # {https://github.com/libimobiledevice/ifuse iFuse GitHub page6}
465
- #
466
- # {https://github.com/osxfuse/osxfuse/wiki/FAQ osxFuse FAQ}
467
- #
468
- # {https://developer.android.com/studio/debug 'Debug Your App' developer article}
469
- #
470
497
  # @param [String] path Absolute path to the folder.
471
498
  # If the path starts with <em>@applicationId/</em> prefix, then the folder will be pulled
472
499
  # from the root of the corresponding application container.
@@ -475,7 +502,6 @@ module Appium
475
502
  # Only pulling files from application containers is supported for iOS Simulator.
476
503
  # Provide the remote path in format
477
504
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
478
- # (Make sure this in ifuse doc)
479
505
  #
480
506
  # @return [Base64-decoded] Base64 decoded data which is zip archived
481
507
  #
@@ -489,19 +515,6 @@ module Appium
489
515
  @bridge.pull_folder(path)
490
516
  end
491
517
 
492
- # Send keyevent on the device.(Only for Selendroid)
493
- # http://developer.android.com/reference/android/view/KeyEvent.html
494
- # @param [integer] key The key to press.
495
- # @param [String] metastate The state the metakeys should be in when pressing the key.
496
- #
497
- # @example
498
- #
499
- # @driver.keyevent 82
500
- #
501
- def keyevent(key, metastate = nil)
502
- @bridge.keyevent(key, metastate)
503
- end
504
-
505
518
  # Press keycode on the device.
506
519
  # http://developer.android.com/reference/android/view/KeyEvent.html
507
520
  # @param [Integer] key The key to press. The values which have +KEYCODE_+ prefix in http://developer.android.com/reference/android/view/KeyEvent.html
@@ -546,6 +559,7 @@ module Appium
546
559
  @bridge.long_press_keycode(key, metastate: metastate, flags: flags)
547
560
  end
548
561
 
562
+ # @deprecated Except for Windows
549
563
  # Start the simulator and application configured with desired capabilities
550
564
  #
551
565
  # @example
@@ -553,9 +567,16 @@ module Appium
553
567
  # @driver.launch_app
554
568
  #
555
569
  def launch_app
570
+ # TODO: Define only in Windows module when ruby_lib_core removes this method
571
+ if @automation_name != :windows
572
+ ::Appium::Logger.warn(
573
+ '[DEPRECATION] launch_app is deprecated. Please use activate_app instead.'
574
+ )
575
+ end
556
576
  @bridge.launch_app
557
577
  end
558
578
 
579
+ # @deprecated Except for Windows
559
580
  # Close an app on device
560
581
  #
561
582
  # @example
@@ -563,9 +584,16 @@ module Appium
563
584
  # @driver.close_app
564
585
  #
565
586
  def close_app
587
+ # TODO: Define only in Windows module when ruby_lib_core removes this method
588
+ if @automation_name != :windows
589
+ ::Appium::Logger.warn(
590
+ '[DEPRECATION] close_app is deprecated. Please use terminate_app instead.'
591
+ )
592
+ end
566
593
  @bridge.close_app
567
594
  end
568
595
 
596
+ # @deprecated
569
597
  # Reset the device, relaunching the application.
570
598
  #
571
599
  # @example
@@ -573,6 +601,10 @@ module Appium
573
601
  # @driver.reset
574
602
  #
575
603
  def reset
604
+ ::Appium::Logger.warn(
605
+ '[DEPRECATION] reset is deprecated. Please use terminate_app and activate_app, ' \
606
+ 'or quit and create a new session instead.'
607
+ )
576
608
  @bridge.reset
577
609
  end
578
610
 
@@ -602,7 +634,9 @@ module Appium
602
634
  @bridge.background_app(duration)
603
635
  end
604
636
 
605
- # Install the given app onto the device
637
+ # Install the given app onto the device.
638
+ # Each options can be snake-case or camel-case. Snake-cases will be converted to camel-case
639
+ # as options value.
606
640
  #
607
641
  # @param [String] path The absolute local path or remote http URL to an .ipa or .apk file,
608
642
  # or a .zip containing one of these.
@@ -616,26 +650,26 @@ module Appium
616
650
  # @param [Boolean] grant_permissions Only for Android. whether to automatically grant application permissions
617
651
  # on Android 6+ after the installation completes. +false+ by default
618
652
  #
653
+ # Other parameters such as https://github.com/appium/appium-xcuitest-driver#mobile-installapp also can be set.
654
+ # Then, arguments in snake case will be camel case as its request parameters.
655
+ #
619
656
  # @example
620
657
  #
621
658
  # @driver.install_app("/path/to/test.apk")
622
659
  # @driver.install_app("/path/to/test.apk", replace: true, timeout: 20000, allow_test_packages: true,
623
660
  # use_sdcard: false, grant_permissions: false)
661
+ # @driver.install_app("/path/to/test.ipa", timeoutMs: 20000)
624
662
  #
625
- def install_app(path,
626
- replace: nil,
627
- timeout: nil,
628
- allow_test_packages: nil,
629
- use_sdcard: nil,
630
- grant_permissions: nil)
631
- @bridge.install_app(path,
632
- replace: replace,
633
- timeout: timeout,
634
- allow_test_packages: allow_test_packages,
635
- use_sdcard: use_sdcard,
636
- grant_permissions: grant_permissions)
663
+ def install_app(path, **options)
664
+ options = options.transform_keys { |key| key.to_s.gsub(/_./) { |v| v[1].upcase } } unless options.nil?
665
+ @bridge.install_app(path, options)
637
666
  end
638
667
 
668
+ # def capitalize(s)
669
+ # chars =
670
+ # chars[1:].map(&:capitalize).join
671
+ # end
672
+
639
673
  # @param [Strong] app_id BundleId for iOS or package name for Android
640
674
  # @param [Boolean] keep_data Only for Android. Whether to keep application data and caches after it is uninstalled.
641
675
  # +false+ by default
@@ -790,7 +824,7 @@ module Appium
790
824
  #
791
825
  # @example: Zoom
792
826
  #
793
- # f1 = @driver.action.add_pointer_input(:touch, 'finger1')
827
+ # f1 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger1')
794
828
  # f1.create_pointer_move(duration: 1, x: 200, y: 500,
795
829
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
796
830
  # f1.create_pointer_down(:left)
@@ -798,7 +832,7 @@ module Appium
798
832
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
799
833
  # f1.create_pointer_up(:left)
800
834
  #
801
- # f2 = @driver.action.add_pointer_input(:touch, 'finger2')
835
+ # f2 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger2')
802
836
  # f2.create_pointer_move(duration: 1, x: 200, y: 500,
803
837
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
804
838
  # f2.create_pointer_down(:left)
@@ -809,7 +843,11 @@ module Appium
809
843
  # @driver.perform_actions [f1, f2] #=> 'nil' if the action succeed
810
844
  #
811
845
  def perform_actions(data)
812
- raise ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
846
+ raise ::Appium::Core::Error::ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
847
+
848
+ # NOTE: 'add_input' in Selenium Ruby implementation has additional 'pause'.
849
+ # This implementation is to avoid the additional pause.
850
+ # https://github.com/SeleniumHQ/selenium/blob/64447d4b03f6986337d1ca8d8b6476653570bcc1/rb/lib/selenium/webdriver/common/action_builder.rb#L207
813
851
 
814
852
  @bridge.send_actions data.map(&:encode).compact
815
853
  data.each(&:clear_actions)
@@ -853,7 +891,7 @@ module Appium
853
891
  end
854
892
 
855
893
  # Get the device window's logs.
856
- # @return [String]
894
+ # @return [Appium::Core::Logs]
857
895
  #
858
896
  # @example
859
897
  #
@@ -879,16 +917,15 @@ module Appium
879
917
  # Retrieve the capabilities of the specified session.
880
918
  # It's almost same as +@driver.capabilities+ but you can get more details.
881
919
  #
882
- # @return [Selenium::WebDriver::Remote::Capabilities]
920
+ # @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::Capabilities]
883
921
  #
884
922
  # @example
885
923
  # @driver.session_capabilities
886
924
  #
887
925
  # #=> uiautomator2
888
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fa38dae1360
926
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fa38dae1360
889
927
  # # @capabilities=
890
- # # {:proxy=>nil,
891
- # # :browser_name=>nil,
928
+ # # {:browser_name=>nil,
892
929
  # # :browser_version=>nil,
893
930
  # # :platform_name=>"android",
894
931
  # # :page_load_strategy=>nil,
@@ -935,10 +972,9 @@ module Appium
935
972
  # # "viewportRect"=>{"left"=>0, "top"=>63, "width"=>1080, "height"=>1731}}>
936
973
  # #
937
974
  # #=> XCUITest
938
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fb15dc01370
975
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fb15dc01370
939
976
  # # @capabilities=
940
- # # {:proxy=>nil,
941
- # # :browser_name=>"UICatalog",
977
+ # # {:browser_name=>"UICatalog",
942
978
  # # :browser_version=>nil,
943
979
  # # :platform_name=>"ios",
944
980
  # # :page_load_strategy=>nil,
@@ -992,11 +1028,14 @@ module Appium
992
1028
  visualize: visualize)
993
1029
  end
994
1030
 
995
- def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil)
1031
+ def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil,
1032
+ multiple: nil, match_neighbour_threshold: nil)
996
1033
  @bridge.find_image_occurrence(full_image: full_image,
997
1034
  partial_image: partial_image,
998
1035
  visualize: visualize,
999
- threshold: threshold)
1036
+ threshold: threshold,
1037
+ multiple: multiple,
1038
+ match_neighbour_threshold: match_neighbour_threshold)
1000
1039
  end
1001
1040
 
1002
1041
  def get_images_similarity(first_image:, second_image:, visualize: false)
@@ -1016,7 +1055,7 @@ module Appium
1016
1055
  #
1017
1056
  # @param [String] img_path A path to a partial image you'd like to find
1018
1057
  #
1019
- # @return [::Selenium::WebDriver::Element]
1058
+ # @return [::Appium::Core::Element]
1020
1059
  #
1021
1060
  # @example
1022
1061
  #
@@ -1038,7 +1077,7 @@ module Appium
1038
1077
  #
1039
1078
  # @param [String] img_path A path to a partial image you'd like to find
1040
1079
  #
1041
- # @return [::Selenium::WebDriver::Element]
1080
+ # @return [Array<Selenium::WebDriver::Element>]
1042
1081
  #
1043
1082
  # @example
1044
1083
  #
@@ -1086,14 +1125,14 @@ module Appium
1086
1125
  @bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
1087
1126
  end
1088
1127
 
1089
- # Convert vanilla element response to ::Selenium::WebDriver::Element
1128
+ # Convert vanilla element response to ::Appium::Core::Element
1090
1129
  #
1091
1130
  # @param [Hash] id The id which can get as a response from server
1092
- # @return [::Selenium::WebDriver::Element]
1131
+ # @return [::Appium::Core::Element]
1093
1132
  #
1094
1133
  # @example
1095
1134
  # response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
1096
- # ele = @driver.convert_to_element(response) #=> ::Selenium::WebDriver::Element
1135
+ # ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
1097
1136
  # ele.rect #=> Can get the rect of the element
1098
1137
  #
1099
1138
  def convert_to_element(id)