Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit 46d2214

Browse files
committed
Ensure groups and examples are registered before invoking config definition hooks.
This is important so that if you load any support files that define hooks from within a definition hook, it can apply the hooks to the already-defined example groups (as is now supported via #2189). Without this change, the current group being defined is not eligible to have newly loaded hooks applied to it.
1 parent f2f259f commit 46d2214

File tree

6 files changed

+69
-28
lines changed

6 files changed

+69
-28
lines changed

lib/rspec/core/dsl.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ def self.expose_example_group_alias(name)
4040
example_group_aliases << name
4141

4242
(class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block|
43-
RSpec.world.register RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block)
43+
group = RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block)
44+
RSpec.world.record(group)
45+
group
4446
end
4547

4648
expose_example_group_alias_globally(name) if exposed_globally?

lib/rspec/core/example.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ def initialize(example_group_class, description, user_metadata, example_block=ni
178178
@example_group_class = example_group_class
179179
@example_block = example_block
180180

181+
example_group_class.examples << self
182+
181183
@metadata = Metadata::ExampleHash.create(
182184
@example_group_class.metadata, user_metadata,
183185
example_group_class.method(:next_runnable_index_for),

lib/rspec/core/example_group.rb

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,7 @@ def self.define_example_method(name, extra_options={})
143143
options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
144144
options.update(extra_options)
145145

146-
example = RSpec::Core::Example.new(self, desc, options, block)
147-
examples << example
148-
example
146+
RSpec::Core::Example.new(self, desc, options, block)
149147
end
150148
end
151149

@@ -235,27 +233,26 @@ def self.define_example_group_method(name, metadata={})
235233
thread_data = RSpec::Support.thread_local_data
236234
top_level = self == ExampleGroup
237235

238-
if top_level
236+
registration_collection = if top_level
239237
if thread_data[:in_example_group]
240238
raise "Creating an isolated context from within a context is " \
241239
"not allowed. Change `RSpec.#{name}` to `#{name}` or " \
242240
"move this to a top-level scope."
243241
end
244242

245243
thread_data[:in_example_group] = true
244+
RSpec.world.example_groups
245+
else
246+
children
246247
end
247248

248249
begin
249-
250250
description = args.shift
251251
combined_metadata = metadata.dup
252252
combined_metadata.merge!(args.pop) if args.last.is_a? Hash
253253
args << combined_metadata
254254

255-
subclass(self, description, args, &example_group_block).tap do |child|
256-
children << child
257-
end
258-
255+
subclass(self, description, args, registration_collection, &example_group_block)
259256
ensure
260257
thread_data.delete(:in_example_group) if top_level
261258
end
@@ -379,9 +376,9 @@ def self.find_and_eval_shared(label, name, inclusion_location, *args, &customiza
379376
# @!endgroup
380377

381378
# @private
382-
def self.subclass(parent, description, args, &example_group_block)
379+
def self.subclass(parent, description, args, registration_collection, &example_group_block)
383380
subclass = Class.new(parent)
384-
subclass.set_it_up(description, *args, &example_group_block)
381+
subclass.set_it_up(description, args, registration_collection, &example_group_block)
385382
subclass.module_exec(&example_group_block) if example_group_block
386383

387384
# The LetDefinitions module must be included _after_ other modules
@@ -394,7 +391,7 @@ def self.subclass(parent, description, args, &example_group_block)
394391
end
395392

396393
# @private
397-
def self.set_it_up(description, *args, &example_group_block)
394+
def self.set_it_up(description, args, registration_collection, &example_group_block)
398395
# Ruby 1.9 has a bug that can lead to infinite recursion and a
399396
# SystemStackError if you include a module in a superclass after
400397
# including it in a subclass: https://gist.github.com/845896
@@ -405,6 +402,8 @@ def self.set_it_up(description, *args, &example_group_block)
405402
# here.
406403
ensure_example_groups_are_configured
407404

405+
registration_collection << self
406+
408407
user_metadata = Metadata.build_hash_from(args)
409408

410409
@metadata = Metadata::ExampleGroupHash.create(
@@ -444,10 +443,10 @@ def self.children
444443
# @private
445444
def self.next_runnable_index_for(file)
446445
if self == ExampleGroup
447-
RSpec.world.num_example_groups_defined_in(file)
446+
RSpec.world.num_example_groups_defined_in(file) + 1
448447
else
449448
children.count + examples.count
450-
end + 1
449+
end
451450
end
452451

453452
# @private

lib/rspec/core/world.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,10 @@ def registered_example_group_files
4848

4949
# @api private
5050
#
51-
# Register an example group.
52-
def register(example_group)
51+
# Records an example group.
52+
def record(example_group)
5353
@configuration.on_example_group_definition_callbacks.each { |block| block.call(example_group) }
54-
example_groups << example_group
5554
@example_group_counts_by_spec_file[example_group.metadata[:absolute_file_path]] += 1
56-
example_group
5755
end
5856

5957
# @private

spec/rspec/core/configuration_spec.rb

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ module RSpec::Core
3030
end
3131

3232
it 'successfully invokes the block' do
33-
example_group = RSpec.describe("group") { it "example 1" do; end}
34-
RSpec.world.register(example_group)
33+
RSpec.describe("group") { it "example 1" do; end}
3534
example = RSpec.world.example_groups.first.examples.first
3635
expect(example.metadata[:new_key]).to eq(:new_value)
3736
end
@@ -1501,6 +1500,50 @@ def exclude?(line)
15011500
expect(example_bar_value).to eq("bar")
15021501
end
15031502

1503+
it 'registers top-level groups before invoking the callback so the logic can configure already registered groups' do
1504+
registered_groups = nil
1505+
1506+
RSpec.configuration.define_derived_metadata do |_meta|
1507+
registered_groups = RSpec.world.example_groups
1508+
end
1509+
1510+
group = RSpec.describe("My group") do
1511+
end
1512+
1513+
expect(registered_groups).to eq [group]
1514+
end
1515+
1516+
it 'registers nested groups before invoking the callback so the logic can configure already registered groups' do
1517+
registered_groups = nil
1518+
1519+
RSpec.configuration.define_derived_metadata(:inner) do |_meta|
1520+
registered_groups = RSpec.world.all_example_groups
1521+
end
1522+
1523+
inner = nil
1524+
outer = RSpec.describe("Outer") do
1525+
inner = context "Inner", :inner do
1526+
end
1527+
end
1528+
1529+
expect(registered_groups).to contain_exactly(outer, inner)
1530+
end
1531+
1532+
it 'registers examples before invoking the callback so the logic can configure already registered groups' do
1533+
registered_examples = nil
1534+
1535+
RSpec.configuration.define_derived_metadata(:ex) do |_meta|
1536+
registered_examples = FlatMap.flat_map(RSpec.world.all_example_groups, &:examples)
1537+
end
1538+
1539+
example = nil
1540+
RSpec.describe("Outer") do
1541+
example = example("ex", :ex)
1542+
end
1543+
1544+
expect(registered_examples).to contain_exactly(example)
1545+
end
1546+
15041547
context "when passed a metadata filter" do
15051548
it 'only applies to the groups and examples that match that filter' do
15061549
RSpec.configure do |c|

spec/rspec/core/world_spec.rb

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ module RSpec::Core
1818
describe "#example_groups" do
1919
it "contains all registered example groups" do
2020
example_group = RSpec.describe("group") {}
21-
world.register(example_group)
2221
expect(world.example_groups).to include(example_group)
2322
end
2423
end
@@ -96,8 +95,6 @@ def preceding_declaration_line(line_num)
9695
end
9796

9897
context "with one example" do
99-
before { world.register(group) }
100-
10198
it "returns nil if no example or group precedes the line" do
10299
expect(preceding_declaration_line(group_declaration_line - 1)).to be_nil
103100
end
@@ -123,8 +120,8 @@ def preceding_declaration_line(line_num)
123120
let(:second_group_declaration_line) { second_group.metadata[:line_number] }
124121

125122
before do
126-
world.register(second_group)
127-
world.register(group)
123+
world.record(second_group)
124+
world.record(group)
128125
end
129126

130127
it 'return line number of group if a group start on that line' do
@@ -146,8 +143,8 @@ def preceding_declaration_line(line_num)
146143
end
147144

148145
before do
149-
world.register(group)
150-
world.register(group_from_another_file)
146+
world.record(group)
147+
world.record(group_from_another_file)
151148
end
152149

153150
it "returns nil if given a file name with no declarations" do

0 commit comments

Comments
 (0)