Add #first, and use it in preference to all(*args).first.

This provides a ~10% speed improvement on selenium specs.
This commit is contained in:
John Firebaugh 2010-12-22 19:24:20 -08:00
parent c7de62d88a
commit 74aa0ca844
2 changed files with 65 additions and 45 deletions

View File

@ -25,7 +25,7 @@ module Capybara
#
def find(*args)
begin
node = wait_conditionally_until { all(*args).first }
node = wait_conditionally_until { first(*args) }
rescue TimeoutError
end
unless node
@ -115,46 +115,39 @@ module Capybara
# @return [Capybara::Element] The found elements
#
def all(*args)
options = if args.last.is_a?(Hash) then args.pop else {} end
options = extract_normalized_options(args)
results = Capybara::Selector.normalize(*args).map do |path|
find_in_base(path)
end.flatten
Capybara::Selector.normalize(*args).
map { |path| find_in_base(path) }.flatten.
select { |node| matches_options(node, options) }.
map { |node| convert_element(node) }
end
if text = options[:text]
text = Regexp.escape(text) unless text.kind_of?(Regexp)
##
#
# 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.
#
# @param [:css, :xpath, String] kind_or_locator Either the kind of selector or the selector itself
# @param [String] locator The selector
# @param [Hash{Symbol => Object}] options Additional options; see {all}
# @return Capybara::Element The found element
#
def first(*args)
options = extract_normalized_options(args)
results = results.select { |node| node.text.match(text) }
Capybara::Selector.normalize(*args).each do |path|
find_in_base(path).each do |node|
if matches_options(node, options)
return convert_element(node)
end
end
end
if value = options[:with]
results = results.select { |node| node.value == value }
end
if options[:checked]
results = results.select { |node| node.checked? }
end
if options[:unchecked]
results = results.reject { |node| node.checked? }
end
if selected = options[:selected]
selected = [selected].flatten
results = results.select { |node| has_selected_options?(node, selected) }
end
ignore_hidden = if options.has_key?(:visible)
options[:visible]
else
Capybara.ignore_hidden_elements
end
if ignore_hidden
results = results.select { |node| node.visible? }
end
convert_elements(results)
nil
end
protected
@ -163,19 +156,46 @@ module Capybara
base.find(xpath)
end
def has_selected_options?(node, expected)
actual = node.find('.//option').select { |option| option.selected? }.map { |option| option.text }
(expected - actual).empty?
end
def convert_elements(elements)
elements.map { |element| Capybara::Node::Element.new(session, element) }
def convert_element(element)
Capybara::Node::Element.new(session, element)
end
def wait_conditionally_until
if wait? then session.wait_until { yield } else yield end
end
def extract_normalized_options(args)
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
if text = options[:text]
options[:text] = Regexp.escape(text) unless text.kind_of?(Regexp)
end
if !options.has_key?(:visible)
options[:visible] = Capybara.ignore_hidden_elements
end
if selected = options[:selected]
options[:selected] = [selected].flatten
end
options
end
def matches_options(node, options)
return false if options[:text] and not node.text.match(options[:text])
return false if options[:visible] and not node.visible?
return false if options[:with] and not node.value == options[:with]
return false if options[:checked] and not node.checked?
return false if options[:unchecked] and node.checked?
return false if options[:selected] and not has_selected_options?(node, options[:selected])
true
end
def has_selected_options?(node, expected)
actual = node.find('.//option').select { |option| option.selected? }.map { |option| option.text }
(expected - actual).empty?
end
end
end
end

View File

@ -104,8 +104,8 @@ module Capybara
native.xpath(xpath).map { |node| self.class.new(node) }
end
def convert_elements(elements)
elements
def convert_element(element)
element
end
def wait?