appium_lib 6.0.0 → 7.0.0

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