describe "Fortitude assigns access", :type => :system do
  it "should expose assigns" do
    wc = widget_class do
      needs :foo, :bar
      def content
        text "assigns[:foo] = #{assigns[:foo]}, assigns[:bar] = #{assigns[:bar]}"
      end
    end

    expect(render(wc.new(:foo => 'the_foo', :bar => 'the_bar'))).to eq("assigns[:foo] = the_foo, assigns[:bar] = the_bar")
  end

  it "should include needs that are left as the default" do
    wc = widget_class do
      needs :foo, :bar => 'def_bar'
      def content
        text "assigns[:foo] = #{assigns[:foo]}, assigns[:bar] = #{assigns[:bar]}"
      end
    end

    expect(render(wc.new(:foo => 'the_foo'))).to eq("assigns[:foo] = the_foo, assigns[:bar] = def_bar")
  end

  it "should not include extra assigns, by default" do
    wc = widget_class do
      needs :foo
      def content
        text "assigns[:foo] = #{assigns[:foo]}, assigns[:bar] = #{assigns[:bar]}"
      end
    end

    expect(render(wc.new(:foo => 'the_foo', :bar => 'the_bar'))).to eq("assigns[:foo] = the_foo, assigns[:bar] = ")
  end

  it "should include extra assigns, if we're using them" do
    wc = widget_class do
      extra_assigns :use
      needs :foo
      def content
        text "assigns[:foo] = #{assigns[:foo]}, assigns[:bar] = #{assigns[:bar]}"
      end
    end

    expect(render(wc.new(:foo => 'the_foo', :bar => 'the_bar'))).to eq("assigns[:foo] = the_foo, assigns[:bar] = the_bar")
  end

  it "should allow accessing extra assigns via method, and also indicate respond_to?" do
    wc = widget_class do
      extra_assigns :use

      def content
        text "foo = #{foo}, respond_to? #{respond_to?(:foo).inspect}"
      end
    end

    expect(render(wc.new(:foo => 'the_foo'))).to eq("foo = the_foo, respond_to? true")
  end

  it "should allow changing assigns, and always return the current value of the assign" do
    wc = widget_class do
      needs :foo
      def content
        text "foo = #{foo}, assigns[:foo] = #{assigns[:foo]}, "
        assigns[:foo] = "new_foo"
        text "now foo = #{foo}, assigns[:foo] = #{assigns[:foo]}"
      end
    end

    expect(render(wc.new(:foo => 'the_foo'))).to eq("foo = the_foo, assigns[:foo] = the_foo, now foo = new_foo, assigns[:foo] = new_foo")
  end

  it "should allow changing assigns from nil to something else, and always return the current value of the assign" do
    wc = widget_class do
      needs :foo
      def content
        text "foo = #{foo}, assigns[:foo] = #{assigns[:foo]}, "
        assigns[:foo] = "new_foo"
        text "now foo = #{foo}, assigns[:foo] = #{assigns[:foo]}"
      end
    end

    expect(render(wc.new(:foo => nil))).to eq("foo = , assigns[:foo] = , now foo = new_foo, assigns[:foo] = new_foo")
  end

  it "should allow changing assigns from a default nil to something else, and always return the current value of the assign" do
    wc = widget_class do
      needs :foo => nil
      def content
        text "foo = #{foo}, assigns[:foo] = #{assigns[:foo]}, "
        assigns[:foo] = "new_foo"
        text "now foo = #{foo}, assigns[:foo] = #{assigns[:foo]}"
      end
    end

    expect(render(wc.new)).to eq("foo = , assigns[:foo] = , now foo = new_foo, assigns[:foo] = new_foo")
  end

  it "should allow changing assigns from a default false to something else, and always return the current value of the assign" do
    wc = widget_class do
      needs :foo => false
      def content
        text "foo = #{foo}, assigns[:foo] = #{assigns[:foo]}, "
        assigns[:foo] = "new_foo"
        text "now foo = #{foo}, assigns[:foo] = #{assigns[:foo]}"
      end
    end

    expect(render(wc.new)).to eq("foo = false, assigns[:foo] = false, now foo = new_foo, assigns[:foo] = new_foo")
  end

  it "should allow changing extra assigns, and always return the current value of the assign" do
    wc = widget_class do
      needs :bar => 'whatever'
      extra_assigns :use

      def content
        text "foo = #{foo}, assigns[:foo] = #{assigns[:foo]}, "
        assigns[:foo] = "new_foo"
        text "now foo = #{foo}, assigns[:foo] = #{assigns[:foo]}"
      end
    end

    expect(render(wc.new(:foo => 'the_foo'))).to eq("foo = the_foo, assigns[:foo] = the_foo, now foo = new_foo, assigns[:foo] = new_foo")
  end

  it "should allow changing extra assigns from nil to something else, and always return the current value of the assign" do
    wc = widget_class do
      needs :bar => 'whatever'
      extra_assigns :use

      def content
        text "foo = #{foo}, assigns[:foo] = #{assigns[:foo]}, "
        assigns[:foo] = "new_foo"
        text "now foo = #{foo}, assigns[:foo] = #{assigns[:foo]}"
      end
    end

    expect(render(wc.new(:foo => nil))).to eq("foo = , assigns[:foo] = , now foo = new_foo, assigns[:foo] = new_foo")
  end

  it "should allow changing extra assigns from false to something else, and always return the current value of the assign" do
    wc = widget_class do
      needs :bar => 'whatever'
      extra_assigns :use

      def content
        text "foo = #{foo}, assigns[:foo] = #{assigns[:foo]}, "
        assigns[:foo] = "new_foo"
        text "now foo = #{foo}, assigns[:foo] = #{assigns[:foo]}"
      end
    end

    expect(render(wc.new(:foo => false))).to eq("foo = false, assigns[:foo] = false, now foo = new_foo, assigns[:foo] = new_foo")
  end

  it "should not let you introduce new assigns" do
    wc = widget_class do
      def content
        assigns[:quux] = 'the_quux'
        text "assigns[:quux]: #{assigns[:quux].inspect}, "

        quux_value = begin
          quux
        rescue => e
          e.class.name
        end

        text "quux: #{quux_value}"
      end
    end

    expect(render(wc)).to eq("assigns[:quux]: nil, quux: NameError")
  end

  it "should return assigns in the order in which they were 'need'ed, or passed to the class" do
    needed = [ ]
    5.times { needed << "need#{rand(1_000_000_000)}".to_sym }
    needed = needed.shuffle

    not_needed = [ ]
    5.times { not_needed << "need#{rand(1_000_000_000)}".to_sym }
    not_needed = not_needed.shuffle

    wc = widget_class { extra_assigns :use }

    remaining_needed = needed.dup
    while remaining_needed.length > 0
      this_slice = remaining_needed.shift(rand(3))
      wc.needs *this_slice
    end

    params = { }
    needed.shuffle.each { |n| params[n] = "value-#{n}" }
    not_needed.each { |nn| params[nn] = "value-#{nn}" }

    instance = wc.new(params)

    expect(instance.assigns.keys.sort_by(&:to_s)).to eq((needed + not_needed).sort_by(&:to_s))

    each_output = [ ]
    instance.assigns.each { |k,v| each_output << k }
    expect(each_output.sort_by(&:to_s)).to eq((needed + not_needed).sort_by(&:to_s))
  end

  it "should tell you whether an assign is the default" do
    wc = widget_class do
      needs :foo, :bar => nil, :baz => 'def_baz', :quux => 'whatever', :marph => 'yep'

      def content
        text "hi"
      end
    end

    instance = wc.new(:foo => 'the_foo', :bar => nil, :baz => 'def_baz', :marph => 'nope')
    expect(instance.assigns[:foo]).to eq('the_foo')
    expect(instance.assigns.is_default?(:foo)).to be_falsey
    expect(instance.assigns[:bar]).to eq(nil)
    expect(instance.assigns.is_default?(:bar)).to be_falsey
    expect(instance.assigns[:baz]).to eq('def_baz')
    expect(instance.assigns.is_default?(:baz)).to be_falsey
    expect(instance.assigns[:quux]).to eq('whatever')
    expect(instance.assigns.is_default?(:quux)).to be_truthy
    expect(instance.assigns[:marph]).to eq('nope')
    expect(instance.assigns.is_default?(:marph)).to be_falsey
  end

  it "should return the same assigns proxy every time" do
    wc = widget_class_with_content do
      text "assigns proxy 1: #{assigns.object_id}, "
      text "assigns proxy 2: #{assigns.object_id}"
    end

    text = render(wc)
    text =~ /assigns proxy 1: (\d+), assigns proxy 2: (\d+)/
    expect($1).to eq($2)
  end

  def should_have_line(text, line)
    lines = text.split(/[\r\n]+/)
    match = lines.detect { |l| l == line }
    unless match
      raise "Couldn't find this line:\n#{line}\nin:\n#{text}"
    end
  end

  def line_matching(text, regexp)
    lines = text.split(/[\r\n]+/)
    match = lines.detect { |l| l =~ regexp }
    unless match
      raise "Couldn't find this regexp:\n#{regexp}\nin:\n#{text}"
    end
    match
  end

  it "should return the right values for many methods" do
    wc = widget_class do
      needs :foo, :bar, :baz => nil

      def content
        text "keys: #{assigns.keys.sort_by(&:to_s).inspect}\n"
        text "has_key?(:baz): #{assigns.has_key?(:baz).inspect}\n"
        text "has_key?(:quux): #{assigns.has_key?(:quux).inspect}\n"
        text "[](:foo): #{assigns[:foo]}\n"
        text "[](:baz): #{assigns[:baz]}\n"
        text "[](:quux): #{assigns[:quux]}\n"
        text "to_hash: #{assigns.to_hash.inspect}\n".html_safe
        text "to_h: #{assigns.to_hash.inspect}\n".html_safe
        text "length: #{assigns.length}\n"
        text "size: #{assigns.size}\n"
        text "to_s: #{assigns.to_s}\n".html_safe
        text "inspect: #{assigns.inspect}\n".html_safe
        text "member?(:foo): #{assigns.member?(:foo)}\n"
        text "member?(:baz): #{assigns.member?(:baz)}\n"
        text "member?(:quux): #{assigns.member?(:quux)}\n"

        text "store(:baz, 12345): #{assigns.store(:baz, 12345)}\n"
        text "new [](:baz): #{assigns[:baz]}\n"
      end
    end

    widget = wc.new(:foo => 'the_foo', :bar => 'the_bar')
    text = render(widget)
    should_have_line(text, "keys: [:bar, :baz, :foo]")
    should_have_line(text, "has_key?(:baz): true")
    should_have_line(text, "has_key?(:quux): false")
    should_have_line(text, "[](:foo): the_foo")
    should_have_line(text, "[](:baz): ")
    should_have_line(text, "[](:quux): ")

    hash_line = line_matching(text, /^to_hash: (.*)/)
    hash_line =~ /^to_hash: (.*)/
    hash_text = $1
    as_hash = eval(hash_text)
    expect(as_hash).to eq({ :foo => 'the_foo', :bar => 'the_bar', :baz => nil })

    should_have_line(text, "to_h: #{hash_text}")
    should_have_line(text, "length: 3")
    should_have_line(text, "size: 3")

    to_s_line = line_matching(text, /^to_s: (.*)/)
    expect(to_s_line).to match(/Assigns for #{widget}/)
    if RUBY_VERSION =~ /^1\.8\./
      expect(to_s_line).to match(/foothe_foo/)
      expect(to_s_line).to match(/barthe_bar/)
      expect(to_s_line).to match(/baz/)
    else
      expect(to_s_line).to match(/:foo\s*\=\>\s*["']the_foo["']/)
      expect(to_s_line).to match(/:bar\s*\=\>\s*["']the_bar["']/)
      expect(to_s_line).to match(/:baz\s*\=\>\s*nil/)
    end

    inspect_line = line_matching(text, /^inspect: (.*)/)
    expect(inspect_line).to match(/Assigns for #{widget}/)
    expect(inspect_line).to match(/:foo\s*\=\>\s*["']the_foo["']/)
    expect(inspect_line).to match(/:bar\s*\=\>\s*["']the_bar["']/)
    expect(inspect_line).to match(/:baz\s*\=\>\s*nil/)

    should_have_line(text, "member?(:foo): true")
    should_have_line(text, "member?(:baz): true")
    should_have_line(text, "member?(:quux): false")
    should_have_line(text, "store(:baz, 12345): 12345")
    should_have_line(text, "new [](:baz): 12345")
  end

  # We currently don't have a test for the massive number of automatically-delegated methods on the AssignsProxy;
  # writing one would be long and tedious, frankly, and the chance that we're going to find a bug in there is close
  # to zero. If you feel we should have one, though, feel free to contribute one. ;)
end
