module Calabash::Cucumber::WaitHelpers

A collection of methods that help you wait for things.

Constants

CALABASH_CONDITIONS

Currently two conditions that can be waited for using ‘wait_for_condition`: `:none_animating` no UIKit object is animating and `:no_network_indicator` status bar network indicator not showing.

CLIENT_TIMEOUT_ADDITION

@!visibility private

DEFAULT_OPTS

The default options used in the “wait*” methods

Public Instance Methods

handle_error_with_options(error, timeout_message, screenshot_on_error) click to toggle source

@!visibility private Raises an error by raising a error and conditionally takes a screenshot based on the value of screenshot_on_error. @param [RuntimeError,nil] error an error to raise @param [String,nil] timeout_message the message of the raise @param [Boolean] screenshot_on_error if true takes a screenshot before

raising an error

@return [nil] @raise [StandardError] If ‘error`, then that kind of error is raised.

Otherwise raise a RuntimeError.
# File lib/calabash-cucumber/wait_helpers.rb, line 455
def handle_error_with_options(error, timeout_message, screenshot_on_error)
  msg = (timeout_message || error)
  if error
    error_class = error.class
  else
    error_class = RuntimeError
  end
  if screenshot_on_error
    screenshot_and_raise msg
  else
    raise error_class, msg
  end
end
screenshot_and_retry(msg) { || ... } click to toggle source

@!visibility private

# File lib/calabash-cucumber/wait_helpers.rb, line 427
def screenshot_and_retry(msg, &block)
  path  = screenshot
  res = yield
  # Validate after taking screenshot
  if res
    FileUtils.rm_f(path)
    return res
  else
    begin
      embed(path, 'image/png', msg)
    rescue NoMethodError
      attach(path, 'image/png')
    end

    raise wait_error(msg)
  end
end
touch_transition(touch_q, done_queries,check_options={},animation_options={}) click to toggle source

Combines touching an element and ‘wait_for_transition` @see wait_for_transition @param [String] touch_q the Calabash query to touch @param [Array] done_queries passed to `wait_for_transition` @param [Hash] check_options ({}) passed to `wait_for_transition` @param [Hash] animation_options ({}) passed to `wait_for_transition`

# File lib/calabash-cucumber/wait_helpers.rb, line 364
def touch_transition(touch_q, done_queries,check_options={},animation_options={})
  touch(touch_q)
  wait_for_transition(done_queries,check_options,animation_options)
end
until_element_does_not_exist(uiquery, opts = {}) click to toggle source

Performs a lambda action until the element (a query string) disappears. The default action is to do nothing.

Raises an error if no uiquery is specified.

@example

until_element_does_not_exist("button", :action => lambda { swipe("up") })

@see wait_poll @param [String] uiquery the Calabash query to wait for disappearing. @param [Hash] opts options for controlling the details of the wait.

The same options as {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} apply.
# File lib/calabash-cucumber/wait_helpers.rb, line 400
def until_element_does_not_exist(uiquery, opts = {})
  condition = lambda {element_does_not_exist(uiquery)}
  extra_opts = { :until => condition, :action => lambda {} }
  opts = DEFAULT_OPTS.merge(extra_opts).merge(opts)
  wait_poll(opts) do
    opts[:action].call
  end
end
until_element_exists(uiquery, opts = {}) click to toggle source

Performs a lambda action until the element (a query string) appears. The default action is to do nothing. Similar to ‘wait_poll`.

Raises an error if no uiquery is specified.

@see wait_poll

@example

until_element_exists("button", :action => lambda { swipe("up") })

@param [String] uiquery the Calabash query to wait for @param [Hash] opts options for controlling the details of the wait.

The same options as {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} apply.
# File lib/calabash-cucumber/wait_helpers.rb, line 381
def until_element_exists(uiquery, opts = {})
  extra_opts = { :until_exists => uiquery, :action => lambda {} }
  opts = DEFAULT_OPTS.merge(extra_opts).merge(opts)
  wait_poll(opts) do
    opts[:action].call
  end
end
wait_error(msg) click to toggle source

@private if msg is a String, a new WaitError is returned. Otherwise msg itself is returned. @param [String,Object] msg a message to raise @return [WaitError] if msg is a String, returns a new WaitError @return [Object] if msg is anything else, returns msg

# File lib/calabash-cucumber/wait_helpers.rb, line 475
def wait_error(msg)
  (msg.is_a?(String) ? WaitError.new(msg) : msg)
end
wait_for(options_or_timeout=DEFAULT_OPTS) { || ... } click to toggle source

Waits for a condition to be true. The condition is specified by a given block that is called repeatedly. If the block returns a ‘trueish’ value the condition is considered true and ‘wait_for` immediately returns. There is a `:timeout` option that specifies a maximum number of seconds to wait. If the given block doesn’t return a ‘trueish’ value before the ‘:timeout` seconds has elapsed, the waiting fails and raises a {Calabash::Cucumber::WaitHelpers::WaitError} error.

The ‘options` hash controls the details of waiting (see `options_or_timeout` below). {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} specifies the default waiting options.

‘wait_for` is a low-level building-block for waiting and often there are higher-level waiting methods what use `wait_for` in their implementation (e.g. `wait_for_element_exists`). @see wait_for_element_exists

@example Waiting for an element (see also ‘wait_for_element_exists`)

wait_for(timeout: 60,
         timeout_message: "Could not find 'Sign in' button") do
  element_exists("button marked:'Sign in'")
end

@param [Hash] options_or_timeout options for controlling the details of the wait.

Note for backwards compatibility with old Calabash versions can also be a number which is
then interpreted as a timeout.

@option options_or_timeout [Numeric] :timeout (30) upper limit on how long to wait (in seconds) @option options_or_timeout [Numeric] :retry_frequency (0.3) how often to poll (i.e., call the given block) @option options_or_timeout [Numeric] :post_timeout (0) if positive, an extra wait is made after the condition

is satisfied

@option options_or_timeout [String] :timeout_message the error message to use if condition is not satisfied

in time

@option options_or_timeout [Boolean] :screenshot_on_error generate a screenshot on error @return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 115
def wait_for(options_or_timeout=DEFAULT_OPTS, &block)
  #note Hash is preferred, number acceptable for backwards compat
  default_timeout = 30
  timeout = options_or_timeout || default_timeout
  post_timeout=0
  retry_frequency=0.3
  timeout_message = nil
  screenshot_on_error = true

  if options_or_timeout.is_a?(Hash)
    timeout = options_or_timeout[:timeout] || default_timeout
    retry_frequency = options_or_timeout[:retry_frequency] || retry_frequency
    post_timeout = options_or_timeout[:post_timeout] || post_timeout
    timeout_message = options_or_timeout[:timeout_message]
    if options_or_timeout.key?(:screenshot_on_error)
      screenshot_on_error = options_or_timeout[:screenshot_on_error]
    end
  end

  begin
    Timeout.timeout(timeout, WaitError) do
      sleep(retry_frequency) until yield
    end
    sleep(post_timeout) if post_timeout > 0
  rescue WaitError => e
    msg = timeout_message || e
    if screenshot_on_error
      sleep(retry_frequency)
      return screenshot_and_retry(msg, &block)
    else
      raise wait_error(msg)
    end
  rescue => e
    handle_error_with_options(e, nil, screenshot_on_error)
  end
end
wait_for_condition(options = {}) click to toggle source

@!visibility private

# File lib/calabash-cucumber/wait_helpers.rb, line 276
def wait_for_condition(options = {})
  timeout = options[:timeout] || ENV['WAIT_TIMEOUT'].to_i
  timeout = DEFAULT_OPTS[:timeout] unless timeout && timeout > 0

  options[:query] = options[:query] || '*'
  if options.has_key?(:condition)
    opt_condition = options[:condition]
    if opt_condition.is_a?(Symbol)
      target_condition = CALABASH_CONDITIONS[opt_condition]
    elsif opt_condition.is_a?(String)
      target_condition = options[:condition]
    end
    options[:condition] = target_condition
  end
  options[:condition] = options[:condition] || CALABASH_CONDITIONS[:none_animating]
  options[:post_timeout] = options[:post_timeout] || 0

  retry_frequency = options[:frequency] = options[:frequency] || 0.2
  timeout_message = options[:timeout_message] = options[:timeout_message] || "Timeout waiting (#{options[:timeout]}) for condition (#{options[:condition]})"
  screenshot_on_error = true
  if options.key?(:screenshot_on_error)
    screenshot_on_error = options[:screenshot_on_error]
  end

  begin
    Timeout.timeout(timeout + CLIENT_TIMEOUT_ADDITION, WaitError) do
      res = http({:method => :post, :path => 'condition'},
                 options)
      res = JSON.parse(res)
      unless res['outcome'] == 'SUCCESS'
        raise WaitError.new(res['reason'])
      end
      sleep(options[:post_timeout]) if options[:post_timeout] > 0
    end
  rescue WaitError => e
    msg = timeout_message || e
    if screenshot_on_error
      sleep(retry_frequency)
      return screenshot_and_retry(msg) do
        res = http({:method => :post, :path => 'condition'},
                   options)
        res = JSON.parse(res)
        res['outcome'] == 'SUCCESS'
      end
    else
      raise wait_error(msg)
    end
  rescue => e
    handle_error_with_options(e, nil, screenshot_on_error)
  end
end
wait_for_element_does_not_exist(element_query, options={}) click to toggle source

Waits for a Calabash query to return an empty result (typically a UI element to disappear). Uses ‘wait_for`. @see wait_for @see wait_for_element_exists @see Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS

@param [String] element_query a Calabash query to be empty (i.e. ‘element_does_not_exist(element_query)`) @param [Hash] options options for controlling the details of the wait.

The same options as {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} apply.

@return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 247
def wait_for_element_does_not_exist(element_query, options={})
  options[:timeout_message] = options[:timeout_message] || "Timeout waiting for element to not exist: #{element_query}"
  wait_for(options) { element_does_not_exist(element_query) }
end
wait_for_element_does_not_exists(element_query, options={})
wait_for_element_exists(element_query, options={}) click to toggle source

Waits for a Calabash query to return a non-empty result (typically a UI element to be visible). Uses ‘wait_for`. @see wait_for @see Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS

@example Waiting for an element to be visible

wait_for_element_exists("button marked:'foo'", timeout: 60)

@param [String] element_query a Calabash query to wait for (i.e. ‘element_exists(element_query)`) @param [Hash] options options for controlling the details of the wait.

The same options as {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} apply.

@return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 210
def wait_for_element_exists(element_query, options={})
  options[:timeout_message] = options[:timeout_message] || "Timeout waiting for element: #{element_query}"
  wait_for(options) { element_exists(element_query) }
end
wait_for_elements_do_not_exist(elements_arr, options={}) click to toggle source

Waits for one or more Calabash queries to all return empty results (typically a UI elements to disappear). Uses ‘wait_for`. @see wait_for @see wait_for_element_exists @see wait_for_element_does_not_exist @see Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS

@param [Array<String>] elements_arr an Array of Calabash queries to be empty (i.e. ‘element_does_not_exist(element_query)`) @param [Hash] options options for controlling the details of the wait.

The same options as {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} apply.

@return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 265
def wait_for_elements_do_not_exist(elements_arr, options={})
  if elements_arr.is_a?(String)
    elements_arr = [elements_arr]
  end
  options[:timeout_message] = options[:timeout_message] || "Timeout waiting for no elements matching: #{elements_arr.join(",")}"
  wait_for(options) do
    elements_arr.none? { |q| element_exists(q) }
  end
end
wait_for_elements_exist(elements_arr, options={}) click to toggle source

Waits for one or more Calabash queries to all return non-empty results (typically a UI elements to be visible). Uses ‘wait_for`. @see wait_for @see wait_for_element_exists @see Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS

@param [Array<String>] elements_arr an Array of Calabash queries to wait for (i.e. ‘element_exists(element_query)`) @param [Hash] options options for controlling the details of the wait.

The same options as {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} apply.

@return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 226
def wait_for_elements_exist(elements_arr, options={})
  if elements_arr.is_a?(String)
    elements_arr = [elements_arr]
  end
  options[:timeout_message] = options[:timeout_message] || "Timeout waiting for elements: #{elements_arr.join(',')}"
  wait_for(options) do
    elements_arr.all? { |q| element_exists(q) }
  end
end
wait_for_no_network_indicator(options = {}) click to toggle source

Waits for the status-bar network indicator to stop animating (network activity done). @param [Hash] options options for controlling the details of the wait. @option options [Numeric] :timeout (30) maximum time to wait @return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 343
def wait_for_no_network_indicator(options = {})
  options[:condition] = CALABASH_CONDITIONS[:no_network_indicator]
  wait_for_condition(options)
end
wait_for_none_animating(options = {}) click to toggle source

Waits for all elements to stop animating (EXPERIMENTAL). @param [Hash] options options for controlling the details of the wait. @option options [Numeric] :timeout (30) maximum time to wait @return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 333
def wait_for_none_animating(options = {})
  options[:condition] = CALABASH_CONDITIONS[:none_animating]
  wait_for_condition(options)
end
wait_for_transition(done_queries, check_options={},animation_options={}) click to toggle source

Combines waiting for elements and waiting for animations. @param [Array] done_queries Calabash queries to wait for (one or more). @param [Hash] check_options ({}) options used for ‘wait_for_elements_exists(done_queries, check_options)` @param [Hash] animation_options ({}) options used for `wait_for_none_animating(animation_options)`

# File lib/calabash-cucumber/wait_helpers.rb, line 352
def wait_for_transition(done_queries, check_options={},animation_options={})
  done_queries = [*done_queries]
  wait_for_elements_exist(done_queries,check_options)
  wait_for_none_animating(animation_options)
end
wait_poll(opts) { || ... } click to toggle source

Repeatedly runs an action (for side-effects) until a condition is satisfied. Similar to ‘wait_for` but specifies both a condition to wait for and an action to repeatedly perform to make the condition true (e.g. scrolling). The return value of the action is ignored.

The block represents the action and options :until or :until_exists specify the condition to wait for. Same options as ‘wait_for` can be provided.

@see wait_for

@example Scrolling until we find an element

wait_poll(timeout: 10,
          timeout_message: 'Unable to find "Example"',
          until_exists: "* marked:'Example'") do
  scroll("tableView", :down)
end

@example Win the battle

wait_poll(timeout: 60,
          timeout_message: 'Defeat!',
          until: lambda { enemy_defeated? }) do
  launch_the_missiles!
end

@param [Hash] opts options for controlling the details of the wait in addition to the options specified below,

all options in {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} also apply and can be overridden.

@option opts [Proc] :until if specified this lambda/Proc becomes the condition to wait for. @option opts [String] :until_exists if specified, a calabash query to wait for. Exactly one of ‘:until` and

`:until_exists` must be specified

@return [nil] when the condition is satisfied @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded

# File lib/calabash-cucumber/wait_helpers.rb, line 181
def wait_poll(opts, &block)
  test = opts[:until]
  if test.nil?
    cond = opts[:until_exists]
    raise 'Must provide :until or :until_exists' unless cond
    test = lambda { element_exists(cond) }
  end
  wait_for(opts) do
    if test.call()
      true
    else
      yield
      false
    end
  end
end
wait_tap(uiquery, options={}) click to toggle source

Performs the ‘tap` gesture on the (first) view that matches query `uiquery`.

As opposed to ‘touch`, `wait_tap` is a high-level method that combines:

  1. waiting for the view to appear,

  2. waiting for animations to complete on the view (and it’s parents) and

  3. actually tapping the view.

This replaces the common pattern:

“‘ wait_for_none_animating wait_for_element_exists(“* marked:’log in’”) touch(“* marked:‘log in’”) “‘

By default, taps the center of the view. @see Calabash::Cucumber::Core#touch @see Calabash::Cucumber::Core#touch_point @param {String} uiquery query describing view to tap. Note ‘nil` is not allowed. @param {Hash} options option for modifying the details of the touch @option options {Hash} :offset (nil) optional offset to tap point. Offset has an `:x` and `:y` key

the tap will be performed on the center of the view plus the offset.

@option options {Hash} :timeout (30) maximum number of seconds to wait for the view to appear @option options {Hash} :frequency (0.2) polling frequency to for checking if the view is present (>= 0.1) @return {Array<Hash>} serialized version of the tapped view

# File lib/calabash-cucumber/wait_helpers.rb, line 77
def wait_tap(uiquery, options={})
  wait_for_none_animating
  wait_for_element_exists(uiquery, options)
  touch(uiquery, options)
end
when_element_exists(uiquery, opts = {}) click to toggle source

Performs a lambda action once the element exists. The default behavior is to touch the specified element.

Raises an error if no uiquery is specified.

@example

when_element_exists("button", :timeout => 10)

@see wait_for @param [String] uiquery the Calabash query to wait for. @param [Hash] opts options for controlling the details of the wait.

The same options as {Calabash::Cucumber::WaitHelpers::DEFAULT_OPTS} apply.
# File lib/calabash-cucumber/wait_helpers.rb, line 420
def when_element_exists(uiquery, opts = {})
  action = opts[:action] || lambda { touch(uiquery) }
  wait_for_element_exists(uiquery, opts)
  action.call
end