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
@@ -1,3 +1,20 @@
1
+ ### Breaking Changes in 9.6.0
2
+ Raise warning if users call `Appium::Driver.new(opts)`.
3
+
4
+ - After
5
+
6
+ ```ruby
7
+ Appium::Driver.new(opts).start_driver # Raise warning.
8
+ Appium::Driver.new(opts, true).start_driver # $driver is defined.
9
+ Appium::Driver.new(opts, false).start_driver # $driver isn't defined.
10
+ ```
11
+
12
+ - Before
13
+
14
+ ```ruby
15
+ Appium::Driver.new(opts).start_driver # $driver is defined.
16
+ ```
17
+
1
18
  ### Breaking Changes in 9.3.7
2
19
  change `@selenium_driver.find_element/s_with_appium` to `@selenium_driver.find_element/s`.
3
20
  ref: https://github.com/appium/ruby_lib/pull/532
@@ -7,5 +7,4 @@ require 'forwardable' unless defined? Forwardable
7
7
  # Init global driver
8
8
  $driver = nil
9
9
 
10
- require_relative 'appium_lib/logger'
11
- require_relative 'appium_lib/driver'
10
+ require_relative 'appium_lib/appium'
@@ -0,0 +1,20 @@
1
+ require_relative 'common/helper'
2
+
3
+ require_relative 'element/alert'
4
+ require_relative 'element/button'
5
+ require_relative 'element/generic'
6
+ require_relative 'element/textfield'
7
+ require_relative 'element/text'
8
+
9
+ # android - uiautomator2
10
+ require_relative 'uiautomator2'
11
+
12
+ module Appium
13
+ module Android
14
+ class Bridge
15
+ def self.for(target)
16
+ target.extend Appium::Android
17
+ end
18
+ end
19
+ end
20
+ end
@@ -91,7 +91,7 @@ module Appium
91
91
  source_is_html = source_header.start_with?(doctype_string, '<html')
92
92
 
93
93
  parser = if source_is_html # parse html from webview
94
- @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
94
+ @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Appium::Common::HTMLElements.new)
95
95
  else
96
96
  @android_native_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
97
97
  end
@@ -1,10 +1,11 @@
1
1
  require_relative 'uiautomator2/helper'
2
2
  require_relative 'uiautomator2/element'
3
+ require_relative 'uiautomator2/bridge'
3
4
 
4
5
  module Appium
5
- module Ios
6
- module Xcuitest
6
+ module Android
7
+ module Uiautomator2
7
8
  # parent
8
- end # module Xcuitest
9
- end # module Ios
9
+ end # module Uiautomator2
10
+ end # module Android
10
11
  end # module Appium
@@ -0,0 +1,16 @@
1
+ require_relative '../android'
2
+
3
+ module Appium
4
+ module Android
5
+ module Uiautomator2
6
+ class Bridge
7
+ def self.for(target)
8
+ target.extend Appium::Android
9
+ target.extend Appium::Android::Uiautomator2
10
+ target.extend Appium::Android::Uiautomator2::Helper
11
+ target.extend Appium::Android::Uiautomator2::Element
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,201 @@
1
+ require 'rubygems'
2
+ require 'selenium-webdriver'
3
+ require 'nokogiri'
4
+
5
+ # base
6
+ require_relative 'driver'
7
+ require_relative 'sauce_labs'
8
+
9
+ # common
10
+ require_relative 'common/wait'
11
+ require_relative 'common/log'
12
+ require_relative 'common/helper'
13
+ require_relative 'common/multi_touch'
14
+ require_relative 'common/touch_actions'
15
+
16
+ # core
17
+ require_relative 'core/core'
18
+
19
+ # ios
20
+ require_relative 'ios/ios'
21
+
22
+ # android
23
+ require_relative 'android/android'
24
+
25
+ module Appium
26
+ class << self
27
+ # Load arbitrary text ([toml format](https://github.com/toml-lang/toml))
28
+ # The toml is parsed by https://github.com/fbernier/tomlrb .
29
+ #
30
+ # ```
31
+ # [caps]
32
+ # app = "path/to/app"
33
+ #
34
+ # [appium_lib]
35
+ # port = 8080
36
+ # ```
37
+ #
38
+ # :app is expanded
39
+ # :require is expanded
40
+ # all keys are converted to symbols
41
+ #
42
+ # @param opts [Hash] file: '/path/to/appium.txt', verbose: true
43
+ # @return [hash] the symbolized hash with updated :app and :require keys
44
+ def load_settings(opts = {})
45
+ raise 'opts must be a hash' unless opts.is_a? Hash
46
+ raise 'opts must not be empty' if opts.empty?
47
+
48
+ toml = opts[:file]
49
+ raise 'Must pass a capability file which has [caps] and [appium_lib]' unless toml
50
+ verbose = opts.fetch :verbose, false
51
+
52
+ Appium::Logger.info "appium settings path: #{toml}" if verbose
53
+
54
+ toml_exists = File.exist? toml
55
+ Appium::Logger.info "Exists? #{toml_exists}" if verbose
56
+
57
+ raise "toml doesn't exist #{toml}" unless toml_exists
58
+ require 'tomlrb'
59
+ Appium::Logger.info "Loading #{toml}" if verbose
60
+
61
+ data = Tomlrb.load_file(toml, symbolize_keys: true)
62
+ if verbose
63
+ Appium::Logger.ap_info data unless data.empty?
64
+ end
65
+
66
+ if data && data[:caps] && data[:caps][:app] && !data[:caps][:app].empty?
67
+ data[:caps][:app] = Appium::Driver.absolute_app_path data
68
+ end
69
+
70
+ if data && data[:appium_lib] && data[:appium_lib][:require]
71
+ parent_dir = File.dirname toml
72
+ data[:appium_lib][:require] = expand_required_files(parent_dir, data[:appium_lib][:require])
73
+ end
74
+
75
+ data
76
+ end
77
+ alias load_appium_txt load_settings
78
+
79
+ # @param [String] base_dir parent directory of loaded appium.txt (toml)
80
+ # @param [String] file_paths
81
+ # @return [Array] list of require files as an array, nil if require doesn't exist
82
+ def expand_required_files(base_dir, file_paths)
83
+ # ensure files are absolute
84
+ Array(file_paths).map! do |f|
85
+ file = File.exist?(f) ? f : File.join(base_dir, f)
86
+ file = File.expand_path file
87
+
88
+ File.exist?(file) ? file : nil
89
+ end
90
+ file_paths.compact! # remove nils
91
+
92
+ files = []
93
+
94
+ # now expand dirs
95
+ file_paths.each do |item|
96
+ unless File.directory? item
97
+ # save file
98
+ files << item
99
+ next # only look inside folders
100
+ end
101
+ Dir.glob(File.expand_path(File.join(item, '**', '*.rb'))) do |f|
102
+ # do not add folders to the file list
103
+ files << File.expand_path(f) unless File.directory? f
104
+ end
105
+ end
106
+
107
+ files
108
+ end
109
+
110
+ # This method is intended to work with page objects that share
111
+ # a common module. For example, Page::HomePage, Page::SignIn
112
+ # those could be promoted on with Appium.promote_singleton_appium_methods Page
113
+ #
114
+ # If you are promoting on an individual class then you should use
115
+ # Appium.promote_appium_methods instead. The singleton method is intended
116
+ # only for the shared module use case.
117
+ #
118
+ # if modules is a module instead of an array, then the constants of
119
+ # that module are promoted on.
120
+ # otherwise, the array of modules will be used as the promotion target.
121
+ def promote_singleton_appium_methods(modules, driver = $driver)
122
+ raise 'Global $driver is nil' if driver.nil?
123
+
124
+ target_modules = []
125
+
126
+ if modules.is_a? Module
127
+ modules.constants.each do |sub_module|
128
+ target_modules << modules.const_get(sub_module)
129
+ end
130
+ else
131
+ raise 'modules must be a module or an array' unless modules.is_a? Array
132
+ target_modules = modules
133
+ end
134
+
135
+ target_modules.each do |const|
136
+ # noinspection RubyResolve
137
+ # rubocop:disable Style/MultilineIfModifier
138
+ driver.public_methods(false).each do |m|
139
+ const.send(:define_singleton_method, m) do |*args, &block|
140
+ begin
141
+ super(*args, &block) # promote.rb
142
+ rescue NoMethodError, ArgumentError
143
+ driver.send m, *args, &block if driver.respond_to?(m)
144
+ end
145
+ # override unless there's an existing method with matching arity
146
+ end unless const.respond_to?(m) && const.method(m).arity == driver.method(m).arity
147
+ end
148
+ # rubocop:enable Style/MultilineIfModifier
149
+ end
150
+ end
151
+
152
+ ##
153
+ # Promote appium methods to class instance methods
154
+ #
155
+ # @param class_array [Array<Class>] An array of classes
156
+ #
157
+ # To promote methods to all classes:
158
+ #
159
+ # ```ruby
160
+ # Appium.promote_appium_methods Object
161
+ # ```
162
+ #
163
+ # It's better to promote on specific classes instead of Object
164
+ #
165
+ # ```ruby
166
+ # # promote on rspec
167
+ # Appium.promote_appium_methods RSpec::Core::ExampleGroup
168
+ # ```
169
+ #
170
+ # ```ruby
171
+ # # promote on minispec
172
+ # Appium.promote_appium_methods Minitest::Spec
173
+ # ```
174
+ def promote_appium_methods(class_array, driver = $driver)
175
+ raise 'Driver is nil' if driver.nil?
176
+ # Wrap single class into an array
177
+ class_array = [class_array] unless class_array.class == Array
178
+ # Promote Appium driver methods to class instance methods.
179
+ class_array.each do |klass|
180
+ driver.public_methods(false).each do |m|
181
+ klass.class_eval do
182
+ define_method m do |*args, &block|
183
+ begin
184
+ # Prefer existing method.
185
+ # super will invoke method missing on driver
186
+ super(*args, &block)
187
+
188
+ # minitest also defines a name method,
189
+ # so rescue argument error
190
+ # and call the name method on $driver
191
+ rescue NoMethodError, ArgumentError
192
+ driver.send m, *args, &block if driver.respond_to?(m)
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+ nil # return nil
199
+ end
200
+ end
201
+ end
@@ -1,14 +1,7 @@
1
- require 'selenium/webdriver/common/error'
2
-
3
1
  # Generic helper methods not specific
4
2
  # to a particular tag name
5
3
  module Appium
6
4
  module Common
7
- # json and ap are required for the source method.
8
- require 'json'
9
- require 'ap' # awesome print
10
- require 'timeout' # for wait
11
-
12
5
  # iOS .name returns the accessibility attribute if it's set. if not set, the string value is used.
13
6
  # Android .name returns the accessibility attribute and nothing if it's not set.
14
7
  #
@@ -42,7 +35,7 @@ module Appium
42
35
  # @param xpath_str [String] the XPath string
43
36
  # @return [Element]
44
37
  def xpath(xpath_str)
45
- find_element :xpath, xpath_str
38
+ @driver.find_element :xpath, xpath_str
46
39
  end
47
40
 
48
41
  # Returns all elements that match the provided xpath.
@@ -50,18 +43,12 @@ module Appium
50
43
  # @param xpath_str [String] the XPath string
51
44
  # @return [Array<Element>]
52
45
  def xpaths(xpath_str)
53
- find_elements :xpath, xpath_str
46
+ @driver.find_elements :xpath, xpath_str
54
47
  end
55
48
 
56
- def _print_source(source)
57
- opts = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
58
- doc = if source.start_with? '<html'
59
- Nokogiri::HTML(source) { |cfg| cfg.options = opts }
60
- else
61
- Nokogiri::XML(source) { |cfg| cfg.options = opts }
62
- end
63
- puts doc.to_xml indent: 2
64
- end
49
+ # json and ap are required for the source method.
50
+ require 'json'
51
+ require 'ap' # awesome print
65
52
 
66
53
  # @private
67
54
  # http://nokogiri.org/Nokogiri/XML/SAX.html
@@ -219,5 +206,16 @@ module Appium
219
206
  error_message = 'An element could not be located on the page using the given search parameters.'
220
207
  raise Selenium::WebDriver::Error::NoSuchElementError, error_message
221
208
  end
222
- end # module Common
223
- end # module Appium
209
+
210
+ # @private
211
+ def _print_source(source)
212
+ opts = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
213
+ doc = if source.start_with? '<html'
214
+ Nokogiri::HTML(source) { |cfg| cfg.options = opts }
215
+ else
216
+ Nokogiri::XML(source) { |cfg| cfg.options = opts }
217
+ end
218
+ puts doc.to_xml indent: 2
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,24 @@
1
+ module Appium
2
+ module Common
3
+ # @param [String|Hash] type You can get particular type's logs.
4
+ # @return [[Selenium::WebDriver::LogEntry]] A list of logs data.
5
+ #
6
+ # @example
7
+ # @driver.get_log("syslog") #=> [[Selenium::WebDriver::LogEntry]]
8
+ # @driver.get_log(:syslog) #=> [[Selenium::WebDriver::LogEntry]]
9
+ #
10
+ def get_log(type)
11
+ Appium::Core::Logs.new(@driver.manage.logs).get type
12
+ end
13
+
14
+ # Get a list of available log types
15
+ #
16
+ # @return [[String]] A list of available log types.
17
+ # @example
18
+ # @driver.get_available_log_types #=> [:syslog, :crashlog, :performance]
19
+ #
20
+ def get_available_log_types
21
+ Appium::Core::Logs.new(@driver.manage.logs).available_types
22
+ end
23
+ end # module Common
24
+ end # module Appium
@@ -0,0 +1,89 @@
1
+ module Appium
2
+ # MultiTouch actions allow for multiple touches to happen at the same time,
3
+ # for instance, to simulate multiple finger swipes.
4
+ #
5
+ # Create a series of touch actions by themselves (without a `prepare()`), then
6
+ # add to a new MultiTouch action. When ready, call `prepare()` and all
7
+ # actions will be executed simultaneously.
8
+ #
9
+ # ```ruby
10
+ # action_1 = TouchAction.new.press(x: 45, y: 100).wait(5).release
11
+ # action_2 = TouchAction.new.tap(element: el, x: 50, y:5, count: 3)
12
+ #
13
+ # multi_touch_action = MultiTouch.new
14
+ # multi_touch_action.add action_1
15
+ # multi_touch_action.add action_2
16
+ # multi_touch_action.perform
17
+ # ```
18
+ #
19
+ # ```
20
+ # # with an arbitrary driver
21
+ # driver = Appium::Driver.new(opts, false).start_driver
22
+ # multi_touch_action = MultiTouch.new(driver)
23
+ # multi_touch_action.add action_1
24
+ # multi_touch_action.add action_2
25
+ # multi_touch_action.perform
26
+ # ```
27
+
28
+ class MultiTouch < ::Appium::Core::MultiTouch
29
+ class << self
30
+ # Convenience method for pinching the screen.
31
+ # Places two fingers at the edges of the screen and brings them together.
32
+ # @param percentage (int) The percent size by which to shrink the screen when pinched.
33
+ # @param auto_perform (boolean) Whether to perform the action immediately (default true)
34
+ # @param driver (Driver) Set a driver to conduct the action. DEfault is global driver($driver)
35
+ #
36
+ # ```ruby
37
+ # pinch 75 #=> Pinch the screen from the top right and bottom left corners
38
+ # ```
39
+ #
40
+ # Without auto_perform
41
+ #
42
+ # ```ruby
43
+ # action = pinch 75, false #=> Pinch the screen from the top right and bottom left corners
44
+ # action.perform #=> to 25% of its size.
45
+ # ```
46
+ #
47
+ # With driver
48
+ #
49
+ # ```ruby
50
+ # driver = Appium::Driver.new(opts, false).start_driver
51
+ # pinch 75, true, driver #=> Pinch the screen from the top right and bottom left corners
52
+ # ```
53
+ def pinch(percentage = 25, auto_perform = true, driver = $driver)
54
+ ::Appium::Core::MultiTouch.pinch percentage: percentage, auto_perform: auto_perform, driver: driver
55
+ end
56
+
57
+ # Convenience method for zooming the screen.
58
+ # Places two fingers at the edges of the screen and brings them together.
59
+ # @param percentage (int) The percent size by which to shrink the screen when pinched.
60
+ # @param auto_perform (boolean) Whether to perform the action immediately (default true)
61
+ # @param driver (Driver) Set a driver to conduct the action. DEfault is global driver($driver)
62
+ #
63
+ # ```ruby
64
+ # action = zoom 200 #=> Zoom in the screen from the center until it doubles in size.
65
+ # ```
66
+ #
67
+ # Without auto_perform
68
+ #
69
+ # ```ruby
70
+ # action = zoom 200, false #=> Zoom in the screen from the center until it doubles in size.
71
+ # action.perform #=> to 25% of its size.
72
+ # ```
73
+ #
74
+ # With driver
75
+ #
76
+ # ```ruby
77
+ # driver = Appium::Driver.new(opts, false).start_driver
78
+ # pinch 200, true, driver #=> Zoom in the screen from the center until it doubles in size.
79
+ # ```
80
+ def zoom(percentage = 200, auto_perform = true, driver = $driver)
81
+ ::Appium::Core::MultiTouch.zoom percentage: percentage, auto_perform: auto_perform, driver: driver
82
+ end
83
+ end # self
84
+
85
+ def initialize(driver = $driver)
86
+ super driver
87
+ end
88
+ end # class MultiTouch
89
+ end # module Appium