appium_lib_core 4.1.0 → 9.0.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +325 -270
  3. data/README.md +65 -15
  4. data/Rakefile +5 -22
  5. data/appium_lib_core.gemspec +6 -9
  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 +80 -17
  12. data/lib/appium_lib_core/common/base/bridge.rb +309 -93
  13. data/lib/appium_lib_core/common/base/capabilities.rb +21 -8
  14. data/lib/appium_lib_core/common/{command/mjsonwp.rb → base/device_ime.rb} +33 -12
  15. data/lib/appium_lib_core/common/base/driver.rb +258 -331
  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 +13 -17
  24. data/lib/appium_lib_core/common/base.rb +1 -5
  25. data/lib/appium_lib_core/common/command.rb +244 -4
  26. data/lib/appium_lib_core/common/device/app_management.rb +2 -26
  27. data/lib/appium_lib_core/common/device/context.rb +1 -5
  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/device/{touch_actions.rb → orientation.rb} +6 -10
  31. data/lib/appium_lib_core/common/error.rb +4 -5
  32. data/lib/appium_lib_core/common/log.rb +5 -4
  33. data/lib/appium_lib_core/common/wait.rb +38 -6
  34. data/lib/appium_lib_core/device.rb +3 -9
  35. data/lib/appium_lib_core/driver.rb +189 -158
  36. data/lib/appium_lib_core/{patch.rb → element.rb} +62 -25
  37. data/lib/appium_lib_core/ios/xcuitest/device.rb +2 -0
  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/{ios.rb → mac2.rb} +2 -5
  42. data/lib/appium_lib_core/support/event_firing_bridge.rb +57 -0
  43. data/lib/appium_lib_core/version.rb +2 -2
  44. data/lib/appium_lib_core.rb +23 -10
  45. metadata +28 -94
  46. data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
  47. data/.github/contributing.md +0 -26
  48. data/.github/issue_template.md +0 -20
  49. data/.github/workflows/unittest.yml +0 -68
  50. data/.gitignore +0 -18
  51. data/.rubocop.yml +0 -58
  52. data/azure-pipelines.yml +0 -15
  53. data/ci-jobs/functional/android_setup.yml +0 -3
  54. data/ci-jobs/functional/ios_setup.yml +0 -7
  55. data/ci-jobs/functional/publish_test_result.yml +0 -18
  56. data/ci-jobs/functional/run_appium.yml +0 -25
  57. data/ci-jobs/functional/start-emulator.sh +0 -26
  58. data/ci-jobs/functional_test.yml +0 -298
  59. data/docs/mobile_command.md +0 -34
  60. data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
  61. data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
  62. data/lib/appium_lib_core/common/command/common.rb +0 -110
  63. data/lib/appium_lib_core/common/command/w3c.rb +0 -56
  64. data/lib/appium_lib_core/common/device/value.rb +0 -52
  65. data/lib/appium_lib_core/common/touch_action/multi_touch.rb +0 -56
  66. data/lib/appium_lib_core/common/touch_action/touch_actions.rb +0 -203
  67. data/lib/appium_lib_core/ios/uiautomation/device.rb +0 -44
  68. data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
  69. data/release_notes.md +0 -816
  70. 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://www.selenium.dev/selenium/docs/api/rb/Selenium/WebDriver/ActionBuilder.html
205
+ # - https://www.selenium.dev/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
@@ -323,16 +434,6 @@ module Appium
323
434
  block_given? ? @bridge.within_context(context, &block) : @bridge.within_context(context)
324
435
  end
325
436
 
326
- # Change to the default context. This is equivalent to +set_context nil+.
327
- #
328
- # @example
329
- #
330
- # @driver.switch_to_default_context
331
- #
332
- def switch_to_default_context
333
- @bridge.switch_to_default_context
334
- end
335
-
336
437
  # @return [String] The context currently being used.
337
438
  #
338
439
  # @example
@@ -361,47 +462,15 @@ module Appium
361
462
  # @driver.set_context "NATIVE_APP"
362
463
  # @driver.context = "NATIVE_APP"
363
464
  #
364
- def context=(context = null)
465
+ def context=(context = nil)
365
466
  @bridge.set_context(context)
366
467
  end
367
468
  alias set_context context=
368
469
 
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
470
  # 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
471
  # On Android, the application under test should be built with debuggable flag enabled in order to get access to
397
472
  # its container on the internal file system.
398
473
  #
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
474
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
406
475
  # If the path starts with application id prefix, then the file will be pushed to the root of
407
476
  # the corresponding application container.
@@ -415,21 +484,14 @@ module Appium
415
484
  # @driver.push_file "/sdcard/Pictures", file # Push a file binary to /sdcard/Pictures path in Android
416
485
  #
417
486
  def push_file(path, filedata)
487
+ # TODO: use 'mobile: pushFile' internally
418
488
  @bridge.push_file(path, filedata)
419
489
  end
420
490
 
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.
491
+ # Pull a file from the remote device.
424
492
  # On Android the application under test should be built with debuggable flag enabled in order to get access
425
493
  # to its container on the internal file system.
426
494
  #
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
495
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
434
496
  # If the path starts with application id prefix, then the file will be pulled from the root
435
497
  # of the corresponding application container.
@@ -438,7 +500,6 @@ module Appium
438
500
  # Only pulling files from application containers is supported for iOS Simulator.
439
501
  # Provide the remote path in format
440
502
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
441
- # (Make sure this in ifuse doc)
442
503
  #
443
504
  # @return [Base64-decoded] Base64 decoded data
444
505
  #
@@ -452,21 +513,14 @@ module Appium
452
513
  # File.open('proper_filename', 'wb') { |f| f<< decoded_file }
453
514
  #
454
515
  def pull_file(path)
516
+ # TODO: use 'mobile: pullFile' internally
455
517
  @bridge.pull_file(path)
456
518
  end
457
519
 
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.
520
+ # Pull a folder content from the remote device.
461
521
  # On Android the application under test should be built with debuggable flag enabled in order to get access to
462
522
  # its container on the internal file system.
463
523
  #
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
524
  # @param [String] path Absolute path to the folder.
471
525
  # If the path starts with <em>@applicationId/</em> prefix, then the folder will be pulled
472
526
  # from the root of the corresponding application container.
@@ -475,7 +529,6 @@ module Appium
475
529
  # Only pulling files from application containers is supported for iOS Simulator.
476
530
  # Provide the remote path in format
477
531
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
478
- # (Make sure this in ifuse doc)
479
532
  #
480
533
  # @return [Base64-decoded] Base64 decoded data which is zip archived
481
534
  #
@@ -486,22 +539,11 @@ module Appium
486
539
  # File.open('proper_filename', 'wb') { |f| f<< decoded_file }
487
540
  #
488
541
  def pull_folder(path)
542
+ # TODO: use 'mobile: pullFolder' internally
489
543
  @bridge.pull_folder(path)
490
544
  end
491
545
 
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
-
546
+ # @deprecated Use 'mobile: pressKey' extension instead.
505
547
  # Press keycode on the device.
506
548
  # http://developer.android.com/reference/android/view/KeyEvent.html
507
549
  # @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 +563,11 @@ module Appium
521
563
  # @driver.press_keycode 66, metastate: [1], flags: [32]
522
564
  #
523
565
  def press_keycode(key, metastate: [], flags: [])
566
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: pressKey' extension instead"
524
567
  @bridge.press_keycode(key, metastate: metastate, flags: flags)
525
568
  end
526
569
 
570
+ # @deprecated Use 'mobile: pressKey' extension instead.
527
571
  # Long press keycode on the device.
528
572
  # http://developer.android.com/reference/android/view/KeyEvent.html
529
573
  # @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,39 +587,11 @@ module Appium
543
587
  # @driver.long_press_keycode 66, metastate: [1], flags: [32, 8192]
544
588
  #
545
589
  def long_press_keycode(key, metastate: [], flags: [])
590
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: pressKey' extension instead"
546
591
  @bridge.long_press_keycode(key, metastate: metastate, flags: flags)
547
592
  end
548
593
 
549
- # Start the simulator and application configured with desired capabilities
550
- #
551
- # @example
552
- #
553
- # @driver.launch_app
554
- #
555
- def launch_app
556
- @bridge.launch_app
557
- end
558
-
559
- # Close an app on device
560
- #
561
- # @example
562
- #
563
- # @driver.close_app
564
- #
565
- def close_app
566
- @bridge.close_app
567
- end
568
-
569
- # Reset the device, relaunching the application.
570
- #
571
- # @example
572
- #
573
- # @driver.reset
574
- #
575
- def reset
576
- @bridge.reset
577
- end
578
-
594
+ # @deprecated Use 'mobile: getAppStrings' extension instead.
579
595
  # Return the hash of all localization strings.
580
596
  # @return [Hash]
581
597
  #
@@ -584,9 +600,11 @@ module Appium
584
600
  # @driver.app_strings #=> "TransitionsTitle"=>"Transitions", "WebTitle"=>"Web"
585
601
  #
586
602
  def app_strings(language = nil)
603
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: getAppStrings' extension instead"
587
604
  @bridge.app_strings(language)
588
605
  end
589
606
 
607
+ # @deprecated Use 'mobile: backgroundApp' extension instead.
590
608
  # Backgrounds the app for a set number of seconds.
591
609
  # This is a blocking application
592
610
  # @param [Integer] duration How many seconds to background the app for.
@@ -602,7 +620,9 @@ module Appium
602
620
  @bridge.background_app(duration)
603
621
  end
604
622
 
605
- # Install the given app onto the device
623
+ # Install the given app onto the device.
624
+ # Each options can be snake-case or camel-case. Snake-cases will be converted to camel-case
625
+ # as options value.
606
626
  #
607
627
  # @param [String] path The absolute local path or remote http URL to an .ipa or .apk file,
608
628
  # or a .zip containing one of these.
@@ -616,26 +636,27 @@ module Appium
616
636
  # @param [Boolean] grant_permissions Only for Android. whether to automatically grant application permissions
617
637
  # on Android 6+ after the installation completes. +false+ by default
618
638
  #
639
+ # Other parameters such as https://github.com/appium/appium-xcuitest-driver#mobile-installapp also can be set.
640
+ # Then, arguments in snake case will be camel case as its request parameters.
641
+ #
619
642
  # @example
620
643
  #
621
644
  # @driver.install_app("/path/to/test.apk")
622
645
  # @driver.install_app("/path/to/test.apk", replace: true, timeout: 20000, allow_test_packages: true,
623
646
  # use_sdcard: false, grant_permissions: false)
647
+ # @driver.install_app("/path/to/test.ipa", timeoutMs: 20000)
624
648
  #
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)
649
+ def install_app(path, **options)
650
+ # TODO: use mobile command in the background?
651
+ options = options.transform_keys { |key| key.to_s.gsub(/_./) { |v| v[1].upcase } } unless options.nil?
652
+ @bridge.install_app(path, options)
637
653
  end
638
654
 
655
+ # def capitalize(s)
656
+ # chars =
657
+ # chars[1:].map(&:capitalize).join
658
+ # end
659
+
639
660
  # @param [Strong] app_id BundleId for iOS or package name for Android
640
661
  # @param [Boolean] keep_data Only for Android. Whether to keep application data and caches after it is uninstalled.
641
662
  # +false+ by default
@@ -647,6 +668,7 @@ module Appium
647
668
  # @driver.remove_app("io.appium.bundle", keep_data: false, timeout, 10000)
648
669
  #
649
670
  def remove_app(app_id, keep_data: nil, timeout: nil)
671
+ # TODO: use mobile command in the background?
650
672
  @bridge.remove_app(app_id, keep_data: keep_data, timeout: timeout)
651
673
  end
652
674
 
@@ -658,6 +680,7 @@ module Appium
658
680
  # @driver.app_installed?("io.appium.bundle")
659
681
  #
660
682
  def app_installed?(app_id)
683
+ # TODO: use mobile command in the background?
661
684
  @bridge.app_installed?(app_id)
662
685
  end
663
686
 
@@ -669,6 +692,7 @@ module Appium
669
692
  # @driver.activate_app("io.appium.bundle") #=> {}
670
693
  #
671
694
  def activate_app(app_id)
695
+ # TODO: use mobile command in the background?
672
696
  @bridge.activate_app(app_id)
673
697
  end
674
698
 
@@ -685,6 +709,7 @@ module Appium
685
709
  # @driver.terminate_app("io.appium.bundle", timeout: 500)
686
710
  #
687
711
  def terminate_app(app_id, timeout: nil)
712
+ # TODO: use mobile command in the background?
688
713
  @bridge.terminate_app(app_id, timeout: timeout)
689
714
  end
690
715
 
@@ -708,6 +733,7 @@ module Appium
708
733
  # @driver.query_app_state("io.appium.bundle") #=> :not_running
709
734
  #
710
735
  def app_state(app_id)
736
+ # TODO: use mobile command in the background?
711
737
  @bridge.app_state(app_id)
712
738
  end
713
739
  alias query_app_state app_state
@@ -747,6 +773,7 @@ module Appium
747
773
  @bridge.stop_and_save_recording_screen(file_path)
748
774
  end
749
775
 
776
+ # @deprecated Use 'mobile: shake' extension instead.
750
777
  # Cause the device to shake
751
778
  #
752
779
  # @example
@@ -754,9 +781,11 @@ module Appium
754
781
  # @driver.shake
755
782
  #
756
783
  def shake
784
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: shake' extension instead"
757
785
  @bridge.shake
758
786
  end
759
787
 
788
+ # @deprecated Use 'mobile: getDeviceTime' extension instead.
760
789
  # Get the time on the device
761
790
  #
762
791
  # @param [String] format The set of format specifiers. Read https://momentjs.com/docs/ to get
@@ -770,18 +799,10 @@ module Appium
770
799
  # @driver.device_time "YYYY-MM-DD" #=> "2018-06-12"
771
800
  #
772
801
  def device_time(format = nil)
802
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: getDeviceTime' extension instead"
773
803
  @bridge.device_time(format)
774
804
  end
775
805
 
776
- # touch actions
777
- def touch_actions(actions)
778
- @bridge.touch_actions(actions)
779
- end
780
-
781
- def multi_touch(actions)
782
- @bridge.multi_touch(actions)
783
- end
784
-
785
806
  #
786
807
  # Send multiple W3C action chains to server. Use +@driver.action+ for single action chain.
787
808
  #
@@ -790,7 +811,7 @@ module Appium
790
811
  #
791
812
  # @example: Zoom
792
813
  #
793
- # f1 = @driver.action.add_pointer_input(:touch, 'finger1')
814
+ # f1 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger1')
794
815
  # f1.create_pointer_move(duration: 1, x: 200, y: 500,
795
816
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
796
817
  # f1.create_pointer_down(:left)
@@ -798,7 +819,7 @@ module Appium
798
819
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
799
820
  # f1.create_pointer_up(:left)
800
821
  #
801
- # f2 = @driver.action.add_pointer_input(:touch, 'finger2')
822
+ # f2 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger2')
802
823
  # f2.create_pointer_move(duration: 1, x: 200, y: 500,
803
824
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
804
825
  # f2.create_pointer_down(:left)
@@ -809,7 +830,11 @@ module Appium
809
830
  # @driver.perform_actions [f1, f2] #=> 'nil' if the action succeed
810
831
  #
811
832
  def perform_actions(data)
812
- raise ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
833
+ raise ::Appium::Core::Error::ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
834
+
835
+ # NOTE: 'add_input' in Selenium Ruby implementation has additional 'pause'.
836
+ # This implementation is to avoid the additional pause.
837
+ # https://github.com/SeleniumHQ/selenium/blob/64447d4b03f6986337d1ca8d8b6476653570bcc1/rb/lib/selenium/webdriver/common/action_builder.rb#L207
813
838
 
814
839
  @bridge.send_actions data.map(&:encode).compact
815
840
  data.each(&:clear_actions)
@@ -853,7 +878,7 @@ module Appium
853
878
  end
854
879
 
855
880
  # Get the device window's logs.
856
- # @return [String]
881
+ # @return [Appium::Core::Logs]
857
882
  #
858
883
  # @example
859
884
  #
@@ -876,107 +901,6 @@ module Appium
876
901
  @bridge.get_timeouts
877
902
  end
878
903
 
879
- # Retrieve the capabilities of the specified session.
880
- # It's almost same as +@driver.capabilities+ but you can get more details.
881
- #
882
- # @return [Selenium::WebDriver::Remote::Capabilities]
883
- #
884
- # @example
885
- # @driver.session_capabilities
886
- #
887
- # #=> uiautomator2
888
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fa38dae1360
889
- # # @capabilities=
890
- # # {:proxy=>nil,
891
- # # :browser_name=>nil,
892
- # # :browser_version=>nil,
893
- # # :platform_name=>"android",
894
- # # :page_load_strategy=>nil,
895
- # # :remote_session_id=>nil,
896
- # # :accessibility_checks=>nil,
897
- # # :profile=>nil,
898
- # # :rotatable=>nil,
899
- # # :device=>nil,
900
- # # "platform"=>"LINUX",
901
- # # "webStorageEnabled"=>false,
902
- # # "takesScreenshot"=>true,
903
- # # "javascriptEnabled"=>true,
904
- # # "databaseEnabled"=>false,
905
- # # "networkConnectionEnabled"=>true,
906
- # # "locationContextEnabled"=>false,
907
- # # "warnings"=>{},
908
- # # "desired"=>
909
- # # {"platformName"=>"android",
910
- # # "automationName"=>"uiautomator2",
911
- # # "app"=>"/path/to/app/api.apk.zip",
912
- # # "platformVersion"=>"8.1.0",
913
- # # "deviceName"=>"Android Emulator",
914
- # # "appPackage"=>"io.appium.android.apis",
915
- # # "appActivity"=>"io.appium.android.apis.ApiDemos",
916
- # # "someCapability"=>"some_capability",
917
- # # "unicodeKeyboard"=>true,
918
- # # "resetKeyboard"=>true},
919
- # # "automationName"=>"uiautomator2",
920
- # # "app"=>"/path/to/app/api.apk.zip",
921
- # # "platformVersion"=>"8.1.0",
922
- # # "deviceName"=>"emulator-5554",
923
- # # "appPackage"=>"io.appium.android.apis",
924
- # # "appActivity"=>"io.appium.android.apis.ApiDemos",
925
- # # "someCapability"=>"some_capability",
926
- # # "unicodeKeyboard"=>true,
927
- # # "resetKeyboard"=>true,
928
- # # "deviceUDID"=>"emulator-5554",
929
- # # "deviceScreenSize"=>"1080x1920",
930
- # # "deviceScreenDensity"=>420,
931
- # # "deviceModel"=>"Android SDK built for x86",
932
- # # "deviceManufacturer"=>"Google",
933
- # # "pixelRatio"=>2.625,
934
- # # "statBarHeight"=>63,
935
- # # "viewportRect"=>{"left"=>0, "top"=>63, "width"=>1080, "height"=>1731}}>
936
- # #
937
- # #=> XCUITest
938
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fb15dc01370
939
- # # @capabilities=
940
- # # {:proxy=>nil,
941
- # # :browser_name=>"UICatalog",
942
- # # :browser_version=>nil,
943
- # # :platform_name=>"ios",
944
- # # :page_load_strategy=>nil,
945
- # # :remote_session_id=>nil,
946
- # # :accessibility_checks=>nil,
947
- # # :profile=>nil,
948
- # # :rotatable=>nil,
949
- # # :device=>"iphone",
950
- # # "udid"=>"DED4DBAD-8E5E-4AD6-BDC4-E75CF9AD84D8",
951
- # # "automationName"=>"XCUITest",
952
- # # "app"=>"/path/to/app/UICatalog.app",
953
- # # "platformVersion"=>"11.4",
954
- # # "deviceName"=>"iPhone Simulator",
955
- # # "useNewWDA"=>true,
956
- # # "useJSONSource"=>true,
957
- # # "someCapability"=>"some_capability",
958
- # # "sdkVersion"=>"11.4",
959
- # # "CFBundleIdentifier"=>"com.example.apple-samplecode.UICatalog",
960
- # # "pixelRatio"=>2,
961
- # # "statBarHeight"=>23.4375,
962
- # # "viewportRect"=>{"left"=>0, "top"=>47, "width"=>750, "height"=>1287}}>
963
- #
964
- def session_capabilities
965
- @bridge.session_capabilities
966
- end
967
-
968
- # Returns available sessions on the Appium server
969
- #
970
- # @return [[Hash]]
971
- #
972
- # @example
973
- #
974
- # @driver.sessions #=> [{'id' => 'c363add8-a7ca-4455-b9e3-9ac4d69e95b3', 'capabilities' => { capabilities as Hash }}]
975
- #
976
- def sessions
977
- @bridge.sessions
978
- end
979
-
980
904
  # Image Comparison
981
905
  def match_images_features(first_image:,
982
906
  second_image:,
@@ -992,11 +916,14 @@ module Appium
992
916
  visualize: visualize)
993
917
  end
994
918
 
995
- def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil)
919
+ def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil,
920
+ multiple: nil, match_neighbour_threshold: nil)
996
921
  @bridge.find_image_occurrence(full_image: full_image,
997
922
  partial_image: partial_image,
998
923
  visualize: visualize,
999
- threshold: threshold)
924
+ threshold: threshold,
925
+ multiple: multiple,
926
+ match_neighbour_threshold: match_neighbour_threshold)
1000
927
  end
1001
928
 
1002
929
  def get_images_similarity(first_image:, second_image:, visualize: false)
@@ -1009,14 +936,14 @@ module Appium
1009
936
 
1010
937
  # @since Appium 1.8.2
1011
938
  # 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}
939
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1013
940
  #
1014
941
  # You can handle settings for the comparision following below.
1015
942
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1016
943
  #
1017
944
  # @param [String] img_path A path to a partial image you'd like to find
1018
945
  #
1019
- # @return [::Selenium::WebDriver::Element]
946
+ # @return [::Appium::Core::Element]
1020
947
  #
1021
948
  # @example
1022
949
  #
@@ -1031,14 +958,14 @@ module Appium
1031
958
 
1032
959
  # @since Appium 1.8.2
1033
960
  # 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}
961
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1035
962
  #
1036
963
  # You can handle settings for the comparision following below.
1037
964
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1038
965
  #
1039
966
  # @param [String] img_path A path to a partial image you'd like to find
1040
967
  #
1041
- # @return [::Selenium::WebDriver::Element]
968
+ # @return [Array<::Appium::Core::Element>]
1042
969
  #
1043
970
  # @example
1044
971
  #
@@ -1086,14 +1013,14 @@ module Appium
1086
1013
  @bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
1087
1014
  end
1088
1015
 
1089
- # Convert vanilla element response to ::Selenium::WebDriver::Element
1016
+ # Convert vanilla element response to ::Appium::Core::Element
1090
1017
  #
1091
1018
  # @param [Hash] id The id which can get as a response from server
1092
- # @return [::Selenium::WebDriver::Element]
1019
+ # @return [::Appium::Core::Element]
1093
1020
  #
1094
1021
  # @example
1095
1022
  # response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
1096
- # ele = @driver.convert_to_element(response) #=> ::Selenium::WebDriver::Element
1023
+ # ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
1097
1024
  # ele.rect #=> Can get the rect of the element
1098
1025
  #
1099
1026
  def convert_to_element(id)