diff --git a/features/fixtures/file.png b/features/fixtures/file.png new file mode 100644 index 00000000..6c10576f Binary files /dev/null and b/features/fixtures/file.png differ diff --git a/features/step_definitions/image_steps.rb b/features/step_definitions/image_steps.rb new file mode 100644 index 00000000..d7d8f724 --- /dev/null +++ b/features/step_definitions/image_steps.rb @@ -0,0 +1,8 @@ +Given /^I move the sample image into the workspace$/ do + FileUtils.cp("features/fixtures/file.png", current_dir) +end + +Then /^the generated documentation should be encoded correctly$/ do + file = File.read(File.join(current_dir, "doc", "api", "foobars", "uploading_a_file.html")) + file.should =~ /file\.png/ +end diff --git a/features/upload_file.feature b/features/upload_file.feature new file mode 100644 index 00000000..90d06032 --- /dev/null +++ b/features/upload_file.feature @@ -0,0 +1,85 @@ +Feature: Uploading a file + Background: + Given a file named "app.rb" with: + """ + require 'rack' + + class App + def self.call(env) + request = Rack::Request.new(env) + [200, {}, [request.params["file"][:filename]]] + end + end + """ + + Scenario: Uploading a text file + Given a file named "file.txt" with: + """ + a file to upload + """ + And a file named "app_spec.rb" with: + """ + require "rspec_api_documentation" + require "rspec_api_documentation/dsl" + require "rack/test" + + RspecApiDocumentation.configure do |config| + config.app = App + end + + resource "FooBars" do + post "/foobar" do + parameter :name, "Name of file" + parameter :file, "File to upload" + + let(:name) { "my-new-file.txt" } + let(:file) do + Rack::Test::UploadedFile.new("file.txt", "text/plain") + end + + example_request "Uploading a file" do + response_body.should == "file.txt" + end + end + end + """ + + When I run `rspec app_spec.rb --require ./app.rb --format RspecApiDocumentation::ApiFormatter` + + Then the output should contain "1 example, 0 failures" + And the exit status should be 0 + + Scenario: Uploading an image file + Given I move the sample image into the workspace + And a file named "app_spec.rb" with: + """ + require "rspec_api_documentation" + require "rspec_api_documentation/dsl" + require "rack/test" + + RspecApiDocumentation.configure do |config| + config.app = App + end + + resource "FooBars" do + post "/foobar" do + parameter :name, "Name of file" + parameter :file, "File to upload" + + let(:name) { "my-new-file.txt" } + let(:file) do + Rack::Test::UploadedFile.new("file.png", "image/png") + end + + example_request "Uploading a file" do + response_body.should == "file.png" + end + end + end + """ + + When I run `rspec app_spec.rb --require ./app.rb --format RspecApiDocumentation::ApiFormatter` + + Then the output should contain "1 example, 0 failures" + And the exit status should be 0 + And the generated documentation should be encoded correctly diff --git a/lib/rspec_api_documentation/client_base.rb b/lib/rspec_api_documentation/client_base.rb index 99ff3d95..f348fafc 100644 --- a/lib/rspec_api_documentation/client_base.rb +++ b/lib/rspec_api_documentation/client_base.rb @@ -49,6 +49,10 @@ def document_example(method, path) request_metadata = {} + if request_content_type =~ /multipart\/form-data/ && respond_to?(:handle_multipart_body, true) + request_body = handle_multipart_body(request_headers, request_body) + end + request_metadata[:request_method] = method request_metadata[:request_path] = path request_metadata[:request_body] = request_body.empty? ? nil : request_body diff --git a/lib/rspec_api_documentation/rack_test_client.rb b/lib/rspec_api_documentation/rack_test_client.rb index b819979e..96084d1f 100644 --- a/lib/rspec_api_documentation/rack_test_client.rb +++ b/lib/rspec_api_documentation/rack_test_client.rb @@ -42,6 +42,22 @@ def headers(*args) headers_to_env(super) end + def handle_multipart_body(request_headers, request_body) + parsed_parameters = Rack::Request.new({ + "CONTENT_TYPE" => request_headers["Content-Type"], + "rack.input" => StringIO.new(request_body) + }).params + + parsed_parameters.each do |_, value| + if value.is_a?(Hash) && value.has_key?(:tempfile) + data = value[:tempfile].read + request_body = request_body.gsub(data, "[uploaded data]") + end + end + + request_body + end + private def rack_test_session