appium_lib 4.0.0 → 4.1.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.
@@ -8,7 +8,7 @@ describe 'common/helper.rb' do
8
8
  before_first
9
9
  end
10
10
 
11
- wait_time = [0.2, 0.2] # max_wait, interval
11
+ wait_opts = { timeout: 0.2, interval: 0.2 } # max_wait, interval
12
12
 
13
13
  =begin
14
14
  There's no `must_not_raise` as the opposite of must_raise
@@ -21,17 +21,20 @@ must_not_raise is a no-op.
21
21
  # max_wait=0 is infinity to use 0.1
22
22
  t 'wait' do
23
23
  # successful wait should not raise error
24
- wait(*wait_time) { true }
25
- wait(*wait_time) { false }
26
- wait(*wait_time) { nil }
24
+ wait(wait_opts) { true }
25
+ wait(wait_opts) { false }
26
+ wait(wait_opts) { nil }
27
27
 
28
28
  # failed wait should error
29
- proc { wait(*wait_time) { raise } }.must_raise Timeout::Error
29
+ proc { wait(wait_opts) { raise } }.must_raise Selenium::WebDriver::Error::TimeOutError
30
30
 
31
31
  # regular rescue will not handle exceptions outside of StandardError hierarchy
32
32
  # must rescue Exception explicitly to rescue everything
33
- proc { wait(*wait_time) { raise NoMemoryError } }.must_raise Timeout::Error
34
- proc { wait(0.2, 0.0) { raise NoMemoryError } }.must_raise Timeout::Error
33
+ proc { wait(wait_opts) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
34
+ proc { wait(timeout: 0.2, interval: 0.0) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
35
+
36
+ # invalid keys are rejected
37
+ proc { wait(invalidkey: 2) { true } }.must_raise RuntimeError
35
38
  end
36
39
 
37
40
  t 'ignore' do
@@ -46,19 +49,22 @@ must_not_raise is a no-op.
46
49
  # wait_true is a success unless the value is not true
47
50
  t 'wait_true' do
48
51
  # successful wait should not error
49
- wait_true(*wait_time) { true }
52
+ wait_true(wait_opts) { true }
50
53
 
51
54
  # failed wait should error
52
- proc { wait_true(*wait_time) { false } }.must_raise Timeout::Error
53
- proc { wait_true(*wait_time) { nil } }.must_raise Timeout::Error
55
+ proc { wait_true(wait_opts) { false } }.must_raise Selenium::WebDriver::Error::TimeOutError
56
+ proc { wait_true(wait_opts) { nil } }.must_raise Selenium::WebDriver::Error::TimeOutError
54
57
 
55
58
  # raise should error
56
- proc { wait_true(*wait_time) { raise } }.must_raise Timeout::Error
59
+ proc { wait_true(wait_opts) { raise } }.must_raise Selenium::WebDriver::Error::TimeOutError
57
60
 
58
61
  # regular rescue will not handle exceptions outside of StandardError hierarchy
59
62
  # must rescue Exception explicitly to rescue everything
60
- proc { wait_true(*wait_time) { raise NoMemoryError } }.must_raise Timeout::Error
61
- proc { wait_true(0.2, 0.0) { raise NoMemoryError } }.must_raise Timeout::Error
63
+ proc { wait_true(wait_opts) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
64
+ proc { wait_true(timeout: 0.2, interval: 0.0) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
65
+
66
+ # invalid keys are rejected
67
+ proc { wait_true(invalidkey: 2) { true } }.must_raise RuntimeError
62
68
  end
63
69
 
64
70
  # t 'id' # id is for Selendroid
@@ -0,0 +1,47 @@
1
+ require 'nokogiri'
2
+
3
+ module Appium
4
+ module Android
5
+ def _nodeset_to_uiselector opts={}
6
+ results = ''
7
+
8
+ nodes = opts[:nodes]
9
+ first = opts[:first]
10
+
11
+ nodes = [nodes[0]] if first
12
+
13
+ nodes.each do |node|
14
+ results += %Q(new UiSelector().className("#{node.name}").instance(#{node.attr('instance')});)
15
+ end
16
+
17
+ results.strip
18
+ end
19
+
20
+ def _client_xpath opts={}
21
+ root_node = Nokogiri::XML(get_source).children.first
22
+
23
+ instance = Hash.new -1
24
+
25
+ root_node.traverse do |node|
26
+ number = instance[node.name] += 1
27
+ node.set_attribute 'instance', number
28
+ end
29
+
30
+ nodes = root_node.xpath(opts[:xpath])
31
+ first = opts[:first]
32
+
33
+ _nodeset_to_uiselector nodes: nodes, first: first
34
+ end
35
+
36
+ def client_xpath xpath
37
+ find_element :uiautomator, _client_xpath(xpath: xpath, first: true)
38
+ end
39
+
40
+ def client_xpaths xpath
41
+ find_elements :uiautomator, _client_xpath(xpath: xpath, first: false)
42
+ end
43
+ end
44
+ end
45
+
46
+ # http://stackoverflow.com/questions/9199415/getting-first-node-in-xpath-result-set
47
+ # '(//android.widget.TextView)[1]' not '//android.widget.TextView[1]'
@@ -18,38 +18,6 @@ module Appium
18
18
  #
19
19
  # find_element :text doesn't work so use XPath to find by text.
20
20
 
21
- # Check every 0.5 seconds to see if block.call doesn't raise an exception.
22
- # if .call raises an exception then it will be tried again.
23
- # if .call doesn't raise an exception then it will stop waiting.
24
- #
25
- # Example: wait { name('back').click }
26
- #
27
- # Give up after 30 seconds.
28
- # @param max_wait [Integer] the maximum time in seconds to wait for.
29
- # Note that max wait 0 means infinity.
30
- # @param interval [Float] the time in seconds to wait after calling the block
31
- # @param block [Block] the block to call
32
- # @return [Object] the result of block.call
33
- def wait max_wait=30, interval=0.5, &block
34
- max_wait = 1 if max_wait <= 0
35
- result = nil
36
- timeout max_wait do
37
- until (
38
- begin
39
- result = block.call || true
40
- rescue Errno::ECONNREFUSED => e
41
- raise e
42
- rescue Exception
43
- sleep interval
44
- # sleep returns truthy value which breaks out of until
45
- # must return false value
46
- false
47
- end)
48
- end
49
- end
50
- result
51
- end
52
-
53
21
  # Return block.call and ignore any exceptions.
54
22
  def ignore &block
55
23
  begin
@@ -58,32 +26,6 @@ module Appium
58
26
  end
59
27
  end
60
28
 
61
- # Check every 0.5 seconds to see if block.call returns a truthy value.
62
- # Note this isn't a strict boolean true, any truthy value is accepted.
63
- # false and nil are considered failures.
64
- # Give up after 30 seconds.
65
- # @param max_wait [Integer] the maximum time in seconds to wait for
66
- # @param interval [Float] the time in seconds to wait after calling the block
67
- # @param block [Block] the block to call
68
- # @return [Object] the result of block.call
69
- def wait_true max_wait=30, interval=0.5, &block
70
- max_wait = 1 if max_wait <= 0
71
- result = nil
72
- timeout max_wait do
73
- until (
74
- begin
75
- result = block.call
76
- rescue Errno::ECONNREFUSED => e
77
- raise e
78
- rescue Exception
79
- ensure
80
- sleep interval unless result
81
- end)
82
- end
83
- end
84
- result
85
- end
86
-
87
29
  # Navigate back.
88
30
  # @return [void]
89
31
  def back
@@ -1,5 +1,5 @@
1
1
  module Appium
2
2
  # Version and Date are defined on the 'Appium' module, not 'Appium::Common'
3
- VERSION = '4.0.0' unless defined? ::Appium::VERSION
4
- DATE = '2014-07-05' unless defined? ::Appium::DATE
3
+ VERSION = '4.1.0' unless defined? ::Appium::VERSION
4
+ DATE = '2014-07-21' unless defined? ::Appium::DATE
5
5
  end
@@ -0,0 +1,99 @@
1
+ module Appium
2
+ module Common
3
+
4
+ # http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html
5
+ # Note that the Ruby timeout module is avoided. timeout has problems.
6
+ # https://coderwall.com/p/1novga
7
+
8
+ # Wait code from the selenium Ruby gem
9
+ # https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
10
+ def _generic_wait opts={}, &block
11
+ valid_keys = [:timeout, :interval, :message, :ignore, :return_if_true]
12
+ invalid_keys = []
13
+ opts.keys.each { |key| invalid_keys << key unless valid_keys.include?(key) }
14
+ # [:one, :two] => :one, :two
15
+ raise "Invalid keys #{invalid_keys.to_s[1..-2]}. Valid keys are #{valid_keys.to_s[1..-2]}" unless invalid_keys.empty?
16
+
17
+ timeout = opts.fetch(:timeout, 30)
18
+ interval = opts.fetch(:interval, 0.5)
19
+ message = opts[:message]
20
+ ignored = Array(opts[:ignore] || ::Exception)
21
+ return_if_true = opts[:return_if_true]
22
+
23
+ end_time = Time.now + timeout
24
+ last_error = nil
25
+
26
+ until Time.now > end_time
27
+ begin
28
+ if return_if_true
29
+ result = block.call
30
+ return result if result
31
+ else
32
+ return block.call
33
+ end
34
+ rescue ::Errno::ECONNREFUSED => e
35
+ raise e
36
+ rescue *ignored => last_error
37
+ # swallowed
38
+ end
39
+
40
+ sleep interval
41
+ end
42
+
43
+ if message
44
+ msg = message.dup
45
+ else
46
+ msg = "timed out after #{timeout} seconds"
47
+ end
48
+
49
+ msg << " (#{last_error.message})" if last_error
50
+
51
+ raise Selenium::WebDriver::Error::TimeOutError, msg
52
+ end
53
+
54
+ # process opts before calling _generic_wait
55
+ def _process_wait_opts opts
56
+ opts = { timeout: opts } if opts.is_a?(Numeric)
57
+ raise 'opts must be a hash' unless opts.is_a? Hash
58
+ opts
59
+ end
60
+
61
+ # Check every interval seconds to see if block.call returns a truthy value.
62
+ # Note this isn't a strict boolean true, any truthy value is accepted.
63
+ # false and nil are considered failures.
64
+ # Give up after timeout seconds.
65
+ #
66
+ # Wait code from the selenium Ruby gem
67
+ # https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
68
+ #
69
+ # If only a number is provided then it's treated as the timeout value.
70
+ #
71
+ # @param [Hash] opts Options
72
+ # @option opts [Numeric] :timeout (30) Seconds to wait before timing out.
73
+ # @option opts [Numeric] :interval (0.5) Seconds to sleep between polls.
74
+ # @option opts [String] :message Exception message if timed out.
75
+ # @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
76
+ def wait_true opts={}, &block
77
+ opts = _process_wait_opts(opts).merge(return_if_true: true)
78
+ _generic_wait opts, &block
79
+ end
80
+
81
+ # Check every interval seconds to see if block.call doesn't raise an exception.
82
+ # Give up after timeout seconds.
83
+ #
84
+ # Wait code from the selenium Ruby gem
85
+ # https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
86
+ #
87
+ # If only a number is provided then it's treated as the timeout value.
88
+ #
89
+ # @param [Hash] opts Options
90
+ # @option opts [Numeric] :timeout (30) Seconds to wait before timing out.
91
+ # @option opts [Numeric] :interval (0.5) Seconds to sleep between polls.
92
+ # @option opts [String] :message Exception message if timed out.
93
+ # @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
94
+ def wait opts={}, &block
95
+ opts = _process_wait_opts(opts).merge(return_if_true: false)
96
+ _generic_wait opts, &block
97
+ end
98
+ end # module Common
99
+ end # module Appium
@@ -93,6 +93,9 @@ module Appium
93
93
  end
94
94
 
95
95
  # Convenience method to peform a swipe.
96
+ #
97
+ # Note that iOS 7 simulators have broken swipe.
98
+ #
96
99
  # @option opts [int] :start_x Where to start swiping, on the x axis. Default 0.
97
100
  # @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
98
101
  # @option opts [int] :end_x Where to end swiping, on the x axis. Default 0.
@@ -8,6 +8,7 @@ require_relative 'awesome_print/ostruct'
8
8
 
9
9
  # common
10
10
  require_relative 'common/helper'
11
+ require_relative 'common/wait'
11
12
  require_relative 'common/patch'
12
13
  require_relative 'common/version'
13
14
  require_relative 'common/element/window'
@@ -26,6 +27,7 @@ require_relative 'ios/mobile_methods'
26
27
  # android
27
28
  require_relative 'android/helper'
28
29
  require_relative 'android/patch'
30
+ require_relative 'android/client_xpath'
29
31
  require_relative 'android/element/alert'
30
32
  require_relative 'android/element/button'
31
33
  require_relative 'android/element/generic'
@@ -92,8 +94,8 @@ module Appium
92
94
  data = Appium::symbolize_keys data
93
95
  ap data unless data.empty? if verbose
94
96
 
95
- if data && data[:caps] && data[:caps][:app]
96
- data[:caps][:app] = Appium::Driver.absolute_app_path data[:caps][:app]
97
+ if data && data[:caps] && data[:caps][:app] && !data[:caps][:app].empty?
98
+ data[:caps][:app] = Appium::Driver.absolute_app_path data
97
99
  end
98
100
 
99
101
  # return list of require files as an array
@@ -147,12 +149,26 @@ module Appium
147
149
  result
148
150
  end
149
151
 
150
- def self.promote_singleton_appium_methods main_module
152
+ # if modules is a module instead of an array, then the constants of
153
+ # that module are promoted on.
154
+ # otherwise, the array of modules will be used as the promotion target.
155
+ def self.promote_singleton_appium_methods modules
151
156
  raise 'Driver is nil' if $driver.nil?
152
- main_module.constants.each do |sub_module|
157
+
158
+ target_modules = []
159
+
160
+ if modules.is_a? Module
161
+ modules.constants.each do |sub_module|
162
+ target_modules << modules.const_get(sub_module)
163
+ end
164
+ else
165
+ raise 'modules must be a module or an array' unless modules.is_a? Array
166
+ target_modules = modules
167
+ end
168
+
169
+ target_modules.each do |const|
153
170
  #noinspection RubyResolve
154
171
  $driver.public_methods(false).each do |m|
155
- const = main_module.const_get(sub_module)
156
172
  const.send(:define_singleton_method, m) do |*args, &block|
157
173
  begin
158
174
  super(*args, &block) # promote.rb
@@ -264,8 +280,8 @@ module Appium
264
280
 
265
281
  # Path to the .apk, .app or .app.zip.
266
282
  # The path can be local or remote for Sauce.
267
- unless !@caps || @caps[:app].nil? || @caps[:app].empty?
268
- @caps[:app] = self.class.absolute_app_path @caps[:app]
283
+ if @caps && @caps[:app] && ! @caps[:app].empty?
284
+ @caps[:app] = self.class.absolute_app_path opts
269
285
  end
270
286
 
271
287
  # https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
@@ -356,12 +372,24 @@ module Appium
356
372
  end
357
373
 
358
374
  # Converts app_path to an absolute path.
375
+ #
376
+ # opts is the full options hash (caps and appium_lib). If server_url is set
377
+ # then the app path is used as is.
378
+ #
379
+ # if app isn't set then an error is raised.
380
+ #
359
381
  # @return [String] APP_PATH as an absolute path
360
- def self.absolute_app_path app_path
361
- raise 'APP_PATH not set!' if app_path.nil? || app_path.empty?
382
+ def self.absolute_app_path opts
383
+ raise 'opts must be a hash' unless opts.is_a? Hash
384
+ caps = opts[:caps] || {}
385
+ appium_lib_opts = opts[:appium_lib] || {}
386
+ server_url = appium_lib_opts.fetch :server_url, false
387
+
388
+ app_path = caps[:app]
389
+ raise 'absolute_app_path invoked and app is not set!' if app_path.nil? || app_path.empty?
362
390
  # may be absolute path to file on remote server.
363
391
  # if the file is on the remote server then we can't check if it exists
364
- return app_path if @custom_url
392
+ return app_path if server_url
365
393
  # Sauce storage API. http://saucelabs.com/docs/rest#storage
366
394
  return app_path if app_path.start_with? 'sauce-storage:'
367
395
  return app_path if app_path.match(/^http/) # public URL for Sauce
data/release_notes.md CHANGED
@@ -1,3 +1,16 @@
1
+ #### v4.1.0 2014-07-21
2
+
3
+ - [a13158f](https://github.com/appium/ruby_lib/commit/a13158fb926212d84f26120c3bc5355c8cd34baf) Release 4.1.0
4
+ - [be1c710](https://github.com/appium/ruby_lib/commit/be1c710134ee9a001b8e71c0ce5cbb5786bebf1e) Update android specs
5
+ - [4edd949](https://github.com/appium/ruby_lib/commit/4edd94989519ec14acf9c694d9ed3ae6a5939b10) Update self.promote_singleton_appium_methods
6
+ - [82a236a](https://github.com/appium/ruby_lib/commit/82a236a84742f1eb6d55acab00d8f222d721bd9a) Update docs.md
7
+ - [a724d5d](https://github.com/appium/ruby_lib/commit/a724d5d3711cdfa3ea420663949c06017189bf02) Fix #224
8
+ - [d05bfe8](https://github.com/appium/ruby_lib/commit/d05bfe85d6c356a8ea6a0f151aaf21dfef979736) Fix docs
9
+ - [c04d6f0](https://github.com/appium/ruby_lib/commit/c04d6f048ee4e24c66bef662e31a0d957360e7a7) Fix wait / wait_true by using selenium wait method
10
+ - [da19c8c](https://github.com/appium/ruby_lib/commit/da19c8cca5753f06576b82bbbb6e77e7e36bcb9c) Add iOS 7 note to swipe
11
+ - [4f4d800](https://github.com/appium/ruby_lib/commit/4f4d80094eac5a4fbc2c11b8050155b2f767839c) Add client side xpath support
12
+
13
+
1
14
  #### v4.0.0 2014-07-05
2
15
 
3
16
  - [8cc004a](https://github.com/appium/ruby_lib/commit/8cc004ad04ec087a8a11c06ca0749a5e2c6586a7) Release 4.0.0
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appium_lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - code@bootstraponline.com
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-05 00:00:00.000000000 Z
11
+ date: 2014-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver
@@ -322,6 +322,7 @@ files:
322
322
  - ios_tests/upload/sauce_storage.rb
323
323
  - ios_tests/upload/upload.rb
324
324
  - lib/appium_lib.rb
325
+ - lib/appium_lib/android/client_xpath.rb
325
326
  - lib/appium_lib/android/element/alert.rb
326
327
  - lib/appium_lib/android/element/button.rb
327
328
  - lib/appium_lib/android/element/generic.rb
@@ -335,6 +336,7 @@ files:
335
336
  - lib/appium_lib/common/helper.rb
336
337
  - lib/appium_lib/common/patch.rb
337
338
  - lib/appium_lib/common/version.rb
339
+ - lib/appium_lib/common/wait.rb
338
340
  - lib/appium_lib/device/device.rb
339
341
  - lib/appium_lib/device/multi_touch.rb
340
342
  - lib/appium_lib/device/touch_actions.rb