2010-07-09 21:06:44 -04:00
|
|
|
require 'capybara/util/timeout'
|
2009-12-31 14:04:38 -05:00
|
|
|
|
2009-11-26 17:47:58 -05:00
|
|
|
module Capybara
|
2010-01-01 11:48:39 -05:00
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
2010-07-14 17:59:23 -04:00
|
|
|
# The Session class represents a single user's interaction with the system. The Session can use
|
|
|
|
# any of the underlying drivers. A session can be initialized manually like this:
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
|
|
|
# session = Capybara::Session.new(:culerity, MyRackApp)
|
|
|
|
#
|
|
|
|
# The application given as the second argument is optional. When running Capybara against an external
|
|
|
|
# page, you might want to leave it out:
|
|
|
|
#
|
|
|
|
# session = Capybara::Session.new(:culerity)
|
|
|
|
# session.visit('http://www.google.com')
|
|
|
|
#
|
|
|
|
# Session provides a number of methods for controlling the navigation of the page, such as +visit+,
|
|
|
|
# +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
|
|
|
|
# the current HTML document. This allows interaction:
|
|
|
|
#
|
|
|
|
# session.fill_in('q', :with => 'Capybara')
|
|
|
|
# session.click_button('Search')
|
|
|
|
# session.should have_content('Capybara')
|
|
|
|
#
|
|
|
|
# When using capybara/dsl, the Session is initialized automatically for you.
|
|
|
|
#
|
|
|
|
class Session
|
2009-12-29 22:05:38 -05:00
|
|
|
DSL_METHODS = [
|
2010-07-10 06:23:30 -04:00
|
|
|
:all, :attach_file, :body, :check, :choose, :click_link_or_button, :click_button, :click_link, :current_url, :drag, :evaluate_script,
|
2010-01-11 17:14:52 -05:00
|
|
|
:field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_css?,
|
|
|
|
:has_no_content?, :has_no_css?, :has_no_xpath?, :has_xpath?, :locate, :save_and_open_page, :select, :source, :uncheck,
|
2010-04-28 19:03:13 -04:00
|
|
|
:visit, :wait_until, :within, :within_fieldset, :within_table, :within_frame, :has_link?, :has_no_link?, :has_button?,
|
2010-07-10 06:23:30 -04:00
|
|
|
:has_no_button?, :has_field?, :has_no_field?, :has_checked_field?, :has_unchecked_field?, :has_no_table?, :has_table?,
|
2010-07-19 15:52:14 -04:00
|
|
|
:unselect, :has_select?, :has_no_select?, :current_path, :scope_to, :click
|
2009-12-29 22:05:38 -05:00
|
|
|
]
|
2010-01-11 17:14:52 -05:00
|
|
|
|
2009-12-18 13:53:01 -05:00
|
|
|
attr_reader :mode, :app
|
2009-11-09 17:51:39 -05:00
|
|
|
|
2010-03-12 13:07:15 -05:00
|
|
|
def initialize(mode, app=nil)
|
2009-12-18 11:40:51 -05:00
|
|
|
@mode = mode
|
2009-11-26 17:47:58 -05:00
|
|
|
@app = app
|
2009-11-16 14:13:06 -05:00
|
|
|
end
|
2010-01-01 11:48:39 -05:00
|
|
|
|
2009-11-26 17:47:58 -05:00
|
|
|
def driver
|
2010-07-29 09:20:11 -04:00
|
|
|
@driver ||= begin
|
|
|
|
unless Capybara.drivers.has_key?(mode)
|
|
|
|
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
|
|
|
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
|
|
|
end
|
|
|
|
Capybara.drivers[mode].call(app)
|
2009-11-26 17:47:58 -05:00
|
|
|
end
|
2009-11-15 07:40:50 -05:00
|
|
|
end
|
2009-11-26 17:47:58 -05:00
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Reset the session, removing all cookies.
|
|
|
|
#
|
2010-07-11 08:00:00 -04:00
|
|
|
def cleanup!
|
|
|
|
driver.cleanup!
|
|
|
|
end
|
2009-11-26 17:47:58 -05:00
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @return [Hash{String => String}] A hash of response headers.
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-11 08:00:00 -04:00
|
|
|
def response_headers
|
|
|
|
driver.response_headers
|
2009-12-13 09:03:19 -05:00
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Returns the current HTTP status code as an Integer. Not supported by all drivers (e.g. Selenium)
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @return [Integer] Current HTTP status code
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-11 08:00:00 -04:00
|
|
|
def status_code
|
|
|
|
driver.status_code
|
2009-11-24 15:45:52 -05:00
|
|
|
end
|
2009-11-07 12:56:04 -05:00
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @return [String] A snapshot of the HTML of the current document, as it looks right now
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-11 08:00:00 -04:00
|
|
|
def body
|
|
|
|
driver.body
|
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @return [String] HTML source of the document, before being modified by JavaScript.
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-11 08:00:00 -04:00
|
|
|
def source
|
|
|
|
driver.source
|
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @return [String] Path of the current page, without any domain information
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-09 21:11:54 -04:00
|
|
|
def current_path
|
|
|
|
URI.parse(current_url).path
|
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @return [String] Fully qualified URL of the current page
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
|
|
|
def current_url
|
|
|
|
driver.current_url
|
|
|
|
end
|
|
|
|
|
|
|
|
##
|
|
|
|
#
|
|
|
|
# Navigate to the given URL. The URL can either be a relative URL or an absolute URL
|
|
|
|
# The behaviour of either depends on the driver.
|
|
|
|
#
|
|
|
|
# session.visit('/foo')
|
|
|
|
# session.visit('http://google.com')
|
|
|
|
#
|
|
|
|
# For drivers which can run against an external application, such as culerity and selenium
|
|
|
|
# giving an absolute URL will navigate to that page. This allows testing applications
|
|
|
|
# running on remote servers. For these drivers, setting Capybara.app_host will make the
|
|
|
|
# remote server the default. For example:
|
|
|
|
#
|
|
|
|
# Capybara.app_host = 'http://google.com'
|
|
|
|
# session.visit('/') # visits the google homepage
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [String] url The URL to navigate to
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-11 08:00:00 -04:00
|
|
|
def visit(url)
|
|
|
|
driver.visit(url)
|
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
2010-07-14 17:59:23 -04:00
|
|
|
# Execute the given block for a particular scope on the page. Within will find the first
|
2010-07-11 11:26:08 -04:00
|
|
|
# element matching the given selector and execute the block scoped to that element:
|
|
|
|
#
|
|
|
|
# within(:xpath, '//div[@id="delivery-address"]') do
|
|
|
|
# fill_in('Street', :with => '12 Main Street')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# It is possible to omit the first parameter, in that case, the selector is assumed to be
|
|
|
|
# of the type set in Capybara.default_selector.
|
|
|
|
#
|
|
|
|
# within('div#delivery-address') do
|
|
|
|
# fill_in('Street', :with => '12 Main Street')
|
|
|
|
# end
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [:css, :xpath, String] kind The type of selector or the selector if the second argument is blank
|
|
|
|
# @param [String] selector The selector within which to execute the given block
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
|
|
|
def within(kind, selector=nil)
|
2010-07-19 14:18:16 -04:00
|
|
|
new_scope = find(kind, selector, :message => "scope '#{selector || kind}' not found on page")
|
2010-02-11 15:46:31 -05:00
|
|
|
begin
|
2010-07-09 19:38:57 -04:00
|
|
|
scopes.push(new_scope)
|
2010-02-11 15:46:31 -05:00
|
|
|
yield
|
|
|
|
ensure
|
|
|
|
scopes.pop
|
|
|
|
end
|
2009-11-26 17:47:58 -05:00
|
|
|
end
|
2009-11-14 09:11:29 -05:00
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Execute the given block within the a specific fieldset given the id or legend of that fieldset.
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [String] locator Id or legend of the fieldset
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2009-11-26 17:47:58 -05:00
|
|
|
def within_fieldset(locator)
|
2010-08-14 12:35:46 -04:00
|
|
|
within :xpath, XPath::HTML.fieldset(locator) do
|
2009-11-26 17:47:58 -05:00
|
|
|
yield
|
|
|
|
end
|
2009-11-10 16:48:31 -05:00
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Execute the given block within the a specific table given the id or caption of that table.
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [String] locator Id or caption of the table
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2009-11-26 17:47:58 -05:00
|
|
|
def within_table(locator)
|
2010-08-14 12:35:46 -04:00
|
|
|
within :xpath, XPath::HTML.table(locator) do
|
2009-11-26 17:47:58 -05:00
|
|
|
yield
|
|
|
|
end
|
|
|
|
end
|
2010-01-01 12:29:30 -05:00
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Execute the given block within the given iframe given the id of that iframe. Only works on
|
|
|
|
# some drivers (e.g. Selenium)
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [String] locator Id of the frame
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-05-09 10:51:11 -04:00
|
|
|
def within_frame(frame_id)
|
2010-04-28 19:03:13 -04:00
|
|
|
driver.within_frame(frame_id) do
|
2009-11-26 17:47:58 -05:00
|
|
|
yield
|
|
|
|
end
|
|
|
|
end
|
2010-01-01 12:29:30 -05:00
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Retry executing the block until a truthy result is returned or the timeout time is exceeded
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [Integer] timeout The amount of seconds to retry executing the given block
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-09 19:42:59 -04:00
|
|
|
def wait_until(timeout = Capybara.default_wait_time)
|
|
|
|
Capybara.timeout(timeout,driver) { yield }
|
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Execute the given script, not returning a result. This is useful for scripts that return
|
|
|
|
# complex objects, such as jQuery statements. +execute_script+ should always be used over
|
|
|
|
# +evaluate_script+ whenever possible.
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [String] script A string of JavaScript to execute
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-09 19:42:59 -04:00
|
|
|
def execute_script(script)
|
|
|
|
driver.execute_script(script)
|
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Evaluate the given JavaScript and return the result. Be careful when using this with
|
|
|
|
# scripts that return complex objects, such as jQuery statements. +execute_script+ might
|
|
|
|
# be a better alternative.
|
|
|
|
#
|
2010-07-14 18:24:59 -04:00
|
|
|
# @param [String] script A string of JavaScript to evaluate
|
|
|
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
2010-07-11 11:26:08 -04:00
|
|
|
#
|
2010-07-09 19:42:59 -04:00
|
|
|
def evaluate_script(script)
|
|
|
|
driver.evaluate_script(script)
|
|
|
|
end
|
|
|
|
|
2010-07-19 14:48:50 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# @deprecated click is deprecated, please use {Capybara::Node::Actions#click_link_or_button} instead
|
|
|
|
#
|
|
|
|
def click(locator)
|
|
|
|
warn "DEPRECATED: click is deprecated, use click_link_or_button instead"
|
|
|
|
current_node.click_link_or_button(locator)
|
|
|
|
end
|
|
|
|
|
2010-07-11 11:26:08 -04:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Save a snapshot of the page and open it in a browser for inspection
|
|
|
|
#
|
2010-07-09 19:42:59 -04:00
|
|
|
def save_and_open_page
|
2010-07-09 21:06:44 -04:00
|
|
|
require 'capybara/util/save_and_open_page'
|
|
|
|
Capybara.save_and_open_page(body)
|
2010-07-09 19:42:59 -04:00
|
|
|
end
|
|
|
|
|
2010-07-11 08:00:00 -04:00
|
|
|
def document
|
|
|
|
Capybara::Document.new(self, driver)
|
|
|
|
end
|
|
|
|
|
|
|
|
def method_missing(*args)
|
|
|
|
current_node.send(*args)
|
|
|
|
end
|
|
|
|
|
|
|
|
def respond_to?(method)
|
|
|
|
super || current_node.respond_to?(method)
|
|
|
|
end
|
|
|
|
|
2009-11-26 17:47:58 -05:00
|
|
|
private
|
|
|
|
|
2010-07-09 19:38:57 -04:00
|
|
|
def current_node
|
|
|
|
scopes.last
|
2009-11-26 17:47:58 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def scopes
|
2010-07-09 19:38:57 -04:00
|
|
|
@scopes ||= [document]
|
2009-11-26 17:47:58 -05:00
|
|
|
end
|
2009-11-14 09:11:29 -05:00
|
|
|
end
|
2009-11-07 09:36:58 -05:00
|
|
|
end
|