Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActionDispatch::Testing::TestResponse#parsed_body parse HTML with Nokogiri #47144

Merged

Conversation

seanpdoyle
Copy link
Contributor

@seanpdoyle seanpdoyle commented Jan 25, 2023

Motivation / Background

Prior to this commit, the only out-of-the-box parsing that ActionDispatch::Testing::TestResponse#parsed_body supported was for application/json requests. This meant that response.body == response.parsed_body for HTML requests.

Using parsed_body for JSON requests supports Hash#fetch, Hash#dig, and Ruby 3.2 destructuring assignment and pattern matching.

Detail

The introduction of Nokogiri support for pattern
matching
poses an opportunity to make assertions about the structure of the HTML response.

On top of that, there is ongoing work to introduce pattern matching support in MiniTest.

get "/posts"
response.content_type         # => "text/html; charset=utf-8"
response.parsed_body.class    # => Nokogiri::HTML5::Document
response.parsed_body.to_html  # => "<!DOCTYPE html>\n<html>\n..."

Checklist

Before submitting the PR make sure the following are checked:

  • This Pull Request is related to one change. Changes that are unrelated should be opened in separate PRs.
  • Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: [Fix #issue-number]
  • Tests are added or updated if you fix a bug or add a feature.
  • CHANGELOG files are updated for the changed libraries if there is a behavior change or additional feature. Minor bug fixes and documentation changes should not be included.

@rails-bot rails-bot bot added the actionpack label Jan 25, 2023
actionpack/lib/action_dispatch/testing/request_encoder.rb Outdated Show resolved Hide resolved
get "/posts"
response.content_type # => "text/html; charset=utf-8"
response.parsed_body.class # => Nokogiri::HTML5::Document
response.parsed_body.to_html # => "<!DOCTYPE html>\n<html>\n..."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that people that were expecting strings will have to rewrite their tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be a breaking change.

people that were expecting strings will have to rewrite their tests?

I wasn't sure about this. Since response.parsed_body and response.body return the same type (String), would they be motivated to use response.parsed_body?

Would upgrading from 7.0 to 7.1 be an appropriate time to introduce a breaking change? Would a breaking change require introducing a configuration? Something like action_dispatch.html_parsed_body_as_document = true that <= 7.0 applications would set to false?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is fine to just explicitly document that the return type of that method is going to change.

@seanpdoyle seanpdoyle force-pushed the action-dispatch-testing-parsed-body-html branch 2 times, most recently from 250c6d4 to 3bc7d1d Compare January 25, 2023 21:52
@@ -35,6 +35,7 @@ Gem::Specification.new do |s|

s.add_dependency "activesupport", version

s.add_dependency "nokogiri", ">= 1.8.5"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version was copied from actiontext/actiontext.gemspec.

Gemfile.lock Outdated Show resolved Hide resolved
Gemfile.lock Outdated Show resolved Hide resolved
Copy link
Member

@flavorjones flavorjones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted in my inline comment, let's please not assume Nokogiri::HTML5 exists, and check for it so we can fall back to Nokogiri::HTML for platforms and versions that don't support it.

@@ -50,6 +52,7 @@ def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
@encoders[mime_name] = new(mime_name, param_encoder, response_parser)
end

register_encoder :html, response_parser: -> body { Nokogiri::HTML5.parse(body) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note that the Nokogiri::HTML5 module/namespace will not exist for Nokogiri < 1.12.0 or for any JRuby user (see sparklemotion/nokogiri#2227).

I think we should take care to gracefully fall back to Nokogiri::HTML.parse(body) unless defined?(::Nokogiri::HTML5)

@seanpdoyle seanpdoyle force-pushed the action-dispatch-testing-parsed-body-html branch from 3bc7d1d to 8b3835e Compare January 27, 2023 00:31
@seanpdoyle seanpdoyle force-pushed the action-dispatch-testing-parsed-body-html branch from 8b3835e to 180fe3e Compare January 27, 2023 14:31
…okogiri

Prior to this commit, the only out-of-the-box parsing that
`ActionDispatch::Testing::TestResponse#parsed_body` supported was for
`application/json` requests. This meant that `response.body ==
response.parsed_body` for HTML requests.

```ruby
get "/posts"
response.content_type         # => "text/html; charset=utf-8"
response.parsed_body.class    # => Nokogiri::HTML5::Document
response.parsed_body.to_html  # => "<!DOCTYPE html>\n<html>\n..."
```

Using `parsed_body` for JSON requests supports `Hash#fetch`, `Hash#dig`,
and Ruby 3.2 destructuring assignment and pattern matching.

The introduction of [Nokogiri support for pattern
matching][nokogiri-pattern-matching] poses an opportunity to make assertions
about the structure of the HTML response.

On top of that, there is ongoing work to [introduce pattern matching
support in MiniTest][minitest-pattern-matching].

[nokogiri-pattern-matching]: sparklemotion/nokogiri#2523
[minitest-pattern-matching]: minitest/minitest#936
@seanpdoyle seanpdoyle force-pushed the action-dispatch-testing-parsed-body-html branch from 180fe3e to ad79ed0 Compare January 28, 2023 04:52
Copy link
Member

@flavorjones flavorjones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for making those changes!

@rafaelfranca rafaelfranca merged commit c819075 into rails:main Jan 30, 2023
@seanpdoyle seanpdoyle deleted the action-dispatch-testing-parsed-body-html branch January 30, 2023 22:54
flavorjones added a commit to flavorjones/rails that referenced this pull request Aug 3, 2023
Use the helpers introduced in rails-dom-testing 2.2.0 instead of
managing the HTML parsers as was done in rails#48523.

See also related rails#47144 / ad79ed0
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Aug 23, 2023
Add notes for [rails#47144][] to the 7.1 Release Notes.

Additionally, in the time since that was merged, both Nokogiri and
Minitest have merged the PRs mentioned to integrate support for Ruby's
Pattern matching (sparklemotion/nokogiri#2523
and minitest/minitest#936, respectively).

This commit adds coverage for those new assertions, and incorporates
guidance into the release notes.

[rails#47144]: rails#47144
paulreece pushed a commit to paulreece/rails-paulreece that referenced this pull request Aug 26, 2023
Use the helpers introduced in rails-dom-testing 2.2.0 instead of
managing the HTML parsers as was done in rails#48523.

See also related rails#47144 / ad79ed0
paulreece pushed a commit to paulreece/rails-paulreece that referenced this pull request Aug 26, 2023
Add notes for [rails#47144][] to the 7.1 Release Notes.

Additionally, in the time since that was merged, both Nokogiri and
Minitest have merged the PRs mentioned to integrate support for Ruby's
Pattern matching (sparklemotion/nokogiri#2523
and minitest/minitest#936, respectively).

This commit adds coverage for those new assertions, and incorporates
guidance into the release notes.

[rails#47144]: rails#47144
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Oct 16, 2023
As of [rails#47144][], `response.parsed_body` supports parsing both `format:
:json` and `format: :html` responses. This commit updates the
documentation to reflect that.

Additionally, it cribs the `ActionDispatch::IntegrationTest`
documentation that mentions `register_encoder`, and copies it to the
method definition so that the documentation entry for that method has
descriptive information.

[rails#47144]: rails#47144
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Jan 2, 2024
As of [rails#47144][], `response.parsed_body` supports parsing both `format:
:json` and `format: :html` responses. This commit updates the
documentation to reflect that.

Additionally, it cribs the `ActionDispatch::IntegrationTest`
documentation that mentions `register_encoder`, and copies it to the
method definition so that the documentation entry for that method has
descriptive information.

[rails#47144]: rails#47144
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Jan 30, 2024
As of [rails#47144][], `response.parsed_body` supports parsing both `format:
:json` and `format: :html` responses. This commit updates the
documentation to reflect that.

Additionally, it cribs the `ActionDispatch::IntegrationTest`
documentation that mentions `register_encoder`, and copies it to the
method definition so that the documentation entry for that method has
descriptive information.

[rails#47144]: rails#47144
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Mar 2, 2024
As of [rails#47144][], `response.parsed_body` supports parsing both `format:
:json` and `format: :html` responses. This commit updates the
documentation to reflect that.

Additionally, it cribs the `ActionDispatch::IntegrationTest`
documentation that mentions `register_encoder`, and copies it to the
method definition so that the documentation entry for that method has
descriptive information.

[rails#47144]: rails#47144
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Mar 8, 2024
As of [rails#47144][], `response.parsed_body` supports parsing both `format:
:json` and `format: :html` responses. This commit updates the
documentation to reflect that.

Additionally, it cribs the `ActionDispatch::IntegrationTest`
documentation that mentions `register_encoder`, and copies it to the
method definition so that the documentation entry for that method has
descriptive information.

[rails#47144]: rails#47144
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Apr 19, 2024
As of [rails#47144][], `response.parsed_body` supports parsing both `format:
:json` and `format: :html` responses. This commit updates the
documentation to reflect that.

Additionally, it cribs the `ActionDispatch::IntegrationTest`
documentation that mentions `register_encoder`, and copies it to the
method definition so that the documentation entry for that method has
descriptive information.

[rails#47144]: rails#47144
seanpdoyle added a commit to seanpdoyle/rails that referenced this pull request Apr 20, 2024
As of [rails#47144][], `response.parsed_body` supports parsing both `format:
:json` and `format: :html` responses. This commit updates the
documentation to reflect that.

Additionally, it cribs the `ActionDispatch::IntegrationTest`
documentation that mentions `register_encoder`, and copies it to the
method definition so that the documentation entry for that method has
descriptive information.

[rails#47144]: rails#47144
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants