Merge branch 'nested_params_support'
This commit is contained in:
commit
e3bcf5c599
@ -1,6 +1,7 @@
|
|||||||
require "cgi"
|
require "cgi"
|
||||||
|
require "digest/md5"
|
||||||
require "webrat/core_extensions/blank"
|
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"
|
require "webrat/core/elements/element"
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ module Webrat
|
|||||||
attr_reader :value
|
attr_reader :value
|
||||||
|
|
||||||
def self.xpath_search
|
def self.xpath_search
|
||||||
[".//button", ".//input", ".//textarea", ".//select"]
|
".//button|.//input|.//textarea|.//select"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.xpath_search_excluding_hidden
|
def self.xpath_search_excluding_hidden
|
||||||
@ -84,19 +85,17 @@ module Webrat
|
|||||||
raise DisabledFieldError.new("Cannot interact with disabled form element (#{self})")
|
raise DisabledFieldError.new("Cannot interact with disabled form element (#{self})")
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
def to_query_string
|
||||||
return nil if disabled?
|
return nil if disabled?
|
||||||
|
|
||||||
params = case Webrat.configuration.mode
|
query_string = case Webrat.configuration.mode
|
||||||
when :rails
|
when :rails, :merb, :rack, :sinatra
|
||||||
parse_rails_request_params("#{name}=#{escaped_value}")
|
build_query_string
|
||||||
when :merb
|
when :mechanize
|
||||||
::Merb::Parse.query("#{name}=#{escaped_value}")
|
build_query_string(false)
|
||||||
else
|
|
||||||
{ name => [*@value].first.to_s }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unescape_params(params)
|
query_string
|
||||||
end
|
end
|
||||||
|
|
||||||
def set(value)
|
def set(value)
|
||||||
@ -109,18 +108,6 @@ module Webrat
|
|||||||
|
|
||||||
protected
|
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
|
def form
|
||||||
Form.load(@session, form_element)
|
Form.load(@session, form_element)
|
||||||
end
|
end
|
||||||
@ -138,23 +125,20 @@ module Webrat
|
|||||||
@element["name"]
|
@element["name"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def escaped_value
|
def build_query_string(escape_value=true)
|
||||||
CGI.escape(@value.to_s)
|
if @value.is_a?(Array)
|
||||||
|
@value.collect {|value| "#{name}=#{ escape_value ? escape(value) : value }" }.join("&")
|
||||||
|
else
|
||||||
|
"#{name}=#{ escape_value ? escape(value) : value }"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Because we have to escape it before sending it to the above case statement,
|
def escape(value)
|
||||||
# we have to make sure we unescape each value when it gets back so assertions
|
CGI.escape(value.to_s)
|
||||||
# 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 escaped_value
|
||||||
|
CGI.escape(@value.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
def labels
|
def labels
|
||||||
@ -186,22 +170,6 @@ module Webrat
|
|||||||
def default_value
|
def default_value
|
||||||
@element["value"]
|
@element["value"]
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
class ButtonField < Field #:nodoc:
|
class ButtonField < Field #:nodoc:
|
||||||
@ -210,7 +178,7 @@ module Webrat
|
|||||||
[".//button", ".//input[@type = 'submit']", ".//input[@type = 'button']", ".//input[@type = 'image']"]
|
[".//button", ".//input[@type = 'submit']", ".//input[@type = 'button']", ".//input[@type = 'image']"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
def to_query_string
|
||||||
return nil if @value.nil?
|
return nil if @value.nil?
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
@ -233,13 +201,13 @@ module Webrat
|
|||||||
".//input[@type = 'hidden']"
|
".//input[@type = 'hidden']"
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
def to_query_string
|
||||||
if collection_name?
|
if collection_name?
|
||||||
super
|
super
|
||||||
else
|
else
|
||||||
checkbox_with_same_name = form.field_named(name, CheckboxField)
|
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
|
super
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
@ -261,7 +229,7 @@ module Webrat
|
|||||||
".//input[@type = 'checkbox']"
|
".//input[@type = 'checkbox']"
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
def to_query_string
|
||||||
return nil if @value.nil?
|
return nil if @value.nil?
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
@ -306,7 +274,7 @@ module Webrat
|
|||||||
".//input[@type = 'radio']"
|
".//input[@type = 'radio']"
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
def to_query_string
|
||||||
return nil if @value.nil?
|
return nil if @value.nil?
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
@ -363,30 +331,32 @@ module Webrat
|
|||||||
attr_accessor :content_type
|
attr_accessor :content_type
|
||||||
|
|
||||||
def set(value, content_type = nil)
|
def set(value, content_type = nil)
|
||||||
|
@original_value = @value
|
||||||
|
@content_type ||= content_type
|
||||||
super(value)
|
super(value)
|
||||||
@content_type = content_type
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
def digest_value
|
||||||
if @value.nil?
|
@value ? Digest::MD5.hexdigest(self.object_id.to_s) : ""
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_query_string
|
||||||
|
@value.nil? ? set("") : set(digest_value)
|
||||||
super
|
super
|
||||||
else
|
|
||||||
replace_param_value(super, @value, test_uploaded_file)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def test_uploaded_file
|
def test_uploaded_file
|
||||||
|
return "" if @original_value.blank?
|
||||||
|
|
||||||
case Webrat.configuration.mode
|
case Webrat.configuration.mode
|
||||||
when :rails
|
when :rails
|
||||||
if content_type
|
if content_type
|
||||||
ActionController::TestUploadedFile.new(@value, content_type)
|
ActionController::TestUploadedFile.new(@original_value, content_type)
|
||||||
else
|
else
|
||||||
ActionController::TestUploadedFile.new(@value)
|
ActionController::TestUploadedFile.new(@original_value)
|
||||||
end
|
end
|
||||||
when :rack, :merb
|
when :rack, :merb
|
||||||
Rack::Test::UploadedFile.new(@value, content_type)
|
Rack::Test::UploadedFile.new(@original_value, content_type)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -450,25 +420,6 @@ module Webrat
|
|||||||
@value.delete(value)
|
@value.delete(value)
|
||||||
end
|
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
|
protected
|
||||||
|
|
||||||
# Overwrite SelectField definition because we don't want to select the first option
|
# Overwrite SelectField definition because we don't want to select the first option
|
||||||
|
@ -38,15 +38,24 @@ module Webrat
|
|||||||
end
|
end
|
||||||
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
|
def params
|
||||||
all_params = {}
|
query_string = []
|
||||||
|
replaces = {}
|
||||||
|
|
||||||
fields.each do |field|
|
fields.each do |field|
|
||||||
next if field.to_param.nil?
|
next if field.to_query_string.nil?
|
||||||
merge(all_params, field.to_param)
|
replaces.merge!({field.digest_value => field.test_uploaded_file}) if field.is_a?(FileField)
|
||||||
|
query_string << field.to_query_string
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def form_method
|
def form_method
|
||||||
@ -57,47 +66,62 @@ module Webrat
|
|||||||
@element["action"].blank? ? @session.current_url : @element["action"]
|
@element["action"].blank? ? @session.current_url : @element["action"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge(all_params, new_param)
|
def self.replace_param_value(params, oval, nval)
|
||||||
new_param.each do |key, value|
|
output = Hash.new
|
||||||
case all_params[key]
|
params.each do |key, value|
|
||||||
when *hash_classes
|
case value
|
||||||
merge_hash_values(all_params[key], value)
|
when Hash
|
||||||
|
value = replace_param_value(value, oval, nval)
|
||||||
when Array
|
when Array
|
||||||
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
|
else
|
||||||
all_params[key] = value
|
params.is_a?(String) ? CGI.unescapeHTML(params) : params
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_hash_values(a, b) # :nodoc:
|
def self.query_string_to_params(query_string)
|
||||||
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]
|
|
||||||
|
|
||||||
case Webrat.configuration.mode
|
case Webrat.configuration.mode
|
||||||
when :rails
|
when :rails
|
||||||
klasses << HashWithIndifferentAccess
|
parse_rails_request_params(query_string)
|
||||||
when :merb
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
klasses
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
class NilClass #:nodoc:
|
class NilClass #:nodoc:
|
||||||
def to_param
|
def to_query_string
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -77,7 +77,7 @@ module Webrat
|
|||||||
|
|
||||||
element = Webrat::XML.document(html).css('input').first
|
element = Webrat::XML.document(html).css('input').first
|
||||||
text_field = TextField.new(nil, element)
|
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
|
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"
|
attach_file "Picture", @filename, "image/png"
|
||||||
click_button
|
click_button
|
||||||
end
|
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
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user