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

Prevent stub_template from leaking between view specs #2714

Merged
merged 2 commits into from Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions example_app_generator/app/views/_example.html.erb
@@ -0,0 +1 @@
TEMPLATE_HTML
2 changes: 2 additions & 0 deletions example_app_generator/generate_stuff.rb
Expand Up @@ -14,10 +14,12 @@ def setup_tasks

def final_tasks
copy_file 'spec/verify_active_record_spec.rb'
copy_file 'app/views/_example.html.erb'
copy_file 'app/views/foo.html'
copy_file 'app/views/some_templates/bar.html'
copy_file 'spec/verify_custom_renderers_spec.rb'
copy_file 'spec/verify_fixture_warning_spec.rb'
copy_file 'spec/verify_view_path_stub_spec.rb'
run('bin/rake db:migrate')
end

Expand Down
17 changes: 17 additions & 0 deletions example_app_generator/spec/verify_view_path_stub_spec.rb
@@ -0,0 +1,17 @@
require 'rails_helper'

RSpec.describe "verify view path doesnt leak stubs between examples", type: :view, order: :defined do
subject(:html) do
render partial: "example"
rendered
end

it "renders the stub template" do
stub_template("_example.html.erb" => "STUB_HTML")
expect(html).to include("STUB_HTML")
end

it "renders the file template" do
expect(html).to include("TEMPLATE_HTML")
end
end
28 changes: 8 additions & 20 deletions lib/rspec/rails/example/view_example_group.rb
Expand Up @@ -83,26 +83,14 @@ def view
_view
end

if ::Rails.version.to_f >= 7.1
# Simulates the presence of a template on the file system by adding a
# Rails' FixtureResolver to the front of the view_paths list. Designed to
# help isolate view examples from partials rendered by the view template
# that is the subject of the example.
#
# stub_template("widgets/_widget.html.erb" => "This content.")
def stub_template(hash)
view.view_paths.send(:initialize_copy, ActionView::PathSet.new([StubResolverCache.resolver_for(hash)] + view.view_paths.paths))
end
else
# Simulates the presence of a template on the file system by adding a
# Rails' FixtureResolver to the front of the view_paths list. Designed to
# help isolate view examples from partials rendered by the view template
# that is the subject of the example.
#
# stub_template("widgets/_widget.html.erb" => "This content.")
def stub_template(hash)
view.view_paths.unshift(StubResolverCache.resolver_for(hash))
end
# Simulates the presence of a template on the file system by adding a
# Rails' FixtureResolver to the front of the view_paths list. Designed to
# help isolate view examples from partials rendered by the view template
# that is the subject of the example.
#
# stub_template("widgets/_widget.html.erb" => "This content.")
def stub_template(hash)
controller.prepend_view_path(StubResolverCache.resolver_for(hash))
end

# Provides access to the params hash that will be available within the
Expand Down
58 changes: 45 additions & 13 deletions spec/rspec/rails/example/view_example_group_spec.rb
Expand Up @@ -280,33 +280,65 @@ def _view; end

describe '#stub_template' do
let(:view_spec_group) do
Class.new do
include ViewExampleGroup::ExampleMethods
def _view
@_view ||= Struct.new(:view_paths).new(ActionView::PathSet.new(['some-path']))
end
RSpec.describe "a view spec" do
include ::RSpec::Rails::ViewExampleGroup
end
end

it 'prepends an ActionView::FixtureResolver to the view path' do
view_spec = view_spec_group.new
view_spec.stub_template('some_path/some_template' => 'stubbed-contents')
result = :not_loaded

result = view_spec.view.view_paths.first
view_spec_group.specify do
stub_template('some_path/some_template' => 'stubbed-contents')
result = view.view_paths.first
end
view_spec_group.run

expect(result).to be_instance_of(ActionView::FixtureResolver)
data = result.respond_to?(:data) ? result.data : result.hash
expect(data).to eq('some_path/some_template' => 'stubbed-contents')
end

it 'caches FixtureResolver instances between examples' do
example_one_view_paths = :not_set
example_two_view_paths = :not_set

view_spec_group.specify do
stub_template('some_path/some_template' => 'stubbed-contents')
example_one_view_paths = view.view_paths
end
view_spec_group.specify do
stub_template('some_path/some_template' => 'stubbed-contents')
example_two_view_paths = view.view_paths
end
view_spec_group.run

expect(example_one_view_paths.first).to eq(example_two_view_paths.first)
end

it 'caches FixtureResolver instances between example groups' do
view_spec_one = view_spec_group.new
view_spec_two = view_spec_group.new
example_one_view_paths = :not_set
example_two_view_paths = :not_set

view_spec_one.stub_template('some_path/some_template' => 'stubbed-contents')
view_spec_two.stub_template('some_path/some_template' => 'stubbed-contents')
RSpec.describe "a view spec" do
include ::RSpec::Rails::ViewExampleGroup

specify do
stub_template('some_path/some_template' => 'stubbed-contents')
example_one_view_paths = view.view_paths
end
end.run

RSpec.describe "another view spec" do
include ::RSpec::Rails::ViewExampleGroup

specify do
stub_template('some_path/some_template' => 'stubbed-contents')
example_two_view_paths = view.view_paths
end
end.run

expect(view_spec_one.view.view_paths.first).to eq(view_spec_two.view.view_paths.first)
expect(example_one_view_paths.first).to eq(example_two_view_paths.first)
end
end
end
Expand Down