appium_lib 9.6.1 → 9.7.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/CHANGELOG.md +43 -0
  4. data/Rakefile +1 -1
  5. data/appium_lib.gemspec +1 -1
  6. data/docs/android_docs.md +440 -1295
  7. data/docs/docs.md +10 -103
  8. data/docs/index_paths.md +2 -0
  9. data/docs/ios_docs.md +725 -1674
  10. data/docs/migration.md +17 -0
  11. data/lib/appium_lib.rb +1 -2
  12. data/lib/appium_lib/android/android.rb +20 -0
  13. data/lib/appium_lib/android/{helper.rb → common/helper.rb} +1 -1
  14. data/lib/appium_lib/android/uiautomator2.rb +5 -4
  15. data/lib/appium_lib/android/uiautomator2/bridge.rb +16 -0
  16. data/lib/appium_lib/appium.rb +201 -0
  17. data/lib/appium_lib/common/helper.rb +18 -20
  18. data/lib/appium_lib/common/log.rb +24 -0
  19. data/lib/appium_lib/common/multi_touch.rb +89 -0
  20. data/lib/appium_lib/common/touch_actions.rb +48 -0
  21. data/lib/appium_lib/common/wait.rb +10 -49
  22. data/lib/appium_lib/core/android.rb +4 -0
  23. data/lib/appium_lib/core/android/device.rb +142 -0
  24. data/lib/appium_lib/core/android/search_context.rb +17 -0
  25. data/lib/appium_lib/core/android/uiautomator1/bridge.rb +16 -0
  26. data/lib/appium_lib/core/android/uiautomator2/bridge.rb +16 -0
  27. data/lib/appium_lib/core/android_uiautomator2.rb +4 -0
  28. data/lib/appium_lib/core/common.rb +6 -0
  29. data/lib/appium_lib/core/common/base.rb +8 -0
  30. data/lib/appium_lib/core/common/base/bridge.rb +47 -0
  31. data/lib/appium_lib/core/common/base/capabilities.rb +16 -0
  32. data/lib/appium_lib/core/common/base/command.rb +10 -0
  33. data/lib/appium_lib/core/common/base/driver.rb +40 -0
  34. data/lib/appium_lib/core/common/base/http_default.rb +12 -0
  35. data/lib/appium_lib/core/common/base/search_context.rb +89 -0
  36. data/lib/appium_lib/core/common/base/wait.rb +56 -0
  37. data/lib/appium_lib/{common → core/common}/command.rb +20 -16
  38. data/lib/appium_lib/core/common/device.rb +470 -0
  39. data/lib/appium_lib/core/common/error.rb +13 -0
  40. data/lib/appium_lib/core/common/log.rb +30 -0
  41. data/lib/appium_lib/{logger.rb → core/common/logger.rb} +2 -0
  42. data/lib/appium_lib/core/core.rb +38 -0
  43. data/lib/appium_lib/core/device/multi_touch.rb +213 -0
  44. data/lib/appium_lib/core/device/touch_actions.rb +206 -0
  45. data/lib/appium_lib/core/driver.rb +274 -0
  46. data/lib/appium_lib/core/ios.rb +6 -0
  47. data/lib/appium_lib/core/ios/device.rb +44 -0
  48. data/lib/appium_lib/core/ios/search_context.rb +27 -0
  49. data/lib/appium_lib/core/ios/uiautomation/bridge.rb +17 -0
  50. data/lib/appium_lib/core/ios/uiautomation/patch.rb +20 -0
  51. data/lib/appium_lib/core/ios/xcuitest/bridge.rb +18 -0
  52. data/lib/appium_lib/{ios → core/ios}/xcuitest/device.rb +5 -5
  53. data/lib/appium_lib/{ios → core/ios}/xcuitest/search_context.rb +13 -9
  54. data/lib/appium_lib/core/ios_xcuitest.rb +7 -0
  55. data/lib/appium_lib/core/patch.rb +56 -0
  56. data/lib/appium_lib/driver.rb +174 -446
  57. data/lib/appium_lib/ios/{errors.rb → common/errors.rb} +0 -0
  58. data/lib/appium_lib/ios/{helper.rb → common/helper.rb} +9 -110
  59. data/lib/appium_lib/ios/ios.rb +20 -0
  60. data/lib/appium_lib/ios/xcuitest.rb +1 -3
  61. data/lib/appium_lib/ios/xcuitest/bridge.rb +19 -0
  62. data/lib/appium_lib/ios/xcuitest/command.rb +4 -1
  63. data/lib/appium_lib/ios/xcuitest/{gestures.rb → command/gestures.rb} +1 -1
  64. data/lib/appium_lib/ios/xcuitest/element.rb +1 -18
  65. data/lib/appium_lib/ios/xcuitest/helper.rb +0 -6
  66. data/lib/appium_lib/sauce_labs.rb +29 -0
  67. data/lib/appium_lib/version.rb +5 -0
  68. data/release_notes.md +8 -0
  69. metadata +50 -25
  70. data/lib/appium_lib/android/client_xpath.rb +0 -51
  71. data/lib/appium_lib/android/device.rb +0 -39
  72. data/lib/appium_lib/android/mobile_methods.rb +0 -15
  73. data/lib/appium_lib/android/patch.rb +0 -16
  74. data/lib/appium_lib/capabilities.rb +0 -13
  75. data/lib/appium_lib/common/element/window.rb +0 -10
  76. data/lib/appium_lib/common/error.rb +0 -8
  77. data/lib/appium_lib/common/patch.rb +0 -190
  78. data/lib/appium_lib/common/search_context.rb +0 -10
  79. data/lib/appium_lib/common/version.rb +0 -5
  80. data/lib/appium_lib/device/device.rb +0 -611
  81. data/lib/appium_lib/device/multi_touch.rb +0 -225
  82. data/lib/appium_lib/device/touch_actions.rb +0 -230
  83. data/lib/appium_lib/ios/mobile_methods.rb +0 -25
  84. data/lib/appium_lib/ios/patch.rb +0 -22
@@ -0,0 +1,274 @@
1
+ module Appium
2
+ module Core
3
+ class Driver
4
+ # Selenium webdriver capabilities
5
+ attr_reader :caps
6
+ # Custom URL for the selenium server
7
+ attr_reader :custom_url
8
+ # Export session id to textfile in /tmp for 3rd party tools
9
+ attr_reader :export_session
10
+ # Default wait time for elements to appear
11
+ # Returns the default client side wait.
12
+ # This value is independent of what the server is using
13
+ # @return [Integer]
14
+ attr_reader :default_wait
15
+ # Appium's server port
16
+ attr_reader :port
17
+ # Device type to request from the appium server
18
+ attr_reader :device
19
+ # Automation name sent to appium server or received from server
20
+ # If automation_name is nil, it is not set both client side and server side.
21
+ attr_reader :automation_name
22
+ # Return a time wait timeout
23
+ # Wait time for ::Appium::Common.wait or ::Appium::Common.wait_true.
24
+ # Provide Appium::Drive like { appium_lib: { wait_timeout: 20 } }
25
+ # @return [Integer]
26
+ attr_reader :wait_timeout
27
+ # Return a time wait timeout
28
+ # Wait interval time for ::Appium::Common.wait or ::Appium::Common.wait_true.
29
+ # Provide Appium::Drive like { appium_lib: { wait_interval: 20 } }
30
+ # @return [Integer]
31
+ attr_reader :wait_interval
32
+ # Return http client called in start_driver()
33
+ # @return [Appium::Core::Base::Http::Default] the http client
34
+ attr_reader :http_client
35
+ # instance of AbstractEventListener for logging support
36
+ attr_reader :listener
37
+
38
+ private
39
+
40
+ # @return [Appium::Core::Base::Driver]
41
+ attr_reader :driver
42
+
43
+ public
44
+
45
+ # @private
46
+ # @see Appium::Core.for
47
+ #
48
+ # @return [Driver]
49
+ #
50
+ def self.for(target, opts = {})
51
+ new(target, opts)
52
+ end
53
+
54
+ # @private
55
+ def initialize(target, opts = {})
56
+ opts = Appium.symbolize_keys opts
57
+ @caps = get_caps(opts)
58
+
59
+ set_appium_lib_specific_values(get_appium_lib_opts(opts))
60
+ set_app_path
61
+ set_appium_device
62
+ set_automation_name
63
+
64
+ extend_for(device: @device, automation_name: @automation_name, target: target)
65
+
66
+ self
67
+ end
68
+
69
+ # Creates a new global driver and quits the old one if it exists.
70
+ # You can customise http_client as the following
71
+ #
72
+ # @example
73
+ # ```ruby
74
+ # require 'rubygems'
75
+ # require 'appium_lib'
76
+ #
77
+ # # platformName takes a string or a symbol.
78
+ #
79
+ # # Start iOS driver
80
+ # opts = {
81
+ # caps: {
82
+ # platformName: :ios,
83
+ # app: '/path/to/MyiOS.app'
84
+ # },
85
+ # appium_lib: {
86
+ # wait_timeout: 30
87
+ # }
88
+ # }
89
+ # Appium::Driver.new(opts).start_driver
90
+ #
91
+ # @option http_client_ops [Hash] :http_client Custom HTTP Client
92
+ # @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
93
+ # @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
94
+ # @return [Selenium::WebDriver] the new global driver
95
+ def start_driver(server_url:,
96
+ http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
97
+
98
+ # open_timeout and read_timeout are explicit wait.
99
+ open_timeout = http_client_ops.delete(:open_timeout)
100
+ read_timeout = http_client_ops.delete(:read_timeout)
101
+
102
+ http_client = http_client_ops.delete(:http_client)
103
+ @http_client ||= http_client ? http_client : Appium::Core::Base::Http::Default.new
104
+
105
+ @http_client.open_timeout = open_timeout if open_timeout
106
+ @http_client.read_timeout = read_timeout if read_timeout
107
+
108
+ begin
109
+ # included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
110
+ @driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
111
+ desired_capabilities: @caps,
112
+ url: server_url,
113
+ listener: @listener)
114
+
115
+ # export session
116
+ write_session_id(@driver.session_id) if @export_session
117
+ rescue Errno::ECONNREFUSED
118
+ raise "ERROR: Unable to connect to Appium. Is the server running on #{server_url}?"
119
+ end
120
+
121
+ # If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
122
+ # Since @automation_name is set only client side before start_driver is called.
123
+ set_automation_name_if_nil
124
+
125
+ @driver
126
+ end
127
+
128
+ # Quits the driver
129
+ # @return [void]
130
+ def quit_driver
131
+ @driver.quit
132
+ rescue
133
+ nil
134
+ end
135
+
136
+ # Returns the server's version info
137
+ #
138
+ # ```ruby
139
+ # {
140
+ # "build" => {
141
+ # "version" => "0.18.1",
142
+ # "revision" => "d242ebcfd92046a974347ccc3a28f0e898595198"
143
+ # }
144
+ # }
145
+ # ```
146
+ #
147
+ # Returns blank hash for Selenium Grid since `remote_status` gets 500 error
148
+ #
149
+ # ```ruby
150
+ # {}
151
+ # ```
152
+ #
153
+ # @return [Hash]
154
+ def appium_server_version
155
+ @driver.remote_status
156
+ rescue Selenium::WebDriver::Error::ServerError => e
157
+ raise ::Appium::Core::Error::ServerError unless e.message.include?('status code 500')
158
+ # driver.remote_status returns 500 error for using selenium grid
159
+ {}
160
+ end
161
+
162
+ # Return the platform version as an array of integers
163
+ # @return [Array<Integer>]
164
+ def platform_version
165
+ p_version = @driver.capabilities['platformVersion']
166
+ p_version.split('.').map(&:to_i)
167
+ end
168
+
169
+ # Takes a png screenshot and saves to the target path.
170
+ #
171
+ # Example: screenshot '/tmp/hi.png'
172
+ #
173
+ # @param png_save_path [String] the full path to save the png
174
+ # @return [nil]
175
+ def screenshot(png_save_path)
176
+ @driver.save_screenshot png_save_path
177
+ nil
178
+ end
179
+
180
+ private
181
+
182
+ # @private
183
+ def extend_for(device:, automation_name:, target:)
184
+ target.extend Appium::Core
185
+ target.extend Appium::Core::Device
186
+
187
+ case device
188
+ when :android
189
+ case automation_name
190
+ when :uiautomator2
191
+ ::Appium::Core::Android::Uiautomator2::Bridge.for(self)
192
+ else # default and UiAutomator
193
+ ::Appium::Core::Android::Uiautomator1::Bridge.for(self)
194
+ end
195
+ when :ios
196
+ case automation_name
197
+ when :xcuitest
198
+ ::Appium::Core::Ios::Xcuitest::Bridge.for(self)
199
+ else # default and UIAutomation
200
+ ::Appium::Core::Ios::Uiautomation::Bridge.for(self)
201
+ end
202
+ when :mac
203
+ # no Mac specific extentions
204
+ Appium::Logger.debug('mac')
205
+ when :windows
206
+ # no windows specific extentions
207
+ Appium::Logger.debug('windows')
208
+ else
209
+ Appium::Logger.warn('no device matched')
210
+ end
211
+
212
+ target
213
+ end
214
+
215
+ # @private
216
+ def get_caps(opts)
217
+ Core::Base::Capabilities.create_capabilities(opts[:caps] || {})
218
+ end
219
+
220
+ # @private
221
+ def get_appium_lib_opts(opts)
222
+ opts[:appium_lib] || {}
223
+ end
224
+
225
+ # @private
226
+ # Path to the .apk, .app or .app.zip.
227
+ # The path can be local or remote for Sauce.
228
+ def set_app_path
229
+ return unless @caps && @caps[:app] && !@caps[:app].empty?
230
+ @caps[:app] = File.expand_path(@caps[:app])
231
+ end
232
+
233
+ # @private
234
+ def set_appium_lib_specific_values(appium_lib_opts)
235
+ @custom_url = appium_lib_opts.fetch :server_url, false
236
+ @export_session = appium_lib_opts.fetch :export_session, false
237
+ @default_wait = appium_lib_opts.fetch :wait, 0
238
+
239
+ @port = appium_lib_opts.fetch :port, 4723
240
+
241
+ # timeout and interval used in ::Appium::Comm.wait/wait_true
242
+ @wait_timeout = appium_lib_opts.fetch :wait_timeout, 30
243
+ @wait_interval = appium_lib_opts.fetch :wait_interval, 0.5
244
+
245
+ # to pass it in Selenium.new.
246
+ # `listener = opts.delete(:listener)` is called in Selenium::Driver.new
247
+ @listener = appium_lib_opts.fetch :listener, nil
248
+ end
249
+
250
+ # @private
251
+ def set_appium_device
252
+ # https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
253
+ @device = @caps[:platformName]
254
+ return @device unless @device
255
+
256
+ @device = @device.is_a?(Symbol) ? @device : @device.downcase.strip.intern
257
+ end
258
+
259
+ # @private
260
+ def set_automation_name
261
+ @automation_name = @caps[:automationName] if @caps[:automationName]
262
+ @automation_name = if @automation_name
263
+ @automation_name.is_a?(Symbol) ? @automation_name : @automation_name.downcase.strip.intern
264
+ end
265
+ end
266
+
267
+ # @private
268
+ def set_automation_name_if_nil
269
+ return unless @automation_name.nil?
270
+ @automation_name = @driver.capabilities['automationName']
271
+ end
272
+ end
273
+ end
274
+ end # module Appium
@@ -0,0 +1,6 @@
1
+ # loaded in common/driver.rb
2
+ require_relative 'ios/search_context'
3
+ require_relative 'ios/device'
4
+
5
+ require_relative 'ios/uiautomation/patch'
6
+ require_relative 'ios/uiautomation/bridge'
@@ -0,0 +1,44 @@
1
+ module Appium
2
+ module Ios
3
+ module Device
4
+ extend Forwardable
5
+
6
+ # @!method touch_id
7
+ # iOS only; Simulate Touch ID with either valid (match == true) or invalid (match == false) fingerprint.
8
+ # @param [Boolean] match fingerprint validity
9
+ # Defaults to true.
10
+ # ```ruby
11
+ # touch_id true #=> Simulate valid fingerprint
12
+ # touch_id false #=> Simulate invalid fingerprint
13
+ # ```
14
+
15
+ # @!method toggle_touch_id_enrollment
16
+ # iOS Simulator only: Toggle touch id enrollment on an iOS Simulator.
17
+ # @param [Boolean] enabled Enable toggle touch id enrollment. Set true by default.
18
+ # ```ruby
19
+ # toggle_touch_id_enrollment #=> Enable toggle enrolled
20
+ # toggle_touch_id_enrollment true #=> Enable toggle enrolled
21
+ # toggle_touch_id_enrollment false #=> Disable toggle enrolled
22
+ # ```
23
+ # toggle_touch_id_enrollment
24
+ class << self
25
+ def extended(_mod)
26
+ ::Appium::Core::Device.extend_webdriver_with_forwardable
27
+
28
+ # TODO: TEST ME
29
+ ::Appium::Core::Device.add_endpoint_method(:touch_id) do
30
+ def touch_id(match = true)
31
+ execute :touch_id, {}, match: match
32
+ end
33
+ end
34
+
35
+ ::Appium::Core::Device.add_endpoint_method(:toggle_touch_id_enrollment) do
36
+ def toggle_touch_id_enrollment(enabled = true)
37
+ execute :toggle_touch_id_enrollment, {}, enabled: enabled
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end # module Device
43
+ end # module iOS
44
+ end # module Appium
@@ -0,0 +1,27 @@
1
+ module Appium
2
+ module Core
3
+ module Ios
4
+ module SearchContext
5
+ # @!method uiautomation_find
6
+ # find_element/s can be used with a [UIAutomation command](https://developer.apple.com/library/ios/documentation/ToolsLanguages/Reference/UIAWindowClassReference/UIAWindow/UIAWindow.html#//apple_ref/doc/uid/TP40009930).
7
+ #
8
+ # ```ruby
9
+ # find_elements :uiautomation, 'elements()
10
+ # ```
11
+ #
12
+ # @!method ios_predicate_string_find
13
+ # find_element/s can be used with a [Predicates](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)
14
+ #
15
+ # ```ruby
16
+ # find_elements :predicate, "isWDVisible == 1"
17
+ # find_elements :predicate, 'wdName == "Buttons"'
18
+ # find_elements :predicate, 'wdValue == "SearchBar" AND isWDDivisible == 1'
19
+ # ```
20
+ def self.extend
21
+ ::Appium::Core::Base::SearchContext.add_finders(uiautomation: '-ios uiautomation')
22
+ ::Appium::Core::Base::SearchContext.add_finders(predicate: '-ios predicate string')
23
+ end
24
+ end # class << self
25
+ end # module Ios
26
+ end # module Core
27
+ end # module Appium
@@ -0,0 +1,17 @@
1
+ require_relative '../../ios'
2
+
3
+ module Appium
4
+ module Core
5
+ module Ios
6
+ module Uiautomation
7
+ module Bridge
8
+ def self.for(target)
9
+ Core::Ios::SearchContext.extend
10
+ target.extend Appium::Ios::Device
11
+ patch_webdriver_element
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module Appium
2
+ module Core
3
+ module Ios
4
+ module Uiautomation
5
+ # @private
6
+ # class_eval inside a method because class Selenium::WebDriver::Element
7
+ # will trigger as soon as the file is required. in contrast a method
8
+ # will trigger only when invoked.
9
+ def patch_webdriver_element
10
+ Selenium::WebDriver::Element.class_eval do
11
+ # Cross platform way of entering text into a textfield
12
+ def type(text, driver = $driver)
13
+ driver.execute_script %(au.getElement('#{ref}').setValue('#{text}');)
14
+ end # def type
15
+ end # Selenium::WebDriver::Element.class_eval
16
+ end # def patch_webdriver_element
17
+ end # module Uiautomation
18
+ end # module Ios
19
+ end # module Core
20
+ end # module Appium
@@ -0,0 +1,18 @@
1
+ require_relative '../../ios_xcuitest'
2
+
3
+ module Appium
4
+ module Core
5
+ module Ios
6
+ module Xcuitest
7
+ module Bridge
8
+ def self.for(target)
9
+ Core::Ios::SearchContext.extend
10
+ Core::Ios::Xcuitest::SearchContext.extend
11
+ target.extend Appium::Ios::Device
12
+ target.extend Appium::Ios::Xcuitest::Device
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,3 @@
1
- require 'base64'
2
-
3
1
  module Appium
4
2
  module Ios
5
3
  module Xcuitest
@@ -30,9 +28,10 @@ module Appium
30
28
 
31
29
  class << self
32
30
  def extended(_mod)
33
- ::Appium::Device.extend_webdriver_with_forwardable
31
+ ::Appium::Core::Device.extend_webdriver_with_forwardable
34
32
 
35
- ::Appium::Device.add_endpoint_method(:hide_keyboard) do
33
+ # Override
34
+ ::Appium::Core::Device.add_endpoint_method(:hide_keyboard) do
36
35
  def hide_keyboard(close_key = nil, strategy = nil)
37
36
  option = {}
38
37
 
@@ -43,7 +42,8 @@ module Appium
43
42
  end
44
43
  end
45
44
 
46
- ::Appium::Device.add_endpoint_method(:background_app) do
45
+ # Override
46
+ ::Appium::Core::Device.add_endpoint_method(:background_app) do
47
47
  def background_app(duration = 0)
48
48
  # https://github.com/appium/ruby_lib/issues/500, https://github.com/appium/appium/issues/7741
49
49
  # `execute :background_app, {}, seconds: { timeout: duration_milli_sec }` works over Appium 1.6.4
@@ -1,8 +1,8 @@
1
1
  module Appium
2
- module Ios
3
- module Xcuitest
4
- module SearchContext
5
- class << self
2
+ module Core
3
+ module Ios
4
+ module Xcuitest
5
+ module SearchContext
6
6
  # @!method ios_class_chain_find
7
7
  # Only for XCUITest(WebDriverAgent)
8
8
  # find_element/s can be used with a [class chain]( https://github.com/facebook/WebDriverAgent/wiki/Queries)
@@ -14,12 +14,16 @@ module Appium
14
14
  # find_elements :class_chain, 'XCUIElementTypeWindow'
15
15
  # # select the second last child of the second child window
16
16
  # find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]'
17
+ # # matching predicate. <code>`</code> is the mark.
18
+ # find_elements :class_chain, 'XCUIElementTypeWindow[`visible = 1][`name = \"bla\"`]'
19
+ # # containing predicate. `$` is the mark.
20
+ # find_elements :class_chain, 'XCUIElementTypeWindow[$name = \"bla$$$bla\"$]'
17
21
  # ```
18
- def extended(_mod)
19
- ::Appium::Driver::SearchContext::FINDERS[:class_chain] = '-ios class chain'
22
+ def self.extend
23
+ ::Appium::Core::Base::SearchContext.add_finders(class_chain: '-ios class chain')
20
24
  end
21
25
  end
22
- end
23
- end # module Xcuitest
24
- end # module Ios
26
+ end # class << self
27
+ end # module Ios
28
+ end # module Core
25
29
  end # module Appium