2010-07-10 00:20:32 +00:00
|
|
|
module Capybara
|
2010-11-21 13:37:36 +00:00
|
|
|
module Node
|
2010-07-10 00:20:32 +00:00
|
|
|
module Finders
|
2010-07-17 17:05:00 +00:00
|
|
|
|
|
|
|
##
|
|
|
|
#
|
2010-08-27 18:52:06 +00:00
|
|
|
# Find an {Capybara::Element} based on the given arguments. +find+ will raise an error if the element
|
2010-07-19 18:18:16 +00:00
|
|
|
# is not found. The error message can be customized through the +:message+ option.
|
2010-07-17 17:05:00 +00:00
|
|
|
#
|
2010-07-19 18:18:16 +00:00
|
|
|
# If the driver is capable of executing JavaScript, +find+ will wait for a set amount of time
|
2010-07-17 17:05:00 +00:00
|
|
|
# and continuously retry finding the element until either the element is found or the time
|
2010-08-27 18:52:06 +00:00
|
|
|
# expires. The length of time +find+ will wait is controlled through {Capybara.default_wait_time}
|
2010-07-17 17:05:00 +00:00
|
|
|
# and defaults to 2 seconds.
|
|
|
|
#
|
2010-07-19 18:18:16 +00:00
|
|
|
# +find+ takes the same options as +all+.
|
2010-07-17 17:05:00 +00:00
|
|
|
#
|
2010-07-19 18:18:16 +00:00
|
|
|
# page.find('#foo').find('.bar')
|
2011-06-27 23:01:46 +00:00
|
|
|
# page.find(:xpath, '//div[contains(., "bar")]')
|
2010-07-19 18:18:16 +00:00
|
|
|
# page.find('li', :text => 'Quox').click_link('Delete')
|
2010-07-17 17:05:00 +00:00
|
|
|
#
|
|
|
|
# @param (see Capybara::Node::Finders#all)
|
2010-08-27 18:52:06 +00:00
|
|
|
# @option options [String] :message An error message in case the element can't be found
|
2010-07-17 17:05:00 +00:00
|
|
|
# @return [Capybara::Element] The found element
|
|
|
|
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
|
|
|
#
|
2010-07-19 18:18:16 +00:00
|
|
|
def find(*args)
|
2011-08-12 11:52:12 +00:00
|
|
|
wait_until { first(*args) or raise_find_error(*args) }
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
2010-07-19 18:40:28 +00:00
|
|
|
|
2010-07-17 17:05:00 +00:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Find a form field on the page. The field can be found by its name, id or label text.
|
|
|
|
#
|
|
|
|
# @param [String] locator Which field to find
|
|
|
|
# @return [Capybara::Element] The found element
|
|
|
|
#
|
2010-07-10 00:20:32 +00:00
|
|
|
def find_field(locator)
|
2011-08-27 02:16:15 +00:00
|
|
|
find(:field, locator)
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
|
|
|
alias_method :field_labeled, :find_field
|
|
|
|
|
2010-07-17 17:05:00 +00:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Find a link on the page. The link can be found by its id or text.
|
|
|
|
#
|
|
|
|
# @param [String] locator Which link to find
|
|
|
|
# @return [Capybara::Element] The found element
|
|
|
|
#
|
2010-07-10 00:20:32 +00:00
|
|
|
def find_link(locator)
|
2011-08-27 02:16:15 +00:00
|
|
|
find(:link, locator)
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
|
|
|
|
2010-07-17 17:05:00 +00:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Find a button on the page. The link can be found by its id, name or value.
|
|
|
|
#
|
|
|
|
# @param [String] locator Which button to find
|
|
|
|
# @return [Capybara::Element] The found element
|
|
|
|
#
|
2010-07-10 00:20:32 +00:00
|
|
|
def find_button(locator)
|
2011-08-27 02:16:15 +00:00
|
|
|
find(:button, locator)
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
|
|
|
|
2010-07-17 17:05:00 +00:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Find a element on the page, given its id.
|
|
|
|
#
|
|
|
|
# @param [String] locator Which element to find
|
|
|
|
# @return [Capybara::Element] The found element
|
|
|
|
#
|
2010-07-10 00:20:32 +00:00
|
|
|
def find_by_id(id)
|
2011-08-27 02:16:15 +00:00
|
|
|
find(:id, id)
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
|
|
|
|
2010-07-17 17:05:00 +00:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Find all elements on the page matching the given selector
|
|
|
|
# and options.
|
|
|
|
#
|
|
|
|
# Both XPath and CSS expressions are supported, but Capybara
|
|
|
|
# does not try to automatically distinguish between them. The
|
|
|
|
# following statements are equivalent:
|
|
|
|
#
|
|
|
|
# page.all(:css, 'a#person_123')
|
|
|
|
# page.all(:xpath, '//a[@id="person_123"]')
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# If the type of selector is left out, Capybara uses
|
2010-08-27 18:52:06 +00:00
|
|
|
# {Capybara.default_selector}. It's set to :css by default.
|
2010-07-17 17:05:00 +00:00
|
|
|
#
|
|
|
|
# page.all("a#person_123")
|
|
|
|
#
|
|
|
|
# Capybara.default_selector = :xpath
|
|
|
|
# page.all('//a[@id="person_123"]')
|
|
|
|
#
|
|
|
|
# The set of found elements can further be restricted by specifying
|
|
|
|
# options. It's possible to select elements by their text or visibility:
|
|
|
|
#
|
|
|
|
# page.all('a', :text => 'Home')
|
|
|
|
# page.all('#menu li', :visible => true)
|
|
|
|
#
|
2011-12-28 21:31:49 +00:00
|
|
|
# @overload all([kind], locator, options)
|
|
|
|
# @param [:css, :xpath] kind The type of selector
|
|
|
|
# @param [String] locator The selector
|
|
|
|
# @option options [String, Regexp] text Only find elements which contain this text or match this regexp
|
|
|
|
# @option options [Boolean] visible Only find elements that are visible on the page. Setting this to false
|
|
|
|
# (the default, unless Capybara.ignore_hidden_elements = true), finds
|
|
|
|
# invisible _and_ visible elements.
|
|
|
|
# @return [Array[Capybara::Element]] The found elements
|
2010-07-17 17:05:00 +00:00
|
|
|
#
|
2010-07-10 00:20:32 +00:00
|
|
|
def all(*args)
|
2011-09-06 03:05:28 +00:00
|
|
|
args, property_options = extract_property_options(args)
|
2010-07-10 00:20:32 +00:00
|
|
|
|
2011-07-13 13:39:17 +00:00
|
|
|
selector = Capybara::Selector.normalize(*args)
|
|
|
|
selector.xpaths.
|
|
|
|
map { |path| find_in_base(selector, path) }.flatten.
|
2011-09-06 03:05:28 +00:00
|
|
|
select { |node| filter_by_properties(node, property_options) }
|
2010-12-23 03:24:20 +00:00
|
|
|
end
|
2010-07-10 00:20:32 +00:00
|
|
|
|
2010-12-23 03:24:20 +00:00
|
|
|
##
|
|
|
|
#
|
|
|
|
# Find the first element on the page matching the given selector
|
|
|
|
# and options, or nil if no element matches.
|
|
|
|
#
|
|
|
|
# When only the first matching element is needed, this method can
|
|
|
|
# be faster than all(*args).first.
|
|
|
|
#
|
2011-12-28 21:31:49 +00:00
|
|
|
# @overload first([kind], locator, options)
|
|
|
|
# @param [:css, :xpath] kind The type of selector
|
|
|
|
# @param [String] locator The selector
|
|
|
|
# @param [Hash] options Additional options; see {all}
|
|
|
|
# @return [Capybara::Element] The found element or nil
|
2010-12-23 03:24:20 +00:00
|
|
|
#
|
|
|
|
def first(*args)
|
2011-09-06 03:05:28 +00:00
|
|
|
args, property_options = extract_property_options(args)
|
2011-02-08 15:05:57 +00:00
|
|
|
found_elements = []
|
2010-12-23 03:24:20 +00:00
|
|
|
|
2011-07-13 13:39:17 +00:00
|
|
|
selector = Capybara::Selector.normalize(*args)
|
|
|
|
selector.xpaths.each do |path|
|
|
|
|
find_in_base(selector, path).each do |node|
|
2011-09-06 03:05:28 +00:00
|
|
|
if filter_by_properties(node, property_options)
|
2011-07-13 13:39:17 +00:00
|
|
|
found_elements << node
|
2011-02-08 15:05:57 +00:00
|
|
|
return found_elements.last if not Capybara.prefer_visible_elements or node.visible?
|
2010-12-23 03:24:20 +00:00
|
|
|
end
|
|
|
|
end
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
2011-02-08 15:05:57 +00:00
|
|
|
found_elements.first
|
2010-12-23 03:24:20 +00:00
|
|
|
end
|
2010-12-23 18:38:44 +00:00
|
|
|
|
2010-12-23 03:24:20 +00:00
|
|
|
protected
|
2010-12-23 18:38:44 +00:00
|
|
|
|
2011-08-12 11:52:12 +00:00
|
|
|
def raise_find_error(*args)
|
2011-09-06 03:05:28 +00:00
|
|
|
args, property_options = extract_property_options(args)
|
|
|
|
normalized = Capybara::Selector.normalize(*args)
|
|
|
|
message = args.last[:message] || "Unable to find #{normalized.name} #{normalized.locator.inspect}"
|
|
|
|
message = normalized.failure_message.call(self, normalized) if normalized.failure_message
|
2011-08-27 21:54:55 +00:00
|
|
|
|
2011-08-12 11:52:12 +00:00
|
|
|
raise Capybara::ElementNotFound, message
|
|
|
|
end
|
|
|
|
|
2011-07-13 13:39:17 +00:00
|
|
|
def find_in_base(selector, xpath)
|
|
|
|
base.find(xpath).map do |node|
|
|
|
|
Capybara::Node::Element.new(session, node, self, selector)
|
|
|
|
end
|
2010-12-23 03:24:20 +00:00
|
|
|
end
|
|
|
|
|
2011-09-06 03:05:28 +00:00
|
|
|
def extract_property_options(args)
|
|
|
|
xpath_options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
|
|
|
property_options = [:text, :visible, :with, :checked, :unchecked, :selected].inject({}) do |opts, key|
|
|
|
|
opts[key] = xpath_options.delete(key) if xpath_options.has_key?(key)
|
|
|
|
opts
|
|
|
|
end
|
2010-12-23 03:24:20 +00:00
|
|
|
|
2011-09-06 03:05:28 +00:00
|
|
|
if text = property_options[:text]
|
|
|
|
property_options[:text] = Regexp.escape(text) unless text.kind_of?(Regexp)
|
2010-12-23 18:38:44 +00:00
|
|
|
end
|
|
|
|
|
2011-09-06 03:05:28 +00:00
|
|
|
if !property_options.has_key?(:visible)
|
|
|
|
property_options[:visible] = Capybara.ignore_hidden_elements
|
2010-12-22 15:10:42 +00:00
|
|
|
end
|
|
|
|
|
2011-09-06 03:05:28 +00:00
|
|
|
if selected = property_options[:selected]
|
|
|
|
property_options[:selected] = [selected].flatten
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
|
|
|
|
2011-09-06 03:05:28 +00:00
|
|
|
[args.push(xpath_options), property_options]
|
|
|
|
end
|
|
|
|
|
|
|
|
def filter_by_properties(node, property_options)
|
|
|
|
return false if property_options[:text] and not node.text.match(property_options[:text])
|
|
|
|
return false if property_options[:visible] and not node.visible?
|
|
|
|
return false if property_options[:with] and not node.value == property_options[:with]
|
|
|
|
return false if property_options[:checked] and not node.checked?
|
|
|
|
return false if property_options[:unchecked] and node.checked?
|
|
|
|
return false if property_options[:selected] and not has_selected_options?(node, property_options[:selected])
|
|
|
|
true
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
|
|
|
|
2011-09-06 03:05:28 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
def has_selected_options?(node, expected)
|
|
|
|
actual = node.all(:xpath, './/option').select { |option| option.selected? }.map { |option| option.text }
|
|
|
|
(expected - actual).empty?
|
|
|
|
end
|
2010-07-10 00:20:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|