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