diff --git a/lib/capybara.rb b/lib/capybara.rb index 3cd87c8f..0109664a 100644 --- a/lib/capybara.rb +++ b/lib/capybara.rb @@ -34,7 +34,7 @@ module Capybara autoload :Server, 'capybara/server' autoload :Session, 'capybara/session' autoload :XPath, 'capybara/xpath' - autoload :Searchable, 'capybara/searchable' + autoload :Node, 'capybara/node' autoload :VERSION, 'capybara/version' module Driver diff --git a/lib/capybara/driver/node.rb b/lib/capybara/driver/node.rb index cbe2d6fe..3b7ffa69 100644 --- a/lib/capybara/driver/node.rb +++ b/lib/capybara/driver/node.rb @@ -1,8 +1,6 @@ module Capybara module Driver class Node - include Searchable - attr_reader :driver, :node def initialize(driver, node) diff --git a/lib/capybara/node.rb b/lib/capybara/node.rb new file mode 100644 index 00000000..eef8e752 --- /dev/null +++ b/lib/capybara/node.rb @@ -0,0 +1,26 @@ +module Capybara + class Node < Session + def initialize(session, driver_node) + @session = session + @driver_node = driver_node + end + + def method_missing(*args) + @driver_node.send(*args) + end + + def respond_to?(method) + super || @driver_node.respond_to?(method) + end + + def driver + @session.driver + end + + def all_unfiltered(locator) + XPath.wrap(locator).paths.map do |path| + @driver_node.send(:all_unfiltered, path) + end.flatten + end + end +end diff --git a/lib/capybara/searchable.rb b/lib/capybara/searchable.rb deleted file mode 100644 index ace9b708..00000000 --- a/lib/capybara/searchable.rb +++ /dev/null @@ -1,60 +0,0 @@ -module Capybara - module Searchable - def find(*args) - all(*args).first - end - - def find_field(locator) - find(:xpath, XPath.field(locator)) - end - alias_method :field_labeled, :find_field - - def find_link(locator) - find(:xpath, XPath.link(locator)) - end - - def find_button(locator) - find(:xpath, XPath.button(locator)) - end - - def find_by_id(id) - find(:css, "##{id}") - end - - def all(*args) - options = if args.last.is_a?(Hash) then args.pop else {} end - if args[1].nil? - kind, locator = Capybara.default_selector, args.first - else - kind, locator = args - end - locator = XPath.from_css(locator) if kind == :css - - results = all_unfiltered(locator) - - if options[:text] - - if options[:text].kind_of?(Regexp) - regexp = options[:text] - else - regexp = Regexp.escape(options[:text]) - end - - results = results.select { |n| n.text.match(regexp) } - end - - if options[:visible] or Capybara.ignore_hidden_elements - results = results.select { |n| n.visible? } - end - - results - end - - private - - def all_unfiltered(locator) - raise "Must be overridden" - end - - end -end diff --git a/lib/capybara/session.rb b/lib/capybara/session.rb index d8a72d21..61d34422 100644 --- a/lib/capybara/session.rb +++ b/lib/capybara/session.rb @@ -4,7 +4,6 @@ require 'capybara/timeout' module Capybara class Session extend Forwardable - include Searchable DSL_METHODS = [ :all, :attach_file, :body, :check, :choose, :click, :click_button, :click_link, :current_url, :drag, :evaluate_script, @@ -128,14 +127,6 @@ module Capybara end end - def scope_to(*locator) - scoped_session = self.clone - scoped_session.instance_eval do - @scopes = scopes + locator - end - scoped_session - end - def has_xpath?(path, options={}) wait_conditionally_until do results = all(:xpath, path, options) @@ -253,8 +244,63 @@ module Capybara driver.evaluate_script(script) end + def find(*args) + all(*args).first + end + + def find_field(locator) + find(:xpath, XPath.field(locator)) + end + alias_method :field_labeled, :find_field + + def find_link(locator) + find(:xpath, XPath.link(locator)) + end + + def find_button(locator) + find(:xpath, XPath.button(locator)) + end + + def find_by_id(id) + find(:css, "##{id}") + end + + def all(*args) + options = if args.last.is_a?(Hash) then args.pop else {} end + if args[1].nil? + kind, locator = Capybara.default_selector, args.first + else + kind, locator = args + end + locator = XPath.from_css(locator) if kind == :css + + results = all_unfiltered(locator) + + if options[:text] + + if options[:text].kind_of?(Regexp) + regexp = options[:text] + else + regexp = Regexp.escape(options[:text]) + end + + results = results.select { |n| n.text.match(regexp) } + end + + if options[:visible] or Capybara.ignore_hidden_elements + results = results.select { |n| n.visible? } + end + + results.map { |n| Capybara::Node.new(self, n) } + end + private + # temporary hack until inheritance chain is fixed + def session + self + end + def wait_conditionally_until if driver.wait? then wait_until { yield } else yield end end @@ -266,7 +312,7 @@ module Capybara end def current_scope - scopes.join('') + scopes.join('/') end def scopes diff --git a/lib/capybara/spec/session.rb b/lib/capybara/spec/session.rb index 93b30249..b3d1348b 100644 --- a/lib/capybara/spec/session.rb +++ b/lib/capybara/spec/session.rb @@ -39,51 +39,51 @@ shared_examples_for "session" do end end - describe '#scope_to' do - let(:scope) { @session.scope_to("//p[@id='first']") } - let(:more_scope) { scope.scope_to("//a[@id='foo']") } + #describe '#scope_to' do + # let(:scope) { @session.scope_to("//p[@id='first']") } + # let(:more_scope) { scope.scope_to(".//a[@id='foo']") } - before do - @session.visit('/with_html') - end + # before do + # @session.visit('/with_html') + # end - it 'has a simple link' do - scope.should have_xpath("//a[@class='simple']") - end + # it 'has a simple link' do + # scope.should have_xpath(".//a[@class='simple']") + # end - it 'does not have a redirect link' do - scope.should have_no_xpath("//a[@id='red']") - end + # it 'does not have a redirect link' do + # scope.should have_no_xpath(".//a[@id='red']") + # end - it 'does have a redirect link' do - @session.should have_xpath("//a[@id='red']") - end + # it 'does have a redirect link' do + # @session.should have_xpath(".//a[@id='red']") + # end - it 'does not share scopes' do - @session.should have_xpath("//a[@id='red']") - scope.should have_no_xpath("//a[@id='red']") - @session.should have_xpath("//a[@id='red']") - end + # it 'does not share scopes' do + # @session.should have_xpath(".//a[@id='red']") + # scope.should have_no_xpath(".//a[@id='red']") + # @session.should have_xpath(".//a[@id='red']") + # end - context 'more_scope' do - it 'has the text for foo' do - more_scope.should have_content('ullamco') - end + # context 'more_scope' do + # it 'has the text for foo' do + # more_scope.should have_content('ullamco') + # end - it 'does not have a simple link' do - more_scope.should have_no_xpath("//a[@class='simple']") - end + # it 'does not have a simple link' do + # more_scope.should have_no_xpath(".//a[@class='simple']") + # end - it 'has not overridden scope' do - scope.should have_xpath("//a[@class='simple']") - end + # it 'has not overridden scope' do + # scope.should have_xpath(".//a[@class='simple']") + # end - it 'has not overridden session' do - @session.should have_xpath("//p[@id='second']") - end - end + # it 'has not overridden session' do + # @session.should have_xpath(".//p[@id='second']") + # end + # end - end + #end it_should_behave_like "all" it_should_behave_like "attach_file" diff --git a/lib/capybara/spec/session/all_spec.rb b/lib/capybara/spec/session/all_spec.rb index 6699f0c4..a6b3a484 100644 --- a/lib/capybara/spec/session/all_spec.rb +++ b/lib/capybara/spec/session/all_spec.rb @@ -65,7 +65,7 @@ shared_examples_for "all" do it "should find any element using the given locator" do @session.within(:xpath, "//div[@id='for_bar']") do - @session.all('//li').should have(2).elements + @session.all('.//li').should have(2).elements end end end diff --git a/lib/capybara/spec/session/find_spec.rb b/lib/capybara/spec/session/find_spec.rb index b714b1a0..1cbf47af 100644 --- a/lib/capybara/spec/session/find_spec.rb +++ b/lib/capybara/spec/session/find_spec.rb @@ -9,6 +9,16 @@ shared_examples_for "find" do @session.find("//input[@id='test_field']")[:value].should == 'monkey' end + it "should act return a session like object" do + @session.visit('/form') + @form = @session.find(:css, '#get-form') + @form.should have_field('Middle Name') + @form.should have_no_field('Languages') + @form.fill_in('Middle Name', :with => 'Monkey') + @form.click_button('med') + extract_results(@session)['middle_name'].should == 'Monkey' + end + context "with css selectors" do it "should find the first element using the given locator" do @session.find(:css, 'h1').text.should == 'This is a test' @@ -49,7 +59,7 @@ shared_examples_for "find" do 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/ + @session.find('.//li').text.should =~ /With Simple HTML/ end end end diff --git a/lib/capybara/spec/session/has_xpath_spec.rb b/lib/capybara/spec/session/has_xpath_spec.rb index 86ff494e..532d6ff8 100644 --- a/lib/capybara/spec/session/has_xpath_spec.rb +++ b/lib/capybara/spec/session/has_xpath_spec.rb @@ -23,8 +23,8 @@ shared_examples_for "has_xpath" do it "should respect scopes" do @session.within "//p[@id='first']" do - @session.should have_xpath("//a[@id='foo']") - @session.should_not have_xpath("//a[@id='red']") + @session.should have_xpath(".//a[@id='foo']") + @session.should_not have_xpath(".//a[@id='red']") end end @@ -84,8 +84,8 @@ shared_examples_for "has_xpath" do it "should respect scopes" do @session.within "//p[@id='first']" do - @session.should_not have_no_xpath("//a[@id='foo']") - @session.should have_no_xpath("//a[@id='red']") + @session.should_not have_no_xpath(".//a[@id='foo']") + @session.should have_no_xpath(".//a[@id='red']") end end diff --git a/lib/capybara/spec/session/locate_spec.rb b/lib/capybara/spec/session/locate_spec.rb index f32bae6c..c5608732 100644 --- a/lib/capybara/spec/session/locate_spec.rb +++ b/lib/capybara/spec/session/locate_spec.rb @@ -57,7 +57,7 @@ shared_examples_for "locate" do it "should find the first element using the given locator" do @session.within(:xpath, "//div[@id='for_bar']") do - @session.locate('//li').text.should =~ /With Simple HTML/ + @session.locate('.//li').text.should =~ /With Simple HTML/ end end end diff --git a/lib/capybara/spec/session/within_spec.rb b/lib/capybara/spec/session/within_spec.rb index 69eb68fb..d05762fb 100644 --- a/lib/capybara/spec/session/within_spec.rb +++ b/lib/capybara/spec/session/within_spec.rb @@ -53,7 +53,7 @@ shared_examples_for "within" do context "with nested scopes" do it "should respect the inner scope" do @session.within("//div[@id='for_bar']") do - @session.within("//li[contains(.,'Bar')]") do + @session.within(".//li[contains(.,'Bar')]") do @session.click_link('Go') end end @@ -62,7 +62,7 @@ shared_examples_for "within" do it "should respect the outer scope" do @session.within("//div[@id='another_foo']") do - @session.within("//li[contains(.,'With Simple HTML')]") do + @session.within(".//li[contains(.,'With Simple HTML')]") do @session.click_link('Go') end end @@ -82,12 +82,12 @@ shared_examples_for "within" do @session.within("//div[@id='for_bar']") do running { running { - @session.within("//div[@id='doesnotexist']") do + @session.within(".//div[@id='doesnotexist']") do end }.should raise_error(Capybara::ElementNotFound) - }.should_not change { @session.has_xpath?("//div[@id='another_foo']") }.from(false) + }.should_not change { @session.has_xpath?(".//div[@id='another_foo']") }.from(false) end - }.should_not change { @session.has_xpath?("//div[@id='another_foo']") }.from(true) + }.should_not change { @session.has_xpath?(".//div[@id='another_foo']") }.from(true) end end diff --git a/lib/capybara/spec/views/form.erb b/lib/capybara/spec/views/form.erb index f91e4a75..3a1e5f9f 100644 --- a/lib/capybara/spec/views/form.erb +++ b/lib/capybara/spec/views/form.erb @@ -175,7 +175,7 @@

-
+

diff --git a/lib/capybara/xpath.rb b/lib/capybara/xpath.rb index d09bbeb5..aeb6bfe7 100644 --- a/lib/capybara/xpath.rb +++ b/lib/capybara/xpath.rb @@ -40,7 +40,7 @@ module Capybara end def scope(scope) - XPath.new(*paths.map { |p| scope + p }) + XPath.new(*paths.map { |p| scope + '/' + p }) end def to_s @@ -56,7 +56,7 @@ module Capybara end def from_css(css) - XPath.new(*[@paths, Nokogiri::CSS.xpath_for(css)].flatten) + XPath.new(*[@paths, Nokogiri::CSS.xpath_for(css).map { |selector| '.' + selector }].flatten) end alias_method :for_css, :from_css @@ -77,7 +77,7 @@ module Capybara end def content(locator) - append("/descendant-or-self::*[contains(normalize-space(.),#{s(locator)})]") + append("./descendant-or-self::*[contains(normalize-space(.),#{s(locator)})]") end def table(locator, options={}) @@ -89,38 +89,38 @@ module Capybara end.join(sibling) conditions << "[.//#{row_conditions}]" end - append("//table[@id=#{s(locator)} or contains(caption,#{s(locator)})]#{conditions}") + append(".//table[@id=#{s(locator)} or contains(caption,#{s(locator)})]#{conditions}") end def fieldset(locator) - append("//fieldset[@id=#{s(locator)} or contains(legend,#{s(locator)})]") + append(".//fieldset[@id=#{s(locator)} or contains(legend,#{s(locator)})]") end def link(locator) - xpath = append("//a[@href][@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)}) or img[contains(@alt,#{s(locator)})]]") - xpath.prepend("//a[@href][text()=#{s(locator)} or @title=#{s(locator)} or img[@alt=#{s(locator)}]]") + xpath = append(".//a[@href][@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)}) or img[contains(@alt,#{s(locator)})]]") + xpath.prepend(".//a[@href][text()=#{s(locator)} or @title=#{s(locator)} or img[@alt=#{s(locator)}]]") end def button(locator) - xpath = append("//input[@type='submit' or @type='image' or @type='button'][@id=#{s(locator)} or contains(@value,#{s(locator)})]") - xpath = xpath.append("//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]") - xpath = xpath.prepend("//input[@type='submit' or @type='image' or @type='button'][@value=#{s(locator)}]") - xpath = xpath.prepend("//input[@type='image'][@alt=#{s(locator)} or contains(@alt,#{s(locator)})]") - xpath = xpath.prepend("//button[@value=#{s(locator)} or text()=#{s(locator)}]") + xpath = append(".//input[@type='submit' or @type='image' or @type='button'][@id=#{s(locator)} or contains(@value,#{s(locator)})]") + xpath = xpath.append(".//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]") + xpath = xpath.prepend(".//input[@type='submit' or @type='image' or @type='button'][@value=#{s(locator)}]") + xpath = xpath.prepend(".//input[@type='image'][@alt=#{s(locator)} or contains(@alt,#{s(locator)})]") + xpath = xpath.prepend(".//button[@value=#{s(locator)} or text()=#{s(locator)}]") end def text_field(locator, options={}) options = options.merge(:value => options[:with]) if options.has_key?(:with) - add_field(locator, "//input[not(@type) or (@type!='radio' and @type!='checkbox' and @type!='hidden')]", options) + add_field(locator, ".//input[not(@type) or (@type!='radio' and @type!='checkbox' and @type!='hidden')]", options) end def text_area(locator, options={}) options = options.merge(:text => options[:with]) if options.has_key?(:with) - add_field(locator, "//textarea", options) + add_field(locator, ".//textarea", options) end def select(locator, options={}) - add_field(locator, "//select", options) + add_field(locator, ".//select", options) end def checkbox(locator, options={}) @@ -139,7 +139,7 @@ module Capybara def input_field(type, locator, options={}) options = options.merge(:value => options[:with]) if options.has_key?(:with) - add_field(locator, "//input[@type='#{type}']", options) + add_field(locator, ".//input[@type='#{type}']", options) end # place this between to nodes to indicate that they should be siblings @@ -152,7 +152,8 @@ module Capybara xpath = append("#{field}[@id=#{s(locator)}]#{postfix}") xpath = xpath.append("#{field}[@name=#{s(locator)}]#{postfix}") xpath = xpath.append("#{field}[@id=//label[contains(.,#{s(locator)})]/@for]#{postfix}") - xpath = xpath.append("//label[contains(.,#{s(locator)})]#{field}#{postfix}") + # FIXME: Label should not be scoped to node, temporary workaround!!! + xpath = xpath.append(".//label[contains(.,#{s(locator)})]/#{field}#{postfix}") xpath.prepend("#{field}[@id=//label[text()=#{s(locator)}]/@for]#{postfix}") end