Merge branch 'nested_params_support'
This commit is contained in:
commit
e3bcf5c599
@ -1,6 +1,7 @@
|
||||
require "cgi"
|
||||
require "digest/md5"
|
||||
require "webrat/core_extensions/blank"
|
||||
require "webrat/core_extensions/nil_to_param"
|
||||
require "webrat/core_extensions/nil_to_query_string"
|
||||
|
||||
require "webrat/core/elements/element"
|
||||
|
||||
@ -13,7 +14,7 @@ module Webrat
|
||||
attr_reader :value
|
||||
|
||||
def self.xpath_search
|
||||
[".//button", ".//input", ".//textarea", ".//select"]
|
||||
".//button|.//input|.//textarea|.//select"
|
||||
end
|
||||
|
||||
def self.xpath_search_excluding_hidden
|
||||
@ -84,19 +85,17 @@ module Webrat
|
||||
raise DisabledFieldError.new("Cannot interact with disabled form element (#{self})")
|
||||
end
|
||||
|
||||
def to_param
|
||||
def to_query_string
|
||||
return nil if disabled?
|
||||
|
||||
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].first.to_s }
|
||||
query_string = case Webrat.configuration.mode
|
||||
when :rails, :merb, :rack, :sinatra
|
||||
build_query_string
|
||||
when :mechanize
|
||||
build_query_string(false)
|
||||
end
|
||||
|
||||
unescape_params(params)
|
||||
query_string
|
||||
end
|
||||
|
||||
def set(value)
|
||||
@ -109,18 +108,6 @@ module Webrat
|
||||
|
||||
protected
|
||||
|
||||
def parse_rails_request_params(params)
|
||||
if defined?(ActionController::AbstractRequest)
|
||||
ActionController::AbstractRequest.parse_query_parameters(params)
|
||||
elsif defined?(ActionController::UrlEncodedPairParser)
|
||||
# For Rails > 2.2
|
||||
ActionController::UrlEncodedPairParser.parse_query_parameters(params)
|
||||
else
|
||||
# For Rails > 2.3
|
||||
Rack::Utils.parse_nested_query(params)
|
||||
end
|
||||
end
|
||||
|
||||
def form
|
||||
Form.load(@session, form_element)
|
||||
end
|
||||
@ -138,23 +125,20 @@ module Webrat
|
||||
@element["name"]
|
||||
end
|
||||
|
||||
def escaped_value
|
||||
CGI.escape(@value.to_s)
|
||||
def build_query_string(escape_value=true)
|
||||
if @value.is_a?(Array)
|
||||
@value.collect {|value| "#{name}=#{ escape_value ? escape(value) : value }" }.join("&")
|
||||
else
|
||||
"#{name}=#{ escape_value ? escape(value) : value }"
|
||||
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
|
||||
def escape(value)
|
||||
CGI.escape(value.to_s)
|
||||
end
|
||||
|
||||
def escaped_value
|
||||
CGI.escape(@value.to_s)
|
||||
end
|
||||
|
||||
def labels
|
||||
@ -186,22 +170,6 @@ module Webrat
|
||||
def default_value
|
||||
@element["value"]
|
||||
end
|
||||
|
||||
def replace_param_value(params, oval, nval)
|
||||
output = Hash.new
|
||||
params.each do |key, value|
|
||||
case value
|
||||
when Hash
|
||||
value = replace_param_value(value, oval, nval)
|
||||
when Array
|
||||
value = value.map { |o| o == oval ? nval : oval }
|
||||
when oval
|
||||
value = nval
|
||||
end
|
||||
output[key] = value
|
||||
end
|
||||
output
|
||||
end
|
||||
end
|
||||
|
||||
class ButtonField < Field #:nodoc:
|
||||
@ -210,7 +178,7 @@ module Webrat
|
||||
[".//button", ".//input[@type = 'submit']", ".//input[@type = 'button']", ".//input[@type = 'image']"]
|
||||
end
|
||||
|
||||
def to_param
|
||||
def to_query_string
|
||||
return nil if @value.nil?
|
||||
super
|
||||
end
|
||||
@ -233,13 +201,13 @@ module Webrat
|
||||
".//input[@type = 'hidden']"
|
||||
end
|
||||
|
||||
def to_param
|
||||
def to_query_string
|
||||
if collection_name?
|
||||
super
|
||||
else
|
||||
checkbox_with_same_name = form.field_named(name, CheckboxField)
|
||||
|
||||
if checkbox_with_same_name.to_param.blank?
|
||||
if checkbox_with_same_name.to_query_string.blank?
|
||||
super
|
||||
else
|
||||
nil
|
||||
@ -261,7 +229,7 @@ module Webrat
|
||||
".//input[@type = 'checkbox']"
|
||||
end
|
||||
|
||||
def to_param
|
||||
def to_query_string
|
||||
return nil if @value.nil?
|
||||
super
|
||||
end
|
||||
@ -306,7 +274,7 @@ module Webrat
|
||||
".//input[@type = 'radio']"
|
||||
end
|
||||
|
||||
def to_param
|
||||
def to_query_string
|
||||
return nil if @value.nil?
|
||||
super
|
||||
end
|
||||
@ -363,30 +331,32 @@ module Webrat
|
||||
attr_accessor :content_type
|
||||
|
||||
def set(value, content_type = nil)
|
||||
@original_value = @value
|
||||
@content_type ||= content_type
|
||||
super(value)
|
||||
@content_type = content_type
|
||||
end
|
||||
|
||||
def to_param
|
||||
if @value.nil?
|
||||
super
|
||||
else
|
||||
replace_param_value(super, @value, test_uploaded_file)
|
||||
end
|
||||
def digest_value
|
||||
@value ? Digest::MD5.hexdigest(self.object_id.to_s) : ""
|
||||
end
|
||||
|
||||
protected
|
||||
def to_query_string
|
||||
@value.nil? ? set("") : set(digest_value)
|
||||
super
|
||||
end
|
||||
|
||||
def test_uploaded_file
|
||||
return "" if @original_value.blank?
|
||||
|
||||
case Webrat.configuration.mode
|
||||
when :rails
|
||||
if content_type
|
||||
ActionController::TestUploadedFile.new(@value, content_type)
|
||||
ActionController::TestUploadedFile.new(@original_value, content_type)
|
||||
else
|
||||
ActionController::TestUploadedFile.new(@value)
|
||||
ActionController::TestUploadedFile.new(@original_value)
|
||||
end
|
||||
when :rack, :merb
|
||||
Rack::Test::UploadedFile.new(@value, content_type)
|
||||
Rack::Test::UploadedFile.new(@original_value, content_type)
|
||||
end
|
||||
end
|
||||
|
||||
@ -450,25 +420,6 @@ module Webrat
|
||||
@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
|
||||
|
@ -38,15 +38,24 @@ module Webrat
|
||||
end
|
||||
end
|
||||
|
||||
# iterate over all form fields to build a request querystring to get params from it,
|
||||
# for file_field we made a work around to pass a digest as value to later replace it
|
||||
# in params hash with the real file.
|
||||
def params
|
||||
all_params = {}
|
||||
query_string = []
|
||||
replaces = {}
|
||||
|
||||
fields.each do |field|
|
||||
next if field.to_param.nil?
|
||||
merge(all_params, field.to_param)
|
||||
next if field.to_query_string.nil?
|
||||
replaces.merge!({field.digest_value => field.test_uploaded_file}) if field.is_a?(FileField)
|
||||
query_string << field.to_query_string
|
||||
end
|
||||
|
||||
all_params
|
||||
query_params = self.class.query_string_to_params(query_string.join('&'))
|
||||
|
||||
query_params = self.class.replace_params_values(query_params, replaces)
|
||||
|
||||
self.class.unescape_params(query_params)
|
||||
end
|
||||
|
||||
def form_method
|
||||
@ -57,47 +66,62 @@ module Webrat
|
||||
@element["action"].blank? ? @session.current_url : @element["action"]
|
||||
end
|
||||
|
||||
def merge(all_params, new_param)
|
||||
new_param.each do |key, value|
|
||||
case all_params[key]
|
||||
when *hash_classes
|
||||
merge_hash_values(all_params[key], value)
|
||||
def self.replace_param_value(params, oval, nval)
|
||||
output = Hash.new
|
||||
params.each do |key, value|
|
||||
case value
|
||||
when Hash
|
||||
value = replace_param_value(value, oval, nval)
|
||||
when Array
|
||||
all_params[key] += value
|
||||
else
|
||||
all_params[key] = value
|
||||
value = value.map { |o| o == oval ? nval : ( o.is_a?(Hash) ? replace_param_value(o, oval, nval) : o) }
|
||||
when oval
|
||||
value = nval
|
||||
end
|
||||
output[key] = value
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
def self.replace_params_values(params, values)
|
||||
values.each do |key, value|
|
||||
params = replace_param_value(params, key, value)
|
||||
end
|
||||
params
|
||||
end
|
||||
|
||||
def self.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
|
||||
params.is_a?(String) ? CGI.unescapeHTML(params) : params
|
||||
end
|
||||
end
|
||||
|
||||
def merge_hash_values(a, b) # :nodoc:
|
||||
a.keys.each do |k|
|
||||
if b.has_key?(k)
|
||||
case [a[k], b[k]].map{|value| value.class}
|
||||
when *hash_classes.zip(hash_classes)
|
||||
a[k] = merge_hash_values(a[k], b[k])
|
||||
b.delete(k)
|
||||
when [Array, Array]
|
||||
a[k] += b[k]
|
||||
b.delete(k)
|
||||
end
|
||||
end
|
||||
end
|
||||
a.merge!(b)
|
||||
end
|
||||
|
||||
def hash_classes
|
||||
klasses = [Hash]
|
||||
|
||||
def self.query_string_to_params(query_string)
|
||||
case Webrat.configuration.mode
|
||||
when :rails
|
||||
klasses << HashWithIndifferentAccess
|
||||
parse_rails_request_params(query_string)
|
||||
when :merb
|
||||
klasses << Mash
|
||||
::Merb::Parse.query(query_string)
|
||||
when :rack, :sinatra
|
||||
Rack::Utils.parse_nested_query(query_string)
|
||||
else
|
||||
query_string.split('&').map {|query| { query.split('=').first => query.split('=').last }}
|
||||
end
|
||||
|
||||
klasses
|
||||
end
|
||||
|
||||
def self.parse_rails_request_params(query_string)
|
||||
if defined?(ActionController::AbstractRequest)
|
||||
ActionController::AbstractRequest.parse_query_parameters(query_string)
|
||||
elsif defined?(ActionController::UrlEncodedPairParser)
|
||||
ActionController::UrlEncodedPairParser.parse_query_parameters(query_string)
|
||||
else
|
||||
Rack::Utils.parse_nested_query(query_string)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,5 @@
|
||||
class NilClass #:nodoc:
|
||||
def to_param
|
||||
def to_query_string
|
||||
nil
|
||||
end
|
||||
end
|
@ -77,7 +77,7 @@ module Webrat
|
||||
|
||||
element = Webrat::XML.document(html).css('input').first
|
||||
text_field = TextField.new(nil, element)
|
||||
text_field.to_param.should == { 'email' => 'user@example.com' }
|
||||
text_field.to_query_string.should == 'email=user@example.com'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
51
spec/private/core/form_spec.rb
Normal file
51
spec/private/core/form_spec.rb
Normal file
@ -0,0 +1,51 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||
|
||||
describe "Multiple nested params" do
|
||||
it "should be corretly posted" do
|
||||
Webrat.configuration.mode = :rails
|
||||
|
||||
with_html <<-HTML
|
||||
<html>
|
||||
<form method="post" action="/family">
|
||||
<div class="couple">
|
||||
<div class="parent">
|
||||
<select name="user[family][parents][0][][gender]">
|
||||
<option selected="selected" value="Mother">Mother</option>
|
||||
<option value="Father">Father</option>
|
||||
</select>
|
||||
<input type="text" value="Alice" name="user[family][parents][0][][name]" />
|
||||
</div>
|
||||
<div class="parent">
|
||||
<select name="user[family][parents][0][][gender]">
|
||||
<option value="Mother">Mother</option>
|
||||
<option selected="selected" value="Father">Father</option>
|
||||
</select>
|
||||
<input type="text" value="Michael" name="user[family][parents][0][][name]" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="couple">
|
||||
<div class="parent">
|
||||
<select name="user[family][parents][1][][gender]">
|
||||
<option selected="selected" value="Mother">Mother</option>
|
||||
<option value="Father">Father</option>
|
||||
</select>
|
||||
<input type="text" value="Jenny" name="user[family][parents][1][][name]" />
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</html>
|
||||
HTML
|
||||
|
||||
params = { "user" => { "family" => { "parents" => {
|
||||
"0" => [ {"name" => "Alice", "gender"=>"Mother"}, {"name" => "Michael", "gender"=>"Father"} ],
|
||||
"1" => [ {"name" => "Jenny", "gender"=>"Mother"} ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webrat_session.should_receive(:post).with("/family", params)
|
||||
click_button
|
||||
end
|
||||
end
|
@ -78,4 +78,37 @@ describe "attach_file" do
|
||||
attach_file "Picture", @filename, "image/png"
|
||||
click_button
|
||||
end
|
||||
|
||||
it "should support nested attributes" do
|
||||
with_html <<-HTML
|
||||
<html>
|
||||
<form method="post" action="/albums">
|
||||
<label for="photo_file1">Photo</label>
|
||||
<input type="file" id="photo_file1" name="album[photos_attributes][][image]" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</html>
|
||||
HTML
|
||||
webrat_session.should_receive(:post).with("/albums", { "album" => { "photos_attributes" => [{"image" => @uploaded_file}] } })
|
||||
attach_file "Photo", @filename
|
||||
click_button
|
||||
end
|
||||
|
||||
it "should support nested attributes with multiple files" do
|
||||
with_html <<-HTML
|
||||
<html>
|
||||
<form method="post" action="/albums">
|
||||
<label for="photo_file1">Photo 1</label>
|
||||
<input type="file" id="photo_file1" name="album[photos_attributes][][image]" />
|
||||
<label for="photo_file2">Photo 2</label>
|
||||
<input type="file" id="photo_file2" name="album[photos_attributes][][image]" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</html>
|
||||
HTML
|
||||
webrat_session.should_receive(:post).with("/albums", { "album" => { "photos_attributes" => [{"image" => @uploaded_file}, {"image" => @uploaded_file}] } })
|
||||
attach_file "Photo 1", @filename
|
||||
attach_file "Photo 2", @filename
|
||||
click_button
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user