Skip to content

Commit 4b1c086

Browse files
authored
Extract wait (#78)
* extract wait * cahnge class to module * refactored * add a line to changelog
1 parent 6754a6f commit 4b1c086

File tree

6 files changed

+163
-124
lines changed

6 files changed

+163
-124
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
33

44
## [Unreleased]
55
### Enhancements
6+
- [internal] No longer have dependency for Selenium's wait
67

78
### Bug fixes
89

lib/appium_lib_core/common.rb

+1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
require_relative 'common/command'
55
require_relative 'common/device'
66
require_relative 'common/base'
7+
require_relative 'common/wait'
78
require_relative 'common/ws/websocket'

lib/appium_lib_core/common/base.rb

-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,3 @@
77
require_relative 'base/http_default'
88
require_relative 'base/search_context'
99
require_relative 'base/command'
10-
require_relative 'base/wait'

lib/appium_lib_core/common/base/wait.rb

-56
This file was deleted.

lib/appium_lib_core/common/wait.rb

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# rubocop:disable Lint/HandleExceptions
2+
module Appium
3+
module Core
4+
module Wait
5+
class TimeoutError < StandardError; end
6+
7+
DEFAULT_TIMEOUT = 30
8+
DEFAULT_INTERVAL = 0.5
9+
10+
class << self
11+
# Check every interval seconds to see if yield doesn't raise an exception.
12+
# Give up after timeout seconds.
13+
#
14+
# If only a number is provided then it's treated as the timeout value.
15+
#
16+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
17+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
18+
# @param [String] message: Exception message if timed out.
19+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
20+
# @param [Object, NilClass] object: Object to evaluate block against
21+
#
22+
# @example
23+
#
24+
# result = Appium::Core::Wait.until { @driver.find_element(:id, 'something') }
25+
#
26+
# result = Appium::Core::Wait.until(object: 'some object') { |object|
27+
# @driver.find_element(:id, object)
28+
# }
29+
#
30+
def until(timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL, message: nil, ignored: nil, object: nil)
31+
ignored = Array(ignored || ::Exception)
32+
33+
end_time = Time.now + timeout
34+
last_error = nil
35+
36+
until Time.now > end_time
37+
begin
38+
return yield(object)
39+
rescue ::Errno::ECONNREFUSED => e
40+
raise e
41+
rescue *ignored => last_error
42+
# swallowed
43+
end
44+
45+
sleep interval
46+
end
47+
48+
msg = message_for timeout, message
49+
msg << " (#{last_error.message})" if last_error
50+
51+
raise TimeoutError, msg
52+
end
53+
54+
# Check every interval seconds to see if yield returns a truthy value.
55+
# Note this isn't a strict boolean true, any truthy value is accepted.
56+
# false and nil are considered failures.
57+
# Give up after timeout seconds.
58+
#
59+
# If only a number is provided then it's treated as the timeout value.
60+
#
61+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
62+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
63+
# @param [String] message: Exception message if timed out.
64+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
65+
# @param [Object, NilClass] object: Object to evaluate block against
66+
#
67+
# @example
68+
#
69+
# Appium::Core::Wait.until_true { @driver.find_element(:id, 'something') }
70+
#
71+
# Appium::Core::Wait.until_true(object: 'some object') { |object|
72+
# @driver.find_element(:id, object)
73+
# }
74+
#
75+
def until_true(timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL, message: nil, ignored: nil, object: nil)
76+
ignored = Array(ignored || ::Exception)
77+
78+
end_time = Time.now + timeout
79+
last_error = nil
80+
81+
until Time.now > end_time
82+
begin
83+
result = yield(object)
84+
return result if result
85+
rescue ::Errno::ECONNREFUSED => e
86+
raise e
87+
rescue *ignored => last_error
88+
# swallowed
89+
end
90+
91+
sleep interval
92+
end
93+
94+
msg = message_for timeout, message
95+
msg << " (#{last_error.message})" if last_error
96+
97+
raise TimeoutError, msg
98+
end
99+
100+
private
101+
102+
def message_for(timeout, message)
103+
msg = "timed out after #{timeout} seconds"
104+
msg << ", #{message}" if message
105+
msg
106+
end
107+
end # self
108+
end # module Wait
109+
110+
module Waitable
111+
# Check every interval seconds to see if yield returns a truthy value.
112+
# Note this isn't a strict boolean true, any truthy value is accepted.
113+
# false and nil are considered failures.
114+
# Give up after timeout seconds.
115+
#
116+
# If only a number is provided then it's treated as the timeout value.
117+
#
118+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
119+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
120+
# @param [String] message: Exception message if timed out.
121+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
122+
#
123+
# @example
124+
#
125+
# @core.wait_true { @driver.find_element :accessibility_id, 'something' }
126+
#
127+
def wait_true(timeout: nil, interval: nil, message: nil, ignored: nil)
128+
Wait.until_true(timeout: timeout || @wait_timeout,
129+
interval: interval || @wait_interval,
130+
message: message,
131+
ignored: ignored,
132+
object: self) { yield }
133+
end
134+
135+
# Check every interval seconds to see if yield doesn't raise an exception.
136+
# Give up after timeout seconds.
137+
#
138+
# If only a number is provided then it's treated as the timeout value.
139+
#
140+
# @param [Integer] timeout: Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
141+
# @param [Integer] interval: Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
142+
# @param [String] message: Exception message if timed out.
143+
# @param [Array, Exception] ignored: Exceptions to ignore while polling (default: Exception)
144+
#
145+
# @example
146+
#
147+
# @core.wait { @driver.find_element :accessibility_id, 'something' }
148+
#
149+
def wait(timeout: nil, interval: nil, message: nil, ignored: nil)
150+
Wait.until(timeout: timeout || @wait_timeout,
151+
interval: interval || @wait_interval,
152+
message: message,
153+
ignored: ignored,
154+
object: self) { yield }
155+
end
156+
end
157+
end # module Core
158+
end # module Appium

lib/appium_lib_core/driver.rb

+3-67
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Appium
22
module Core
33
class Driver
4+
include Waitable
45
# Selenium webdriver capabilities
56
# @return [Core::Base::Capabilities]
67
attr_reader :caps
@@ -278,73 +279,8 @@ def screenshot(png_save_path)
278279
@driver.save_screenshot png_save_path
279280
end
280281

281-
# Check every interval seconds to see if yield returns a truthy value.
282-
# Note this isn't a strict boolean true, any truthy value is accepted.
283-
# false and nil are considered failures.
284-
# Give up after timeout seconds.
285-
#
286-
# Wait code from the selenium Ruby gem
287-
# https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
288-
#
289-
# If only a number is provided then it's treated as the timeout value.
290-
#
291-
# @param [Hash] opts Options
292-
# @option opts [Numeric] :timeout Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
293-
# @option opts [Numeric] :interval Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
294-
# @option opts [String] :message Exception message if timed out.
295-
# @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
296-
#
297-
# @example
298-
#
299-
# @core.wait_true { @driver.find_element :accessibility_id, 'something' }
300-
#
301-
def wait_true(opts = {})
302-
opts = process_wait_opts(opts).merge(return_if_true: true)
303-
304-
opts[:timeout] ||= @wait_timeout
305-
opts[:interval] ||= @wait_interval
306-
307-
wait = ::Appium::Core::Base::Wait.new opts
308-
wait.until { yield }
309-
end
310-
311-
# Check every interval seconds to see if yield doesn't raise an exception.
312-
# Give up after timeout seconds.
313-
#
314-
# Wait code from the selenium Ruby gem
315-
# https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
316-
#
317-
# If only a number is provided then it's treated as the timeout value.
318-
#
319-
# @param [Hash] opts Options
320-
# @option opts [Numeric] :timeout Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
321-
# @option opts [Numeric] :interval Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
322-
# @option opts [String] :message Exception message if timed out.
323-
# @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
324-
#
325-
# @example
326-
#
327-
# @core.wait { @driver.find_element :accessibility_id, 'something' }
328-
#
329-
def wait(opts = {})
330-
opts = process_wait_opts(opts).merge(return_if_true: false)
331-
332-
opts[:timeout] ||= @wait_timeout
333-
opts[:interval] ||= @wait_interval
334-
335-
wait = ::Appium::Core::Base::Wait.new opts
336-
wait.until { yield }
337-
end
338-
339282
private
340283

341-
# @private
342-
def process_wait_opts(opts)
343-
opts = { timeout: opts } if opts.is_a?(Numeric)
344-
raise 'opts must be a hash' unless opts.is_a? Hash
345-
opts
346-
end
347-
348284
# @private
349285
def extend_for(device:, automation_name:, target:)
350286
target.extend Appium::Core
@@ -432,8 +368,8 @@ def set_appium_lib_specific_values(appium_lib_opts)
432368
@port = appium_lib_opts.fetch :port, DEFAULT_APPIUM_PORT
433369

434370
# timeout and interval used in ::Appium::Comm.wait/wait_true
435-
@wait_timeout = appium_lib_opts.fetch :wait_timeout, 30
436-
@wait_interval = appium_lib_opts.fetch :wait_interval, 0.5
371+
@wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
372+
@wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
437373

438374
# to pass it in Selenium.new.
439375
# `listener = opts.delete(:listener)` is called in Selenium::Driver.new

0 commit comments

Comments
 (0)