Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ViewComponent 4.x output-buffer ordering bug — minimal repro

This repository is a self-contained repro for a test-ordering bug in ViewComponent 4.x when Slim templates are rendered via a Temple::Generators::RailsOutputBuffer handler with streaming: true.

The bug

When a spec/components/ example that calls render_inline runs before a spec/controllers/ example that uses render_views, the controller spec produces response.body == "" instead of the expected HTML.

The bug does not appear when the controller spec runs in isolation.

Repro command

bundle install
bundle exec rspec spec/components/null_component_spec.rb \
                  spec/controllers/search_controller_spec.rb

Expected output

2 examples, 0 failures

Actual output

Failures:

  1) SearchController GET #index responds with rendered HTML
     Failure/Error: expect(response.body).to include('search results')
       expected "" to include "search results"

The second spec passes when run alone:

bundle exec rspec spec/controllers/search_controller_spec.rb  # passes

Affected versions

Gem Version
rails ~> 8.1
view_component ~> 4.2 (confirmed 4.12.0)
slim 5.2.1
slim-rails ~> 4.0
temple ~> 0.10

Root cause

ViewComponent::Base#render_in calls output_buffer.with_buffer { render_template }. with_buffer temporarily swaps @raw_buffer on the current ActionView::OutputBuffer for a new empty buffer, yields, then restores the original.

The Slim template handler configured with streaming: true (see config/initializers/view_component_slim.rb) generates the following at the top of every .html.slim template:

@output_buffer = output_buffer || ActionView::OutputBuffer.new

When this line executes inside with_buffer, @output_buffer is reassigned to the captured reference (the pre-swap buffer). The interaction between this reassignment and ViewComponent's buffer-swap mechanism leaves @raw_buffer pointing at the temporary buffer after the component spec teardown. Subsequent controller specs then write rendered HTML into the now-orphaned temporary buffer rather than the response buffer, producing an empty response.body.

Workaround

Save and restore @raw_buffer explicitly around every render_in call:

# spec/support/view_component_output_buffer.rb
module ViewComponent
  module OutputBufferFix
    def render_in(view_context, &block)
      saved = view_context.output_buffer.instance_variable_get(:@raw_buffer)
      result = super
      view_context.output_buffer.instance_variable_set(:@raw_buffer, saved)
      result
    end
  end
end

ViewComponent::Base.prepend(ViewComponent::OutputBufferFix)

Add require 'support/view_component_output_buffer' to spec/rails_helper.rb and the ordering dependency disappears.

Configuration replicated here

config/initializers/view_component_slim.rb mirrors the view_component_slim_compatibility internal gem used in the G2 application:

ActiveSupport.on_load(:view_component) do
  slim_handler_factory = Temple::Templates::Rails(
    Slim::Engine,
    register_as: :slim,
    generator: Temple::Generators::RailsOutputBuffer,
    disable_capture: true,
    streaming: true
  )
  ActionView::Template.register_template_handler(:slim, slim_handler_factory.new)
end

The bug disappears when streaming: false is passed (or when the Slim template handler is not re-registered and the default slim-rails handler is used instead), because the generated template code then creates a fresh ActionView::OutputBuffer.new rather than capturing the ambient one.

About

Minimal repro for ViewComponent 4.x output-buffer ordering bug with Slim templates

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages