2010-11-21 08:37:36 -05:00
module Capybara
module Node
##
#
# A {Capybara::Node::Base} represents either an element on a page through the subclass
# {Capybara::Node::Element} or a document through {Capybara::Node::Document}.
#
# Both types of Node share the same methods, used for interacting with the
# elements on the page. These methods are divided into three categories,
# finders, actions and matchers. These are found in the modules
# {Capybara::Node::Finders}, {Capybara::Node::Actions} and {Capybara::Node::Matchers}
# respectively.
#
# A {Capybara::Session} exposes all methods from {Capybara::Node::Document} directly:
#
# session = Capybara::Session.new(:rack_test, my_app)
# session.visit('/')
# session.fill_in('Foo', :with => 'Bar') # from Capybara::Node::Actions
# bar = session.find('#bar') # from Capybara::Node::Finders
# bar.select('Baz', :from => 'Quox') # from Capybara::Node::Actions
# session.has_css?('#foobar') # from Capybara::Node::Matchers
#
class Base
2011-07-13 09:39:17 -04:00
attr_reader :session , :base , :parent
2010-11-21 08:37:36 -05:00
include Capybara :: Node :: Finders
include Capybara :: Node :: Actions
include Capybara :: Node :: Matchers
def initialize ( session , base )
@session = session
@base = base
end
2012-02-01 08:16:17 -05:00
# overridden in subclasses, e.g. Capybara::Node::Element
2011-07-13 09:39:17 -04:00
def reload
self
end
2012-07-13 14:19:23 -04:00
##
#
2014-03-19 19:28:26 -04:00
# This method is Capybara's primary defence against asynchronicity
2012-07-13 14:19:23 -04:00
# problems. It works by attempting to run a given block of code until it
# succeeds. The exact behaviour of this method depends on a number of
2012-09-06 04:47:11 -04:00
# factors. Basically there are certain exceptions which, when raised
2012-07-13 14:19:23 -04:00
# from the block, instead of bubbling up, are caught, and the block is
# re-run.
#
2014-03-19 19:28:26 -04:00
# Certain drivers, such as RackTest, have no support for asynchronous
2012-07-13 14:19:23 -04:00
# processes, these drivers run the block, and any error raised bubbles up
# immediately. This allows faster turn around in the case where an
# expectation fails.
#
# Only exceptions that are {Capybara::ElementNotFound} or any subclass
# thereof cause the block to be rerun. Drivers may specify additional
# exceptions which also cause reruns. This usually occurs when a node is
# manipulated which no longer exists on the page. For example, the
# Selenium driver specifies
# `Selenium::WebDriver::Error::ObsoleteElementError`.
#
# As long as any of these exceptions are thrown, the block is re-run,
# until a certain amount of time passes. The amount of time defaults to
2014-03-19 19:28:26 -04:00
# {Capybara.default_wait_time} and can be overridden through the `seconds`
2012-09-06 04:47:11 -04:00
# argument. This time is compared with the system time to see how much
2013-03-04 16:35:11 -05:00
# time has passed. If the return value of `Time.now` is stubbed out,
2012-09-06 04:47:11 -04:00
# Capybara will raise `Capybara::FrozenInTime`.
2012-07-13 14:19:23 -04:00
#
2013-03-04 16:35:11 -05:00
# @param [Integer] seconds Number of seconds to retry this block
2014-04-08 17:28:16 -04:00
# @param options [Hash]
# @option options [Array<Exception>] :errors (driver.invalid_element_errors +
# [Capybara::ElementNotFound]) exception types that cause the block to be rerun
2012-07-13 14:33:14 -04:00
# @return [Object] The result of the given block
2013-03-04 16:35:11 -05:00
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
2012-07-13 14:19:23 -04:00
#
2014-04-08 17:28:16 -04:00
def synchronize ( seconds = Capybara . default_wait_time , options = { } )
2012-07-12 10:21:43 -04:00
start_time = Time . now
2011-08-12 07:52:12 -04:00
2013-03-11 19:11:05 -04:00
if session . synchronized
2011-08-12 07:52:12 -04:00
yield
2013-03-11 19:11:05 -04:00
else
session . synchronized = true
begin
yield
rescue = > e
2014-03-02 09:49:00 -05:00
session . raise_server_error!
2013-03-11 19:11:05 -04:00
raise e unless driver . wait?
2014-04-08 17:28:16 -04:00
raise e unless catch_error? ( e , options [ :errors ] )
2013-03-11 19:11:05 -04:00
raise e if ( Time . now - start_time ) > = seconds
sleep ( 0 . 05 )
raise Capybara :: FrozenInTime , " time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead " if Time . now == start_time
reload if Capybara . automatic_reload
retry
ensure
session . synchronized = false
end
2011-08-12 07:52:12 -04:00
end
end
2012-02-01 08:16:17 -05:00
protected
2014-04-08 17:28:16 -04:00
def catch_error? ( error , errors = nil )
errors || = ( driver . invalid_element_errors + [ Capybara :: ElementNotFound ] )
errors . any? do | type |
2013-03-14 05:03:49 -04:00
error . is_a? ( type )
end
end
2010-11-21 08:37:36 -05:00
def driver
session . driver
end
end
end
end