diff --git a/README.rdoc b/README.rdoc index c89b79681..25b1a42d5 100644 --- a/README.rdoc +++ b/README.rdoc @@ -382,19 +382,28 @@ Alternatively you can set the default selector to XPath: Capybara allows you to add custom selectors, which can be very useful if you find yourself using the same kinds of selectors very often: - Capybara::Selector.add(:id) { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } - Capybara::Selector.add(:row) { |num| ".//tbody/tr[#{num}]" } + Capybara.add_selector(:id) do + xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } + end + + Capybara.add_selector(:row) do + xpath { |num| ".//tbody/tr[#{num}]" } + end -These must always return an XPath expression as a String, or an XPath expression -generated through the XPath gem. You can now use these selectors like this: +The block given to xpath must always return an XPath expression as a String, or +an XPath expression generated through the XPath gem. You can now use these +selectors like this: find(:id, 'post_123') find(:row, 3) -You can specify an optional :for option which will automatically use the -selector if it matches the argument to find using ===: +You can specify an optional match option which will automatically use the +selector if it matches the argument: - Capybara::Selector.add(:id, :for => Symbol) { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } + Capybara.add_selector(:id) do + xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } + match { |value| value.is_a?(Symbol) } + end Now use it like this: diff --git a/lib/capybara.rb b/lib/capybara.rb index 54e1561ed..0fef58acb 100644 --- a/lib/capybara.rb +++ b/lib/capybara.rb @@ -64,6 +64,10 @@ def register_driver(name, &block) drivers[name] = block end + def add_selector(name, &block) + Capybara::Selector.add(name, &block) + end + def drivers @drivers ||= {} end diff --git a/lib/capybara/selector.rb b/lib/capybara/selector.rb index 2d8a98c40..a3eb4306b 100644 --- a/lib/capybara/selector.rb +++ b/lib/capybara/selector.rb @@ -1,14 +1,14 @@ module Capybara class Selector - attr_reader :name, :options, :block + attr_reader :name class << self def all @selectors ||= {} end - def add(name, options={}, &block) - all[name.to_sym] = Capybara::Selector.new(name.to_sym, options, &block) + def add(name, &block) + all[name.to_sym] = Capybara::Selector.new(name.to_sym, &block) end def remove(name) @@ -31,22 +31,40 @@ def normalize(name_or_locator, locator=nil) end end - def initialize(name, options={}, &block) + def initialize(name, &block) @name = name - @options = options - @block = block + instance_eval(&block) + end + + def xpath(&block) + @xpath = block if block + @xpath + end + + def match(&block) + @match = block if block + @match end def call(locator) - @block.call(locator) + @xpath.call(locator) end def match?(locator) - @options[:for] and @options[:for] === locator + @match and @match.call(locator) end end end -Capybara::Selector.add(:xpath) { |xpath| xpath } -Capybara::Selector.add(:css) { |css| XPath.css(css) } -Capybara::Selector.add(:id, :for => Symbol) { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } +Capybara.add_selector(:xpath) do + xpath { |xpath| xpath } +end + +Capybara.add_selector(:css) do + xpath { |css| XPath.css(css) } +end + +Capybara.add_selector(:id) do + xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } + match { |value| value.is_a?(Symbol) } +end diff --git a/lib/capybara/spec/session/find_spec.rb b/lib/capybara/spec/session/find_spec.rb index 033f58a7a..d5b62fdb1 100644 --- a/lib/capybara/spec/session/find_spec.rb +++ b/lib/capybara/spec/session/find_spec.rb @@ -64,7 +64,9 @@ context "with custom selector" do it "should use the custom selector" do - Capybara::Selector.add(:monkey) { |name| ".//*[@id='#{name}_monkey']" } + Capybara.add_selector(:monkey) do + xpath { |name| ".//*[@id='#{name}_monkey']" } + end @session.find(:monkey, 'john').text.should == 'Monkey John' @session.find(:monkey, 'paul').text.should == 'Monkey Paul' end @@ -72,7 +74,10 @@ context "with custom selector with :for option" do it "should use the selector when it matches the :for option" do - Capybara::Selector.add(:monkey, :for => Fixnum) { |num| ".//*[contains(@id, 'monkey')][#{num}]" } + Capybara.add_selector(:monkey) do + xpath { |num| ".//*[contains(@id, 'monkey')][#{num}]" } + match { |value| value.is_a?(Fixnum) } + end @session.find(:monkey, '2').text.should == 'Monkey Paul' @session.find(1).text.should == 'Monkey John' @session.find(2).text.should == 'Monkey Paul' @@ -115,7 +120,7 @@ it "should find the first element using the given locator" do @session.within(:xpath, "//div[@id='for_bar']") do @session.find('.//li').text.should =~ /With Simple HTML/ - end + end end end end