appium_lib_core 4.1.0 → 7.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +276 -271
  3. data/README.md +65 -15
  4. data/Rakefile +5 -22
  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 +4 -1
  8. data/lib/appium_lib_core/android/device/network.rb +10 -0
  9. data/lib/appium_lib_core/android/device/performance.rb +3 -0
  10. data/lib/appium_lib_core/android/device/screen.rb +2 -0
  11. data/lib/appium_lib_core/android/device.rb +82 -4
  12. data/lib/appium_lib_core/common/base/bridge.rb +320 -90
  13. data/lib/appium_lib_core/common/base/capabilities.rb +8 -9
  14. data/lib/appium_lib_core/common/base/device_ime.rb +49 -0
  15. data/lib/appium_lib_core/common/base/driver.rb +270 -192
  16. data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
  17. data/lib/appium_lib_core/common/base/has_location.rb +80 -0
  18. data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
  19. data/lib/appium_lib_core/common/base/http_default.rb +15 -38
  20. data/lib/appium_lib_core/{ios/uiautomation/bridge.rb → common/base/remote_status.rb} +9 -8
  21. data/lib/appium_lib_core/common/base/rotable.rb +62 -0
  22. data/lib/appium_lib_core/common/base/screenshot.rb +8 -8
  23. data/lib/appium_lib_core/common/base/search_context.rb +20 -6
  24. data/lib/appium_lib_core/common/base.rb +1 -3
  25. data/lib/appium_lib_core/common/command.rb +260 -4
  26. data/lib/appium_lib_core/common/device/app_management.rb +8 -14
  27. data/lib/appium_lib_core/common/device/context.rb +1 -1
  28. data/lib/appium_lib_core/common/device/image_comparison.rb +12 -4
  29. data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
  30. data/lib/appium_lib_core/common/{command/mjsonwp.rb → device/orientation.rb} +14 -11
  31. data/lib/appium_lib_core/common/device/touch_actions.rb +2 -0
  32. data/lib/appium_lib_core/common/device/value.rb +6 -8
  33. data/lib/appium_lib_core/common/error.rb +4 -5
  34. data/lib/appium_lib_core/common/log.rb +4 -1
  35. data/lib/appium_lib_core/common/touch_action/multi_touch.rb +19 -0
  36. data/lib/appium_lib_core/common/touch_action/touch_actions.rb +16 -2
  37. data/lib/appium_lib_core/common/wait.rb +38 -6
  38. data/lib/appium_lib_core/device.rb +1 -5
  39. data/lib/appium_lib_core/driver.rb +194 -108
  40. data/lib/appium_lib_core/{patch.rb → element.rb} +75 -9
  41. data/lib/appium_lib_core/ios/xcuitest/device.rb +2 -0
  42. data/lib/appium_lib_core/{common/base/command.rb → mac2/bridge.rb} +9 -8
  43. data/lib/appium_lib_core/mac2/device/screen.rb +48 -0
  44. data/lib/appium_lib_core/mac2/device.rb +92 -0
  45. data/lib/appium_lib_core/{ios.rb → mac2.rb} +2 -5
  46. data/lib/appium_lib_core/support/event_firing_bridge.rb +57 -0
  47. data/lib/appium_lib_core/version.rb +2 -2
  48. data/lib/appium_lib_core/windows/device/app_management.rb +38 -0
  49. data/lib/appium_lib_core/windows/device.rb +2 -0
  50. data/lib/appium_lib_core.rb +21 -10
  51. metadata +31 -86
  52. data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
  53. data/.github/contributing.md +0 -26
  54. data/.github/issue_template.md +0 -20
  55. data/.github/workflows/unittest.yml +0 -68
  56. data/.gitignore +0 -18
  57. data/.rubocop.yml +0 -58
  58. data/azure-pipelines.yml +0 -15
  59. data/ci-jobs/functional/android_setup.yml +0 -3
  60. data/ci-jobs/functional/ios_setup.yml +0 -7
  61. data/ci-jobs/functional/publish_test_result.yml +0 -18
  62. data/ci-jobs/functional/run_appium.yml +0 -25
  63. data/ci-jobs/functional/start-emulator.sh +0 -26
  64. data/ci-jobs/functional_test.yml +0 -298
  65. data/docs/mobile_command.md +0 -34
  66. data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
  67. data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
  68. data/lib/appium_lib_core/common/command/common.rb +0 -110
  69. data/lib/appium_lib_core/common/command/w3c.rb +0 -56
  70. data/lib/appium_lib_core/ios/uiautomation/device.rb +0 -44
  71. data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
  72. data/release_notes.md +0 -816
  73. 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,125 @@ 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
+
227
+ # @deprecated Use 'mobile: lock' extension instead.
77
228
  # Lock the device
78
229
  # @return [String]
79
230
  #
@@ -84,9 +235,11 @@ module Appium
84
235
  # # Block other commands during locking the device.
85
236
  #
86
237
  def lock(duration = nil)
238
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: lock' extension instead"
87
239
  @bridge.lock(duration)
88
240
  end
89
241
 
242
+ # @deprecated Use 'mobile: isLocked' extension instead.
90
243
  # Check current device status is weather locked or not
91
244
  #
92
245
  # @example
@@ -95,10 +248,12 @@ module Appium
95
248
  # @driver.locked?
96
249
  #
97
250
  def locked?
251
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: isLocked' extension instead"
98
252
  @bridge.device_locked?
99
253
  end
100
254
  alias device_locked? locked?
101
255
 
256
+ # @deprecated Use 'mobile: unlock' extension instead.
102
257
  # Unlock the device
103
258
  #
104
259
  # @example
@@ -106,9 +261,11 @@ module Appium
106
261
  # @driver.unlock
107
262
  #
108
263
  def unlock
264
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: unlock' extension instead"
109
265
  @bridge.unlock
110
266
  end
111
267
 
268
+ # @deprecated Use 'mobile: hideKeyboard' extension instead.
112
269
  # Hide the onscreen keyboard
113
270
  # @param [String] close_key The name of the key which closes the keyboard.
114
271
  # Defaults to 'Done' for iOS(except for XCUITest).
@@ -123,9 +280,11 @@ module Appium
123
280
  # @driver.hide_keyboard(nil, :tapOutside) # Close a keyboard with tapping out side of keyboard
124
281
  #
125
282
  def hide_keyboard(close_key = nil, strategy = nil)
283
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: hideKeyboard' extension instead"
126
284
  @bridge.hide_keyboard close_key, strategy
127
285
  end
128
286
 
287
+ # @deprecated Use 'mobile: isKeyboardShown' extension instead.
129
288
  # Get whether keyboard is displayed or not.
130
289
  # @return [Boolean] Return true if keyboard is shown. Return false if keyboard is hidden.
131
290
  #
@@ -134,42 +293,11 @@ module Appium
134
293
  # @driver.keyboard_shown? # true
135
294
  #
136
295
  def keyboard_shown?
296
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: isKeyboardShown' extension instead"
137
297
  @bridge.is_keyboard_shown
138
298
  end
139
299
  alias is_keyboard_shown keyboard_shown?
140
300
 
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
301
  # Returns an instance of DriverSettings to call get/update.
174
302
  #
175
303
  # @example
@@ -178,7 +306,7 @@ module Appium
178
306
  # @driver.settings.update('allowInvisibleElements': true)
179
307
  #
180
308
  def settings
181
- @driver_settings ||= DriverSettings.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
309
+ @settings ||= DriverSettings.new(@bridge)
182
310
  end
183
311
 
184
312
  # Get appium Settings for current test session.
@@ -200,8 +328,8 @@ module Appium
200
328
  #
201
329
  # @example
202
330
  #
203
- # @driver.update_settings('allowInvisibleElements': true)
204
- # @driver.settings.update('allowInvisibleElements': true)
331
+ # @driver.update_settings({ 'allowInvisibleElements': true })
332
+ # @driver.settings.update({ 'allowInvisibleElements': true })
205
333
  # @driver.settings = { 'allowInvisibleElements': true }
206
334
  #
207
335
  def settings=(value)
@@ -209,36 +337,11 @@ module Appium
209
337
  end
210
338
  alias update_settings settings=
211
339
 
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
-
340
+ # @deprecated Use 'mobile: shell' extension instead.
240
341
  # Returns an instance of DeviceIME
241
342
  #
343
+ # @return [Appium::Core::Base::Driver::DeviceIME]
344
+ #
242
345
  # @example
243
346
  #
244
347
  # @driver.ime.activate engine: 'com.android.inputmethod.latin/.LatinIME'
@@ -248,9 +351,11 @@ module Appium
248
351
  # @driver.ime.deactivate #=> Deactivate current IME engine
249
352
  #
250
353
  def ime
251
- @device_ime ||= DeviceIME.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
354
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: shell' extension instead"
355
+ @ime ||= DeviceIME.new(@bridge)
252
356
  end
253
357
 
358
+ # @deprecated Use 'mobile: shell' extension instead.
254
359
  # Android only. Make an engine that is available active.
255
360
  #
256
361
  # @param [String] ime_name The IME owning the activity [required]
@@ -264,6 +369,7 @@ module Appium
264
369
  ime.activate(ime_name)
265
370
  end
266
371
 
372
+ # @deprecated Use 'mobile: shell' extension instead.
267
373
  # Android only. List all available input engines on the machine.
268
374
  #
269
375
  # @example
@@ -275,6 +381,7 @@ module Appium
275
381
  ime.available_engines
276
382
  end
277
383
 
384
+ # @deprecated Use 'mobile: shell' extension instead.
278
385
  # Android only. Get the name of the active IME engine.
279
386
  #
280
387
  # @example
@@ -286,9 +393,12 @@ module Appium
286
393
  ime.active_engine
287
394
  end
288
395
 
396
+ # @deprecated Use 'mobile: shell' extension instead.
289
397
  # @!method ime_activated
290
398
  # Android only. Indicates whether IME input is active at the moment (not if it is available).
291
399
  #
400
+ # @return [Boolean]
401
+ #
292
402
  # @example
293
403
  #
294
404
  # @driver.ime_activated #=> True if IME is activated
@@ -298,6 +408,7 @@ module Appium
298
408
  ime.activated?
299
409
  end
300
410
 
411
+ # @deprecated Use 'mobile: shell' extension instead.
301
412
  # Android only. De-activates the currently-active IME engine.
302
413
  #
303
414
  # @example
@@ -361,47 +472,15 @@ module Appium
361
472
  # @driver.set_context "NATIVE_APP"
362
473
  # @driver.context = "NATIVE_APP"
363
474
  #
364
- def context=(context = null)
475
+ def context=(context = nil)
365
476
  @bridge.set_context(context)
366
477
  end
367
478
  alias set_context context=
368
479
 
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
480
  # 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
481
  # On Android, the application under test should be built with debuggable flag enabled in order to get access to
397
482
  # its container on the internal file system.
398
483
  #
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
484
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
406
485
  # If the path starts with application id prefix, then the file will be pushed to the root of
407
486
  # the corresponding application container.
@@ -415,21 +494,14 @@ module Appium
415
494
  # @driver.push_file "/sdcard/Pictures", file # Push a file binary to /sdcard/Pictures path in Android
416
495
  #
417
496
  def push_file(path, filedata)
497
+ # TODO: use 'mobile: pushFile' internally
418
498
  @bridge.push_file(path, filedata)
419
499
  end
420
500
 
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.
501
+ # Pull a file from the remote device.
424
502
  # On Android the application under test should be built with debuggable flag enabled in order to get access
425
503
  # to its container on the internal file system.
426
504
  #
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
505
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
434
506
  # If the path starts with application id prefix, then the file will be pulled from the root
435
507
  # of the corresponding application container.
@@ -438,7 +510,6 @@ module Appium
438
510
  # Only pulling files from application containers is supported for iOS Simulator.
439
511
  # Provide the remote path in format
440
512
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
441
- # (Make sure this in ifuse doc)
442
513
  #
443
514
  # @return [Base64-decoded] Base64 decoded data
444
515
  #
@@ -452,21 +523,14 @@ module Appium
452
523
  # File.open('proper_filename', 'wb') { |f| f<< decoded_file }
453
524
  #
454
525
  def pull_file(path)
526
+ # TODO: use 'mobile: pullFile' internally
455
527
  @bridge.pull_file(path)
456
528
  end
457
529
 
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.
530
+ # Pull a folder content from the remote device.
461
531
  # On Android the application under test should be built with debuggable flag enabled in order to get access to
462
532
  # its container on the internal file system.
463
533
  #
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
534
  # @param [String] path Absolute path to the folder.
471
535
  # If the path starts with <em>@applicationId/</em> prefix, then the folder will be pulled
472
536
  # from the root of the corresponding application container.
@@ -475,7 +539,6 @@ module Appium
475
539
  # Only pulling files from application containers is supported for iOS Simulator.
476
540
  # Provide the remote path in format
477
541
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
478
- # (Make sure this in ifuse doc)
479
542
  #
480
543
  # @return [Base64-decoded] Base64 decoded data which is zip archived
481
544
  #
@@ -486,22 +549,11 @@ module Appium
486
549
  # File.open('proper_filename', 'wb') { |f| f<< decoded_file }
487
550
  #
488
551
  def pull_folder(path)
552
+ # TODO: use 'mobile: pullFolder' internally
489
553
  @bridge.pull_folder(path)
490
554
  end
491
555
 
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
-
556
+ # @deprecated Use 'mobile: pressKey' extension instead.
505
557
  # Press keycode on the device.
506
558
  # http://developer.android.com/reference/android/view/KeyEvent.html
507
559
  # @param [Integer] key The key to press. The values which have +KEYCODE_+ prefix in http://developer.android.com/reference/android/view/KeyEvent.html
@@ -521,9 +573,11 @@ module Appium
521
573
  # @driver.press_keycode 66, metastate: [1], flags: [32]
522
574
  #
523
575
  def press_keycode(key, metastate: [], flags: [])
576
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: pressKey' extension instead"
524
577
  @bridge.press_keycode(key, metastate: metastate, flags: flags)
525
578
  end
526
579
 
580
+ # @deprecated Use 'mobile: pressKey' extension instead.
527
581
  # Long press keycode on the device.
528
582
  # http://developer.android.com/reference/android/view/KeyEvent.html
529
583
  # @param [Integer] key The key to long press. The values which have +KEYCODE_+ prefix in http://developer.android.com/reference/android/view/KeyEvent.html
@@ -543,9 +597,11 @@ module Appium
543
597
  # @driver.long_press_keycode 66, metastate: [1], flags: [32, 8192]
544
598
  #
545
599
  def long_press_keycode(key, metastate: [], flags: [])
600
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: pressKey' extension instead"
546
601
  @bridge.long_press_keycode(key, metastate: metastate, flags: flags)
547
602
  end
548
603
 
604
+ # @deprecated Will be removed, or use 'windows: launchApp' extension instead for Windows.
549
605
  # Start the simulator and application configured with desired capabilities
550
606
  #
551
607
  # @example
@@ -556,6 +612,7 @@ module Appium
556
612
  @bridge.launch_app
557
613
  end
558
614
 
615
+ # @deprecated Will be removed, or use 'windows: closeApp' extension instead for Windows.
559
616
  # Close an app on device
560
617
  #
561
618
  # @example
@@ -566,6 +623,7 @@ module Appium
566
623
  @bridge.close_app
567
624
  end
568
625
 
626
+ # @deprecated
569
627
  # Reset the device, relaunching the application.
570
628
  #
571
629
  # @example
@@ -573,9 +631,14 @@ module Appium
573
631
  # @driver.reset
574
632
  #
575
633
  def reset
634
+ ::Appium::Logger.warn(
635
+ '[DEPRECATION] reset is deprecated. Please use terminate_app and activate_app, ' \
636
+ 'or quit and create a new session instead.'
637
+ )
576
638
  @bridge.reset
577
639
  end
578
640
 
641
+ # @deprecated Use 'mobile: getAppStrings' extension instead.
579
642
  # Return the hash of all localization strings.
580
643
  # @return [Hash]
581
644
  #
@@ -584,9 +647,11 @@ module Appium
584
647
  # @driver.app_strings #=> "TransitionsTitle"=>"Transitions", "WebTitle"=>"Web"
585
648
  #
586
649
  def app_strings(language = nil)
650
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: getAppStrings' extension instead"
587
651
  @bridge.app_strings(language)
588
652
  end
589
653
 
654
+ # @deprecated Use 'mobile: backgroundApp' extension instead.
590
655
  # Backgrounds the app for a set number of seconds.
591
656
  # This is a blocking application
592
657
  # @param [Integer] duration How many seconds to background the app for.
@@ -602,7 +667,9 @@ module Appium
602
667
  @bridge.background_app(duration)
603
668
  end
604
669
 
605
- # Install the given app onto the device
670
+ # Install the given app onto the device.
671
+ # Each options can be snake-case or camel-case. Snake-cases will be converted to camel-case
672
+ # as options value.
606
673
  #
607
674
  # @param [String] path The absolute local path or remote http URL to an .ipa or .apk file,
608
675
  # or a .zip containing one of these.
@@ -616,26 +683,27 @@ module Appium
616
683
  # @param [Boolean] grant_permissions Only for Android. whether to automatically grant application permissions
617
684
  # on Android 6+ after the installation completes. +false+ by default
618
685
  #
686
+ # Other parameters such as https://github.com/appium/appium-xcuitest-driver#mobile-installapp also can be set.
687
+ # Then, arguments in snake case will be camel case as its request parameters.
688
+ #
619
689
  # @example
620
690
  #
621
691
  # @driver.install_app("/path/to/test.apk")
622
692
  # @driver.install_app("/path/to/test.apk", replace: true, timeout: 20000, allow_test_packages: true,
623
693
  # use_sdcard: false, grant_permissions: false)
694
+ # @driver.install_app("/path/to/test.ipa", timeoutMs: 20000)
624
695
  #
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)
696
+ def install_app(path, **options)
697
+ # TODO: use mobile command in the background?
698
+ options = options.transform_keys { |key| key.to_s.gsub(/_./) { |v| v[1].upcase } } unless options.nil?
699
+ @bridge.install_app(path, options)
637
700
  end
638
701
 
702
+ # def capitalize(s)
703
+ # chars =
704
+ # chars[1:].map(&:capitalize).join
705
+ # end
706
+
639
707
  # @param [Strong] app_id BundleId for iOS or package name for Android
640
708
  # @param [Boolean] keep_data Only for Android. Whether to keep application data and caches after it is uninstalled.
641
709
  # +false+ by default
@@ -647,6 +715,7 @@ module Appium
647
715
  # @driver.remove_app("io.appium.bundle", keep_data: false, timeout, 10000)
648
716
  #
649
717
  def remove_app(app_id, keep_data: nil, timeout: nil)
718
+ # TODO: use mobile command in the background?
650
719
  @bridge.remove_app(app_id, keep_data: keep_data, timeout: timeout)
651
720
  end
652
721
 
@@ -658,6 +727,7 @@ module Appium
658
727
  # @driver.app_installed?("io.appium.bundle")
659
728
  #
660
729
  def app_installed?(app_id)
730
+ # TODO: use mobile command in the background?
661
731
  @bridge.app_installed?(app_id)
662
732
  end
663
733
 
@@ -669,6 +739,7 @@ module Appium
669
739
  # @driver.activate_app("io.appium.bundle") #=> {}
670
740
  #
671
741
  def activate_app(app_id)
742
+ # TODO: use mobile command in the background?
672
743
  @bridge.activate_app(app_id)
673
744
  end
674
745
 
@@ -685,6 +756,7 @@ module Appium
685
756
  # @driver.terminate_app("io.appium.bundle", timeout: 500)
686
757
  #
687
758
  def terminate_app(app_id, timeout: nil)
759
+ # TODO: use mobile command in the background?
688
760
  @bridge.terminate_app(app_id, timeout: timeout)
689
761
  end
690
762
 
@@ -708,6 +780,7 @@ module Appium
708
780
  # @driver.query_app_state("io.appium.bundle") #=> :not_running
709
781
  #
710
782
  def app_state(app_id)
783
+ # TODO: use mobile command in the background?
711
784
  @bridge.app_state(app_id)
712
785
  end
713
786
  alias query_app_state app_state
@@ -747,6 +820,7 @@ module Appium
747
820
  @bridge.stop_and_save_recording_screen(file_path)
748
821
  end
749
822
 
823
+ # @deprecated Use 'mobile: shake' extension instead.
750
824
  # Cause the device to shake
751
825
  #
752
826
  # @example
@@ -754,9 +828,11 @@ module Appium
754
828
  # @driver.shake
755
829
  #
756
830
  def shake
831
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: shake' extension instead"
757
832
  @bridge.shake
758
833
  end
759
834
 
835
+ # @deprecated Use 'mobile: getDeviceTime' extension instead.
760
836
  # Get the time on the device
761
837
  #
762
838
  # @param [String] format The set of format specifiers. Read https://momentjs.com/docs/ to get
@@ -770,6 +846,7 @@ module Appium
770
846
  # @driver.device_time "YYYY-MM-DD" #=> "2018-06-12"
771
847
  #
772
848
  def device_time(format = nil)
849
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: getDeviceTime' extension instead"
773
850
  @bridge.device_time(format)
774
851
  end
775
852
 
@@ -790,7 +867,7 @@ module Appium
790
867
  #
791
868
  # @example: Zoom
792
869
  #
793
- # f1 = @driver.action.add_pointer_input(:touch, 'finger1')
870
+ # f1 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger1')
794
871
  # f1.create_pointer_move(duration: 1, x: 200, y: 500,
795
872
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
796
873
  # f1.create_pointer_down(:left)
@@ -798,7 +875,7 @@ module Appium
798
875
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
799
876
  # f1.create_pointer_up(:left)
800
877
  #
801
- # f2 = @driver.action.add_pointer_input(:touch, 'finger2')
878
+ # f2 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger2')
802
879
  # f2.create_pointer_move(duration: 1, x: 200, y: 500,
803
880
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
804
881
  # f2.create_pointer_down(:left)
@@ -809,7 +886,11 @@ module Appium
809
886
  # @driver.perform_actions [f1, f2] #=> 'nil' if the action succeed
810
887
  #
811
888
  def perform_actions(data)
812
- raise ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
889
+ raise ::Appium::Core::Error::ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
890
+
891
+ # NOTE: 'add_input' in Selenium Ruby implementation has additional 'pause'.
892
+ # This implementation is to avoid the additional pause.
893
+ # https://github.com/SeleniumHQ/selenium/blob/64447d4b03f6986337d1ca8d8b6476653570bcc1/rb/lib/selenium/webdriver/common/action_builder.rb#L207
813
894
 
814
895
  @bridge.send_actions data.map(&:encode).compact
815
896
  data.each(&:clear_actions)
@@ -853,7 +934,7 @@ module Appium
853
934
  end
854
935
 
855
936
  # Get the device window's logs.
856
- # @return [String]
937
+ # @return [Appium::Core::Logs]
857
938
  #
858
939
  # @example
859
940
  #
@@ -879,16 +960,15 @@ module Appium
879
960
  # Retrieve the capabilities of the specified session.
880
961
  # It's almost same as +@driver.capabilities+ but you can get more details.
881
962
  #
882
- # @return [Selenium::WebDriver::Remote::Capabilities]
963
+ # @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::Capabilities]
883
964
  #
884
965
  # @example
885
966
  # @driver.session_capabilities
886
967
  #
887
968
  # #=> uiautomator2
888
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fa38dae1360
969
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fa38dae1360
889
970
  # # @capabilities=
890
- # # {:proxy=>nil,
891
- # # :browser_name=>nil,
971
+ # # {:browser_name=>nil,
892
972
  # # :browser_version=>nil,
893
973
  # # :platform_name=>"android",
894
974
  # # :page_load_strategy=>nil,
@@ -914,8 +994,6 @@ module Appium
914
994
  # # "appPackage"=>"io.appium.android.apis",
915
995
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
916
996
  # # "someCapability"=>"some_capability",
917
- # # "unicodeKeyboard"=>true,
918
- # # "resetKeyboard"=>true},
919
997
  # # "automationName"=>"uiautomator2",
920
998
  # # "app"=>"/path/to/app/api.apk.zip",
921
999
  # # "platformVersion"=>"8.1.0",
@@ -923,8 +1001,6 @@ module Appium
923
1001
  # # "appPackage"=>"io.appium.android.apis",
924
1002
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
925
1003
  # # "someCapability"=>"some_capability",
926
- # # "unicodeKeyboard"=>true,
927
- # # "resetKeyboard"=>true,
928
1004
  # # "deviceUDID"=>"emulator-5554",
929
1005
  # # "deviceScreenSize"=>"1080x1920",
930
1006
  # # "deviceScreenDensity"=>420,
@@ -935,10 +1011,9 @@ module Appium
935
1011
  # # "viewportRect"=>{"left"=>0, "top"=>63, "width"=>1080, "height"=>1731}}>
936
1012
  # #
937
1013
  # #=> XCUITest
938
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fb15dc01370
1014
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fb15dc01370
939
1015
  # # @capabilities=
940
- # # {:proxy=>nil,
941
- # # :browser_name=>"UICatalog",
1016
+ # # {:browser_name=>"UICatalog",
942
1017
  # # :browser_version=>nil,
943
1018
  # # :platform_name=>"ios",
944
1019
  # # :page_load_strategy=>nil,
@@ -992,11 +1067,14 @@ module Appium
992
1067
  visualize: visualize)
993
1068
  end
994
1069
 
995
- def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil)
1070
+ def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil,
1071
+ multiple: nil, match_neighbour_threshold: nil)
996
1072
  @bridge.find_image_occurrence(full_image: full_image,
997
1073
  partial_image: partial_image,
998
1074
  visualize: visualize,
999
- threshold: threshold)
1075
+ threshold: threshold,
1076
+ multiple: multiple,
1077
+ match_neighbour_threshold: match_neighbour_threshold)
1000
1078
  end
1001
1079
 
1002
1080
  def get_images_similarity(first_image:, second_image:, visualize: false)
@@ -1009,14 +1087,14 @@ module Appium
1009
1087
 
1010
1088
  # @since Appium 1.8.2
1011
1089
  # 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}
1090
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1013
1091
  #
1014
1092
  # You can handle settings for the comparision following below.
1015
1093
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1016
1094
  #
1017
1095
  # @param [String] img_path A path to a partial image you'd like to find
1018
1096
  #
1019
- # @return [::Selenium::WebDriver::Element]
1097
+ # @return [::Appium::Core::Element]
1020
1098
  #
1021
1099
  # @example
1022
1100
  #
@@ -1031,14 +1109,14 @@ module Appium
1031
1109
 
1032
1110
  # @since Appium 1.8.2
1033
1111
  # 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}
1112
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1035
1113
  #
1036
1114
  # You can handle settings for the comparision following below.
1037
1115
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1038
1116
  #
1039
1117
  # @param [String] img_path A path to a partial image you'd like to find
1040
1118
  #
1041
- # @return [::Selenium::WebDriver::Element]
1119
+ # @return [Array<::Appium::Core::Element>]
1042
1120
  #
1043
1121
  # @example
1044
1122
  #
@@ -1086,14 +1164,14 @@ module Appium
1086
1164
  @bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
1087
1165
  end
1088
1166
 
1089
- # Convert vanilla element response to ::Selenium::WebDriver::Element
1167
+ # Convert vanilla element response to ::Appium::Core::Element
1090
1168
  #
1091
1169
  # @param [Hash] id The id which can get as a response from server
1092
- # @return [::Selenium::WebDriver::Element]
1170
+ # @return [::Appium::Core::Element]
1093
1171
  #
1094
1172
  # @example
1095
1173
  # response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
1096
- # ele = @driver.convert_to_element(response) #=> ::Selenium::WebDriver::Element
1174
+ # ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
1097
1175
  # ele.rect #=> Can get the rect of the element
1098
1176
  #
1099
1177
  def convert_to_element(id)