appium_lib 6.0.0 → 7.0.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +28 -0
  4. data/.travis.yml +10 -0
  5. data/Rakefile +9 -1
  6. data/android_tests/Gemfile +1 -1
  7. data/android_tests/Rakefile +20 -13
  8. data/android_tests/lib/android/specs/android/element/alert.rb +1 -1
  9. data/android_tests/lib/android/specs/android/element/button.rb +2 -2
  10. data/android_tests/lib/android/specs/android/element/generic.rb +1 -2
  11. data/android_tests/lib/android/specs/android/element/text.rb +2 -3
  12. data/android_tests/lib/android/specs/android/element/textfield.rb +2 -2
  13. data/android_tests/lib/android/specs/android/helper.rb +5 -3
  14. data/android_tests/lib/android/specs/android/patch.rb +2 -2
  15. data/android_tests/lib/android/specs/common/device.rb +16 -9
  16. data/android_tests/lib/android/specs/common/device_touchaction.rb +5 -2
  17. data/android_tests/lib/android/specs/common/element/window.rb +1 -1
  18. data/android_tests/lib/android/specs/common/helper.rb +14 -15
  19. data/android_tests/lib/android/specs/common/patch.rb +11 -9
  20. data/android_tests/lib/android/specs/common/version.rb +3 -3
  21. data/android_tests/lib/android/specs/common/web_context.rb +2 -3
  22. data/android_tests/lib/android/specs/driver.rb +38 -29
  23. data/android_tests/lib/android/specs/install.rb +3 -3
  24. data/android_tests/lib/format.rb +6 -8
  25. data/android_tests/lib/run.rb +25 -17
  26. data/android_tests/readme.md +4 -2
  27. data/appium_lib.gemspec +13 -11
  28. data/contributing.md +1 -1
  29. data/docs/android_docs.md +358 -274
  30. data/docs/ios_docs.md +333 -270
  31. data/docs/migration.md +10 -0
  32. data/docs_gen/make_docs.rb +3 -1
  33. data/ios_tests/Gemfile +1 -1
  34. data/ios_tests/Rakefile +17 -10
  35. data/ios_tests/appium.txt +1 -1
  36. data/ios_tests/lib/common.rb +8 -4
  37. data/ios_tests/lib/format.rb +5 -7
  38. data/ios_tests/lib/ios/specs/common/element/window.rb +1 -1
  39. data/ios_tests/lib/ios/specs/common/helper.rb +40 -39
  40. data/ios_tests/lib/ios/specs/common/patch.rb +15 -11
  41. data/ios_tests/lib/ios/specs/common/version.rb +3 -3
  42. data/ios_tests/lib/ios/specs/common/web_context.rb +1 -2
  43. data/ios_tests/lib/ios/specs/device/device.rb +7 -7
  44. data/ios_tests/lib/ios/specs/device/multi_touch.rb +6 -8
  45. data/ios_tests/lib/ios/specs/device/touch_actions.rb +12 -12
  46. data/ios_tests/lib/ios/specs/driver.rb +23 -22
  47. data/ios_tests/lib/ios/specs/ios/element/alert.rb +6 -2
  48. data/ios_tests/lib/ios/specs/ios/element/button.rb +2 -2
  49. data/ios_tests/lib/ios/specs/ios/element/generic.rb +1 -1
  50. data/ios_tests/lib/ios/specs/ios/element/text.rb +4 -1
  51. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +6 -6
  52. data/ios_tests/lib/ios/specs/ios/helper.rb +5 -5
  53. data/ios_tests/lib/ios/specs/ios/patch.rb +2 -2
  54. data/ios_tests/lib/run.rb +1 -1
  55. data/ios_tests/readme.md +3 -3
  56. data/ios_tests/upload/sauce_storage.rb +8 -8
  57. data/ios_tests/upload/upload.rb +1 -1
  58. data/lib/appium_lib/android/client_xpath.rb +7 -7
  59. data/lib/appium_lib/android/element/alert.rb +2 -2
  60. data/lib/appium_lib/android/element/button.rb +16 -16
  61. data/lib/appium_lib/android/element/generic.rb +12 -13
  62. data/lib/appium_lib/android/element/text.rb +5 -5
  63. data/lib/appium_lib/android/element/textfield.rb +5 -5
  64. data/lib/appium_lib/android/helper.rb +82 -52
  65. data/lib/appium_lib/android/mobile_methods.rb +2 -2
  66. data/lib/appium_lib/android/patch.rb +3 -3
  67. data/lib/appium_lib/common/element/window.rb +1 -1
  68. data/lib/appium_lib/common/helper.rb +30 -35
  69. data/lib/appium_lib/common/patch.rb +22 -20
  70. data/lib/appium_lib/common/version.rb +3 -3
  71. data/lib/appium_lib/common/wait.rb +9 -10
  72. data/lib/appium_lib/device/device.rb +39 -33
  73. data/lib/appium_lib/device/multi_touch.rb +5 -7
  74. data/lib/appium_lib/device/touch_actions.rb +14 -15
  75. data/lib/appium_lib/driver.rb +97 -76
  76. data/lib/appium_lib/ios/element/alert.rb +1 -1
  77. data/lib/appium_lib/ios/element/button.rb +5 -5
  78. data/lib/appium_lib/ios/element/generic.rb +5 -6
  79. data/lib/appium_lib/ios/element/text.rb +5 -5
  80. data/lib/appium_lib/ios/element/textfield.rb +15 -15
  81. data/lib/appium_lib/ios/helper.rb +103 -90
  82. data/lib/appium_lib/ios/mobile_methods.rb +2 -2
  83. data/lib/appium_lib/ios/patch.rb +4 -4
  84. data/lib/appium_lib/logger.rb +7 -5
  85. data/lib/appium_lib/rails/duplicable.rb +3 -1
  86. data/readme.md +7 -1
  87. data/release_notes.md +152 -0
  88. metadata +28 -54
@@ -1,5 +1,4 @@
1
1
  module Appium
2
-
3
2
  # MultiTouch actions allow for multiple touches to happen at the same time,
4
3
  # for instance, to simulate multiple finger swipes.
5
4
  #
@@ -17,7 +16,6 @@ module Appium
17
16
  # multi_touch_action.perform
18
17
  class MultiTouch
19
18
  class << self
20
-
21
19
  # Convenience method for pinching the screen.
22
20
  # Places two fingers at the edges of the screen and brings them together.
23
21
  # @param percentage (int) The percent size by which to shrink the screen when pinched.
@@ -27,8 +25,8 @@ module Appium
27
25
  # action = pinch 75 #=> Pinch the screen from the top right and bottom left corners
28
26
  # action.perform #=> to 25% of its size.
29
27
  # ```
30
- def pinch(percentage=25, auto_perform=true)
31
- raise ArgumentError("Can't pinch to greater than screen size.") if percentage > 100
28
+ def pinch(percentage = 25, auto_perform = true)
29
+ fail ArgumentError("Can't pinch to greater than screen size.") if percentage > 100
32
30
 
33
31
  p = Float(percentage) / 100
34
32
  i = 1 - p
@@ -55,8 +53,8 @@ module Appium
55
53
  # action = zoom 200 #=> Zoom in the screen from the center until it doubles in size.
56
54
  # action.perform
57
55
  # ```
58
- def zoom(percentage=200, auto_perform=true)
59
- raise ArgumentError("Can't zoom to smaller then screen size.") if percentage < 100
56
+ def zoom(percentage = 200, auto_perform = true)
57
+ fail ArgumentError("Can't zoom to smaller then screen size.") if percentage < 100
60
58
 
61
59
  p = 100 / Float(percentage)
62
60
  i = 1 - p
@@ -91,4 +89,4 @@ module Appium
91
89
  $driver.multi_touch @actions
92
90
  end
93
91
  end # class MultiTouch
94
- end # module Appium
92
+ end # module Appium
@@ -1,5 +1,4 @@
1
1
  module Appium
2
-
3
2
  # Perform a series of gestures, one after another. Gestures are chained
4
3
  # together and only performed when `perform()` is called.
5
4
  #
@@ -15,7 +14,7 @@ module Appium
15
14
  class << self
16
15
  COMPLEX_ACTIONS.each do |action|
17
16
  define_method(action) do |opts|
18
- auto_perform = opts.delete(:auto_perform) { |k| true }
17
+ auto_perform = opts.delete(:auto_perform) { |_k| true }
19
18
  ta = TouchAction.new
20
19
  ta.send(action, opts)
21
20
  return ta unless auto_perform
@@ -45,7 +44,7 @@ module Appium
45
44
  # @option y [integer] y co-ordinate to press on.
46
45
  # @option duration [integer] Number of milliseconds to press.
47
46
  def long_press(opts)
48
- args = opts.select { |k, v| [:element, :x, :y, :duration].include? k }
47
+ args = opts.select { |k, _v| [:element, :x, :y, :duration].include? k }
49
48
  args = args_with_ele_ref(args)
50
49
  chain_method(:longPress, args) # longPress is what the appium server expects
51
50
  end
@@ -57,7 +56,7 @@ module Appium
57
56
  # @option opts [integer] :x x co-ordinate to press on
58
57
  # @option opts [integer] :y y co-ordinate to press on
59
58
  def press(opts)
60
- args = opts.select { |k, v| [:element, :x, :y].include? k }
59
+ args = opts.select { |k, _v| [:element, :x, :y].include? k }
61
60
  args = args_with_ele_ref(args)
62
61
  chain_method(:press, args)
63
62
  end
@@ -67,7 +66,7 @@ module Appium
67
66
  # @option opts [WebDriver::Element] :element (Optional) Element to release from.
68
67
  # @option opts [integer] :x x co-ordinate to release from
69
68
  # @option opts [integer] :y y co-ordinate to release from
70
- def release(opts=nil)
69
+ def release(opts = nil)
71
70
  args = args_with_ele_ref(opts) if opts
72
71
  chain_method(:release, args)
73
72
  end
@@ -79,9 +78,9 @@ module Appium
79
78
  # @option opts [integer] :y y co-ordinate to tap
80
79
  # @option opts [integer] :fingers how many fingers to tap with (Default 1)
81
80
  def tap(opts)
82
- opts[:count] = opts.delete(:fingers) if opts[:fingers]
83
- opts_with_defaults = { count: 1 }.merge opts
84
- args = args_with_ele_ref opts
81
+ opts[:count] = opts.delete(:fingers) if opts[:fingers]
82
+ opts[:count] ||= 1
83
+ args = args_with_ele_ref opts
85
84
  chain_method(:tap, args)
86
85
  end
87
86
 
@@ -108,10 +107,10 @@ module Appium
108
107
  end_y = opts.fetch :end_y, 0
109
108
  duration = opts[:duration]
110
109
 
111
- self.press x: start_x, y: start_y
112
- self.wait(duration) if duration
113
- self.move_to x: end_x, y: end_y
114
- self.release
110
+ press x: start_x, y: start_y
111
+ wait(duration) if duration
112
+ move_to x: end_x, y: end_y
113
+ release
115
114
  self
116
115
  end
117
116
 
@@ -130,7 +129,7 @@ module Appium
130
129
 
131
130
  private
132
131
 
133
- def chain_method(method, args=nil)
132
+ def chain_method(method, args = nil)
134
133
  if args
135
134
  @actions << { action: method, options: args }
136
135
  else
@@ -140,8 +139,8 @@ module Appium
140
139
  end
141
140
 
142
141
  def args_with_ele_ref(args)
143
- args[:element] = args[:element].ref if args.has_key? :element
142
+ args[:element] = args[:element].ref if args.key? :element
144
143
  args
145
144
  end
146
145
  end # class TouchAction
147
- end # module Appium
146
+ end # module Appium
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'ap'
3
3
  require 'selenium-webdriver'
4
+ require 'selenium/client/errors' # used in helper.rb for CommandError
4
5
  require 'nokogiri'
5
6
 
6
7
  # common
@@ -66,29 +67,29 @@ module Appium
66
67
  #
67
68
  # @param opts [Hash] file: '/path/to/appium.txt', verbose: true
68
69
  # @return [hash] the symbolized hash with updated :app and :require keys
69
- def self.load_appium_txt opts={}
70
- raise 'opts must be a hash' unless opts.kind_of? Hash
71
- raise 'opts must not be empty' if opts.empty?
70
+ def self.load_appium_txt(opts = {})
71
+ fail 'opts must be a hash' unless opts.is_a? Hash
72
+ fail 'opts must not be empty' if opts.empty?
72
73
 
73
74
  file = opts[:file]
74
- raise 'Must pass file' unless file
75
+ fail 'Must pass file' unless file
75
76
  verbose = opts.fetch :verbose, false
76
77
 
77
78
  parent_dir = File.dirname file
78
79
  toml = File.expand_path File.join parent_dir, 'appium.txt'
79
80
  Appium::Logger.info "appium.txt path: #{toml}" if verbose
80
81
 
81
- toml_exists = File.exists? toml
82
+ toml_exists = File.exist? toml
82
83
  Appium::Logger.info "Exists? #{toml_exists}" if verbose
83
84
 
84
- raise "toml doesn't exist #{toml}" unless toml_exists
85
+ fail "toml doesn't exist #{toml}" unless toml_exists
85
86
  require 'toml'
86
87
  Appium::Logger.info "Loading #{toml}" if verbose
87
88
 
88
89
  data = File.read toml
89
90
  data = TOML::Parser.new(data).parsed
90
91
  # TOML creates string keys. must symbolize
91
- data = Appium::symbolize_keys data
92
+ data = Appium.symbolize_keys data
92
93
  Appium::Logger.ap_info data unless data.empty? if verbose
93
94
 
94
95
  if data && data[:caps] && data[:caps][:app] && !data[:caps][:app].empty?
@@ -99,14 +100,13 @@ module Appium
99
100
  # nil if require doesn't exist
100
101
  if data && data[:appium_lib] && data[:appium_lib][:require]
101
102
  r = data[:appium_lib][:require]
102
- r = r.kind_of?(Array) ? r : [r]
103
+ r = r.is_a?(Array) ? r : [r]
103
104
  # ensure files are absolute
104
- r.map! do |file|
105
- file = File.exists?(file) ? file :
106
- File.join(parent_dir, file)
105
+ r.map! do |f|
106
+ file = File.exist?(f) ? f : File.join(parent_dir, f)
107
107
  file = File.expand_path file
108
108
 
109
- File.exists?(file) ? file : nil
109
+ File.exist?(file) ? file : nil
110
110
  end
111
111
  r.compact! # remove nils
112
112
 
@@ -119,9 +119,9 @@ module Appium
119
119
  files << item
120
120
  next # only look inside folders
121
121
  end
122
- Dir.glob(File.expand_path(File.join(item, '**', '*.rb'))) do |file|
122
+ Dir.glob(File.expand_path(File.join(item, '**', '*.rb'))) do |f|
123
123
  # do not add folders to the file list
124
- files << File.expand_path(file) unless File.directory? file
124
+ files << File.expand_path(f) unless File.directory? f
125
125
  end
126
126
  end
127
127
 
@@ -136,21 +136,29 @@ module Appium
136
136
  #
137
137
  # based on deep_symbolize_keys & deep_transform_keys from rails
138
138
  # https://github.com/rails/docrails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/activesupport/lib/active_support/core_ext/hash/keys.rb#L84
139
- def self.symbolize_keys hash
140
- raise 'symbolize_keys requires a hash' unless hash.is_a? Hash
139
+ def self.symbolize_keys(hash)
140
+ fail 'symbolize_keys requires a hash' unless hash.is_a? Hash
141
141
  result = {}
142
142
  hash.each do |key, value|
143
- key = key.to_sym rescue key
143
+ key = key.to_sym rescue key # rubocop:disable Style/RescueModifier
144
144
  result[key] = value.is_a?(Hash) ? symbolize_keys(value) : value
145
145
  end
146
146
  result
147
147
  end
148
148
 
149
+ # This method is intended to work with page objects that share
150
+ # a common module. For example, Page::HomePage, Page::SignIn
151
+ # those could be promoted on with Appium.promote_singleton_appium_methods Page
152
+ #
153
+ # If you are promoting on an individual class then you should use
154
+ # Appium.promote_appium_methods instead. The singleton method is intended
155
+ # only for the shared module use case.
156
+ #
149
157
  # if modules is a module instead of an array, then the constants of
150
158
  # that module are promoted on.
151
159
  # otherwise, the array of modules will be used as the promotion target.
152
- def self.promote_singleton_appium_methods modules
153
- raise 'Driver is nil' if $driver.nil?
160
+ def self.promote_singleton_appium_methods(modules)
161
+ fail 'Driver is nil' if $driver.nil?
154
162
 
155
163
  target_modules = []
156
164
 
@@ -159,12 +167,12 @@ module Appium
159
167
  target_modules << modules.const_get(sub_module)
160
168
  end
161
169
  else
162
- raise 'modules must be a module or an array' unless modules.is_a? Array
170
+ fail 'modules must be a module or an array' unless modules.is_a? Array
163
171
  target_modules = modules
164
172
  end
165
173
 
166
174
  target_modules.each do |const|
167
- #noinspection RubyResolve
175
+ # noinspection RubyResolve
168
176
  $driver.public_methods(false).each do |m|
169
177
  const.send(:define_singleton_method, m) do |*args, &block|
170
178
  begin
@@ -173,8 +181,7 @@ module Appium
173
181
  $driver.send m, *args, &block if $driver.respond_to?(m)
174
182
  end
175
183
  # override unless there's an existing method with matching arity
176
- end unless const.respond_to?(m) &&
177
- const.method(m).arity == $driver.method(m).arity
184
+ end unless const.respond_to?(m) && const.method(m).arity == $driver.method(m).arity
178
185
  end
179
186
  end
180
187
  end
@@ -189,8 +196,20 @@ module Appium
189
196
  # ```ruby
190
197
  # Appium.promote_appium_methods Object
191
198
  # ```
192
- def self.promote_appium_methods class_array
193
- raise 'Driver is nil' if $driver.nil?
199
+ #
200
+ # It's better to promote on specific classes instead of Object
201
+ #
202
+ # ```ruby
203
+ # # promote on rspec
204
+ # Appium.promote_appium_methods RSpec::Core::ExampleGroup
205
+ # ```
206
+ #
207
+ # ```ruby
208
+ # # promote on minispec
209
+ # Appium.promote_appium_methods Minitest::Spec
210
+ # ```
211
+ def self.promote_appium_methods(class_array)
212
+ fail 'Driver is nil' if $driver.nil?
194
213
  # Wrap single class into an array
195
214
  class_array = [class_array] unless class_array.class == Array
196
215
  # Promote Appium driver methods to class instance methods.
@@ -202,9 +221,10 @@ module Appium
202
221
  # Prefer existing method.
203
222
  # super will invoke method missing on driver
204
223
  super(*args, &block)
205
- # minitest also defines a name method,
206
- # so rescue argument error
207
- # and call the name method on $driver
224
+
225
+ # minitest also defines a name method,
226
+ # so rescue argument error
227
+ # and call the name method on $driver
208
228
  rescue NoMethodError, ArgumentError
209
229
  $driver.send m, *args, &block if $driver.respond_to?(m)
210
230
  end
@@ -216,14 +236,11 @@ module Appium
216
236
  end
217
237
 
218
238
  class Driver
219
- @@loaded = false
220
-
221
239
  # attr readers are promoted to global scope. To avoid clobbering, they're
222
240
  # made available via the driver_attributes method
223
241
  #
224
242
  # attr_accessor is repeated for each one so YARD documents them properly.
225
243
 
226
-
227
244
  # The amount to sleep in seconds before every webdriver http call.
228
245
  attr_accessor :global_webdriver_http_sleep
229
246
  # Selenium webdriver capabilities
@@ -233,6 +250,9 @@ module Appium
233
250
  # Export session id to textfile in /tmp for 3rd party tools
234
251
  attr_accessor :export_session
235
252
  # Default wait time for elements to appear
253
+ # Returns the default client side wait.
254
+ # This value is independent of what the server is using
255
+ # @return [Integer]
236
256
  attr_accessor :default_wait
237
257
  # Array of previous wait time values
238
258
  attr_accessor :last_waits
@@ -247,6 +267,10 @@ module Appium
247
267
  # Boolean debug mode for the Appium Ruby bindings
248
268
  attr_accessor :appium_debug
249
269
 
270
+ # Returns the driver
271
+ # @return [Driver] the driver
272
+ attr_reader :driver
273
+
250
274
  # Creates a new driver
251
275
  #
252
276
  # ```ruby
@@ -266,12 +290,12 @@ module Appium
266
290
  #
267
291
  # @param opts [Object] A hash containing various options.
268
292
  # @return [Driver]
269
- def initialize opts={}
293
+ def initialize(opts = {})
270
294
  # quit last driver
271
295
  $driver.driver_quit if $driver
272
- raise 'opts must be a hash' unless opts.kind_of? Hash
296
+ fail 'opts must be a hash' unless opts.is_a? Hash
273
297
 
274
- opts = Appium::symbolize_keys opts
298
+ opts = Appium.symbolize_keys opts
275
299
 
276
300
  # default to {} to prevent nil.fetch and other nil errors
277
301
  @caps = opts[:caps] || {}
@@ -280,7 +304,7 @@ module Appium
280
304
  # appium_lib specific values
281
305
  @custom_url = appium_lib_opts.fetch :server_url, false
282
306
  @export_session = appium_lib_opts.fetch :export_session, false
283
- @default_wait = appium_lib_opts.fetch :wait, 30
307
+ @default_wait = appium_lib_opts.fetch :wait, 0
284
308
  @last_waits = [@default_wait]
285
309
  @sauce_username = appium_lib_opts.fetch :sauce_username, ENV['SAUCE_USERNAME']
286
310
  @sauce_username = nil if !@sauce_username || (@sauce_username.is_a?(String) && @sauce_username.empty?)
@@ -297,8 +321,8 @@ module Appium
297
321
  # https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
298
322
  @appium_device = @caps[:platformName]
299
323
  @appium_device = @appium_device.is_a?(Symbol) ? @appium_device : @appium_device.downcase.strip.intern if @appium_device
300
- raise "platformName must be set. Not found in options: #{opts}" unless @appium_device
301
- raise 'platformName must be Android or iOS' unless [:android, :ios].include?(@appium_device)
324
+ fail "platformName must be set. Not found in options: #{opts}" unless @appium_device
325
+ fail 'platformName must be Android or iOS' unless [:android, :ios].include?(@appium_device)
302
326
 
303
327
  # load common methods
304
328
  extend Appium::Common
@@ -325,18 +349,9 @@ module Appium
325
349
  patch_webdriver_bridge
326
350
  end
327
351
 
328
-
329
352
  # Save global reference to last created Appium driver for top level methods.
330
353
  $driver = self
331
354
 
332
- # Promote exactly once the first time the driver is created.
333
- # Subsequent drivers do not trigger promotion.
334
- unless @@loaded
335
- @@loaded = true
336
- # Promote only on Minitest::Spec (minitest 5) by default
337
- Appium.promote_appium_methods ::Minitest::Spec
338
- end
339
-
340
355
  self # return newly created driver
341
356
  end
342
357
 
@@ -351,7 +366,7 @@ module Appium
351
366
  sauce_access_key: @sauce_access_key,
352
367
  port: @appium_port,
353
368
  device: @appium_device,
354
- debug: @appium_debug,
369
+ debug: @appium_debug
355
370
  }
356
371
 
357
372
  # Return duplicates so attributes are immutable
@@ -389,14 +404,14 @@ module Appium
389
404
  # if app isn't set then an error is raised.
390
405
  #
391
406
  # @return [String] APP_PATH as an absolute path
392
- def self.absolute_app_path opts
393
- raise 'opts must be a hash' unless opts.is_a? Hash
407
+ def self.absolute_app_path(opts)
408
+ fail 'opts must be a hash' unless opts.is_a? Hash
394
409
  caps = opts[:caps] || {}
395
410
  appium_lib_opts = opts[:appium_lib] || {}
396
411
  server_url = appium_lib_opts.fetch :server_url, false
397
412
 
398
413
  app_path = caps[:app]
399
- raise 'absolute_app_path invoked and app is not set!' if app_path.nil? || app_path.empty?
414
+ fail 'absolute_app_path invoked and app is not set!' if app_path.nil? || app_path.empty?
400
415
  # may be absolute path to file on remote server.
401
416
  # if the file is on the remote server then we can't check if it exists
402
417
  return app_path if server_url
@@ -405,7 +420,7 @@ module Appium
405
420
  return app_path if app_path.match(/^http/) # public URL for Sauce
406
421
  if app_path.match(/^(\/|[a-zA-Z]:)/) # absolute file path
407
422
  app_path = File.expand_path app_path unless File.exist? app_path
408
- raise "App doesn't exist. #{app_path}" unless File.exist? app_path
423
+ fail "App doesn't exist. #{app_path}" unless File.exist? app_path
409
424
  return app_path
410
425
  end
411
426
 
@@ -416,7 +431,7 @@ module Appium
416
431
  # absolute_app_path is called from load_appium_txt
417
432
  # and the txt file path is the base of the app path in that case.
418
433
  app_path = File.expand_path app_path
419
- raise "App doesn't exist #{app_path}" unless File.exist? app_path
434
+ fail "App doesn't exist #{app_path}" unless File.exist? app_path
420
435
  app_path
421
436
  end
422
437
 
@@ -438,19 +453,13 @@ module Appium
438
453
  start_driver
439
454
  end
440
455
 
441
- # Returns the driver
442
- # @return [Driver] the driver
443
- def driver
444
- @driver
445
- end
446
-
447
456
  # Takes a png screenshot and saves to the target path.
448
457
  #
449
458
  # Example: screenshot '/tmp/hi.png'
450
459
  #
451
460
  # @param png_save_path [String] the full path to save the png
452
461
  # @return [nil]
453
- def screenshot png_save_path
462
+ def screenshot(png_save_path)
454
463
  @driver.save_screenshot png_save_path
455
464
  nil
456
465
  end
@@ -459,6 +468,7 @@ module Appium
459
468
  # @return [void]
460
469
  def driver_quit
461
470
  # rescue NoSuchDriverError or nil driver
471
+ # rubocop:disable Style/RescueModifier
462
472
  @driver.quit rescue nil
463
473
  end
464
474
 
@@ -466,17 +476,19 @@ module Appium
466
476
  #
467
477
  # @return [Selenium::WebDriver] the new global driver
468
478
  def start_driver
469
- @client = @client || Selenium::WebDriver::Remote::Http::Default.new
470
- @client.timeout = 999999
479
+ @client ||= Selenium::WebDriver::Remote::Http::Default.new
480
+ @client.timeout = 999_999
471
481
 
472
482
  begin
473
483
  driver_quit
474
484
  @driver = Selenium::WebDriver.for :remote, http_client: @client, desired_capabilities: @caps, url: server_url
475
485
  # Load touch methods.
476
486
  @driver.extend Selenium::WebDriver::DriverExtensions::HasTouchScreen
487
+ @driver.extend Selenium::WebDriver::DriverExtensions::HasLocation
477
488
 
478
489
  # export session
479
490
  if @export_session
491
+ # rubocop:disable Style/RescueModifier
480
492
  File.open('/tmp/appium_lib_session', 'w') do |f|
481
493
  f.puts @driver.session_id
482
494
  end rescue nil
@@ -510,7 +522,7 @@ module Appium
510
522
  #
511
523
  # @param timeout [Integer] the timeout in seconds
512
524
  # @return [void]
513
- def set_wait timeout=nil
525
+ def set_wait(timeout = nil)
514
526
  if timeout.nil?
515
527
  # Appium::Logger.info "timeout = @default_wait = @last_wait"
516
528
  # Appium::Logger.info "timeout = @default_wait = #{@last_waits}"
@@ -525,13 +537,6 @@ module Appium
525
537
  @driver.manage.timeouts.implicit_wait = timeout
526
538
  end
527
539
 
528
- # Returns the default client side wait.
529
- # This value is independent of what the server is using
530
- # @return [Integer]
531
- def default_wait
532
- @default_wait
533
- end
534
-
535
540
  # Returns existence of element.
536
541
  #
537
542
  # Example:
@@ -544,7 +549,7 @@ module Appium
544
549
  # wait to after checking existance
545
550
  # @param search_block [Block] the block to call
546
551
  # @return [Boolean]
547
- def exists pre_check=0, post_check=@default_wait, &search_block
552
+ def exists(pre_check = 0, post_check = @default_wait, &search_block)
548
553
  # do not uset set_wait here.
549
554
  # it will cause problems with other methods reading the default_wait of 0
550
555
  # which then gets converted to a 1 second wait.
@@ -568,7 +573,7 @@ module Appium
568
573
  # @param script [String] the script to execute
569
574
  # @param args [*args] the args to pass to the script
570
575
  # @return [Object]
571
- def execute_script script, *args
576
+ def execute_script(script, *args)
572
577
  @driver.execute_script script, *args
573
578
  end
574
579
 
@@ -576,16 +581,32 @@ module Appium
576
581
  #
577
582
  # @param args [*args] the args to use
578
583
  # @return [Array<Element>] Array is empty when no elements are found.
579
- def find_elements *args
580
- @driver.find_elements *args
584
+ def find_elements(*args)
585
+ @driver.find_elements(*args)
581
586
  end
582
587
 
583
588
  # Calls @driver.find_elements
584
589
  #
585
590
  # @param args [*args] the args to use
586
591
  # @return [Element]
587
- def find_element *args
588
- @driver.find_element *args
592
+ def find_element(*args)
593
+ @driver.find_element(*args)
594
+ end
595
+
596
+ # Calls @driver.set_location
597
+ #
598
+ # @note This method does not work on real devices.
599
+ #
600
+ # @param [Hash] opts consisting of:
601
+ # @option opts [Float] :latitude the latitude in degrees (required)
602
+ # @option opts [Float] :longitude the longitude in degees (required)
603
+ # @option opts [Float] :altitude the altitude, defaulting to 75
604
+ # @return [Selenium::WebDriver::Location] the location constructed by the selenium webdriver
605
+ def set_location(opts = {})
606
+ latitude = opts.fetch(:latitude)
607
+ longitude = opts.fetch(:longitude)
608
+ altitude = opts.fetch(:altitude, 75)
609
+ @driver.set_location(latitude, longitude, altitude)
589
610
  end
590
611
 
591
612
  # Quit the driver and Pry.
@@ -601,4 +622,4 @@ end # module Appium
601
622
  # Paging in Pry is annoying :q required to exit.
602
623
  # With pager disabled, the output is similar to IRB
603
624
  # Only set if Pry is defined.
604
- Pry.config.pager = false if defined?(Pry)
625
+ Pry.config.pager = false if defined?(Pry)