Various improvements, refinements, and bugfixes for select field usage.

Multiple select boxes are now handled a lot better.
* You can now select more than one field (where as previously, the last one select would override previous selections)
* You can now unselect a value. It works like select, takes the same params, but removes the selection value

Issues regarding ampersands in values have been resolved.
* Values are now unescaped when the to_param method is run on elements, to make assertions less complicated
* Locating an option with ampersand values now works as expected (it will check for both escaped and unescaped occurrences)

Bunch of new specs and 3 broken pending ones have now been fixed. [#145 status:resolved]
This commit is contained in:
Kieran Pilkington 2009-08-31 18:34:04 +12:00
parent 98efa54900
commit a8d0cebde3
8 changed files with 285 additions and 22 deletions

View File

@ -38,7 +38,12 @@ module Webrat
def self.field_class(element) def self.field_class(element)
case element.name case element.name
when "button" then ButtonField when "button" then ButtonField
when "select" then SelectField when "select"
if element.attributes["multiple"].nil?
SelectField
else
MultipleSelectField
end
when "textarea" then TextareaField when "textarea" then TextareaField
else else
case element["type"] case element["type"]
@ -82,14 +87,16 @@ module Webrat
def to_param def to_param
return nil if disabled? return nil if disabled?
case Webrat.configuration.mode params = case Webrat.configuration.mode
when :rails when :rails
parse_rails_request_params("#{name}=#{escaped_value}") parse_rails_request_params("#{name}=#{escaped_value}")
when :merb when :merb
::Merb::Parse.query("#{name}=#{escaped_value}") ::Merb::Parse.query("#{name}=#{escaped_value}")
else else
{ name => value } { name => [*@value].first.to_s }
end end
unescape_params(params)
end end
def set(value) def set(value)
@ -135,6 +142,21 @@ module Webrat
CGI.escape([*@value].first.to_s) CGI.escape([*@value].first.to_s)
end end
# Because we have to escape it before sending it to the above case statement,
# we have to make sure we unescape each value when it gets back so assertions
# involving characters like <, >, and & work as expected
def unescape_params(params)
case params.class.name
when 'Hash', 'Mash'
params.each { |key,value| params[key] = unescape_params(value) }
params
when 'Array'
params.collect { |value| unescape_params(value) }
else
CGI.unescapeHTML(params)
end
end
def labels def labels
@labels ||= label_elements.map do |element| @labels ||= label_elements.map do |element|
Label.load(@session, element) Label.load(@session, element)
@ -385,13 +407,17 @@ module Webrat
class SelectField < Field #:nodoc: class SelectField < Field #:nodoc:
def self.xpath_search def self.xpath_search
[".//select"] [".//select[not(@multiple)]"]
end end
def options def options
@options ||= SelectOption.load_all(@session, @element) @options ||= SelectOption.load_all(@session, @element)
end end
def unset(value)
@value = []
end
protected protected
def default_value def default_value
@ -405,4 +431,53 @@ module Webrat
end end
end end
class MultipleSelectField < SelectField #:nodoc:
def self.xpath_search
[".//select[@multiple='multiple']"]
end
def set(value)
@value << value
end
def unset(value)
@value.delete(value)
end
# We have to overide how the uri string is formed when dealing with multiples
# Where normally a select field might produce name=value with a multiple,
# we need to form something like name[]=value1&name[]=value2
def to_param
return nil if disabled?
uri_string = @value.collect {|value| "#{name}=#{CGI.escape(value)}"}.join("&")
params = case Webrat.configuration.mode
when :rails
parse_rails_request_params(uri_string)
when :merb
::Merb::Parse.query(uri_string)
else
{ name => @value }
end
unescape_params(params)
end
protected
# Overwrite SelectField definition because we don't want to select the first option
# (mutliples don't select the first option unlike their non multiple versions)
def default_value
selected_options = @element.xpath(".//option[@selected = 'selected']")
selected_options.map do |option|
return "" if option.nil?
option["value"] || option.inner_html
end.uniq
end
end
end end

View File

@ -12,6 +12,11 @@ module Webrat
select.set(value) select.set(value)
end end
def unchoose
select.raise_error_if_disabled
select.unset(value)
end
protected protected
def select def select
@ -31,5 +36,9 @@ module Webrat
@element["value"] || @element.inner_html @element["value"] || @element.inner_html
end end
def label
@element.inner_html
end
end end
end end

View File

@ -21,7 +21,7 @@ module Webrat
if @option_text.is_a?(Regexp) if @option_text.is_a?(Regexp)
o.element.inner_html =~ @option_text o.element.inner_html =~ @option_text
else else
o.element.inner_html == @option_text.to_s escaped_or_non_escaped_values.include?(o.element.inner_html)
end end
end end
else else
@ -29,7 +29,7 @@ module Webrat
if @option_text.is_a?(Regexp) if @option_text.is_a?(Regexp)
o.inner_html =~ @option_text o.inner_html =~ @option_text
else else
o.inner_html == @option_text.to_s escaped_or_non_escaped_values.include?(o.inner_html)
end end
end end
@ -37,6 +37,10 @@ module Webrat
end end
end end
def escaped_or_non_escaped_values
[@option_text.to_s, CGI.escapeHTML(@option_text.to_s)]
end
def option_elements def option_elements
@dom.xpath(*SelectOption.xpath_search) @dom.xpath(*SelectOption.xpath_search)
end end

View File

@ -39,6 +39,7 @@ module Webrat
:unchecks, :uncheck, :unchecks, :uncheck,
:chooses, :choose, :chooses, :choose,
:selects, :select, :selects, :select,
:unselects, :unselect,
:attaches_file, :attach_file, :attaches_file, :attach_file,
:current_page, :current_page,
:current_url, :current_url,

View File

@ -112,6 +112,21 @@ module Webrat
webrat_deprecate :selects, :select webrat_deprecate :selects, :select
# Verifies that a an option element exists on the current page with the specified
# text. You can optionally restrict the search to a specific select list by
# assigning <tt>options[:from]</tt> the value of the select list's name or
# a label. Remove the option's value before the form is submitted.
#
# Examples:
# unselect "January"
# unselect "February", :from => "event_month"
# unselect "February", :from => "Event Month"
def unselect(option_text, options={})
select_option(option_text, options[:from]).unchoose
end
webrat_deprecate :unselects, :unselect
DATE_TIME_SUFFIXES = { DATE_TIME_SUFFIXES = {
:year => '1i', :year => '1i',
:month => '2i', :month => '2i',

View File

@ -260,6 +260,7 @@ For example:
def_delegators :current_scope, :uncheck, :unchecks def_delegators :current_scope, :uncheck, :unchecks
def_delegators :current_scope, :choose, :chooses def_delegators :current_scope, :choose, :chooses
def_delegators :current_scope, :select, :selects def_delegators :current_scope, :select, :selects
def_delegators :current_scope, :unselect, :unselects
def_delegators :current_scope, :select_datetime, :selects_datetime def_delegators :current_scope, :select_datetime, :selects_datetime
def_delegators :current_scope, :select_date, :selects_date def_delegators :current_scope, :select_date, :selects_date
def_delegators :current_scope, :select_time, :selects_time def_delegators :current_scope, :select_time, :selects_time

View File

@ -331,7 +331,6 @@ describe "click_button" do
end end
it "should properly handle HTML entities in textarea default values" do it "should properly handle HTML entities in textarea default values" do
pending "needs bug fix" do
with_html <<-HTML with_html <<-HTML
<html> <html>
<form method="post" action="/posts"> <form method="post" action="/posts">
@ -343,7 +342,6 @@ describe "click_button" do
webrat_session.should_receive(:post).with("/posts", "post" => {"body" => "Peanut butter & jelly"}) webrat_session.should_receive(:post).with("/posts", "post" => {"body" => "Peanut butter & jelly"})
click_button click_button
end end
end
it "should send default selected option value from select" do it "should send default selected option value from select" do
with_html <<-HTML with_html <<-HTML

View File

@ -201,7 +201,6 @@ describe "select" do
end end
it "should properly handle submitting HTML entities in select values" do it "should properly handle submitting HTML entities in select values" do
pending "needs bug fix" do
with_html <<-HTML with_html <<-HTML
<html> <html>
<form method="post" action="/login"> <form method="post" action="/login">
@ -213,10 +212,8 @@ describe "select" do
webrat_session.should_receive(:post).with("/login", "month" => "Peanut butter & jelly") webrat_session.should_receive(:post).with("/login", "month" => "Peanut butter & jelly")
click_button click_button
end end
end
it "should properly handle locating with HTML entities in select values" do it "should properly handle locating with HTML entities in select values" do
pending "needs bug fix" do
with_html <<-HTML with_html <<-HTML
<html> <html>
<form method="post" action="/login"> <form method="post" action="/login">
@ -230,7 +227,6 @@ describe "select" do
select "Peanut butter & jelly" select "Peanut butter & jelly"
}.should_not raise_error(Webrat::NotFoundError) }.should_not raise_error(Webrat::NotFoundError)
end end
end
it "should submit duplicates selected options as a single value" do it "should submit duplicates selected options as a single value" do
with_html <<-HTML with_html <<-HTML
@ -246,4 +242,168 @@ describe "select" do
click_button click_button
end end
it "should allow fields to be unselected" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes">
<option value="tshirt" selected="selected">tshirt</option>
<option value="pants">pants</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", {"clothes"=>""})
unselect "tshirt"
click_button
end
#
# Mutliple Selection Fields
#
it "should not submit any values for multiples without any selected" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option value="tshirt">tshirt</option>
<option value="pants">pants</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", {})
click_button
end
it "should submit with preselected values" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option value="tshirt" selected="selected">tshirt</option>
<option value="pants" selected="selected">pants</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", "clothes" => ['tshirt', 'pants'])
click_button
end
it "should allow selection of multiple fields" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option value="tshirt">tshirt</option>
<option value="pants">pants</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", "clothes" => ['pants'])
select 'pants'
click_button
end
it "should not overwrite preselected multiples" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option value="tshirt" selected="selected">tshirt</option>
<option value="pants">pants</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", "clothes" => ['tshirt', 'pants'])
select 'pants'
click_button
end
it "should allow fields that exist to be selected or throw errors" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option value="top" selected="selected">shirt</option>
<option value="pants">trousers</option>
</select>
<input type="submit" />
</form>
</html>
HTML
lambda { select "shirt" }.should_not raise_error(Webrat::NotFoundError)
lambda { select "trousers" }.should_not raise_error(Webrat::NotFoundError)
lambda { select "shoes" }.should raise_error(Webrat::NotFoundError)
end
it "should allow selected fields to be unselected" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option value="tshirt" selected="selected">tshirt</option>
<option value="pants" selected="selected">pants</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", "clothes" => ['pants'])
unselect 'tshirt'
click_button
end
it "should be able to select options with special characters" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option>tshirt &amp; sweater</option>
<option>pants &amp; socks</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", "clothes" => ['pants & socks'])
select 'pants & socks'
click_button
end
it "should be able to unselect options with special characters" do
with_html <<-HTML
<html>
<form method="post" action="/login">
<select name="clothes[]" multiple="multiple">
<option selected="selected">tshirt &amp; sweater</option>
<option selected="selected">pants &amp; socks</option>
</select>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/login", "clothes" => ['tshirt & sweater'])
unselect 'pants & socks'
click_button
end
end end