Skip to content

Commit

Permalink
Treat as: :html tests request params as :url_encoded_form
Browse files Browse the repository at this point in the history
Closes [#50345][]

First, handle the exception mentioned in [#50345][]:

```
BugTest#test_params_with_htm_content_type:
NoMethodError: undefined method `to_html' for {:name=>"Muad'Dib"}:Hash
    .../actionpack/lib/action_dispatch/testing/request_encoder.rb:39:in `encode_params'
    .../actionpack/lib/action_dispatch/testing/integration.rb:251:in `process'
```

Calls with `as: :html` result in a `NoMethodError` because
`Hash#to_html` does not exist.

Passing `as: :html` implies that the request parameters will come from a
`POST` body encoded as `text/html`. That isn't entirely true -- browsers
will encode `POST` parameters as with the `Content-Type:` header set to
either [application/x-www-form-urlencoded][] or [multipart/form-data][].
This commit skips setting the `CONTENT_TYPE` Rack header when processed
with `as: :html`.

To account for that, extend the `RequestEncoder` constructor to accept a
`content_type `argument to use when provided. When omitted, continue to
fall back to the provided MIME type. Extend the default `:html` encoder
configuration to default to submitting with `Content-Type:
x-www-form-urlencoded`.

[#50345]: #50345
[application/x-www-form-urlencoded]: https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data#the_post_method
[multipart/form-data]: https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data#the_enctype_attribute
  • Loading branch information
seanpdoyle committed May 16, 2024
1 parent 705b70a commit aad22ad
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 9 deletions.
18 changes: 9 additions & 9 deletions actionpack/lib/action_dispatch/testing/request_encoder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# :markup: markdown

require "nokogiri"
require "action_dispatch/http/mime_type"

module ActionDispatch
class RequestEncoder # :nodoc:
Expand All @@ -15,9 +16,9 @@ def response_parser; -> body { body }; end

@encoders = { identity: IdentityEncoder.new }

attr_reader :response_parser
attr_reader :response_parser, :content_type

def initialize(mime_name, param_encoder, response_parser)
def initialize(mime_name, param_encoder, response_parser, content_type)
@mime = Mime[mime_name]

unless @mime
Expand All @@ -27,10 +28,7 @@ def initialize(mime_name, param_encoder, response_parser)

@response_parser = response_parser || -> body { body }
@param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
end

def content_type
@mime.to_s
@content_type = content_type || @mime.to_s
end

def accept_header
Expand All @@ -50,11 +48,13 @@ def self.encoder(name)
@encoders[name] || @encoders[:identity]
end

def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
@encoders[mime_name] = new(mime_name, param_encoder, response_parser)
def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil, content_type: nil)
@encoders[mime_name] = new(mime_name, param_encoder, response_parser, content_type)
end

register_encoder :html, response_parser: -> body { Rails::Dom::Testing.html_document.parse(body) }
register_encoder :html, response_parser: -> body { Rails::Dom::Testing.html_document.parse(body) },
param_encoder: -> param { param },
content_type: Mime[:url_encoded_form].to_s
register_encoder :json, response_parser: -> body { JSON.parse(body, object_class: ActiveSupport::HashWithIndifferentAccess) }
end
end
17 changes: 17 additions & 0 deletions actionpack/test/controller/integration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,12 @@ def foos
render plain: "ok"
end

def foos_html
render inline: <<~ERB
<code><%= params.permit(:foo) %></code>
ERB
end

def foos_json
render json: params.permit(:foo)
end
Expand Down Expand Up @@ -1127,6 +1133,17 @@ def test_standard_json_encoding_works
end
end

def test_encoding_as_html
post_to_foos as: :html do
assert_response :success
assert_equal "application/x-www-form-urlencoded", request.media_type
assert_equal "text/html", request.accepts.first.to_s
assert_equal :html, request.format.ref
assert_equal({ "foo" => "fighters" }, request.request_parameters)
assert_equal({ "foo" => "fighters" }.to_s, response.parsed_body.at("code").text)
end
end

def test_encoding_as_json
post_to_foos as: :json do
assert_response :success
Expand Down

0 comments on commit aad22ad

Please sign in to comment.