diff --git a/README.rdoc b/README.rdoc index dacfcd77..107db0f9 100644 --- a/README.rdoc +++ b/README.rdoc @@ -337,6 +337,7 @@ The following people have dedicated their time and effort to Capybara: * Aaron Patterson * Dan Dofter * Thorbjørn Hermansen +* Louis T. == License: diff --git a/lib/capybara.rb b/lib/capybara.rb index 41c10af8..92d435b7 100644 --- a/lib/capybara.rb +++ b/lib/capybara.rb @@ -10,7 +10,8 @@ module Capybara class NotSupportedByDriverError < CapybaraError; end class TimeoutError < CapybaraError; end class LocateHiddenElementError < CapybaraError; end - + class InfiniteRedirectError < TimeoutError; end + class << self attr_accessor :debug, :asset_root, :app_host attr_writer :default_selector, :default_wait_time diff --git a/lib/capybara/driver/rack_test_driver.rb b/lib/capybara/driver/rack_test_driver.rb index ab514af6..ccb7e95f 100644 --- a/lib/capybara/driver/rack_test_driver.rb +++ b/lib/capybara/driver/rack_test_driver.rb @@ -144,7 +144,7 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base def visit(path, attributes = {}) get(path, attributes) - follow_redirect! while response.redirect? + follow_redirects! cache_body end @@ -158,7 +158,7 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base def submit(path, attributes) post(path, attributes) - follow_redirect! while response.redirect? + follow_redirects! cache_body end @@ -167,6 +167,16 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base end private + + def follow_redirects! + Capybara::WaitUntil.timeout(4) do + redirect = response.redirect? + follow_redirect! if redirect + not redirect + end + rescue Capybara::TimeoutError + raise Capybara::InfiniteRedirectError, "infinite redirect detected!" + end def cache_body @body = response.body diff --git a/lib/capybara/xpath.rb b/lib/capybara/xpath.rb index 40576a98..ce80d0e7 100644 --- a/lib/capybara/xpath.rb +++ b/lib/capybara/xpath.rb @@ -34,11 +34,13 @@ module Capybara end def field(locator) - fillable_field(locator).file_field(locator).checkbox(locator).radio_button(locator).select(locator) + fillable_field(locator).input_field(:file, locator).checkbox(locator).radio_button(locator).select(locator) end def fillable_field(locator) - text_field(locator).password_field(locator).text_area(locator) + [:text, :password, :email, :url, :search, :tel, :color].inject(text_area(locator)) do |all, type| + all.input_field(type, locator) + end end def content(locator) @@ -54,40 +56,27 @@ module Capybara end def link(locator) - append("//a[@id=#{s(locator)} or contains(.,#{s(locator)}) or @title=#{s(locator)}]") + xpath = append("//a[@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)})]") + xpath.prepend("//a[text()=#{s(locator)} or @title=#{s(locator)}]") end def button(locator) xpath = append("//input[@type='submit' or @type='image'][@id=#{s(locator)} or contains(@value,#{s(locator)})]") - xpath.append("//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]") - end - - def text_field(locator) - add_field(locator, "//input[@type='text']") - end - - def password_field(locator) - add_field(locator, "//input[@type='password']") + 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'][@value=#{s(locator)}]") + xpath = xpath.prepend("//button[@value=#{s(locator)} or text()=#{s(locator)}]") end def text_area(locator) add_field(locator, "//textarea") end - def radio_button(locator) - add_field(locator, "//input[@type='radio']") - end - - def checkbox(locator) - add_field(locator, "//input[@type='checkbox']") - end - def select(locator) add_field(locator, "//select") end - def file_field(locator) - add_field(locator, "//input[@type='file']") + def input_field(type, locator) + add_field(locator, "//input[@type='#{type}']") end def scope(scope) @@ -106,6 +95,22 @@ module Capybara XPath.new(*[XPath.wrap(path).paths, @paths].flatten) end + def checkbox(locator) + input_field(:checkbox, locator) + end + + def radio_button(locator) + input_field(:radio, locator) + end + + [:text, :password, :email, :url, :search, :tel, :color, :file].each do |type| + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{type}_field(locator) + input_field(:#{type}, locator) + end + RUBY + end + protected def add_field(locator, field) diff --git a/spec/dsl/click_button_spec.rb b/spec/dsl/click_button_spec.rb index 67143c35..e287879d 100644 --- a/spec/dsl/click_button_spec.rb +++ b/spec/dsl/click_button_spec.rb @@ -115,6 +115,11 @@ module ClickButtonSpec @session.click_button('Click') extract_results(@session)['first_name'].should == 'John' end + + it "should prefer exact matches over partial matches" do + @session.click_button('Just an input') + extract_results(@session)['button'].should == 'button_second' + end end context "with id given on a button defined by + + + +

+ \ No newline at end of file diff --git a/spec/views/with_html.erb b/spec/views/with_html.erb index 4ae95042..d57be83a 100644 --- a/spec/views/with_html.erb +++ b/spec/views/with_html.erb @@ -16,6 +16,8 @@

+ A link came first + A link