Skip to content

Commit 0387ff2

Browse files
committed
Fix spec type inference.
- In #970 we added `infer_spec_type_from_file_location!` but forgot to remove the code in `lib/rspec/rails/configuration.rb` that made it always infer - so the opt-in API was there but it was always enabled. Here I've made it truly opt-in. - The logic of `infer_spec_type_from_file_location!` also setup the helper module inclusion via explicit `:type` metadata but was only intended to setup the implicit mapping of spec file location to type. Here I've made the module inclusion via `:type` always work w/o an explicit config option needed to enable it. - We used to mutate the `:type` metadata on inclusion of the helper module, but this caused bugs such as #825. The problem is that rspec-core uses a raw hash for the metadata and doesn't re-apply filtering/inclusion logic when the metadata hash is mutated. Instead, we use a new explicit `define_derived_metadata` API. Fixes #825 and closes #829.
1 parent 2fec960 commit 0387ff2

File tree

3 files changed

+63
-130
lines changed

3 files changed

+63
-130
lines changed

lib/rspec/rails/configuration.rb

Lines changed: 31 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
module RSpec
22
module Rails
33
# @private
4-
def self.add_rspec_rails_config_api_to(config)
4+
def self.initialize_configuration(config)
5+
config.backtrace_exclusion_patterns << /vendor\//
6+
config.backtrace_exclusion_patterns << /lib\/rspec\/rails/
7+
8+
config.include RSpec::Rails::ControllerExampleGroup, :type => :controller
9+
config.include RSpec::Rails::HelperExampleGroup, :type => :helper
10+
config.include RSpec::Rails::ModelExampleGroup, :type => :model
11+
config.include RSpec::Rails::RequestExampleGroup, :type => :request
12+
config.include RSpec::Rails::RoutingExampleGroup, :type => :routing
13+
config.include RSpec::Rails::ViewExampleGroup, :type => :view
14+
config.include RSpec::Rails::FeatureExampleGroup, :type => :feature
15+
16+
if defined?(RSpec::Rails::MailerExampleGroup)
17+
config.include RSpec::Rails::MailerExampleGroup, :type => :mailer
18+
end
19+
520
# controller settings
621
config.add_setting :infer_base_class_for_anonymous_controllers, :default => true
722

@@ -34,137 +49,24 @@ def config.render_views?
3449
end
3550

3651
def config.infer_spec_type_from_file_location!
37-
def self.escaped_path(*parts)
38-
Regexp.compile(parts.join('[\\\/]') + '[\\\/]')
39-
end
40-
41-
controller_path_regex = self.escaped_path(%w[spec controllers])
42-
self.include RSpec::Rails::ControllerExampleGroup,
43-
:type => :controller,
44-
:file_path => lambda { |file_path, metadata|
45-
metadata[:type].nil? && controller_path_regex =~ file_path
46-
}
47-
48-
helper_path_regex = self.escaped_path(%w[spec helpers])
49-
self.include RSpec::Rails::HelperExampleGroup,
50-
:type => :helper,
51-
:file_path => lambda { |file_path, metadata|
52-
metadata[:type].nil? && helper_path_regex =~ file_path
53-
}
54-
55-
mailer_path_regex = self.escaped_path(%w[spec mailers])
56-
if defined?(RSpec::Rails::MailerExampleGroup)
57-
self.include RSpec::Rails::MailerExampleGroup,
58-
:type => :mailer,
59-
:file_path => lambda { |file_path, metadata|
60-
metadata[:type].nil? && mailer_path_regex =~ file_path
61-
}
52+
{
53+
:controller => %w[spec controllers],
54+
:helper => %w[spec helpers],
55+
:mailer => %w[spec mailers],
56+
:model => %w[spec models],
57+
:request => %w[spec (requests|integration|api)],
58+
:routing => %w[spec routing],
59+
:view => %w[spec views],
60+
:feature => %w[spec features]
61+
}.each do |type, dir_parts|
62+
escaped_path = Regexp.compile(dir_parts.join('[\\\/]') + '[\\\/]')
63+
define_derived_metadata(:file_path => escaped_path) do |metadata|
64+
metadata[:type] ||= type
65+
end
6266
end
63-
64-
model_path_regex = self.escaped_path(%w[spec models])
65-
self.include RSpec::Rails::ModelExampleGroup,
66-
:type => :model,
67-
:file_path => lambda { |file_path, metadata|
68-
metadata[:type].nil? && model_path_regex =~ file_path
69-
}
70-
71-
request_path_regex = self.escaped_path(%w[spec (requests|integration|api)])
72-
self.include RSpec::Rails::RequestExampleGroup,
73-
:type => :request,
74-
:file_path => lambda { |file_path, metadata|
75-
metadata[:type].nil? && request_path_regex =~ file_path
76-
}
77-
78-
routing_path_regex = self.escaped_path(%w[spec routing])
79-
self.include RSpec::Rails::RoutingExampleGroup,
80-
:type => :routing,
81-
:file_path => lambda { |file_path, metadata|
82-
metadata[:type].nil? && routing_path_regex =~ file_path
83-
}
84-
85-
view_path_regex = self.escaped_path(%w[spec views])
86-
self.include RSpec::Rails::ViewExampleGroup,
87-
:type => :view,
88-
:file_path => lambda { |file_path, metadata|
89-
metadata[:type].nil? && view_path_regex =~ file_path
90-
}
91-
92-
feature_example_regex = self.escaped_path(%w[spec features])
93-
self.include RSpec::Rails::FeatureExampleGroup,
94-
:type => :feature,
95-
:file_path => lambda { |file_path, metadata|
96-
metadata[:type].nil? && feature_example_regex =~ file_path
97-
}
9867
end
9968
end
100-
end
101-
end
102-
103-
RSpec.configure do |c|
104-
RSpec::Rails.add_rspec_rails_config_api_to(c)
105-
106-
c.backtrace_exclusion_patterns << /vendor\//
107-
c.backtrace_exclusion_patterns << /lib\/rspec\/rails/
108-
109-
def c.escaped_path(*parts)
110-
Regexp.compile(parts.join('[\\\/]') + '[\\\/]')
111-
end
112-
113-
controller_path_regex = c.escaped_path(%w[spec controllers])
114-
c.include RSpec::Rails::ControllerExampleGroup,
115-
:type => :controller,
116-
:file_path => lambda { |file_path, metadata|
117-
metadata[:type].nil? && controller_path_regex =~ file_path
118-
}
11969

120-
helper_path_regex = c.escaped_path(%w[spec helpers])
121-
c.include RSpec::Rails::HelperExampleGroup,
122-
:type => :helper,
123-
:file_path => lambda { |file_path, metadata|
124-
metadata[:type].nil? && helper_path_regex =~ file_path
125-
}
126-
127-
mailer_path_regex = c.escaped_path(%w[spec mailers])
128-
if defined?(RSpec::Rails::MailerExampleGroup)
129-
c.include RSpec::Rails::MailerExampleGroup,
130-
:type => :mailer,
131-
:file_path => lambda { |file_path, metadata|
132-
metadata[:type].nil? && mailer_path_regex =~ file_path
133-
}
70+
initialize_configuration RSpec.configuration
13471
end
135-
136-
model_path_regex = c.escaped_path(%w[spec models])
137-
c.include RSpec::Rails::ModelExampleGroup,
138-
:type => :model,
139-
:file_path => lambda { |file_path, metadata|
140-
metadata[:type].nil? && model_path_regex =~ file_path
141-
}
142-
143-
request_path_regex = c.escaped_path(%w[spec (requests|integration|api)])
144-
c.include RSpec::Rails::RequestExampleGroup,
145-
:type => :request,
146-
:file_path => lambda { |file_path, metadata|
147-
metadata[:type].nil? && request_path_regex =~ file_path
148-
}
149-
150-
routing_path_regex = c.escaped_path(%w[spec routing])
151-
c.include RSpec::Rails::RoutingExampleGroup,
152-
:type => :routing,
153-
:file_path => lambda { |file_path, metadata|
154-
metadata[:type].nil? && routing_path_regex =~ file_path
155-
}
156-
157-
view_path_regex = c.escaped_path(%w[spec views])
158-
c.include RSpec::Rails::ViewExampleGroup,
159-
:type => :view,
160-
:file_path => lambda { |file_path, metadata|
161-
metadata[:type].nil? && view_path_regex =~ file_path
162-
}
163-
164-
feature_example_regex = c.escaped_path(%w[spec features])
165-
c.include RSpec::Rails::FeatureExampleGroup,
166-
:type => :feature,
167-
:file_path => lambda { |file_path, metadata|
168-
metadata[:type].nil? && feature_example_regex =~ file_path
169-
}
17072
end

spec/support/helpers.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Helpers
22
def with_isolated_config
33
original_config = RSpec.configuration
44
RSpec.configuration = RSpec::Core::Configuration.new
5-
RSpec::Rails.add_rspec_rails_config_api_to(RSpec.configuration)
5+
RSpec::Rails.initialize_configuration(RSpec.configuration)
66
yield
77
ensure
88
RSpec.configuration = original_config

spec/support/shared_examples.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ def define_group_in(path, group_definition)
5151
expect(group.metadata).to include(:type => :other)
5252
expect(group.included_modules).not_to include(mixin)
5353
end
54+
55+
it "applies configured `before(:context)` hooks with `:type => #{type.inspect}` metadata" do
56+
block_run = false
57+
RSpec.configuration.before(:context, :type => type) { block_run = true }
58+
59+
group = define_group_in path, "RSpec.describe('group') { it { } }"
60+
group.run(double.as_null_object)
61+
62+
expect(block_run).to eq(true)
63+
end
5464
end
5565
end
5666

@@ -59,4 +69,25 @@ def define_group_in(path, group_definition)
5969
expect(group.included_modules).to include(mixin)
6070
end
6171
end
72+
73+
context 'when `infer_spec_type_from_file_location!` is not configured' do
74+
it "includes itself in example groups tagged with `:type => #{type.inspect}`" do
75+
group = define_group_in "spec/other", "RSpec.describe 'group', :type => #{type.inspect}"
76+
expect(group.included_modules).to include(mixin)
77+
end
78+
79+
paths.each do |path|
80+
context "for an example group defined in a file in the #{path} directory" do
81+
it "does not include itself in the example group" do
82+
group = define_group_in path, "RSpec.describe"
83+
expect(group.included_modules).not_to include(mixin)
84+
end
85+
86+
it "does not tag groups in that directory with `:type => #{type.inspect}`" do
87+
group = define_group_in path, "RSpec.describe"
88+
expect(group.metadata).not_to include(:type)
89+
end
90+
end
91+
end
92+
end
6293
end

0 commit comments

Comments
 (0)