# frozen_string_literal: true
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require_relative 'wait/timer'
module Appium
module Core
module Wait
class TimeoutError < StandardError; end
DEFAULT_TIMEOUT = 30
DEFAULT_INTERVAL = 0.5
class << self
# Check every interval seconds to see if yield doesn't raise an exception.
# Give up after timeout seconds.
#
# If only a number is provided then it's treated as the timeout value.
#
# @param [Integer] timeout Seconds to wait before timing out. Set default by +appium_wait_timeout+ (30).
# @param [Integer] interval Seconds to sleep between polls. Set default by +appium_wait_interval+ (0.5).
# @param [String] message Exception message if timed out.
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
# @param [Object, NilClass] object Object to evaluate block against
#
# @example
#
# result = Appium::Core::Wait.until { @driver.find_element(:id, 'something') }
#
# result = Appium::Core::Wait.until(timeout: 30, message: 'timeout') { @driver.find_element(:id, 'something') }
#
# result = Appium::Core::Wait.until(object: 'some object') { |object|
# @driver.find_element(:id, object)
# }
#
def until(timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL, message: nil, ignored: nil, object: nil)
ignored = Array(ignored || ::Exception)
last_error = nil
timer = Wait::Timer.new(timeout)
until timer.timeout?
begin
return yield(object)
rescue ::Errno::ECONNREFUSED => e
raise e
rescue *ignored => last_error
# swallowed
end
sleep interval
end
msg = message_for timeout, message
msg += " (#{last_error.message})" if last_error
raise TimeoutError, msg
end
# Check every interval seconds to see if yield returns a truthy value.
# Note this isn't a strict boolean true, any truthy value is accepted.
# false and nil are considered failures.
# Give up after timeout seconds.
#
# If only a number is provided then it's treated as the timeout value.
#
# @param [Integer] timeout Seconds to wait before timing out. Set default by +appium_wait_timeout+ (30).
# @param [Integer] interval Seconds to sleep between polls. Set default by +appium_wait_interval+ (0.5).
# @param [String] message Exception message if timed out.
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
# @param [Object, NilClass] object Object to evaluate block against
#
# @example
#
# Appium::Core::Wait.until_true { @driver.find_element(:id, 'something') }
#
# Appium::Core::Wait.until_true(timeout: 30) { @driver.find_element(:id, 'something') }
#
# Appium::Core::Wait.until_true(object: 'some object') { |object|
# @driver.find_element(:id, object)
# }
#
def until_true(timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL, message: nil, ignored: nil, object: nil)
ignored = Array(ignored || ::Exception)
last_error = nil
timer = Wait::Timer.new(timeout)
until timer.timeout?
begin
result = yield(object)
return result if result
rescue ::Errno::ECONNREFUSED => e
raise e
rescue *ignored => last_error
# swallowed
end
sleep interval
end
msg = message_for timeout, message
msg += " (#{last_error.message})" if last_error
raise TimeoutError, msg
end
private
def message_for(timeout, message)
msg = "timed out after #{timeout} seconds"
msg += ", #{message}" if message
msg
end
end # self
end # module Wait
module Waitable
# Check every interval seconds to see if yield returns a truthy value.
# Note this isn't a strict boolean true, any truthy value is accepted.
# false and nil are considered failures.
# Give up after timeout seconds.
#
# If only a number is provided then it's treated as the timeout value.
#
# @param [Integer] timeout Seconds to wait before timing out. Set default by +appium_wait_timeout+ (30).
# @param [Integer] interval Seconds to sleep between polls. Set default by +appium_wait_interval+ (0.5).
# @param [String] message Exception message if timed out.
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
#
# @example With core instance
#
# @core.wait_true { @driver.find_element :accessibility_id, 'something' }
# @core.wait_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
#
# @core.wait_until_true { @driver.find_element :accessibility_id, 'something' }
# @core.wait_until_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
#
# @example With driver instance
#
# @driver.wait_true { |d| d.find_element :accessibility_id, 'something' }
# @driver.wait_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
#
# @driver.wait_until_true { |d| d.find_element :accessibility_id, 'something' }
# @driver.wait_until_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
#
def wait_until_true(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
Wait.until_true(timeout: timeout || @wait_timeout,
interval: interval || @wait_interval,
message: message,
ignored: ignored,
object: self,
&block)
end
alias wait_true wait_until_true
# Check every interval seconds to see if yield doesn't raise an exception.
# Give up after timeout seconds.
#
# If only a number is provided then it's treated as the timeout value.
#
# @param [Integer] timeout Seconds to wait before timing out. Set default by +appium_wait_timeout+ (30).
# @param [Integer] interval Seconds to sleep between polls. Set default by +appium_wait_interval+ (0.5).
# @param [String] message Exception message if timed out.
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
#
# @example With core instance
#
# @core.wait { @driver.find_element :accessibility_id, 'something' }
# @core.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
#
# @core.wait_until { @driver.find_element :accessibility_id, 'something' }
# @core.wait_until(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
#
# @example With driver instance
#
# @driver.wait { @driver.find_element :accessibility_id, 'something' }
# @driver.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
#
# @driver.wait_until { |d| d.find_element :accessibility_id, 'something' }
# @driver.wait_until(timeout: 30, interval: 2) { |d| d.find_element :accessibility_id, 'something' }
#
def wait_until(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
Wait.until(timeout: timeout || @wait_timeout,
interval: interval || @wait_interval,
message: message,
ignored: ignored,
object: self,
&block)
end
alias wait wait_until
end
end # module Core
end # module Appium