Skip to content

Commit c04d6f0

Browse files
Fix wait / wait_true by using selenium wait method
Fix #234
1 parent da19c8c commit c04d6f0

File tree

5 files changed

+145
-84
lines changed

5 files changed

+145
-84
lines changed

android_tests/lib/android/specs/common/helper.rb

+26-13
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
describe 'common/helper' do
2-
wait_time = [0.2, 0.2] # max_wait, interval
2+
wait_opts = { timeout: 0.2, interval: 0.2 } # max_wait, interval
3+
4+
=begin
5+
There's no `must_not_raise` as the opposite of must_raise
6+
7+
By default code is expected to not raise exceptions.
8+
must_not_raise is a no-op.
9+
=end
310

411
# wait is a success unless an error is raised
512
# max_wait=0 is infinity to use 0.1
613
t 'wait' do
714
# successful wait should not raise error
8-
wait(*wait_time) { true }
9-
wait(*wait_time) { false }
10-
wait(*wait_time) { nil }
15+
wait(wait_opts) { true }
16+
wait(wait_opts) { false }
17+
wait(wait_opts) { nil }
1118

1219
# failed wait should error
13-
proc { wait(*wait_time) { raise } }.must_raise Timeout::Error
20+
proc { wait(wait_opts) { raise } }.must_raise Selenium::WebDriver::Error::TimeOutError
1421

1522
# regular rescue will not handle exceptions outside of StandardError hierarchy
1623
# must rescue Exception explicitly to rescue everything
17-
proc { wait(*wait_time) { raise NoMemoryError } }.must_raise Timeout::Error
18-
proc { wait(0.2, 0.0) { raise NoMemoryError } }.must_raise Timeout::Error
24+
proc { wait(wait_opts) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
25+
proc { wait(timeout: 0.2, interval: 0.0) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
26+
27+
# invalid keys are rejected
28+
proc { wait(invalidkey: 2) { true } }.must_raise RuntimeError
1929
end
2030

2131
t 'ignore' do
@@ -30,19 +40,22 @@
3040
# wait_true is a success unless the value is not true
3141
t 'wait_true' do
3242
# successful wait should not error
33-
wait_true(*wait_time) { true }
43+
wait_true(wait_opts) { true }
3444

3545
# failed wait should error
36-
proc { wait_true(*wait_time) { false } }.must_raise Timeout::Error
37-
proc { wait_true(*wait_time) { nil } }.must_raise Timeout::Error
46+
proc { wait_true(wait_opts) { false } }.must_raise Selenium::WebDriver::Error::TimeOutError
47+
proc { wait_true(wait_opts) { nil } }.must_raise Selenium::WebDriver::Error::TimeOutError
3848

3949
# raise should error
40-
proc { wait_true(*wait_time) { raise } }.must_raise Timeout::Error
50+
proc { wait_true(wait_opts) { raise } }.must_raise Selenium::WebDriver::Error::TimeOutError
4151

4252
# regular rescue will not handle exceptions outside of StandardError hierarchy
4353
# must rescue Exception explicitly to rescue everything
44-
proc { wait_true(*wait_time) { raise NoMemoryError } }.must_raise Timeout::Error
45-
proc { wait_true(0.2, 0.0) { raise NoMemoryError } }.must_raise Timeout::Error
54+
proc { wait_true(wait_opts) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
55+
proc { wait_true(timeout: 0.2, interval: 0.0) { raise NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
56+
57+
# invalid keys are rejected
58+
proc { wait_true(invalidkey: 2) { true } }.must_raise RuntimeError
4659
end
4760

4861
t 'back' do

ios_tests/lib/ios/specs/common/helper.rb

+19-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def before_first
88
before_first
99
end
1010

11-
wait_time = [0.2, 0.2] # max_wait, interval
11+
wait_opts = { timeout: 0.2, interval: 0.2 } # max_wait, interval
1212

1313
=begin
1414
There's no `must_not_raise` as the opposite of must_raise
@@ -21,17 +21,20 @@ def before_first
2121
# max_wait=0 is infinity to use 0.1
2222
t 'wait' do
2323
# 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 }
2727

2828
# 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
3030

3131
# regular rescue will not handle exceptions outside of StandardError hierarchy
3232
# 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
3538
end
3639

3740
t 'ignore' do
@@ -46,19 +49,22 @@ def before_first
4649
# wait_true is a success unless the value is not true
4750
t 'wait_true' do
4851
# successful wait should not error
49-
wait_true(*wait_time) { true }
52+
wait_true(wait_opts) { true }
5053

5154
# 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
5457

5558
# 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
5760

5861
# regular rescue will not handle exceptions outside of StandardError hierarchy
5962
# 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
6268
end
6369

6470
# t 'id' # id is for Selendroid

lib/appium_lib/common/helper.rb

-58
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,6 @@ module Common
1818
#
1919
# find_element :text doesn't work so use XPath to find by text.
2020

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-
5321
# Return block.call and ignore any exceptions.
5422
def ignore &block
5523
begin
@@ -58,32 +26,6 @@ def ignore &block
5826
end
5927
end
6028

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-
8729
# Navigate back.
8830
# @return [void]
8931
def back

lib/appium_lib/common/wait.rb

+99
Original file line numberDiff line numberDiff line change
@@ -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 (5) Seconds to wait before timing out.
73+
# @option opts [Numeric] :interval (0.2) 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 (5) Seconds to wait before timing out.
91+
# @option opts [Numeric] :interval (0.2) 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

lib/appium_lib/driver.rb

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
# common
1010
require_relative 'common/helper'
11+
require_relative 'common/wait'
1112
require_relative 'common/patch'
1213
require_relative 'common/version'
1314
require_relative 'common/element/window'

0 commit comments

Comments
 (0)