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

Commit 99f3de0

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 0191b56 commit 99f3de0

File tree

6 files changed

+77
-35
lines changed

6 files changed

+77
-35
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: 21 additions & 21 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,27 @@ 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
239-
if thread_data[:in_example_group]
240-
raise "Creating an isolated context from within a context is " \
241-
"not allowed. Change `RSpec.#{name}` to `#{name}` or " \
242-
"move this to a top-level scope."
236+
registration_collection =
237+
if top_level
238+
if thread_data[:in_example_group]
239+
raise "Creating an isolated context from within a context is " \
240+
"not allowed. Change `RSpec.#{name}` to `#{name}` or " \
241+
"move this to a top-level scope."
242+
end
243+
244+
thread_data[:in_example_group] = true
245+
RSpec.world.example_groups
246+
else
247+
children
243248
end
244249

245-
thread_data[:in_example_group] = true
246-
end
247-
248250
begin
249-
250251
description = args.shift
251252
combined_metadata = metadata.dup
252253
combined_metadata.merge!(args.pop) if args.last.is_a? Hash
253254
args << combined_metadata
254255

255-
subclass(self, description, args, &example_group_block).tap do |child|
256-
children << child
257-
end
258-
256+
subclass(self, description, args, registration_collection, &example_group_block)
259257
ensure
260258
thread_data.delete(:in_example_group) if top_level
261259
end
@@ -379,9 +377,9 @@ def self.find_and_eval_shared(label, name, inclusion_location, *args, &customiza
379377
# @!endgroup
380378

381379
# @private
382-
def self.subclass(parent, description, args, &example_group_block)
380+
def self.subclass(parent, description, args, registration_collection, &example_group_block)
383381
subclass = Class.new(parent)
384-
subclass.set_it_up(description, *args, &example_group_block)
382+
subclass.set_it_up(description, args, registration_collection, &example_group_block)
385383
subclass.module_exec(&example_group_block) if example_group_block
386384

387385
# The LetDefinitions module must be included _after_ other modules
@@ -394,7 +392,7 @@ def self.subclass(parent, description, args, &example_group_block)
394392
end
395393

396394
# @private
397-
def self.set_it_up(description, *args, &example_group_block)
395+
def self.set_it_up(description, args, registration_collection, &example_group_block)
398396
# Ruby 1.9 has a bug that can lead to infinite recursion and a
399397
# SystemStackError if you include a module in a superclass after
400398
# including it in a subclass: https://gist.github.com/845896
@@ -405,6 +403,8 @@ def self.set_it_up(description, *args, &example_group_block)
405403
# here.
406404
ensure_example_groups_are_configured
407405

406+
registration_collection << self
407+
408408
user_metadata = Metadata.build_hash_from(args)
409409

410410
@metadata = Metadata::ExampleGroupHash.create(
@@ -444,10 +444,10 @@ def self.children
444444
# @private
445445
def self.next_runnable_index_for(file)
446446
if self == ExampleGroup
447-
RSpec.world.num_example_groups_defined_in(file)
447+
RSpec.world.num_example_groups_defined_in(file) + 1
448448
else
449449
children.count + examples.count
450-
end + 1
450+
end
451451
end
452452

453453
# @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)