module AePageObjects

Constants

VERSION

Attributes

default_router[RW]

Public Class Methods

browser() click to toggle source
# File lib/ae_page_objects.rb, line 21
def browser
  @browser ||= begin
    driver = Capybara.current_session.driver

    case driver
    when Capybara::Selenium::Driver then
      require 'ae_page_objects/multiple_windows/browser'
      MultipleWindows::Browser.new
    else
      require 'ae_page_objects/single_window/browser'
      SingleWindow::Browser.new
    end
  end
end
wait_until(seconds_to_wait = nil, error_message = nil, &block) click to toggle source
# File lib/ae_page_objects.rb, line 36
def wait_until(seconds_to_wait = nil, error_message = nil, &block)
  @wait_until ||= 0
  @wait_until += 1

  result = nil

  if @wait_until > 1
    # We want to ensure that only the top-level wait_until does the waiting error handling,
    # which allows correct timing and best chance of recovering from an error.
    result = call_wait_until_block(error_message, &block)
  else
    seconds_to_wait ||= default_max_wait_time
    start_time      = Time.now

    # In an effort to avoid flakiness, Capybara waits, rescues errors, reloads nodes, and
    # retries.
    #
    # There are cases when Capybara will rescue an error and either nodes are not
    # reloadable or the DOM has changed in a such a way that no amount of reloading will
    # help (but perhaps a retry at the higher level may have a chance of success). This leads
    # to us needless waiting for a long time just to fail.
    #
    # There are also cases when Selenium will take such a long time to respond with an error
    # that Capybara's timeout will be exceeded and no reloading / retrying will occur. Instead,
    # Capabara will just raise the error.
    #
    # In order to combat the two cases, we start with a lower Capybara wait time and increase
    # it each iteration. This logic is encapsulated in a little utility class.
    time_manager = WaitTimeManager.new(1.0, seconds_to_wait)
    begin
      result = time_manager.using_wait_time { call_wait_until_block(error_message, &block) }
    rescue => e
      errors = Capybara.current_session.driver.invalid_element_errors
      errors += [Capybara::ElementNotFound]
      errors += [DocumentLoadError, LoadingElementFailed, LoadingPageFailed]
      errors += [WaitTimeoutError]
      raise e unless errors.include?(e.class)

      delay = seconds_to_wait - (Time.now - start_time)

      if delay <= 0
        # Raising the WaitTimeoutError in the rescue block ensures that Ruby attaches
        # the original exception as the cause for our WaitTimeoutError.
        raise WaitTimeoutError, e.message
      end

      sleep(0.05)
      raise FrozenInTime, "Time appears to be frozen" if Time.now == start_time

      retry
    end
  end

  result
ensure
  @wait_until -= 1
end

Private Class Methods

call_wait_until_block(error_message, &block) click to toggle source
# File lib/ae_page_objects.rb, line 96
def call_wait_until_block(error_message, &block)
  result = block.call
  result ? result : raise(WaitTimeoutError, error_message || "Timed out waiting for condition")
end
default_max_wait_time() click to toggle source
# File lib/ae_page_objects.rb, line 101
def default_max_wait_time
  Capybara.default_max_wait_time
end