require 'spec_helper'

class SelfObserver
  def self.cache
    @cache ||= []
  end

  def initialize
    self.class.cache << self
  end
end

module RSpec::Core
  describe ExampleGroup do
    it_behaves_like "metadata hash builder" do
      def metadata_hash(*args)
        group = ExampleGroup.describe('example description', *args)
        group.metadata
      end
    end

    context 'when RSpec.configuration.treat_symbols_as_metadata_keys_with_true_values is set to false' do
      before(:each) do
        RSpec.configure { |c| c.treat_symbols_as_metadata_keys_with_true_values = false }
      end

      it 'processes string args as part of the description' do
        group = ExampleGroup.describe("some", "separate", "strings")
        group.description.should eq("some separate strings")
      end

      it 'processes symbol args as part of the description' do
        Kernel.stub(:warn) # to silence Symbols as args warning
        group = ExampleGroup.describe(:some, :separate, :symbols)
        group.description.should eq("some separate symbols")
      end
    end

    context 'when RSpec.configuration.treat_symbols_as_metadata_keys_with_true_values is set to true' do
      let(:group) { ExampleGroup.describe(:symbol) }

      before(:each) do
        RSpec.configure { |c| c.treat_symbols_as_metadata_keys_with_true_values = true }
      end

      it 'does not treat the first argument as a metadata key even if it is a symbol' do
        group.metadata.should_not include(:symbol)
      end

      it 'treats the first argument as part of the description when it is a symbol' do
        group.description.should eq("symbol")
      end
    end

    describe "top level group" do
      it "runs its children" do
        examples_run = []
        group = ExampleGroup.describe("parent") do
          describe("child") do
            it "does something" do
              examples_run << example
            end
          end
        end

        group.run
        examples_run.should have(1).example
      end

      context "with a failure in the top level group" do
        it "runs its children " do
          examples_run = []
          group = ExampleGroup.describe("parent") do
            it "fails" do
              examples_run << example
              raise "fail"
            end
            describe("child") do
              it "does something" do
                examples_run << example
              end
            end
          end

          group.run
          examples_run.should have(2).examples
        end
      end

      describe "descendants" do
        it "returns self + all descendants" do
          group = ExampleGroup.describe("parent") do
            describe("child") do
              describe("grandchild 1") {}
              describe("grandchild 2") {}
            end
          end
          group.descendants.size.should eq(4)
        end
      end
    end

    describe "child" do
      it "is known by parent" do
        parent = ExampleGroup.describe
        child = parent.describe
        parent.children.should eq([child])
      end

      it "is not registered in world" do
        world = RSpec::Core::World.new
        parent = ExampleGroup.describe
        world.register(parent)
        parent.describe
        world.example_groups.should eq([parent])
      end
    end

    describe "filtering" do
      let(:world) { World.new }

      shared_examples "matching filters" do
        context "inclusion" do
          before do
            filter_manager = FilterManager.new
            filter_manager.include filter_metadata
            world.stub(:filter_manager => filter_manager)
          end

          it "includes examples in groups matching filter" do
            group = ExampleGroup.describe("does something", spec_metadata)
            group.stub(:world) { world }
            all_examples = [ group.example("first"), group.example("second") ]

            group.filtered_examples.should eq(all_examples)
          end

          it "includes examples directly matching filter" do
            group = ExampleGroup.describe("does something")
            group.stub(:world) { world }
            filtered_examples = [
              group.example("first", spec_metadata),
              group.example("second", spec_metadata)
            ]
            group.example("third (not-filtered)")

            group.filtered_examples.should eq(filtered_examples)
          end
        end

        context "exclusion" do
          before do
            filter_manager = FilterManager.new
            filter_manager.exclude filter_metadata
            world.stub(:filter_manager => filter_manager)
          end

          it "excludes examples in groups matching filter" do
            group = ExampleGroup.describe("does something", spec_metadata)
            group.stub(:world) { world }
            [ group.example("first"), group.example("second") ]

            group.filtered_examples.should be_empty
          end

          it "excludes examples directly matching filter" do
            group = ExampleGroup.describe("does something")
            group.stub(:world) { world }
            [
              group.example("first", spec_metadata),
              group.example("second", spec_metadata)
            ]
            unfiltered_example = group.example("third (not-filtered)")

            group.filtered_examples.should eq([unfiltered_example])
          end
        end
      end

      context "matching false" do
        let(:spec_metadata)    { { :awesome => false }}

        context "against false" do
          let(:filter_metadata)  { { :awesome => false }}
          include_examples "matching filters"
        end

        context "against 'false'" do
          let(:filter_metadata)  { { :awesome => 'false' }}
          include_examples "matching filters"
        end

        context "against :false" do
          let(:filter_metadata)  { { :awesome => :false }}
          include_examples "matching filters"
        end
      end

      context "matching true" do
        let(:spec_metadata)    { { :awesome => true }}

        context "against true" do
          let(:filter_metadata)  { { :awesome => true }}
          include_examples "matching filters"
        end

        context "against 'true'" do
          let(:filter_metadata)  { { :awesome => 'true' }}
          include_examples "matching filters"
        end

        context "against :true" do
          let(:filter_metadata)  { { :awesome => :true }}
          include_examples "matching filters"
        end
      end

      context "matching a string" do
        let(:spec_metadata)    { { :type => 'special' }}

        context "against a string" do
          let(:filter_metadata)  { { :type => 'special' }}
          include_examples "matching filters"
        end

        context "against a symbol" do
          let(:filter_metadata)  { { :type => :special }}
          include_examples "matching filters"
        end
      end

      context "matching a symbol" do
        let(:spec_metadata)    { { :type => :special }}

        context "against a string" do
          let(:filter_metadata)  { { :type => 'special' }}
          include_examples "matching filters"
        end

        context "against a symbol" do
          let(:filter_metadata)  { { :type => :special }}
          include_examples "matching filters"
        end
      end

      context "with no filters" do
        it "returns all" do
          group = ExampleGroup.describe
          group.stub(:world) { world }
          example = group.example("does something")
          group.filtered_examples.should eq([example])
        end
      end

      context "with no examples or groups that match filters" do
        it "returns none" do
          filter_manager = FilterManager.new
          filter_manager.include :awesome => false
          world.stub(:filter_manager => filter_manager)
          group = ExampleGroup.describe
          group.stub(:world) { world }
          group.example("does something")
          group.filtered_examples.should eq([])
        end
      end
    end

    describe '#described_class' do

      context "with a constant as the first parameter" do
        it "is that constant" do
          ExampleGroup.describe(Object) { }.described_class.should eq(Object)
        end
      end

      context "with a string as the first parameter" do
        it "is nil" do
          ExampleGroup.describe("i'm a computer") { }.described_class.should be_nil
        end
      end

      context "with a constant in an outer group" do
        context "and a string in an inner group" do
          it "is the top level constant" do
            group = ExampleGroup.describe(String) do
              describe :symbol do
                example "described_class is String" do
                  described_class.should eq(String)
                end
              end
            end

            group.run.should be_true
          end
        end

        context "and metadata redefinition after `described_class` call" do
          it "is the redefined level constant" do
            group = ExampleGroup.describe(String) do
              described_class
              metadata[:example_group][:described_class] = Object
              describe :symbol do
                example "described_class is Object" do
                  described_class.should eq(Object)
                end
              end
            end

            group.run.should be_true
          end
        end
      end

      context "in a nested group" do
        it "inherits the described class/module from the outer group" do
          group = ExampleGroup.describe(String) do
            describe Array do
              example "desribes is String" do
                described_class.should eq(String)
              end
            end
          end

          group.run.should be_true, "expected examples in group to pass"
        end
      end
    end

    describe '#described_class' do
      it "is the same as described_class" do
        self.class.described_class.should eq(self.class.described_class)
      end
    end

    describe '#description' do
      it "grabs the description from the metadata" do
        group = ExampleGroup.describe(Object, "my desc") { }
        group.description.should eq(group.metadata[:example_group][:description])
      end
    end

    describe '#metadata' do
      it "adds the third parameter to the metadata" do
        ExampleGroup.describe(Object, nil, 'foo' => 'bar') { }.metadata.should include({ "foo" => 'bar' })
      end

      it "adds the the file_path to metadata" do
        ExampleGroup.describe(Object) { }.metadata[:example_group][:file_path].should eq(relative_path(__FILE__))
      end

      it "has a reader for file_path" do
        ExampleGroup.describe(Object) { }.file_path.should eq(relative_path(__FILE__))
      end

      it "adds the line_number to metadata" do
        ExampleGroup.describe(Object) { }.metadata[:example_group][:line_number].should eq(__LINE__)
      end
    end

    [:focus, :focused].each do |example_alias|
      describe "##{example_alias}" do
        let(:group) { ExampleGroup.describe }
        subject { group.send example_alias, "a focused example" }

        it 'defines an example that can be filtered with :focused => true' do
          subject.metadata.should include(:focused => true)
        end

        it 'defines an example that can be filtered with :focus => true' do
          subject.metadata.should include(:focus => true)
        end
      end
    end

    describe "#before, after, and around hooks" do
      it "runs the before alls in order" do
        group = ExampleGroup.describe
        order = []
        group.before(:all) { order << 1 }
        group.before(:all) { order << 2 }
        group.before(:all) { order << 3 }
        group.example("example") {}

        group.run

        order.should eq([1,2,3])
      end

      it "runs the before eachs in order" do
        group = ExampleGroup.describe
        order = []
        group.before(:each) { order << 1 }
        group.before(:each) { order << 2 }
        group.before(:each) { order << 3 }
        group.example("example") {}

        group.run

        order.should eq([1,2,3])
      end

      it "runs the after eachs in reverse order" do
        group = ExampleGroup.describe
        order = []
        group.after(:each) { order << 1 }
        group.after(:each) { order << 2 }
        group.after(:each) { order << 3 }
        group.example("example") {}

        group.run

        order.should eq([3,2,1])
      end

      it "runs the after alls in reverse order" do
        group = ExampleGroup.describe
        order = []
        group.after(:all) { order << 1 }
        group.after(:all) { order << 2 }
        group.after(:all) { order << 3 }
        group.example("example") {}

        group.run

        order.should eq([3,2,1])
      end

      it "only runs before/after(:all) hooks from example groups that have specs that run" do
        hooks_run = []

        RSpec.configure do |c|
          c.filter_run :focus => true
        end

        unfiltered_group = ExampleGroup.describe "unfiltered" do
          before(:all) { hooks_run << :unfiltered_before_all }
          after(:all)  { hooks_run << :unfiltered_after_all  }

          context "a subcontext" do
            it("has an example") { }
          end
        end

        filtered_group = ExampleGroup.describe "filtered", :focus => true do
          before(:all) { hooks_run << :filtered_before_all }
          after(:all)  { hooks_run << :filtered_after_all  }

          context "a subcontext" do
            it("has an example") { }
          end
        end

        unfiltered_group.run
        filtered_group.run

        hooks_run.should eq([:filtered_before_all, :filtered_after_all])
      end

      it "runs before_all_defined_in_config, before all, before each, example, after each, after all, after_all_defined_in_config in that order" do
        order = []

        RSpec.configure do |c|
          c.before(:all) { order << :before_all_defined_in_config }
          c.after(:all) { order << :after_all_defined_in_config }
        end

        group = ExampleGroup.describe
        group.before(:all)  { order << :top_level_before_all  }
        group.before(:each) { order << :before_each }
        group.after(:each)  { order << :after_each  }
        group.after(:all)   { order << :top_level_after_all   }
        group.example("top level example") { order << :top_level_example }

        context1 = group.describe("context 1")
        context1.before(:all) { order << :nested_before_all }
        context1.example("nested example 1") { order << :nested_example_1 }

        context2 = group.describe("context 2")
        context2.after(:all) { order << :nested_after_all }
        context2.example("nested example 2") { order << :nested_example_2 }

        group.run

        order.should eq([
          :before_all_defined_in_config,
          :top_level_before_all,
          :before_each,
          :top_level_example,
          :after_each,
          :nested_before_all,
          :before_each,
          :nested_example_1,
          :after_each,
          :before_each,
          :nested_example_2,
          :after_each,
          :nested_after_all,
          :top_level_after_all,
          :after_all_defined_in_config
        ])
      end

      context "after(:all)" do
        let(:outer) { ExampleGroup.describe }
        let(:inner) { outer.describe }

        it "has access to state defined before(:all)" do
          outer.before(:all) { @outer = "outer" }
          inner.before(:all) { @inner = "inner" }

          outer.after(:all) do
            @outer.should eq("outer")
            @inner.should eq("inner")
          end
          inner.after(:all) do
            @inner.should eq("inner")
            @outer.should eq("outer")
          end

          outer.run
        end

        it "cleans up ivars in after(:all)" do
          outer.before(:all) { @outer = "outer" }
          inner.before(:all) { @inner = "inner" }

          outer.run

          inner.before_all_ivars[:@inner].should be_nil
          inner.before_all_ivars[:@outer].should be_nil
          outer.before_all_ivars[:@inner].should be_nil
          outer.before_all_ivars[:@outer].should be_nil
        end
      end

      it "treats an error in before(:each) as a failure" do
        group = ExampleGroup.describe
        group.before(:each) { raise "error in before each" }
        example = group.example("equality") { 1.should eq(2) }
        group.run.should be(false)

        example.metadata[:execution_result][:exception].message.should eq("error in before each")
      end

      it "treats an error in before(:all) as a failure" do
        group = ExampleGroup.describe
        group.before(:all) { raise "error in before all" }
        example = group.example("equality") { 1.should eq(2) }
        group.run.should be_false

        example.metadata.should_not be_nil
        example.metadata[:execution_result].should_not be_nil
        example.metadata[:execution_result][:exception].should_not be_nil
        example.metadata[:execution_result][:exception].message.should eq("error in before all")
      end

      it "treats an error in before(:all) as a failure for a spec in a nested group" do
        example = nil
        group = ExampleGroup.describe do
          before(:all) { raise "error in before all" }

          describe "nested" do
            example = it("equality") { 1.should eq(2) }
          end
        end
        group.run

        example.metadata.should_not be_nil
        example.metadata[:execution_result].should_not be_nil
        example.metadata[:execution_result][:exception].should_not be_nil
        example.metadata[:execution_result][:exception].message.should eq("error in before all")
      end

      context "when an error occurs in an after(:all) hook" do
        before(:each) do
          RSpec.configuration.reporter.stub(:message)
        end

        let(:group) do
          ExampleGroup.describe do
            after(:all) { raise "error in after all" }
            it("equality") { 1.should eq(1) }
          end
        end

        it "allows the example to pass" do
          group.run
          example = group.examples.first
          example.metadata.should_not be_nil
          example.metadata[:execution_result].should_not be_nil
          example.metadata[:execution_result][:status].should eq("passed")
        end

        it "rescues the error and prints it out" do
          RSpec.configuration.reporter.should_receive(:message).with(/error in after all/)
          group.run
        end
      end

      it "has no 'running example' within before(:all)" do
        group = ExampleGroup.describe
        running_example = :none
        group.before(:all) { running_example = example }
        group.example("no-op") { }
        group.run
        running_example.should be(nil)
      end

      it "has access to example options within before(:each)" do
        group = ExampleGroup.describe
        option = nil
        group.before(:each) { option = example.options[:data] }
        group.example("no-op", :data => :sample) { }
        group.run
        option.should eq(:sample)
      end

      it "has access to example options within after(:each)" do
        group = ExampleGroup.describe
        option = nil
        group.after(:each) { option = example.options[:data] }
        group.example("no-op", :data => :sample) { }
        group.run
        option.should eq(:sample)
      end

      it "has no 'running example' within after(:all)" do
        group = ExampleGroup.describe
        running_example = :none
        group.after(:all) { running_example = example }
        group.example("no-op") { }
        group.run
        running_example.should be(nil)
      end
    end

    %w[pending xit xspecify xexample].each do |method_name|
      describe "::#{method_name}" do
        before do
          @group = ExampleGroup.describe
          @group.send(method_name, "is pending") { }
        end

        it "generates a pending example" do
          @group.run
          @group.examples.first.should be_pending
        end

        it "sets the pending message", :if => method_name == 'pending' do
          @group.run
          @group.examples.first.metadata[:execution_result][:pending_message].should eq(RSpec::Core::Pending::NO_REASON_GIVEN)
        end

        it "sets the pending message", :unless => method_name == 'pending' do
          @group.run
          @group.examples.first.metadata[:execution_result][:pending_message].should eq("Temporarily disabled with #{method_name}")
        end
      end
    end

    describe "adding examples" do

      it "allows adding an example using 'it'" do
        group = ExampleGroup.describe
        group.it("should do something") { }
        group.examples.size.should eq(1)
      end

      it "exposes all examples at examples" do
        group = ExampleGroup.describe
        group.it("should do something 1") { }
        group.it("should do something 2") { }
        group.it("should do something 3") { }
        group.should have(3).examples
      end

      it "maintains the example order" do
        group = ExampleGroup.describe
        group.it("should 1") { }
        group.it("should 2") { }
        group.it("should 3") { }
        group.examples[0].description.should eq('should 1')
        group.examples[1].description.should eq('should 2')
        group.examples[2].description.should eq('should 3')
      end

    end

    describe Object, "describing nested example_groups", :little_less_nested => 'yep' do

      describe "A sample nested group", :nested_describe => "yep" do
        it "sets the described class to the described class of the outer most group" do
          example.example_group.described_class.should eq(ExampleGroup)
        end

        it "sets the description to 'A sample nested describe'" do
          example.example_group.description.should eq('A sample nested group')
        end

        it "has top level metadata from the example_group and its ancestors" do
          example.example_group.metadata.should include(:little_less_nested => 'yep', :nested_describe => 'yep')
        end

        it "exposes the parent metadata to the contained examples" do
          example.metadata.should include(:little_less_nested => 'yep', :nested_describe => 'yep')
        end
      end

    end

    describe "#run_examples" do

      let(:reporter) { double("reporter").as_null_object }

      it "returns true if all examples pass" do
        group = ExampleGroup.describe('group') do
          example('ex 1') { 1.should eq(1) }
          example('ex 2') { 1.should eq(1) }
        end
        group.stub(:filtered_examples) { group.examples.extend(Extensions::Ordered) }
        group.run(reporter).should be_true
      end

      it "returns false if any of the examples fail" do
        group = ExampleGroup.describe('group') do
          example('ex 1') { 1.should eq(1) }
          example('ex 2') { 1.should eq(2) }
        end
        group.stub(:filtered_examples) { group.examples.extend(Extensions::Ordered) }
        group.run(reporter).should be_false
      end

      it "runs all examples, regardless of any of them failing" do
        group = ExampleGroup.describe('group') do
          example('ex 1') { 1.should eq(2) }
          example('ex 2') { 1.should eq(1) }
        end
        group.stub(:filtered_examples) { group.examples.extend(Extensions::Ordered) }
        group.filtered_examples.each do |example|
          example.should_receive(:run)
        end
        group.run(reporter).should be_false
      end
    end

    describe "how instance variables are inherited" do
      before(:all) do
        @before_all_top_level = 'before_all_top_level'
      end

      before(:each) do
        @before_each_top_level = 'before_each_top_level'
      end

      it "can access a before each ivar at the same level" do
        @before_each_top_level.should eq('before_each_top_level')
      end

      it "can access a before all ivar at the same level" do
        @before_all_top_level.should eq('before_all_top_level')
      end

      it "can access the before all ivars in the before_all_ivars hash", :ruby => 1.8 do
        example.example_group.before_all_ivars.should include('@before_all_top_level' => 'before_all_top_level')
      end

      it "can access the before all ivars in the before_all_ivars hash", :ruby => 1.9 do
        example.example_group.before_all_ivars.should include(:@before_all_top_level => 'before_all_top_level')
      end

      describe "but now I am nested" do
        it "can access a parent example groups before each ivar at a nested level" do
          @before_each_top_level.should eq('before_each_top_level')
        end

        it "can access a parent example groups before all ivar at a nested level" do
          @before_all_top_level.should eq("before_all_top_level")
        end

        it "changes to before all ivars from within an example do not persist outside the current describe" do
          @before_all_top_level = "ive been changed"
        end

        describe "accessing a before_all ivar that was changed in a parent example_group" do
          it "does not have access to the modified version" do
            @before_all_top_level.should eq('before_all_top_level')
          end
        end
      end

    end

    describe "ivars are not shared across examples" do
      it "(first example)" do
        @a = 1
        defined?(@b).should be_false
      end

      it "(second example)" do
        @b = 2
        defined?(@a).should be_false
      end
    end


    describe "#top_level_description" do
      it "returns the description from the outermost example group" do
        group = nil
        ExampleGroup.describe("top") do
          context "middle" do
            group = describe "bottom" do
            end
          end
        end

        group.top_level_description.should eq("top")
      end
    end

    describe "#run" do
      let(:reporter) { double("reporter").as_null_object }

      context "with fail_fast? => true" do
        it "does not run examples after the failed example" do
          group = RSpec::Core::ExampleGroup.describe
          group.stub(:fail_fast?) { true }
          examples_run = []
          group.example('example 1') { examples_run << self }
          group.example('example 2') { examples_run << self; fail; }
          group.example('example 3') { examples_run << self }

          group.run

          examples_run.length.should eq(2)
        end
      end

      context "with RSpec.wants_to_quit=true" do
        let(:group) { RSpec::Core::ExampleGroup.describe }

        before do
          RSpec.stub(:wants_to_quit) { true }
          RSpec.stub(:clear_remaining_example_groups)
        end

        it "returns without starting the group" do
          reporter.should_not_receive(:example_group_started)
          group.run(reporter)
        end

        context "at top level" do
          it "purges remaining groups" do
            RSpec.should_receive(:clear_remaining_example_groups)
            group.run(reporter)
          end
        end

        context "in a nested group" do
          it "does not purge remaining groups" do
            nested_group = group.describe
            RSpec.should_not_receive(:clear_remaining_example_groups)
            nested_group.run(reporter)
          end
        end
      end

      context "with all examples passing" do
        it "returns true" do
          group = describe("something") do
            it "does something" do
              # pass
            end
            describe "nested" do
              it "does something else" do
                # pass
              end
            end
          end

          group.run(reporter).should be_true
        end
      end

      context "with top level example failing" do
        it "returns false" do
          group = describe("something") do
            it "does something (wrong - fail)" do
              raise "fail"
            end
            describe "nested" do
              it "does something else" do
                # pass
              end
            end
          end

          group.run(reporter).should be_false
        end
      end

      context "with nested example failing" do
        it "returns true" do
          group = describe("something") do
            it "does something" do
              # pass
            end
            describe "nested" do
              it "does something else (wrong -fail)" do
                raise "fail"
              end
            end
          end

          group.run(reporter).should be_false
        end
      end
    end

    %w[include_examples include_context].each do |name|
      describe "##{name}" do
        before do
          shared_examples "named this" do
            example("does something") {}
          end
        end

        it "includes the named examples" do
          group = ExampleGroup.describe
          group.send(name, "named this")
          group.examples.first.description.should eq("does something")
        end

        it "raises a helpful error message when shared content is not found" do
          group = ExampleGroup.describe
          expect do
            group.send(name, "shared stuff")
          end.to raise_error(ArgumentError, /Could not find .* "shared stuff"/)
        end

        it "passes parameters to the shared content" do
          passed_params = {}

          shared_examples "named this with params" do |param1, param2|
            it("has access to the given parameters") do
              passed_params[:param1] = param1
              passed_params[:param2] = param2
            end
          end

          group = ExampleGroup.describe
          group.send(name, "named this with params", :value1, :value2)
          group.run

          passed_params.should eq({ :param1 => :value1, :param2 => :value2 })
        end

        it "adds shared instance methods to the group" do
          shared_examples "named this with params" do |param1|
            def foo; end
          end
          group = ExampleGroup.describe('fake group')
          group.send(name, "named this with params", :a)
          group.public_instance_methods.map{|m| m.to_s}.should include("foo")
        end

        it "evals the shared example group only once" do
          eval_count = 0
          shared_examples("named this with params") { |p| eval_count += 1 }
          group = ExampleGroup.describe('fake group')
          group.send(name, "named this with params", :a)
          eval_count.should eq(1)
        end

        it "warns the user that blocks are not supported when given a block" do
          group = ExampleGroup.describe
          group.should_receive(:warn).with(/blocks not supported for #{name}/)
          group.send(name, "named this with block") {}
        end
      end
    end

    describe "#it_should_behave_like" do
      it "creates a nested group" do
        shared_examples_for("thing") {}
        group = ExampleGroup.describe('fake group')
        group.it_should_behave_like("thing")
        group.should have(1).children
      end

      it "creates a nested group for a class" do
        klass = Class.new
        shared_examples_for(klass) {}
        group = ExampleGroup.describe('fake group')
        group.it_should_behave_like(klass)
        group.should have(1).children
      end

      it "adds shared examples to nested group" do
        shared_examples_for("thing") do
          it("does something")
        end
        group = ExampleGroup.describe('fake group')
        shared_group = group.it_should_behave_like("thing")
        shared_group.should have(1).examples
      end

      it "adds shared instance methods to nested group" do
        shared_examples_for("thing") do
          def foo; end
        end
        group = ExampleGroup.describe('fake group')
        shared_group = group.it_should_behave_like("thing")
        shared_group.public_instance_methods.map{|m| m.to_s}.should include("foo")
      end

      it "adds shared class methods to nested group" do
        shared_examples_for("thing") do
          def self.foo; end
        end
        group = ExampleGroup.describe('fake group')
        shared_group = group.it_should_behave_like("thing")
        shared_group.methods.map{|m| m.to_s}.should include("foo")
      end

      it "passes parameters to the shared example group" do
        passed_params = {}

        shared_examples_for("thing") do |param1, param2|
          it("has access to the given parameters") do
            passed_params[:param1] = param1
            passed_params[:param2] = param2
          end
        end

        group = ExampleGroup.describe("group") do
          it_should_behave_like "thing", :value1, :value2
        end
        group.run

        passed_params.should eq({ :param1 => :value1, :param2 => :value2 })
      end

      it "adds shared instance methods to nested group" do
        shared_examples_for("thing") do |param1|
          def foo; end
        end
        group = ExampleGroup.describe('fake group')
        shared_group = group.it_should_behave_like("thing", :a)
        shared_group.public_instance_methods.map{|m| m.to_s}.should include("foo")
      end

      it "evals the shared example group only once" do
        eval_count = 0
        shared_examples_for("thing") { |p| eval_count += 1 }
        group = ExampleGroup.describe('fake group')
        group.it_should_behave_like("thing", :a)
        eval_count.should eq(1)
      end

      context "given a block" do
        it "evaluates the block in nested group" do
          scopes = []
          shared_examples_for("thing") do
            it("gets run in the nested group") do
              scopes << self.class
            end
          end
          group = ExampleGroup.describe("group") do
            it_should_behave_like "thing" do
              it("gets run in the same nested group") do
                scopes << self.class
              end
            end
          end
          group.run

          scopes[0].should be(scopes[1])
        end
      end

      it "raises a helpful error message when shared context is not found" do
        expect do
          ExampleGroup.describe do
            it_should_behave_like "shared stuff"
          end
        end.to raise_error(ArgumentError,%q|Could not find shared examples "shared stuff"|)
      end
    end
  end
end