From 09509025c3a0406692ebfc18f34c13c4ae12981d Mon Sep 17 00:00:00 2001 From: Ryan Carver Date: Fri, 24 Apr 2009 13:56:28 -0700 Subject: [PATCH 1/3] Allow Merb to do file uploads --- lib/webrat/core/elements/field.rb | 14 ++-- lib/webrat/merb_session.rb | 33 +++++++-- spec/private/merb/attaches_file_spec.rb | 93 +++++++++++++++++++++++++ spec/private/merb/merb_session_spec.rb | 19 +++++ 4 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 spec/private/merb/attaches_file_spec.rb diff --git a/lib/webrat/core/elements/field.rb b/lib/webrat/core/elements/field.rb index 7eecc94..bc3d580 100644 --- a/lib/webrat/core/elements/field.rb +++ b/lib/webrat/core/elements/field.rb @@ -356,10 +356,16 @@ module Webrat protected def test_uploaded_file - if content_type - ActionController::TestUploadedFile.new(@value, content_type) - else - ActionController::TestUploadedFile.new(@value) + case Webrat.configuration.mode + when :rails + if content_type + ActionController::TestUploadedFile.new(@value, content_type) + else + ActionController::TestUploadedFile.new(@value) + end + when :merb + # TODO: support content_type + File.new(@value) end end diff --git a/lib/webrat/merb_session.rb b/lib/webrat/merb_session.rb index fcc9099..b21468f 100644 --- a/lib/webrat/merb_session.rb +++ b/lib/webrat/merb_session.rb @@ -5,6 +5,8 @@ gem "extlib" require "extlib" require "merb-core" +require "merb-core/two-oh" + # HashWithIndifferentAccess = Mash module Webrat @@ -36,14 +38,37 @@ module Webrat def response_code @response.status end + include Merb::Test::MultipartRequestHelper def do_request(url, data, headers, method) - @response = request(url, - :params => (data && data.any?) ? data : nil, - :headers => headers, - :method => method) + if method == "POST" && has_file?(data) + @response = multipart_post(url, data, :headers => headers) + + elsif method == "PUT" && has_file?(data) + @response = multipart_put(url, data, :headers => headers) + + else + @response = request(url, + :params => (data && data.any?) ? data : nil, + :headers => headers, + :method => method) + end end + protected + + # Recursively search the data for a file attachment. + def has_file?(data) + data.each do |key, value| + if value.is_a?(Hash) + return has_file?(value) + else + return true if value.is_a?(File) + end + end + return false + end + end end diff --git a/spec/private/merb/attaches_file_spec.rb b/spec/private/merb/attaches_file_spec.rb new file mode 100644 index 0000000..5c17cfa --- /dev/null +++ b/spec/private/merb/attaches_file_spec.rb @@ -0,0 +1,93 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') + +describe "attach_file with merb" do + before do + Webrat.configuration.mode = :merb + @filename = __FILE__ + end + + it "should fail if no file field found" do + with_html <<-HTML + +
+
+ + HTML + lambda { attach_file("Doc", "/some/path") }.should raise_error(Webrat::NotFoundError) + end + + it "should submit empty strings for blank file fields" do + with_html <<-HTML + +
+ + +
+ + HTML + webrat_session.should_receive(:post).with("/widgets", { "widget" => { "file" => "" } }) + click_button + end + + it "should submit the attached file" do + with_html <<-HTML + +
+ + + +
+ + HTML + webrat_session.should_receive(:post).with { |path, params| + path.should == "/widgets" + params.should have_key("widget") + params["widget"].should have_key("file") + params["widget"]["file"].should be_an_instance_of(File) + params["widget"]["file"].path.should == @filename + } + attach_file "Document", @filename + click_button + end + + it "should support collections" do + with_html <<-HTML + +
+ + + + + +
+ + HTML + webrat_session.should_receive(:post).with { |path, params| + path.should == "/widgets" + params.should have_key("widget") + params["widget"].should have_key("files") + params["widget"]["files"][0].should be_an_instance_of(File) + params["widget"]["files"][0].path.should == @filename + params["widget"]["files"][1].should be_an_instance_of(File) + params["widget"]["files"][1].path.should == @filename + } + attach_file "Document", @filename + attach_file "Spreadsheet", @filename + click_button + end + + xit "should allow the content type to be specified" do + with_html <<-HTML + +
+ + + +
+ + HTML + ActionController::TestUploadedFile.should_receive(:new).with(@filename, "image/png").any_number_of_times + attach_file "Picture", @filename, "image/png" + click_button + end +end diff --git a/spec/private/merb/merb_session_spec.rb b/spec/private/merb/merb_session_spec.rb index 25560f7..06b4b99 100644 --- a/spec/private/merb/merb_session_spec.rb +++ b/spec/private/merb/merb_session_spec.rb @@ -22,6 +22,25 @@ describe Webrat::MerbSession do end end + %w{post put}.each do |request_method| + it "should call do request with method #{request_method.upcase} with a file attachment" do + session = Webrat::MerbSession.new + response = OpenStruct.new + response.status = 200 + + file = File.new(__FILE__) + session.should_receive(:request).with { |path, env| + path.should == "url" + env[:method].should == request_method.upcase + env[:headers].should be_nil + env[:input].should be_an_instance_of(StringIO) + env["CONTENT_LENGTH"].should be_an_instance_of(Fixnum) + env["CONTENT_TYPE"].should match(/multipart.*boundary/) + }.and_return(response) + session.send(request_method, 'url', { :file => file }, nil) + end + end + context "a session with a response" do before do @session = Webrat::MerbSession.new From 26edfbc7cb2c82282789fabfceb7812a83d1c666 Mon Sep 17 00:00:00 2001 From: Ryan Carver Date: Fri, 24 Apr 2009 14:11:49 -0700 Subject: [PATCH 2/3] Integration tests for Merb file uploads --- spec/integration/merb/app/controllers/testing.rb | 9 +++++++++ spec/integration/merb/app/views/testing/upload.html.erb | 9 +++++++++ spec/integration/merb/config/router.rb | 1 + spec/integration/merb/spec/webrat_spec.rb | 7 +++++++ 4 files changed, 26 insertions(+) create mode 100644 spec/integration/merb/app/views/testing/upload.html.erb diff --git a/spec/integration/merb/app/controllers/testing.rb b/spec/integration/merb/app/controllers/testing.rb index 31ccbeb..14cba1b 100644 --- a/spec/integration/merb/app/controllers/testing.rb +++ b/spec/integration/merb/app/controllers/testing.rb @@ -4,6 +4,15 @@ class Testing < Application render end + def upload + case request.method + when :get then render + when :post then + uploaded_file = params[:uploaded_file] + render [uploaded_file[:filename], uploaded_file[:tempfile].class.name].inspect + end + end + def submit_form end diff --git a/spec/integration/merb/app/views/testing/upload.html.erb b/spec/integration/merb/app/views/testing/upload.html.erb new file mode 100644 index 0000000..f750c55 --- /dev/null +++ b/spec/integration/merb/app/views/testing/upload.html.erb @@ -0,0 +1,9 @@ +

Webrat Form

+ +
+ + +
\ No newline at end of file diff --git a/spec/integration/merb/config/router.rb b/spec/integration/merb/config/router.rb index c57ec70..6ff8c3c 100644 --- a/spec/integration/merb/config/router.rb +++ b/spec/integration/merb/config/router.rb @@ -28,6 +28,7 @@ Merb.logger.info("Compiling routes...") Merb::Router.prepare do match("/").to(:controller => "testing", :action => "show_form") + match("/upload").to(:controller => "testing", :action => "upload") match("/internal_redirect").to(:controller => "testing", :action => "internal_redirect") match("/external_redirect").to(:controller => "testing", :action => "external_redirect") end diff --git a/spec/integration/merb/spec/webrat_spec.rb b/spec/integration/merb/spec/webrat_spec.rb index bdaad20..6264dae 100644 --- a/spec/integration/merb/spec/webrat_spec.rb +++ b/spec/integration/merb/spec/webrat_spec.rb @@ -29,4 +29,11 @@ describe "Webrat" do response = visit "/external_redirect" response.status.should == 302 end + + it "should upload files" do + visit "/upload" + attach_file "File", __FILE__ + response = click_button "Upload" + response.should contain(%(["webrat_spec.rb", "Tempfile"])) + end end From 1a110fe9083c742a7532348d5c067185ccfa2c9d Mon Sep 17 00:00:00 2001 From: Ryan Carver Date: Fri, 24 Apr 2009 14:23:00 -0700 Subject: [PATCH 3/3] Improve forward and backward compatibility for Merb uploads --- lib/webrat/merb_session.rb | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/webrat/merb_session.rb b/lib/webrat/merb_session.rb index b21468f..6c6ab94 100644 --- a/lib/webrat/merb_session.rb +++ b/lib/webrat/merb_session.rb @@ -5,7 +5,12 @@ gem "extlib" require "extlib" require "merb-core" -require "merb-core/two-oh" +begin + # Require Merb::Test::MultipartRequestHelper with multipart support. + require "merb-core/two-oh" +rescue LoadError => e + # Maybe Merb got rid of this. We'll do more checking for multiparth support. +end # HashWithIndifferentAccess = Mash @@ -38,13 +43,14 @@ module Webrat def response_code @response.status end + include Merb::Test::MultipartRequestHelper def do_request(url, data, headers, method) - if method == "POST" && has_file?(data) + if method == "POST" && supports_multipart? && has_file?(data) @response = multipart_post(url, data, :headers => headers) - elsif method == "PUT" && has_file?(data) + elsif method == "PUT" && supports_multipart? && has_file?(data) @response = multipart_put(url, data, :headers => headers) else @@ -57,6 +63,13 @@ module Webrat protected + # multipart_post and multipart_put which use request to do their + # business through multipart_request. Older implementations of + # multipart_post and multipart_put use the controller directly. + def supports_multipart? + respond_to?(:multipart_request) + end + # Recursively search the data for a file attachment. def has_file?(data) data.each do |key, value|