appium_lib_core 4.1.0 → 7.1.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +250 -271
  3. data/README.md +65 -15
  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 +45 -4
  9. data/lib/appium_lib_core/common/base/bridge.rb +320 -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 +232 -191
  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 +15 -38
  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 +62 -0
  19. data/lib/appium_lib_core/common/base/screenshot.rb +8 -8
  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 +194 -108
  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/support/event_firing_bridge.rb +57 -0
  42. data/lib/appium_lib_core/version.rb +2 -2
  43. data/lib/appium_lib_core/windows/device/app_management.rb +38 -0
  44. data/lib/appium_lib_core/windows/device.rb +2 -0
  45. data/lib/appium_lib_core.rb +21 -10
  46. metadata +31 -86
  47. data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
  48. data/.github/contributing.md +0 -26
  49. data/.github/issue_template.md +0 -20
  50. data/.github/workflows/unittest.yml +0 -68
  51. data/.gitignore +0 -18
  52. data/.rubocop.yml +0 -58
  53. data/azure-pipelines.yml +0 -15
  54. data/ci-jobs/functional/android_setup.yml +0 -3
  55. data/ci-jobs/functional/ios_setup.yml +0 -7
  56. data/ci-jobs/functional/publish_test_result.yml +0 -18
  57. data/ci-jobs/functional/run_appium.yml +0 -25
  58. data/ci-jobs/functional/start-emulator.sh +0 -26
  59. data/ci-jobs/functional_test.yml +0 -298
  60. data/docs/mobile_command.md +0 -34
  61. data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
  62. data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
  63. data/lib/appium_lib_core/common/command/common.rb +0 -110
  64. data/lib/appium_lib_core/common/command/w3c.rb +0 -56
  65. data/lib/appium_lib_core/ios/uiautomation/device.rb +0 -44
  66. data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
  67. data/release_notes.md +0 -816
  68. 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,75 @@ 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
+ # override
48
+ def initialize(bridge: nil, listener: nil, **opts) # rubocop:disable Lint/MissingSuper
49
+ original_opts = opts.dup
50
+
51
+ # For ::Appium::Core::Waitable
52
+ @wait_timeout = opts.delete(:wait_timeout)
53
+ @wait_interval = opts.delete(:wait_interval)
54
+
55
+ # Selenium WebDriver attributes
56
+ @devtools = nil
57
+ @bidi = nil
58
+
59
+ # in the selenium webdriver as well
60
+ bridge ||= create_bridge(**opts)
61
+ add_extensions(bridge.browser)
62
+ @bridge = listener ? ::Appium::Support::EventFiringBridge.new(bridge, listener, **original_opts) : bridge
50
63
  end
51
64
 
52
- # Get the dialect value
53
- # @return [:oss|:w3c]
54
- def dialect
55
- @bridge.dialect
65
+ private
66
+
67
+ # Create a proper bridge instance.
68
+ #
69
+ # @return [::Appium::Core::Base::Bridge]
70
+ #
71
+ def create_bridge(**opts)
72
+ # for a new session request
73
+ capabilities = opts.delete(:capabilities)
74
+ bridge_opts = { http_client: opts.delete(:http_client), url: opts.delete(:url) }
75
+
76
+ # for attaching to an existing session
77
+ session_id = opts.delete(:existing_session_id)
78
+ automation_name = opts.delete(:automation_name)
79
+ platform_name = opts.delete(:platform_name)
80
+
81
+ raise ::Appium::Core::Error::ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
82
+
83
+ bridge = ::Appium::Core::Base::Bridge.new(**bridge_opts)
84
+
85
+ if session_id.nil?
86
+ bridge.create_session(capabilities)
87
+ else
88
+ # attach to the existing session id
89
+ bridge.attach_to(session_id, platform_name, automation_name)
90
+ end
91
+
92
+ bridge
56
93
  end
57
94
 
95
+ public
96
+
58
97
  # Update +server_url+ and HTTP clients following this arguments, protocol, host, port and path.
59
98
  # After this method, +@bridge.http+ will be a new instance following them instead of +server_url+ which is
60
99
  # set before creating session.
100
+ # If +@bridge.http+ did not have +update_sending_request_to+ method, this method returns immediately.
61
101
  #
62
102
  # @example
63
103
  #
@@ -66,14 +106,124 @@ module Appium
66
106
  # driver.manage.timeouts.implicit_wait = 10 # @bridge.http is for 'https://example2.com:9000/wd/hub/'
67
107
  #
68
108
  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)
109
+ unless @bridge.http&.class&.method_defined? :update_sending_request_to
110
+ ::Appium::Logger.warn "#{@bridge.http&.class} has no 'update_sending_request_to'. " \
111
+ 'It keeps current connection target.'
112
+ return
113
+ end
114
+
115
+ @bridge.http&.update_sending_request_to(scheme: protocol,
116
+ host: host,
117
+ port: port,
118
+ path: path)
119
+ end
120
+
121
+ AVAILABLE_METHODS = [
122
+ :get, :head, :post, :put, :delete,
123
+ :connect, :options, :trace, :patch
124
+ ].freeze
125
+ # Define a new custom method to the driver so that you can define your own method for
126
+ # drivers/plugins in Appium 2.0. Appium 2.0 and its custom drivers/plugins allow you
127
+ # to define custom commands that are not part of W3C spec.
128
+ #
129
+ # @param [Symbol] method HTTP request method as https://www.w3.org/TR/webdriver/#endpoints
130
+ # @param [string] url The url to URL template as https://www.w3.org/TR/webdriver/#endpoints.
131
+ # +:session_id+ is the placeholder of 'session id'.
132
+ # Other place holders can be specified with +:+ prefix like +:id+.
133
+ # Then, the +:id+ will be replaced with a given value as the seconds argument of +execute+
134
+ # @param [Symbol] name The name of method that is called as the driver instance method.
135
+ # @param [Proc] block The block to involve as the method.
136
+ # Please define a method that has the same +name+ with arguments you want.
137
+ # The method must has +execute+ method. tHe +execute+ is calls the +url+
138
+ # with the given parameters.
139
+ # The first argument should be +name+ as symbol.
140
+ # The second argument should be hash. If keys in the hash matches +:+ prefix
141
+ # string in the given url, the matched string in the given url will be
142
+ # values in the hash.
143
+ # The third argument should be hash. The hash will be the request body.
144
+ # Please read examples below for more details.
145
+ # @raise [ArgumentError] If the given +method+ is invalid value.
146
+ #
147
+ # @example
148
+ #
149
+ # @driver.add_command(
150
+ # method: :get,
151
+ # url: 'session/:session_id/path/to/custom/url',
152
+ # name: :test_command
153
+ # )
154
+ # # Send a GET request to 'session/<session id>/path/to/custom/url'
155
+ # @driver.test_command
156
+ #
157
+ #
158
+ # @driver.add_command(
159
+ # method: :post,
160
+ # url: 'session/:session_id/path/to/custom/url',
161
+ # name: :test_command
162
+ # ) do
163
+ # def test_command(argument)
164
+ # execute(:test_command, {}, { dummy: argument })
165
+ # end
166
+ # end
167
+ # # Send a POST request to 'session/<session id>/path/to/custom/url'
168
+ # # with body "{ dummy: 1 }" as JSON object. "1" is the argument.
169
+ # # ':session_id' in the given 'url' is replaced with current 'session id'.
170
+ # @driver.test_command(1)
171
+ #
172
+ #
173
+ # @driver.add_command(
174
+ # method: :post,
175
+ # url: 'session/:session_id/element/:id/custom/action',
176
+ # name: :test_action_command
177
+ # ) do
178
+ # def test_action_command(element_id, action)
179
+ # execute(:test_action_command, {id: element_id}, { dummy_action: action })
180
+ # end
181
+ # end
182
+ # # Send a POST request to 'session/<session id>/element/<element id>/custom/action'
183
+ # # with body "{ dummy_action: #{action} }" as JSON object. "action" is the seconds argument.
184
+ # # ':session_id' in the given url is replaced with current 'session id'.
185
+ # # ':id' in the given url is replaced with the given 'element_id'.
186
+ # e = @driver.find_element :accessibility_id, 'an element'
187
+ # @driver.test_action_command(e.id, 'action')
188
+ #
189
+ def add_command(method:, url:, name:, &block)
190
+ unless AVAILABLE_METHODS.include? method
191
+ raise ::Appium::Core::Error::ArgumentError, "Available method is either #{AVAILABLE_METHODS}"
192
+ end
193
+
194
+ # TODO: Remove this logger before Appium 2.0 release
195
+ ::Appium::Logger.info '[Experimental] this method is experimental for Appium 2.0. This interface may change.'
196
+
197
+ @bridge.add_command method: method, url: url, name: name, &block
73
198
  end
74
199
 
75
200
  ### Methods for Appium
76
201
 
202
+ # Perform 'key' actions for W3C module.
203
+ # Generate +key+ pointer action here and users can use this via +driver.key_action+
204
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/W3CActionBuilder.html
205
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/KeyActions.html
206
+ #
207
+ # The pointer type is 'key' by default in the Appium Ruby client.
208
+ # +driver.action+ in Appium Ruby client has 'pointer' action by default.
209
+ # This method is a shortcut to set 'key' type.
210
+ # Hense this method is equal to +driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')])+
211
+ # as below example.
212
+ #
213
+ # @example
214
+ #
215
+ # element = @driver.find_element(:id, "some id")
216
+ # @driver.key_action.send_key('hiあ').perform # The 'send_key' is a part of 'KeyActions'
217
+ # # is equal to:
218
+ # # @driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')]).send_keys('hiあ').perform
219
+ #
220
+ def key_action(async: false)
221
+ @bridge.action(
222
+ async: async,
223
+ devices: [::Selenium::WebDriver::Interactions.key('keyboard')]
224
+ )
225
+ end
226
+
77
227
  # Lock the device
78
228
  # @return [String]
79
229
  #
@@ -138,38 +288,6 @@ module Appium
138
288
  end
139
289
  alias is_keyboard_shown keyboard_shown?
140
290
 
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
291
  # Returns an instance of DriverSettings to call get/update.
174
292
  #
175
293
  # @example
@@ -178,7 +296,7 @@ module Appium
178
296
  # @driver.settings.update('allowInvisibleElements': true)
179
297
  #
180
298
  def settings
181
- @driver_settings ||= DriverSettings.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
299
+ @settings ||= DriverSettings.new(@bridge)
182
300
  end
183
301
 
184
302
  # Get appium Settings for current test session.
@@ -200,8 +318,8 @@ module Appium
200
318
  #
201
319
  # @example
202
320
  #
203
- # @driver.update_settings('allowInvisibleElements': true)
204
- # @driver.settings.update('allowInvisibleElements': true)
321
+ # @driver.update_settings({ 'allowInvisibleElements': true })
322
+ # @driver.settings.update({ 'allowInvisibleElements': true })
205
323
  # @driver.settings = { 'allowInvisibleElements': true }
206
324
  #
207
325
  def settings=(value)
@@ -209,36 +327,10 @@ module Appium
209
327
  end
210
328
  alias update_settings settings=
211
329
 
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
330
  # Returns an instance of DeviceIME
241
331
  #
332
+ # @return [Appium::Core::Base::Driver::DeviceIME]
333
+ #
242
334
  # @example
243
335
  #
244
336
  # @driver.ime.activate engine: 'com.android.inputmethod.latin/.LatinIME'
@@ -248,7 +340,7 @@ module Appium
248
340
  # @driver.ime.deactivate #=> Deactivate current IME engine
249
341
  #
250
342
  def ime
251
- @device_ime ||= DeviceIME.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
343
+ @ime ||= DeviceIME.new(@bridge)
252
344
  end
253
345
 
254
346
  # Android only. Make an engine that is available active.
@@ -289,6 +381,8 @@ module Appium
289
381
  # @!method ime_activated
290
382
  # Android only. Indicates whether IME input is active at the moment (not if it is available).
291
383
  #
384
+ # @return [Boolean]
385
+ #
292
386
  # @example
293
387
  #
294
388
  # @driver.ime_activated #=> True if IME is activated
@@ -366,42 +460,10 @@ module Appium
366
460
  end
367
461
  alias set_context context=
368
462
 
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
463
  # 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
464
  # On Android, the application under test should be built with debuggable flag enabled in order to get access to
397
465
  # its container on the internal file system.
398
466
  #
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
467
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
406
468
  # If the path starts with application id prefix, then the file will be pushed to the root of
407
469
  # the corresponding application container.
@@ -418,18 +480,10 @@ module Appium
418
480
  @bridge.push_file(path, filedata)
419
481
  end
420
482
 
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.
483
+ # Pull a file from the remote device.
424
484
  # On Android the application under test should be built with debuggable flag enabled in order to get access
425
485
  # to its container on the internal file system.
426
486
  #
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
487
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
434
488
  # If the path starts with application id prefix, then the file will be pulled from the root
435
489
  # of the corresponding application container.
@@ -438,7 +492,6 @@ module Appium
438
492
  # Only pulling files from application containers is supported for iOS Simulator.
439
493
  # Provide the remote path in format
440
494
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
441
- # (Make sure this in ifuse doc)
442
495
  #
443
496
  # @return [Base64-decoded] Base64 decoded data
444
497
  #
@@ -455,18 +508,10 @@ module Appium
455
508
  @bridge.pull_file(path)
456
509
  end
457
510
 
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.
511
+ # Pull a folder content from the remote device.
461
512
  # On Android the application under test should be built with debuggable flag enabled in order to get access to
462
513
  # its container on the internal file system.
463
514
  #
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
515
  # @param [String] path Absolute path to the folder.
471
516
  # If the path starts with <em>@applicationId/</em> prefix, then the folder will be pulled
472
517
  # from the root of the corresponding application container.
@@ -475,7 +520,6 @@ module Appium
475
520
  # Only pulling files from application containers is supported for iOS Simulator.
476
521
  # Provide the remote path in format
477
522
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
478
- # (Make sure this in ifuse doc)
479
523
  #
480
524
  # @return [Base64-decoded] Base64 decoded data which is zip archived
481
525
  #
@@ -489,19 +533,6 @@ module Appium
489
533
  @bridge.pull_folder(path)
490
534
  end
491
535
 
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
536
  # Press keycode on the device.
506
537
  # http://developer.android.com/reference/android/view/KeyEvent.html
507
538
  # @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 +577,7 @@ module Appium
546
577
  @bridge.long_press_keycode(key, metastate: metastate, flags: flags)
547
578
  end
548
579
 
580
+ # @deprecated Except for Windows
549
581
  # Start the simulator and application configured with desired capabilities
550
582
  #
551
583
  # @example
@@ -556,6 +588,7 @@ module Appium
556
588
  @bridge.launch_app
557
589
  end
558
590
 
591
+ # @deprecated Except for Windows
559
592
  # Close an app on device
560
593
  #
561
594
  # @example
@@ -566,6 +599,7 @@ module Appium
566
599
  @bridge.close_app
567
600
  end
568
601
 
602
+ # @deprecated
569
603
  # Reset the device, relaunching the application.
570
604
  #
571
605
  # @example
@@ -573,6 +607,10 @@ module Appium
573
607
  # @driver.reset
574
608
  #
575
609
  def reset
610
+ ::Appium::Logger.warn(
611
+ '[DEPRECATION] reset is deprecated. Please use terminate_app and activate_app, ' \
612
+ 'or quit and create a new session instead.'
613
+ )
576
614
  @bridge.reset
577
615
  end
578
616
 
@@ -602,7 +640,9 @@ module Appium
602
640
  @bridge.background_app(duration)
603
641
  end
604
642
 
605
- # Install the given app onto the device
643
+ # Install the given app onto the device.
644
+ # Each options can be snake-case or camel-case. Snake-cases will be converted to camel-case
645
+ # as options value.
606
646
  #
607
647
  # @param [String] path The absolute local path or remote http URL to an .ipa or .apk file,
608
648
  # or a .zip containing one of these.
@@ -616,26 +656,26 @@ module Appium
616
656
  # @param [Boolean] grant_permissions Only for Android. whether to automatically grant application permissions
617
657
  # on Android 6+ after the installation completes. +false+ by default
618
658
  #
659
+ # Other parameters such as https://github.com/appium/appium-xcuitest-driver#mobile-installapp also can be set.
660
+ # Then, arguments in snake case will be camel case as its request parameters.
661
+ #
619
662
  # @example
620
663
  #
621
664
  # @driver.install_app("/path/to/test.apk")
622
665
  # @driver.install_app("/path/to/test.apk", replace: true, timeout: 20000, allow_test_packages: true,
623
666
  # use_sdcard: false, grant_permissions: false)
667
+ # @driver.install_app("/path/to/test.ipa", timeoutMs: 20000)
624
668
  #
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)
669
+ def install_app(path, **options)
670
+ options = options.transform_keys { |key| key.to_s.gsub(/_./) { |v| v[1].upcase } } unless options.nil?
671
+ @bridge.install_app(path, options)
637
672
  end
638
673
 
674
+ # def capitalize(s)
675
+ # chars =
676
+ # chars[1:].map(&:capitalize).join
677
+ # end
678
+
639
679
  # @param [Strong] app_id BundleId for iOS or package name for Android
640
680
  # @param [Boolean] keep_data Only for Android. Whether to keep application data and caches after it is uninstalled.
641
681
  # +false+ by default
@@ -790,7 +830,7 @@ module Appium
790
830
  #
791
831
  # @example: Zoom
792
832
  #
793
- # f1 = @driver.action.add_pointer_input(:touch, 'finger1')
833
+ # f1 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger1')
794
834
  # f1.create_pointer_move(duration: 1, x: 200, y: 500,
795
835
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
796
836
  # f1.create_pointer_down(:left)
@@ -798,7 +838,7 @@ module Appium
798
838
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
799
839
  # f1.create_pointer_up(:left)
800
840
  #
801
- # f2 = @driver.action.add_pointer_input(:touch, 'finger2')
841
+ # f2 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger2')
802
842
  # f2.create_pointer_move(duration: 1, x: 200, y: 500,
803
843
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
804
844
  # f2.create_pointer_down(:left)
@@ -809,7 +849,11 @@ module Appium
809
849
  # @driver.perform_actions [f1, f2] #=> 'nil' if the action succeed
810
850
  #
811
851
  def perform_actions(data)
812
- raise ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
852
+ raise ::Appium::Core::Error::ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
853
+
854
+ # NOTE: 'add_input' in Selenium Ruby implementation has additional 'pause'.
855
+ # This implementation is to avoid the additional pause.
856
+ # https://github.com/SeleniumHQ/selenium/blob/64447d4b03f6986337d1ca8d8b6476653570bcc1/rb/lib/selenium/webdriver/common/action_builder.rb#L207
813
857
 
814
858
  @bridge.send_actions data.map(&:encode).compact
815
859
  data.each(&:clear_actions)
@@ -853,7 +897,7 @@ module Appium
853
897
  end
854
898
 
855
899
  # Get the device window's logs.
856
- # @return [String]
900
+ # @return [Appium::Core::Logs]
857
901
  #
858
902
  # @example
859
903
  #
@@ -879,16 +923,15 @@ module Appium
879
923
  # Retrieve the capabilities of the specified session.
880
924
  # It's almost same as +@driver.capabilities+ but you can get more details.
881
925
  #
882
- # @return [Selenium::WebDriver::Remote::Capabilities]
926
+ # @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::Capabilities]
883
927
  #
884
928
  # @example
885
929
  # @driver.session_capabilities
886
930
  #
887
931
  # #=> uiautomator2
888
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fa38dae1360
932
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fa38dae1360
889
933
  # # @capabilities=
890
- # # {:proxy=>nil,
891
- # # :browser_name=>nil,
934
+ # # {:browser_name=>nil,
892
935
  # # :browser_version=>nil,
893
936
  # # :platform_name=>"android",
894
937
  # # :page_load_strategy=>nil,
@@ -914,8 +957,6 @@ module Appium
914
957
  # # "appPackage"=>"io.appium.android.apis",
915
958
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
916
959
  # # "someCapability"=>"some_capability",
917
- # # "unicodeKeyboard"=>true,
918
- # # "resetKeyboard"=>true},
919
960
  # # "automationName"=>"uiautomator2",
920
961
  # # "app"=>"/path/to/app/api.apk.zip",
921
962
  # # "platformVersion"=>"8.1.0",
@@ -923,8 +964,6 @@ module Appium
923
964
  # # "appPackage"=>"io.appium.android.apis",
924
965
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
925
966
  # # "someCapability"=>"some_capability",
926
- # # "unicodeKeyboard"=>true,
927
- # # "resetKeyboard"=>true,
928
967
  # # "deviceUDID"=>"emulator-5554",
929
968
  # # "deviceScreenSize"=>"1080x1920",
930
969
  # # "deviceScreenDensity"=>420,
@@ -935,10 +974,9 @@ module Appium
935
974
  # # "viewportRect"=>{"left"=>0, "top"=>63, "width"=>1080, "height"=>1731}}>
936
975
  # #
937
976
  # #=> XCUITest
938
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fb15dc01370
977
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fb15dc01370
939
978
  # # @capabilities=
940
- # # {:proxy=>nil,
941
- # # :browser_name=>"UICatalog",
979
+ # # {:browser_name=>"UICatalog",
942
980
  # # :browser_version=>nil,
943
981
  # # :platform_name=>"ios",
944
982
  # # :page_load_strategy=>nil,
@@ -992,11 +1030,14 @@ module Appium
992
1030
  visualize: visualize)
993
1031
  end
994
1032
 
995
- def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil)
1033
+ def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil,
1034
+ multiple: nil, match_neighbour_threshold: nil)
996
1035
  @bridge.find_image_occurrence(full_image: full_image,
997
1036
  partial_image: partial_image,
998
1037
  visualize: visualize,
999
- threshold: threshold)
1038
+ threshold: threshold,
1039
+ multiple: multiple,
1040
+ match_neighbour_threshold: match_neighbour_threshold)
1000
1041
  end
1001
1042
 
1002
1043
  def get_images_similarity(first_image:, second_image:, visualize: false)
@@ -1009,14 +1050,14 @@ module Appium
1009
1050
 
1010
1051
  # @since Appium 1.8.2
1011
1052
  # 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}
1053
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1013
1054
  #
1014
1055
  # You can handle settings for the comparision following below.
1015
1056
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1016
1057
  #
1017
1058
  # @param [String] img_path A path to a partial image you'd like to find
1018
1059
  #
1019
- # @return [::Selenium::WebDriver::Element]
1060
+ # @return [::Appium::Core::Element]
1020
1061
  #
1021
1062
  # @example
1022
1063
  #
@@ -1031,14 +1072,14 @@ module Appium
1031
1072
 
1032
1073
  # @since Appium 1.8.2
1033
1074
  # 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}
1075
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1035
1076
  #
1036
1077
  # You can handle settings for the comparision following below.
1037
1078
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1038
1079
  #
1039
1080
  # @param [String] img_path A path to a partial image you'd like to find
1040
1081
  #
1041
- # @return [::Selenium::WebDriver::Element]
1082
+ # @return [Array<::Appium::Core::Element>]
1042
1083
  #
1043
1084
  # @example
1044
1085
  #
@@ -1086,14 +1127,14 @@ module Appium
1086
1127
  @bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
1087
1128
  end
1088
1129
 
1089
- # Convert vanilla element response to ::Selenium::WebDriver::Element
1130
+ # Convert vanilla element response to ::Appium::Core::Element
1090
1131
  #
1091
1132
  # @param [Hash] id The id which can get as a response from server
1092
- # @return [::Selenium::WebDriver::Element]
1133
+ # @return [::Appium::Core::Element]
1093
1134
  #
1094
1135
  # @example
1095
1136
  # response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
1096
- # ele = @driver.convert_to_element(response) #=> ::Selenium::WebDriver::Element
1137
+ # ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
1097
1138
  # ele.rect #=> Can get the rect of the element
1098
1139
  #
1099
1140
  def convert_to_element(id)