appium_lib 9.6.1 → 9.7.0

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