Skip to content
Browse files

Selectors can specify default filter

  • Loading branch information...
1 parent 3e78a6e commit 9e69f4bad492abf6d9221acf7596cc8d0b4f29f1 @jnicklas committed Dec 30, 2012
View
9 lib/capybara/filter.rb
@@ -26,6 +26,15 @@ def match(&block)
@match
end
+ def compile(&block)
+ @compile = block if block
+ @compile
+ end
+
+ def run_compile(xpath, value)
+ @compile and XPath.instance_exec(xpath, value, &@compile)
+ end
+
def match?(node, value)
@match and @match.call(node, value)
end
View
8 lib/capybara/node/finders.rb
@@ -108,13 +108,7 @@ def find_by_id(id)
# @return [Array[Capybara::Element]] The found elements
#
def all(*args)
- query = Capybara::Query.new(*args)
- elements = synchronize do
- base.find(query.xpath).map do |node|
- Capybara::Node::Element.new(session, node, self, query)
- end
- end
- Capybara::Result.new(elements, query)
+ Capybara::Query.new(*args).resolve!(self)
end
##
View
38 lib/capybara/query.rb
@@ -1,6 +1,6 @@
module Capybara
class Query
- attr_accessor :selector, :locator, :options, :xpath, :find, :negative
+ attr_accessor :selector, :locator, :options, :find, :negative
VALID_KEYS = [:between, :count, :maximum, :minimum]
@@ -20,8 +20,6 @@ def initialize(*args)
end
@selector ||= Selector.all[Capybara.default_selector]
- @xpath = @selector.call(@locator).to_s
-
assert_valid_keys!
end
@@ -34,13 +32,41 @@ def description
@description
end
+ def compileable_filters
+ selector.filters.values.select(&:compile)
+ end
+
+ def other_filters
+ selector.filters.values - compileable_filters
+ end
+
+ def xpath
+ base = selector.default_filter.run_compile(selector.xpath, @locator)
+ compileable_filters.inject(base) do |xpath, filter|
+ if options.has_key?(filter.name)
+ filter.run_compile(xpath, options[filter.name])
+ else
+ xpath
+ end
+ end
+ end
+
+ def resolve!(parent)
+ elements = parent.synchronize do
+ parent.base.find(xpath.to_s).map do |node|
+ matches_filters? Capybara::Node::Element.new(parent.session, node, parent, self)
+ end
+ end
+ Capybara::Result.new(elements.compact, self)
+ end
+
def matches_filters?(node)
node.unsynchronized do
- selector.filters.each do |name, filter|
- return false if options.has_key?(name) and not filter.match?(node, options[name])
+ other_filters.each do |filter|
+ return nil if options.has_key?(filter.name) and not filter.match?(node, options[filter.name])
end
- true
end
+ node
end
def matches_count?(count)
View
7 lib/capybara/result.rb
@@ -5,10 +5,9 @@ class Result
include Enumerable
extend Forwardable
- def initialize(elements, query)
- @elements = elements
- @result = elements.select { |node| query.matches_filters?(node) }
- @rest = @elements - @result
+ def initialize(result, query)
+ @result = result
+ @rest = []
@query = query
end
View
105 lib/capybara/selector.rb
@@ -26,7 +26,7 @@ def initialize(name, &block)
end
def xpath(&block)
- @xpath = block if block
+ @xpath = XPath.instance_eval(&block) if block
@xpath
end
@@ -43,24 +43,16 @@ def select(&block)
@select
end
- def match(&block)
- @match = block if block
- @match
+ def default_filter(&block)
+ @default_filter = Filter.new(:default, &block) if block
+ @default_filter
end
def label(label=nil)
@label = label if label
@label
end
- def call(locator)
- @xpath.call(locator)
- end
-
- def match?(locator)
- @match and @match.call(locator)
- end
-
def select?(locator)
@select and @select.call(locator)
end
@@ -77,18 +69,51 @@ def filters
Capybara.add_selector(:xpath) do
xpath { |xpath| xpath }
+ default_filter do
+ compile do |_, value|
+ if value.is_a?(XPath::Expression)
+ value
+ else
+ XPath::Expression.new(:literal, XPath::Literal.new(value.to_s))
+ end
+ end
+ end
end
Capybara.add_selector(:css) do
- css { |css| css }
+ default_filter do
+ compile do |_, value|
+ css(value)
+ end
+ end
end
Capybara.add_selector(:id) do
- xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
+ default_filter do
+ compile do |_, value|
+ descendant[attr(:id) == value.to_s]
+ end
+ end
end
Capybara.add_selector(:field) do
- xpath { |locator| XPath::HTML.field(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.field(locator) }
+ end
+ filter(:label) do
+ match do |node, value|
+ node.document.first(xpath: "ancestors::label", text: value) or
+ node.document.first(for: node[:id], text: value)
+ end
+ compile do |xpath, value|
+ xpath[attr(:id).equals(anywhere(:label)[string.n.contains(value)].attr(:for))] +
+ descendant(:label)[string.n.contains(value)].descendant(xpath)
+ end
+ end
+ filter(:disabled) do
+ match { |node, value| value ^ node.disabled? }
+ compile { |xpath, value| if value then xpath[attr(:disabled)] else xpath[~attr(:disabled)] end }
+ end
filter(:checked) do
match { |node, value| not(value ^ node.checked?) }
end
@@ -104,16 +129,24 @@ def filters
end
Capybara.add_selector(:fieldset) do
- xpath { |locator| XPath::HTML.fieldset(locator) }
+ xpath { descendant(:fieldset) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.fieldset(locator) }
+ end
end
Capybara.add_selector(:link_or_button) do
label "link or button"
- xpath { |locator| XPath::HTML.link_or_button(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.link_or_button(locator) }
+ end
end
Capybara.add_selector(:link) do
- xpath { |locator| XPath::HTML.link(locator) }
+ xpath { descendant(:a)[attr(:href)] }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.link(locator) }
+ end
filter(:href) do
match do |node, href|
node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)])
@@ -122,17 +155,23 @@ def filters
end
Capybara.add_selector(:button) do
- xpath { |locator| XPath::HTML.button(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.button(locator) }
+ end
end
Capybara.add_selector(:fillable_field) do
label "field"
- xpath { |locator| XPath::HTML.fillable_field(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.fillable_field(locator) }
+ end
end
Capybara.add_selector(:radio_button) do
label "radio button"
- xpath { |locator| XPath::HTML.radio_button(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.radio_button(locator) }
+ end
filter(:checked) do
match { |node, value| not(value ^ node.checked?) }
end
@@ -142,7 +181,9 @@ def filters
end
Capybara.add_selector(:checkbox) do
- xpath { |locator| XPath::HTML.checkbox(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.checkbox(locator) }
+ end
filter(:checked) do
match { |node, value| not(value ^ node.checked?) }
end
@@ -153,7 +194,9 @@ def filters
Capybara.add_selector(:select) do
label "select box"
- xpath { |locator| XPath::HTML.select(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.select(locator) }
+ end
filter(:options) do
match do |node, options|
actual = node.all(:xpath, './/option').map { |option| option.text }
@@ -172,18 +215,20 @@ def filters
end
Capybara.add_selector(:option) do
- xpath { |locator| XPath::HTML.option(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.option(locator) }
+ end
end
Capybara.add_selector(:file_field) do
label "file field"
- xpath { |locator| XPath::HTML.file_field(locator) }
-end
-
-Capybara.add_selector(:content) do
- xpath { |content| XPath::HTML.content(content) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.file_field(locator) }
+ end
end
Capybara.add_selector(:table) do
- xpath { |locator| XPath::HTML.table(locator) }
+ default_filter do
+ compile { |xpath, locator| XPath::HTML.table(locator) }
+ end
end
View
15 lib/capybara/spec/session/find_spec.rb
@@ -53,7 +53,10 @@
context "with custom selector" do
it "should use the custom selector" do
Capybara.add_selector(:monkey) do
- xpath { |name| ".//*[@id='#{name}_monkey']" }
+ xpath { descendant }
+ default_filter do
+ compile { |xpath, value| xpath[attr(:id) == "#{value}_monkey"] }
+ end
end
@session.find(:monkey, 'john').text.should == 'Monkey John'
@session.find(:monkey, 'paul').text.should == 'Monkey Paul'
@@ -63,7 +66,10 @@
context "with custom selector with select option" do
it "should use the selector when it matches the select option" do
Capybara.add_selector(:monkey) do
- xpath { |num| ".//*[contains(@id, 'monkey')][#{num}]" }
+ xpath { descendant[attr(:id).contains('monkey')] }
+ default_filter do
+ compile { |xpath, value| xpath[value.to_i] }
+ end
select { |value| value.is_a?(Fixnum) }
end
@session.find(:monkey, '2').text.should == 'Monkey Paul'
@@ -76,7 +82,10 @@
context "with custom selector with custom filter" do
before do
Capybara.add_selector(:monkey) do
- xpath { |num| ".//*[contains(@id, 'monkey')][#{num}]" }
+ xpath { descendant[attr(:id).contains('monkey')] }
+ default_filter do
+ compile { |xpath, value| xpath[value.to_i] }
+ end
filter(:name) do
match { |node, name| node.text == name }
end

0 comments on commit 9e69f4b

Please sign in to comment.
Something went wrong with that request. Please try again.