diff --git a/lib/fixture_kit/rspec.rb b/lib/fixture_kit/rspec.rb index 00281da..8170cd2 100644 --- a/lib/fixture_kit/rspec.rb +++ b/lib/fixture_kit/rspec.rb @@ -30,7 +30,11 @@ module ClassMethods def fixture(name = nil, extends: nil, &block) definition = Definition.new(extends: extends, &block) if block_given? declaration = ::RSpec.configuration.fixture_kit.register(name || definition, self) - metadata[DECLARATION_METADATA_KEY] = declaration + # Use update_inherited_metadata to set the declaration on this group + # AND propagate to any child groups already created (e.g., shared + # example groups auto-included via `config.include_context` during + # RSpec configuration). + update_inherited_metadata(DECLARATION_METADATA_KEY => declaration) prepend_before(:context) do self.class.metadata[DECLARATION_METADATA_KEY].generate diff --git a/spec/dummy/spec/integration/fixture_kit_integration.rb b/spec/dummy/spec/integration/fixture_kit_integration.rb index 7367fa5..b6b3952 100644 --- a/spec/dummy/spec/integration/fixture_kit_integration.rb +++ b/spec/dummy/spec/integration/fixture_kit_integration.rb @@ -12,6 +12,26 @@ module FixtureKitIntegrationTimeHelpers config.include(FixtureKitIntegrationTimeHelpers) end +# Shared examples auto-included via config.include_context to reproduce the +# ordering issue: RSpec creates the shared group during configuration (before +# the host group's `fixture` call sets metadata), so fixture declarations +# must propagate through runtime metadata inheritance. +RSpec.shared_examples "auto-included shared examples" do + context "inside auto-included shared context" do + let(:shared_user) { User.find_by!(email: "alice@team.test") } + + it "can access fixture data through let blocks defined in the host group" do + expect(fixture.alice.email).to eq("alice@team.test") + expect(shared_user).to eq(fixture.alice) + puts "FKIT_ASSERT:SHARED_EXAMPLE_FIXTURE_ACCESS" + end + end +end + +RSpec.configure do |config| + config.include_context "auto-included shared examples", :include_shared_fixture_test +end + RSpec.describe "FixtureKit integration" do describe "fixture preload timing" do fixture "teams/basic" @@ -220,6 +240,15 @@ module FixtureKitIntegrationTimeHelpers end end + describe "fixture with auto-included shared examples", :include_shared_fixture_test do + fixture "teams/basic" + + it "works in the host group" do + expect(fixture.alice.name).to eq("Alice") + puts "FKIT_ASSERT:SHARED_EXAMPLE_HOST_FIXTURE" + end + end + describe "fixture instance reader without declaration" do it "raises a helpful error message" do expect do diff --git a/spec/integration/dummy_app_spec.rb b/spec/integration/dummy_app_spec.rb index 27685a0..610aa06 100644 --- a/spec/integration/dummy_app_spec.rb +++ b/spec/integration/dummy_app_spec.rb @@ -76,6 +76,13 @@ def run_dummy_tests "FKIT_ASSERT:UNDECLARED_FIXTURE_READER" ] + if INTEGRATION_FRAMEWORK == "rspec" + expected_markers += [ + "FKIT_ASSERT:SHARED_EXAMPLE_FIXTURE_ACCESS", + "FKIT_ASSERT:SHARED_EXAMPLE_HOST_FIXTURE" + ] + end + expected_markers.each do |marker| expect(output).to include(marker), "Expected marker #{marker.inspect} in output.\nOutput:\n#{output}" end diff --git a/spec/unit/rspec_entrypoint_spec.rb b/spec/unit/rspec_entrypoint_spec.rb index a796f53..1e8e657 100644 --- a/spec/unit/rspec_entrypoint_spec.rb +++ b/spec/unit/rspec_entrypoint_spec.rb @@ -109,6 +109,10 @@ def build_group(parent_metadata = {}) @fixture_kit_before_context_hook end + define_singleton_method(:update_inherited_metadata) do |updates| + metadata.update(updates) + end + define_singleton_method(:append_after) do |scope, &block| raise "Unexpected scope: #{scope}" unless scope == :context