appium_lib_core 4.1.0 → 7.4.0

Sign up to get free protection for your applications and to get access to all the features.
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)