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)
case element.name
when "button" then ButtonField
when "select" then SelectField
when "select"
if element.attributes["multiple"].nil?
SelectField
else
MultipleSelectField
end
when "textarea" then TextareaField
else
case element["type"]
@ -82,14 +87,16 @@ module Webrat
def to_param
return nil if disabled?
case Webrat.configuration.mode
params = case Webrat.configuration.mode
when :rails
parse_rails_request_params("#{name}=#{escaped_value}")
when :merb
::Merb::Parse.query("#{name}=#{escaped_value}")
else
{ name => value }
{ name => [*@value].first.to_s }
end
unescape_params(params)
end
def set(value)
@ -135,6 +142,21 @@ module Webrat
CGI.escape([*@value].first.to_s)
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
@labels ||= label_elements.map do |element|
Label.load(@session, element)
@ -385,13 +407,17 @@ module Webrat
class SelectField < Field #:nodoc:
def self.xpath_search
[".//select"]
[".//select[not(@multiple)]"]
end
def options
@options ||= SelectOption.load_all(@session, @element)
end
def unset(value)
@value = []
end
protected
def default_value
@ -405,4 +431,53 @@ module Webrat
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

View File

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

View File

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

View File

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

View File

@ -112,6 +112,21 @@ module Webrat
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 = {
:year => '1i',
:month => '2i',

View File

@ -260,6 +260,7 @@ For example:
def_delegators :current_scope, :uncheck, :unchecks
def_delegators :current_scope, :choose, :chooses
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_date, :selects_date
def_delegators :current_scope, :select_time, :selects_time

View File

@ -331,18 +331,16 @@ describe "click_button" do
end
it "should properly handle HTML entities in textarea default values" do
pending "needs bug fix" do
with_html <<-HTML
<html>
<form method="post" action="/posts">
<textarea name="post[body]">Peanut butter &amp; jelly</textarea>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/posts", "post" => {"body" => "Peanut butter & jelly"})
click_button
end
with_html <<-HTML
<html>
<form method="post" action="/posts">
<textarea name="post[body]">Peanut butter &amp; jelly</textarea>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/posts", "post" => {"body" => "Peanut butter & jelly"})
click_button
end
it "should send default selected option value from select" do

View File

@ -201,7 +201,6 @@ describe "select" do
end
it "should properly handle submitting HTML entities in select values" do
pending "needs bug fix" do
with_html <<-HTML
<html>
<form method="post" action="/login">
@ -212,11 +211,9 @@ describe "select" do
HTML
webrat_session.should_receive(:post).with("/login", "month" => "Peanut butter & jelly")
click_button
end
end
it "should properly handle locating with HTML entities in select values" do
pending "needs bug fix" do
with_html <<-HTML
<html>
<form method="post" action="/login">
@ -229,7 +226,6 @@ describe "select" do
lambda {
select "Peanut butter & jelly"
}.should_not raise_error(Webrat::NotFoundError)
end
end
it "should submit duplicates selected options as a single value" do
@ -246,4 +242,168 @@ describe "select" do
click_button
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