appium_lib_core 4.1.0 → 7.4.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -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 +321 -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 +272 -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 +182 -148
  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://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,6 +434,7 @@ module Appium
323
434
  block_given? ? @bridge.within_context(context, &block) : @bridge.within_context(context)
324
435
  end
325
436
 
437
+ # @deprecated Use set context with proper context name instead.
326
438
  # Change to the default context. This is equivalent to +set_context nil+.
327
439
  #
328
440
  # @example
@@ -330,6 +442,7 @@ module Appium
330
442
  # @driver.switch_to_default_context
331
443
  #
332
444
  def switch_to_default_context
445
+ ::Appium::Logger.warn '[DEPRECATION] Please set proper context instead of calling this method.'
333
446
  @bridge.switch_to_default_context
334
447
  end
335
448
 
@@ -361,47 +474,15 @@ module Appium
361
474
  # @driver.set_context "NATIVE_APP"
362
475
  # @driver.context = "NATIVE_APP"
363
476
  #
364
- def context=(context = null)
477
+ def context=(context = nil)
365
478
  @bridge.set_context(context)
366
479
  end
367
480
  alias set_context context=
368
481
 
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
482
  # 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
483
  # On Android, the application under test should be built with debuggable flag enabled in order to get access to
397
484
  # its container on the internal file system.
398
485
  #
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
486
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
406
487
  # If the path starts with application id prefix, then the file will be pushed to the root of
407
488
  # the corresponding application container.
@@ -415,21 +496,14 @@ module Appium
415
496
  # @driver.push_file "/sdcard/Pictures", file # Push a file binary to /sdcard/Pictures path in Android
416
497
  #
417
498
  def push_file(path, filedata)
499
+ # TODO: use 'mobile: pushFile' internally
418
500
  @bridge.push_file(path, filedata)
419
501
  end
420
502
 
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.
503
+ # Pull a file from the remote device.
424
504
  # On Android the application under test should be built with debuggable flag enabled in order to get access
425
505
  # to its container on the internal file system.
426
506
  #
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
507
  # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
434
508
  # If the path starts with application id prefix, then the file will be pulled from the root
435
509
  # of the corresponding application container.
@@ -438,7 +512,6 @@ module Appium
438
512
  # Only pulling files from application containers is supported for iOS Simulator.
439
513
  # Provide the remote path in format
440
514
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
441
- # (Make sure this in ifuse doc)
442
515
  #
443
516
  # @return [Base64-decoded] Base64 decoded data
444
517
  #
@@ -452,21 +525,14 @@ module Appium
452
525
  # File.open('proper_filename', 'wb') { |f| f<< decoded_file }
453
526
  #
454
527
  def pull_file(path)
528
+ # TODO: use 'mobile: pullFile' internally
455
529
  @bridge.pull_file(path)
456
530
  end
457
531
 
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.
532
+ # Pull a folder content from the remote device.
461
533
  # On Android the application under test should be built with debuggable flag enabled in order to get access to
462
534
  # its container on the internal file system.
463
535
  #
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
536
  # @param [String] path Absolute path to the folder.
471
537
  # If the path starts with <em>@applicationId/</em> prefix, then the folder will be pulled
472
538
  # from the root of the corresponding application container.
@@ -475,7 +541,6 @@ module Appium
475
541
  # Only pulling files from application containers is supported for iOS Simulator.
476
542
  # Provide the remote path in format
477
543
  # <code>@bundle.identifier:container_type/relative_path_in_container</code>
478
- # (Make sure this in ifuse doc)
479
544
  #
480
545
  # @return [Base64-decoded] Base64 decoded data which is zip archived
481
546
  #
@@ -486,22 +551,11 @@ module Appium
486
551
  # File.open('proper_filename', 'wb') { |f| f<< decoded_file }
487
552
  #
488
553
  def pull_folder(path)
554
+ # TODO: use 'mobile: pullFolder' internally
489
555
  @bridge.pull_folder(path)
490
556
  end
491
557
 
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
-
558
+ # @deprecated Use 'mobile: pressKey' extension instead.
505
559
  # Press keycode on the device.
506
560
  # http://developer.android.com/reference/android/view/KeyEvent.html
507
561
  # @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 +575,11 @@ module Appium
521
575
  # @driver.press_keycode 66, metastate: [1], flags: [32]
522
576
  #
523
577
  def press_keycode(key, metastate: [], flags: [])
578
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: pressKey' extension instead"
524
579
  @bridge.press_keycode(key, metastate: metastate, flags: flags)
525
580
  end
526
581
 
582
+ # @deprecated Use 'mobile: pressKey' extension instead.
527
583
  # Long press keycode on the device.
528
584
  # http://developer.android.com/reference/android/view/KeyEvent.html
529
585
  # @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 +599,11 @@ module Appium
543
599
  # @driver.long_press_keycode 66, metastate: [1], flags: [32, 8192]
544
600
  #
545
601
  def long_press_keycode(key, metastate: [], flags: [])
602
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: pressKey' extension instead"
546
603
  @bridge.long_press_keycode(key, metastate: metastate, flags: flags)
547
604
  end
548
605
 
606
+ # @deprecated Will be removed, or use 'windows: launchApp' extension instead for Windows.
549
607
  # Start the simulator and application configured with desired capabilities
550
608
  #
551
609
  # @example
@@ -556,6 +614,7 @@ module Appium
556
614
  @bridge.launch_app
557
615
  end
558
616
 
617
+ # @deprecated Will be removed, or use 'windows: closeApp' extension instead for Windows.
559
618
  # Close an app on device
560
619
  #
561
620
  # @example
@@ -566,6 +625,7 @@ module Appium
566
625
  @bridge.close_app
567
626
  end
568
627
 
628
+ # @deprecated
569
629
  # Reset the device, relaunching the application.
570
630
  #
571
631
  # @example
@@ -573,9 +633,14 @@ module Appium
573
633
  # @driver.reset
574
634
  #
575
635
  def reset
636
+ ::Appium::Logger.warn(
637
+ '[DEPRECATION] reset is deprecated. Please use terminate_app and activate_app, ' \
638
+ 'or quit and create a new session instead.'
639
+ )
576
640
  @bridge.reset
577
641
  end
578
642
 
643
+ # @deprecated Use 'mobile: getAppStrings' extension instead.
579
644
  # Return the hash of all localization strings.
580
645
  # @return [Hash]
581
646
  #
@@ -584,9 +649,11 @@ module Appium
584
649
  # @driver.app_strings #=> "TransitionsTitle"=>"Transitions", "WebTitle"=>"Web"
585
650
  #
586
651
  def app_strings(language = nil)
652
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: getAppStrings' extension instead"
587
653
  @bridge.app_strings(language)
588
654
  end
589
655
 
656
+ # @deprecated Use 'mobile: backgroundApp' extension instead.
590
657
  # Backgrounds the app for a set number of seconds.
591
658
  # This is a blocking application
592
659
  # @param [Integer] duration How many seconds to background the app for.
@@ -602,7 +669,9 @@ module Appium
602
669
  @bridge.background_app(duration)
603
670
  end
604
671
 
605
- # Install the given app onto the device
672
+ # Install the given app onto the device.
673
+ # Each options can be snake-case or camel-case. Snake-cases will be converted to camel-case
674
+ # as options value.
606
675
  #
607
676
  # @param [String] path The absolute local path or remote http URL to an .ipa or .apk file,
608
677
  # or a .zip containing one of these.
@@ -616,26 +685,27 @@ module Appium
616
685
  # @param [Boolean] grant_permissions Only for Android. whether to automatically grant application permissions
617
686
  # on Android 6+ after the installation completes. +false+ by default
618
687
  #
688
+ # Other parameters such as https://github.com/appium/appium-xcuitest-driver#mobile-installapp also can be set.
689
+ # Then, arguments in snake case will be camel case as its request parameters.
690
+ #
619
691
  # @example
620
692
  #
621
693
  # @driver.install_app("/path/to/test.apk")
622
694
  # @driver.install_app("/path/to/test.apk", replace: true, timeout: 20000, allow_test_packages: true,
623
695
  # use_sdcard: false, grant_permissions: false)
696
+ # @driver.install_app("/path/to/test.ipa", timeoutMs: 20000)
624
697
  #
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)
698
+ def install_app(path, **options)
699
+ # TODO: use mobile command in the background?
700
+ options = options.transform_keys { |key| key.to_s.gsub(/_./) { |v| v[1].upcase } } unless options.nil?
701
+ @bridge.install_app(path, options)
637
702
  end
638
703
 
704
+ # def capitalize(s)
705
+ # chars =
706
+ # chars[1:].map(&:capitalize).join
707
+ # end
708
+
639
709
  # @param [Strong] app_id BundleId for iOS or package name for Android
640
710
  # @param [Boolean] keep_data Only for Android. Whether to keep application data and caches after it is uninstalled.
641
711
  # +false+ by default
@@ -647,6 +717,7 @@ module Appium
647
717
  # @driver.remove_app("io.appium.bundle", keep_data: false, timeout, 10000)
648
718
  #
649
719
  def remove_app(app_id, keep_data: nil, timeout: nil)
720
+ # TODO: use mobile command in the background?
650
721
  @bridge.remove_app(app_id, keep_data: keep_data, timeout: timeout)
651
722
  end
652
723
 
@@ -658,6 +729,7 @@ module Appium
658
729
  # @driver.app_installed?("io.appium.bundle")
659
730
  #
660
731
  def app_installed?(app_id)
732
+ # TODO: use mobile command in the background?
661
733
  @bridge.app_installed?(app_id)
662
734
  end
663
735
 
@@ -669,6 +741,7 @@ module Appium
669
741
  # @driver.activate_app("io.appium.bundle") #=> {}
670
742
  #
671
743
  def activate_app(app_id)
744
+ # TODO: use mobile command in the background?
672
745
  @bridge.activate_app(app_id)
673
746
  end
674
747
 
@@ -685,6 +758,7 @@ module Appium
685
758
  # @driver.terminate_app("io.appium.bundle", timeout: 500)
686
759
  #
687
760
  def terminate_app(app_id, timeout: nil)
761
+ # TODO: use mobile command in the background?
688
762
  @bridge.terminate_app(app_id, timeout: timeout)
689
763
  end
690
764
 
@@ -708,6 +782,7 @@ module Appium
708
782
  # @driver.query_app_state("io.appium.bundle") #=> :not_running
709
783
  #
710
784
  def app_state(app_id)
785
+ # TODO: use mobile command in the background?
711
786
  @bridge.app_state(app_id)
712
787
  end
713
788
  alias query_app_state app_state
@@ -747,6 +822,7 @@ module Appium
747
822
  @bridge.stop_and_save_recording_screen(file_path)
748
823
  end
749
824
 
825
+ # @deprecated Use 'mobile: shake' extension instead.
750
826
  # Cause the device to shake
751
827
  #
752
828
  # @example
@@ -754,9 +830,11 @@ module Appium
754
830
  # @driver.shake
755
831
  #
756
832
  def shake
833
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: shake' extension instead"
757
834
  @bridge.shake
758
835
  end
759
836
 
837
+ # @deprecated Use 'mobile: getDeviceTime' extension instead.
760
838
  # Get the time on the device
761
839
  #
762
840
  # @param [String] format The set of format specifiers. Read https://momentjs.com/docs/ to get
@@ -770,6 +848,7 @@ module Appium
770
848
  # @driver.device_time "YYYY-MM-DD" #=> "2018-06-12"
771
849
  #
772
850
  def device_time(format = nil)
851
+ ::Appium::Logger.warn "[DEPRECATION] Please use 'mobile: getDeviceTime' extension instead"
773
852
  @bridge.device_time(format)
774
853
  end
775
854
 
@@ -790,7 +869,7 @@ module Appium
790
869
  #
791
870
  # @example: Zoom
792
871
  #
793
- # f1 = @driver.action.add_pointer_input(:touch, 'finger1')
872
+ # f1 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger1')
794
873
  # f1.create_pointer_move(duration: 1, x: 200, y: 500,
795
874
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
796
875
  # f1.create_pointer_down(:left)
@@ -798,7 +877,7 @@ module Appium
798
877
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
799
878
  # f1.create_pointer_up(:left)
800
879
  #
801
- # f2 = @driver.action.add_pointer_input(:touch, 'finger2')
880
+ # f2 = ::Selenium::WebDriver::Interactions.pointer(:touch, name: 'finger2')
802
881
  # f2.create_pointer_move(duration: 1, x: 200, y: 500,
803
882
  # origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
804
883
  # f2.create_pointer_down(:left)
@@ -809,7 +888,11 @@ module Appium
809
888
  # @driver.perform_actions [f1, f2] #=> 'nil' if the action succeed
810
889
  #
811
890
  def perform_actions(data)
812
- raise ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
891
+ raise ::Appium::Core::Error::ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
892
+
893
+ # NOTE: 'add_input' in Selenium Ruby implementation has additional 'pause'.
894
+ # This implementation is to avoid the additional pause.
895
+ # https://github.com/SeleniumHQ/selenium/blob/64447d4b03f6986337d1ca8d8b6476653570bcc1/rb/lib/selenium/webdriver/common/action_builder.rb#L207
813
896
 
814
897
  @bridge.send_actions data.map(&:encode).compact
815
898
  data.each(&:clear_actions)
@@ -853,7 +936,7 @@ module Appium
853
936
  end
854
937
 
855
938
  # Get the device window's logs.
856
- # @return [String]
939
+ # @return [Appium::Core::Logs]
857
940
  #
858
941
  # @example
859
942
  #
@@ -879,16 +962,15 @@ module Appium
879
962
  # Retrieve the capabilities of the specified session.
880
963
  # It's almost same as +@driver.capabilities+ but you can get more details.
881
964
  #
882
- # @return [Selenium::WebDriver::Remote::Capabilities]
965
+ # @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::Capabilities]
883
966
  #
884
967
  # @example
885
968
  # @driver.session_capabilities
886
969
  #
887
970
  # #=> uiautomator2
888
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fa38dae1360
971
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fa38dae1360
889
972
  # # @capabilities=
890
- # # {:proxy=>nil,
891
- # # :browser_name=>nil,
973
+ # # {:browser_name=>nil,
892
974
  # # :browser_version=>nil,
893
975
  # # :platform_name=>"android",
894
976
  # # :page_load_strategy=>nil,
@@ -914,8 +996,6 @@ module Appium
914
996
  # # "appPackage"=>"io.appium.android.apis",
915
997
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
916
998
  # # "someCapability"=>"some_capability",
917
- # # "unicodeKeyboard"=>true,
918
- # # "resetKeyboard"=>true},
919
999
  # # "automationName"=>"uiautomator2",
920
1000
  # # "app"=>"/path/to/app/api.apk.zip",
921
1001
  # # "platformVersion"=>"8.1.0",
@@ -923,8 +1003,6 @@ module Appium
923
1003
  # # "appPackage"=>"io.appium.android.apis",
924
1004
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
925
1005
  # # "someCapability"=>"some_capability",
926
- # # "unicodeKeyboard"=>true,
927
- # # "resetKeyboard"=>true,
928
1006
  # # "deviceUDID"=>"emulator-5554",
929
1007
  # # "deviceScreenSize"=>"1080x1920",
930
1008
  # # "deviceScreenDensity"=>420,
@@ -935,10 +1013,9 @@ module Appium
935
1013
  # # "viewportRect"=>{"left"=>0, "top"=>63, "width"=>1080, "height"=>1731}}>
936
1014
  # #
937
1015
  # #=> XCUITest
938
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fb15dc01370
1016
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fb15dc01370
939
1017
  # # @capabilities=
940
- # # {:proxy=>nil,
941
- # # :browser_name=>"UICatalog",
1018
+ # # {:browser_name=>"UICatalog",
942
1019
  # # :browser_version=>nil,
943
1020
  # # :platform_name=>"ios",
944
1021
  # # :page_load_strategy=>nil,
@@ -992,11 +1069,14 @@ module Appium
992
1069
  visualize: visualize)
993
1070
  end
994
1071
 
995
- def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil)
1072
+ def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil,
1073
+ multiple: nil, match_neighbour_threshold: nil)
996
1074
  @bridge.find_image_occurrence(full_image: full_image,
997
1075
  partial_image: partial_image,
998
1076
  visualize: visualize,
999
- threshold: threshold)
1077
+ threshold: threshold,
1078
+ multiple: multiple,
1079
+ match_neighbour_threshold: match_neighbour_threshold)
1000
1080
  end
1001
1081
 
1002
1082
  def get_images_similarity(first_image:, second_image:, visualize: false)
@@ -1009,14 +1089,14 @@ module Appium
1009
1089
 
1010
1090
  # @since Appium 1.8.2
1011
1091
  # 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}
1092
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1013
1093
  #
1014
1094
  # You can handle settings for the comparision following below.
1015
1095
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1016
1096
  #
1017
1097
  # @param [String] img_path A path to a partial image you'd like to find
1018
1098
  #
1019
- # @return [::Selenium::WebDriver::Element]
1099
+ # @return [::Appium::Core::Element]
1020
1100
  #
1021
1101
  # @example
1022
1102
  #
@@ -1031,14 +1111,14 @@ module Appium
1031
1111
 
1032
1112
  # @since Appium 1.8.2
1033
1113
  # 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}
1114
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1035
1115
  #
1036
1116
  # You can handle settings for the comparision following below.
1037
1117
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1038
1118
  #
1039
1119
  # @param [String] img_path A path to a partial image you'd like to find
1040
1120
  #
1041
- # @return [::Selenium::WebDriver::Element]
1121
+ # @return [Array<::Appium::Core::Element>]
1042
1122
  #
1043
1123
  # @example
1044
1124
  #
@@ -1086,14 +1166,14 @@ module Appium
1086
1166
  @bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
1087
1167
  end
1088
1168
 
1089
- # Convert vanilla element response to ::Selenium::WebDriver::Element
1169
+ # Convert vanilla element response to ::Appium::Core::Element
1090
1170
  #
1091
1171
  # @param [Hash] id The id which can get as a response from server
1092
- # @return [::Selenium::WebDriver::Element]
1172
+ # @return [::Appium::Core::Element]
1093
1173
  #
1094
1174
  # @example
1095
1175
  # response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
1096
- # ele = @driver.convert_to_element(response) #=> ::Selenium::WebDriver::Element
1176
+ # ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
1097
1177
  # ele.rect #=> Can get the rect of the element
1098
1178
  #
1099
1179
  def convert_to_element(id)