describe "Fortitude around_content operations", :type => :system do
  it "should operate with no around_content declared" do
    wc = widget_class do
      def content
        text "foo"
      end
    end

    expect(wc.send(:around_content_methods)).to eq([ ])
    expect(render(wc.new)).to eq("foo")
  end

  it "should run an around_content method around the content method" do
    wc = widget_class do
      def content
        text "content"
      end

      def around_content
        text "around_before"
        yield
        text "around_after"
      end

      around_content :around_content
    end

    expect(render(wc.new)).to eq("around_beforecontentaround_after")
  end

  it "should still let #content call #yield properly" do
    wc = widget_class do
      def content
        yield "aaa", "bbb"
        text "content"
      end

      def around_content
        text "around_before"
        yield
        text "around_after"
      end

      around_content :around_content
    end

    yielded = [ ]
    rendering_context = rc(:yield_block => lambda { |*args| yielded << args; "" })
    expect(render(wc.new, :rendering_context => rendering_context)).to eq("around_beforecontentaround_after")
    expect(yielded).to eq([ [ "aaa", "bbb" ] ])
  end

  it "should not run the content method or other around_content methods if you don't call yield" do
    wc = widget_class do
      def content
        text "content"
      end

      def around1
        text "around1_before"
        text "around1_after"
      end

      def around2
        text "around2_before"
        yield
        text "around2_after"
      end

      around_content :around1, :around2
    end

    expect(render(wc.new)).to eq("around1_beforearound1_after")
  end

  it "should work even if you declare around_content after the method you're calling" do
    wc = widget_class do
      around_content :around1

      def content
        text "content"
      end

      def around1
        text "around1_before"
        yield
        text "around1_after"
      end
    end

    expect(render(wc.new)).to eq("around1_beforecontentaround1_after")
  end

  it "should propagate an exception out of the content method, using normal Ruby semantics" do
    wc = widget_class do
      def mark(token)
        @marks ||= [ ]
        @marks << token
      end

      def marks
        @marks ||= [ ]
      end

      def content
        mark(:content)
        raise("kaboom")
      end

      def around1
        mark(:around1_before)
        yield
        mark(:around1_after)
      end

      def around2
        mark(:around2_before)
        yield
        mark(:around2_after)
      end

      around_content :around1, :around2
    end

    instance = wc.new
    expect { render(instance) }.to raise_error("kaboom")
    expect(instance.marks).to eq([ :around1_before, :around2_before, :content ])
  end

  it "should propagate an exception out of an around_content method, using normal Ruby semantics" do
    wc = widget_class do
      def mark(token)
        @marks ||= [ ]
        @marks << token
      end

      def marks
        @marks ||= [ ]
      end

      def content
        mark(:content)
      end

      def around1
        mark(:around1_before)
        yield
        mark(:around1_after)
      end

      def around2
        mark(:around2_before)
        raise("kaboom")
        yield
        mark(:around2_after)
      end

      around_content :around1, :around2
    end

    instance = wc.new
    expect { render(instance) }.to raise_error("kaboom")
    expect(instance.marks).to eq([ :around1_before, :around2_before ])
  end

  it "should let you remove around_content methods, and let you add them back, and remove them again" do
    wc = widget_class do
      def around1
        text "around1"
        yield
      end

      def around2
        text "around2"
        yield
      end

      def content
        text "content"
      end
    end

    expect(render(wc)).to eq("content")
    wc.around_content :around1, :around2
    expect(render(wc)).to eq("around1around2content")
    wc.remove_around_content(:around2)
    expect(render(wc)).to eq("around1content")
    wc.around_content :around2
    expect(render(wc)).to eq("around1around2content")

    wc.remove_around_content :around2, :around1
    expect(render(wc)).to eq("content")
    wc.around_content :around2, :around1
    expect(render(wc)).to eq("around2around1content")
  end

  it "should fail if you try to remove an around_content method that's not present, or defined on a superclass" do
    parent = widget_class do
      def around1
        text "around1"
        yield
      end

      around_content :around1
    end

    child = widget_class(:superclass => parent) do
      def around2
        text "around2"
        yield
      end

      def content
        text "content"
      end

      around_content :around2
    end

    expect { parent.remove_around_content(:foobar) }.to raise_error(ArgumentError)
    expect { parent.remove_around_content(:around2) }.to raise_error(ArgumentError)
    expect { child.remove_around_content(:foobar) }.to raise_error(ArgumentError)
    expect { child.remove_around_content(:around1) }.to raise_error(ArgumentError)
  end


  it "should not fail if you try to remove an around_content method that's not present, and you tell it not to care" do
    parent = widget_class do
      def around1
        text "around1"
        yield
      end

      around_content :around1
    end

    child = widget_class(:superclass => parent) do
      def around2
        text "around2"
        yield
      end

      def content
        text "content"
      end

      around_content :around2
    end

    parent.remove_around_content(:foobar, :fail_if_not_present => false)
    parent.remove_around_content(:around2, :fail_if_not_present => false)
    child.remove_around_content(:foobar, :fail_if_not_present => false)
    child.remove_around_content(:around1, :fail_if_not_present => false)

    expect(render(child)).to eq("around1around2content")
  end

  it "should run around_content methods in the order they are declared" do
    wc = widget_class do
      def around1
        text "around1_before"
        yield
        text "around1_after"
      end

      def around2
        text "around2_before"
        yield
        text "around2_after"
      end

      def content
        text "content"
      end

      around_content :around1
      around_content :around2
    end

    expect(render(wc.new)).to eq("around1_beforearound2_beforecontentaround2_afteraround1_after")
  end

  it "should run superclass around_content methods before subclass ones" do
    grandparent = widget_class do
      def gp1
        text "gp1_before"
        yield
        text "gp1_after"
      end

      def gp2
        text "gp2_before"
        yield
        text "gp2_after"
      end
    end

    parent = widget_class(:superclass => grandparent) do
      def p1
        text "p1_before"
        yield
        text "p1_after"
      end

      def p2
        text "p2_before"
        yield
        text "p2_after"
      end
    end

    child = widget_class(:superclass => parent) do
      def c1
        text "c1_before"
        yield
        text "c1_after"
      end

      def c2
        text "c2_before"
        yield
        text "c2_after"
      end

      def content
        text "content"
      end
    end

    # we do it in this order to make sure the order in which they're declared as around_content methods
    # doesn't make any difference
    parent.around_content :p2, :p1
    child.around_content :c1, :c2
    grandparent.around_content :gp1, :gp2

    expect(render(child.new)).to eq("gp1_beforegp2_beforep2_beforep1_beforec1_beforec2_beforecontentc2_afterc1_afterp1_afterp2_aftergp2_aftergp1_after")
  end

  it "should allow two child classes to both inherit around_content methods of a parent, but add their own ones, as well" do
    parent = widget_class do
      def p1
        text "p1_before"
        yield
        text "p1_after"
      end

      def p2
        text "p2_before"
        yield
        text "p2_after"
      end

      around_content :p1, :p2
    end

    c1 = widget_class(:superclass => parent) do
      def c1a1
        text "c1a1_before"
        yield
        text "c1a1_after"
      end

      def c1a2
        text "c1a2_before"
        yield
        text "c1a2_after"
      end

      def content
        text "content"
      end

      around_content :c1a1, :c1a2
    end

    c2 = widget_class(:superclass => parent) do
      def c2a1
        text "c2a1_before"
        yield
        text "c2a1_after"
      end

      def c2a2
        text "c2a2_before"
        yield
        text "c2a2_after"
      end

      def content
        text "content"
      end

      around_content :c2a1, :c2a2
    end

    expect(render(c1.new)).to eq("p1_beforep2_beforec1a1_beforec1a2_beforecontentc1a2_afterc1a1_afterp2_afterp1_after")
    expect(render(c2.new)).to eq("p1_beforep2_beforec2a1_beforec2a2_beforecontentc2a2_afterc2a1_afterp2_afterp1_after")
  end
end
