From 520cd8f0e3320ccbb5873bfe334b90f6e56b3dad Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Wed, 14 Feb 2024 19:18:25 -0500 Subject: [PATCH 1/2] Action View Test Case `rendered` memoization Follow-up to [49856][] Follow-up to [49194][] The introduction of memoization as an optimization posed a backwards incompatible change to View tests that call `render` multiple times. This commit changes the `@rendered` instance variable from a `String` to an instance of the `RenderedViewContent` specialized `String` subclass. The end result is that there is no memoization to reset, and the memoization optimization side-effect is preserved after rendering for test cases where `rendered` (or parser methods like `rendered.html`) might be invoked more than once. [49856]: https://github.com/rails/rails/pull/49856#issuecomment-1945039015 [49194]: https://github.com/rails/rails/pull/49194/files#diff-ce84a807f3491121a5230d37bd40454bb1407fcca71179e1a2fa76d4c0ddfa2aR293 --- actionview/lib/action_view/test_case.rb | 10 +++++----- actionview/test/template/test_case_test.rb | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb index c12feeac954a9..6c79054f612ba 100644 --- a/actionview/lib/action_view/test_case.rb +++ b/actionview/lib/action_view/test_case.rb @@ -60,7 +60,7 @@ module Behavior include ActiveSupport::Testing::ConstantLookup delegate :lookup_context, to: :controller - attr_accessor :controller, :request, :output_buffer + attr_accessor :controller, :request, :output_buffer, :rendered module ClassMethods def inherited(descendant) # :nodoc: @@ -223,7 +223,7 @@ def setup_with_controller @request = @controller.request @view_flow = ActionView::OutputFlow.new @output_buffer = ActionView::OutputBuffer.new - @rendered = +"" + @rendered = self.class.content_class.new(+"") test_case_instance = self controller_class.define_method(:_test_case) { test_case_instance } @@ -243,6 +243,9 @@ def rendered_views @_rendered_views ||= RenderedViewsCollection.new end + ## + # :method: rendered + # # Returns the content rendered by the last +render+ call. # # The returned object behaves like a string but also exposes a number of methods @@ -290,9 +293,6 @@ def rendered_views # # assert_pattern { rendered.json => { title: "Hello, world" } } # end - def rendered - @_rendered ||= self.class.content_class.new(@rendered) - end def _routes @controller._routes if @controller.respond_to?(:_routes) diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index 163ec700d2255..8d43ab3599004 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -394,6 +394,26 @@ class RenderedViewContentTest < ActionView::TestCase assert_match(/#{developer.name}/, rendered) assert_includes rendered, developer.name end + + test "#rendered resets after each render" do + render "developers/developer", developer: DeveloperStruct.new("first") + + assert_includes rendered, "first" + assert_not_includes rendered, "second" + assert_not_includes rendered, "third" + + render "developers/developer", developer: DeveloperStruct.new("second") + + assert_includes rendered, "first" + assert_includes rendered, "second" + assert_not_includes rendered, "third" + + render "developers/developer", developer: DeveloperStruct.new("third") + + assert_includes rendered, "first" + assert_includes rendered, "second" + assert_includes rendered, "third" + end end class HTMLParserTest < ActionView::TestCase From 01502fe2395737d04f169245fdab9ccc415a5b77 Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Wed, 14 Feb 2024 20:41:08 -0500 Subject: [PATCH 2/2] Remove Upgrade Guidance --- guides/source/upgrading_ruby_on_rails.md | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 03a26731e2ae5..665c7e689d900 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -283,30 +283,6 @@ const fileInputSelector = Rails.fileInputSelector Rails.fileInputSelector(...) ``` -### `ActionView::TestCase#rendered` no longer returns a `String` - -Starting from Rails 7.1, `ActionView::TestCase#rendered` returns an object that -responds to various format methods (for example, `rendered.html` and -`rendered.json`). To preserve backward compatibility, the object returned from -`rendered` will delegate missing methods to the `String` rendered during the -test. For example, the following [assert_match][] assertion will pass: - -```ruby -assert_match(/some content/i, rendered) -``` - -However, if your tests rely on `ActionView::TestCase#rendered` returning an -instance of `String`, they will fail. To restore the original behavior, you can -override the `#rendered` method to read from the `@rendered` instance variable: - -```ruby -# config/initializers/action_view.rb - -ActiveSupport.on_load :action_view_test_case do - attr_reader :rendered -end -``` - ### `Rails.logger` now returns an `ActiveSupport::BroadcastLogger` instance The `ActiveSupport::BroadcastLogger` class is a new logger that allows to broadcast logs to different sinks (STDOUT, a log file...) in an easy way.