From 8d341948fd00f1f2388b5856535e53e09cdfddb8 Mon Sep 17 00:00:00 2001 From: Jonas Nicklas Date: Sat, 14 Aug 2010 13:42:53 +0200 Subject: [PATCH] select_option/unselect_option are now called on option node This means that we can move the find object into Capybara::Node instead of Capybara::Driver::Node which gives us AJAX waiting and preference for exact matches for free. --- lib/capybara/driver/celerity_driver.rb | 33 ++++++++++---------- lib/capybara/driver/node.rb | 4 +-- lib/capybara/driver/rack_test_driver.rb | 35 ++++++++-------------- lib/capybara/driver/selenium_driver.rb | 28 +++++++---------- lib/capybara/node.rb | 16 ++++------ lib/capybara/node/actions.rb | 12 +++++--- lib/capybara/spec/session/select_spec.rb | 2 +- lib/capybara/spec/session/unselect_spec.rb | 2 +- lib/capybara/xpath.rb | 4 +++ 9 files changed, 62 insertions(+), 74 deletions(-) diff --git a/lib/capybara/driver/celerity_driver.rb b/lib/capybara/driver/celerity_driver.rb index a0c38c8a..63078031 100644 --- a/lib/capybara/driver/celerity_driver.rb +++ b/lib/capybara/driver/celerity_driver.rb @@ -25,28 +25,19 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base native.set(value) end - def select_option(option) - native.select(option) - rescue - options = find("//option").map { |o| "'#{o.text}'" }.join(', ') - raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" + def select_option + native.click end - def unselect_option(option) - unless native.multiple? - raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box." + def unselect_option + unless select_node.native.multiple? + raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." end # FIXME: couldn't find a clean way to unselect, so clear and reselect - selected_options = native.selected_options - if unselect_option = selected_options.detect { |value| value == option } || - selected_options.detect { |value| value.index(option) } - native.clear - (selected_options - [unselect_option]).each { |value| native.select_value(value) } - else - options = find("//option").map { |o| "'#{o.text}'" }.join(', ') - raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" - end + selected_nodes = select_node.find('.//option[@selected]') + select_node.native.clear + selected_nodes.each { |n| n.click unless n.path == path } end def click @@ -83,6 +74,14 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base if all_nodes.empty? then [] else driver.find(all_nodes) end end + protected + + # a reference to the select node if this is an option node + def select_node + find('./ancestor::select').first + end + + end attr_reader :app, :rack_server diff --git a/lib/capybara/driver/node.rb b/lib/capybara/driver/node.rb index 0838489e..d31d0ddd 100644 --- a/lib/capybara/driver/node.rb +++ b/lib/capybara/driver/node.rb @@ -24,11 +24,11 @@ module Capybara raise NotImplementedError end - def select_option(option) + def select_option raise NotImplementedError end - def unselect_option(option) + def unselect_option raise NotImplementedError end diff --git a/lib/capybara/driver/rack_test_driver.rb b/lib/capybara/driver/rack_test_driver.rb index c963d168..e14e8762 100644 --- a/lib/capybara/driver/rack_test_driver.rb +++ b/lib/capybara/driver/rack_test_driver.rb @@ -51,32 +51,18 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base end end - def select_option(option) - if native['multiple'] != 'multiple' - native.xpath(".//option[@selected]").each { |node| node.remove_attribute("selected") } - end - - if option_node = native.xpath(".//option[text()=#{Capybara::XPath.escape(option)}]").first || - native.xpath(".//option[contains(.,#{Capybara::XPath.escape(option)})]").first - option_node["selected"] = 'selected' - else - options = native.xpath(".//option").map { |o| "'#{o.text}'" }.join(', ') - raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" + def select_option + if select_node['multiple'] != 'multiple' + select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") } end + native["selected"] = 'selected' end - def unselect_option(option) - if native['multiple'] != 'multiple' - raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box." - end - - if option_node = native.xpath(".//option[text()=#{Capybara::XPath.escape(option)}]").first || - native.xpath(".//option[contains(.,#{Capybara::XPath.escape(option)})]").first - option_node.remove_attribute('selected') - else - options = native.xpath(".//option").map { |o| "'#{o.text}'" }.join(', ') - raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" + def unselect_option + if select_node['multiple'] != 'multiple' + raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." end + native.remove_attribute('selected') end def click @@ -106,6 +92,11 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base private + # a reference to the select node if this is an option node + def select_node + find('./ancestor::select').first + end + def type native[:type] end diff --git a/lib/capybara/driver/selenium_driver.rb b/lib/capybara/driver/selenium_driver.rb index 6572907b..62f86e99 100644 --- a/lib/capybara/driver/selenium_driver.rb +++ b/lib/capybara/driver/selenium_driver.rb @@ -35,26 +35,15 @@ class Capybara::Driver::Selenium < Capybara::Driver::Base end end - def select_option(option) - option_node = native.find_element(:xpath, ".//option[normalize-space(text())=#{Capybara::XPath.escape(option)}]") || native.find_element(:xpath, ".//option[contains(.,#{Capybara::XPath.escape(option)})]") - option_node.select - rescue - options = native.find_elements(:xpath, ".//option").map { |o| "'#{o.text}'" }.join(', ') - raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" + def select_option + native.select end - def unselect_option(option) - if native['multiple'] != 'multiple' - raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box." - end - - begin - option_node = native.find_element(:xpath, ".//option[normalize-space(text())=#{Capybara::XPath.escape(option)}]") || native.find_element(:xpath, ".//option[contains(.,#{Capybara::XPath.escape(option)})]") - option_node.clear - rescue - options = native.find_elements(:xpath, ".//option").map { |o| "'#{o.text}'" }.join(', ') - raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}" + def unselect_option + if select_node['multiple'] != 'multiple' + raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." end + native.clear end def click @@ -79,6 +68,11 @@ class Capybara::Driver::Selenium < Capybara::Driver::Base private + # a reference to the select node if this is an option node + def select_node + find('./ancestor::select').first + end + def type self[:type] end diff --git a/lib/capybara/node.rb b/lib/capybara/node.rb index f7a3f3ec..4801ace2 100644 --- a/lib/capybara/node.rb +++ b/lib/capybara/node.rb @@ -113,22 +113,18 @@ module Capybara ## # - # Select the given option if the element is a select box + # Select this node if is an option element inside a select tag # - # @param [String] option The option to select - # - def select_option(option) - base.select_option(option) + def select_option + base.select_option end ## # - # Unselect the given option if the element is a select box + # Unselect this node if is an option element inside a multiple select tag # - # @param [String] option The option to unselect - # - def unselect_option(option) - base.unselect_option(option) + def unselect_option + base.unselect_option end ## diff --git a/lib/capybara/node/actions.rb b/lib/capybara/node/actions.rb index de9b6116..9a709865 100644 --- a/lib/capybara/node/actions.rb +++ b/lib/capybara/node/actions.rb @@ -106,8 +106,10 @@ module Capybara # @param [String] locator Which check box to uncheck # def select(value, options={}) - msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found" - find(:xpath, XPath.select(options[:from]), :message => msg).select_option(value) + no_select_msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found" + no_option_msg = "cannot select option, no option with text '#{value}' in select box '#{options[:from]}'" + select = find(:xpath, XPath.select(options[:from]), :message => no_select_msg) + select.find(:xpath, XPath.option(value), :message => no_option_msg).select_option end ## @@ -121,8 +123,10 @@ module Capybara # @param [String] locator Which check box to uncheck # def unselect(value, options={}) - msg = "cannot unselect option, no select box with id, name, or label '#{options[:from]}' found" - find(:xpath, XPath.select(options[:from]), :message => msg).unselect_option(value) + no_select_msg = "cannot unselect option, no select box with id, name, or label '#{options[:from]}' found" + no_option_msg = "cannot unselect option, no option with text '#{value}' in select box '#{options[:from]}'" + select = find(:xpath, XPath.select(options[:from]), :message => no_select_msg) + select.find(:xpath, XPath.option(value), :message => no_option_msg).unselect_option end ## diff --git a/lib/capybara/spec/session/select_spec.rb b/lib/capybara/spec/session/select_spec.rb index dcc5695e..8b0dad38 100644 --- a/lib/capybara/spec/session/select_spec.rb +++ b/lib/capybara/spec/session/select_spec.rb @@ -59,7 +59,7 @@ shared_examples_for "select" do context "with an option that doesn't exist" do it "should raise an error" do - running { @session.select('Does not Exist', :from => 'form_locale') }.should raise_error(Capybara::OptionNotFound) + running { @session.select('Does not Exist', :from => 'form_locale') }.should raise_error(Capybara::ElementNotFound) end end diff --git a/lib/capybara/spec/session/unselect_spec.rb b/lib/capybara/spec/session/unselect_spec.rb index 4a53f275..632970c8 100644 --- a/lib/capybara/spec/session/unselect_spec.rb +++ b/lib/capybara/spec/session/unselect_spec.rb @@ -47,7 +47,7 @@ shared_examples_for "unselect" do context "with an option that doesn't exist" do it "should raise an error" do - running { @session.unselect('Does not Exist', :from => 'form_underwear') }.should raise_error(Capybara::OptionNotFound) + running { @session.unselect('Does not Exist', :from => 'form_underwear') }.should raise_error(Capybara::ElementNotFound) end end end diff --git a/lib/capybara/xpath.rb b/lib/capybara/xpath.rb index bafedfd2..4b29257b 100644 --- a/lib/capybara/xpath.rb +++ b/lib/capybara/xpath.rb @@ -137,6 +137,10 @@ module Capybara input_field(:file, locator, options) end + def option(name) + append(".//option[normalize-space(text())=#{s(name)}]").append(".//option[contains(.,#{s(name)})]") + end + protected def input_field(type, locator, options={})