# encoding: UTF-8
shared_examples_for "documentation on faces" do
  defined?(Attrs) or
    Attrs = [:summary, :description, :examples, :short_description, :notes, :author]

  defined?(SingleLineAttrs) or
    SingleLineAttrs = [:summary, :author]

  # Simple, procedural tests that apply to a bunch of methods.
  Attrs.each do |attr|
    it "should accept a #{attr}" do
      expect { subject.send("#{attr}=", "hello") }.not_to raise_error
      expect(subject.send(attr)).to eq("hello")
    end

    it "should accept a long (single line) value for #{attr}" do
      text = "I never know when to stop with the word banana" + ("na" * 1000)
      expect { subject.send("#{attr}=", text) }.to_not raise_error
      expect(subject.send(attr)).to eq(text)
    end
  end

  Attrs.each do |getter|
    setter = "#{getter}=".to_sym
    context "#{getter}" do
      it "should strip leading whitespace on a single line" do
        subject.send(setter, "  death to whitespace")
        expect(subject.send(getter)).to eq("death to whitespace")
      end

      it "should strip trailing whitespace on a single line" do
        subject.send(setter, "death to whitespace  ")
        expect(subject.send(getter)).to eq("death to whitespace")
      end

      it "should strip whitespace at both ends at once" do
        subject.send(setter, "  death to whitespace  ")
        expect(subject.send(getter)).to eq("death to whitespace")
      end

      multiline_text = "with\nnewlines"
      if SingleLineAttrs.include? getter then
        it "should not accept multiline values" do
          expect { subject.send(setter, multiline_text) }.
            to raise_error ArgumentError, /#{getter} should be a single line/
          expect(subject.send(getter)).to be_nil
        end
      else
        it "should accept multiline values" do
          expect { subject.send(setter, multiline_text) }.not_to raise_error
          expect(subject.send(getter)).to eq(multiline_text)
        end

        [1, 2, 4, 7, 25].each do |length|
          context "#{length} chars indent" do
            indent = ' ' * length

            it "should strip leading whitespace on multiple lines" do
              text = "this\nis\the\final\outcome"
              subject.send(setter, text.gsub(/^/, indent))
              expect(subject.send(getter)).to eq(text)
            end

            it "should not remove formatting whitespace, only global indent" do
              text = "this\n  is\n    the\n  ultimate\ntest"
              subject.send(setter, text.gsub(/^/, indent))
              expect(subject.send(getter)).to eq(text)
            end
          end
        end

        it "should strip whitespace with a blank line" do
          subject.send(setter, "  this\n\n  should outdent")
          expect(subject.send(getter)).to eq("this\n\nshould outdent")
        end
      end
    end
  end

  describe "#short_description" do
    it "should return the set value if set after description" do
      subject.description = "hello\ngoodbye"
      subject.short_description = "whatever"
      expect(subject.short_description).to eq("whatever")
    end

    it "should return the set value if set before description" do
      subject.short_description = "whatever"
      subject.description = "hello\ngoodbye"
      expect(subject.short_description).to eq("whatever")
    end

    it "should return nothing if not set and no description" do
      expect(subject.short_description).to be_nil
    end

    it "should return the first paragraph of description if not set (where it is one line long)" do
      subject.description = "hello"
      expect(subject.short_description).to eq(subject.description)
    end

    it "should return the first paragraph of description if not set (where there is no paragraph break)" do
      subject.description = "hello\ngoodbye"
      expect(subject.short_description).to eq(subject.description)
    end

    it "should return the first paragraph of description if not set (where there is a paragraph break)" do
      subject.description = "hello\ngoodbye\n\nmore\ntext\nhere\n\nfinal\nparagraph"
      expect(subject.short_description).to eq("hello\ngoodbye")
    end

    it "should trim a very, very long first paragraph and add ellipsis" do
      line = "this is a very, very, very long long line full of text\n"
      subject.description = line * 20 + "\n\nwhatever, dude."

      expect(subject.short_description).to eq((line * 5).chomp + ' [...]')
    end

    it "should trim a very very long only paragraph even if it is followed by a new paragraph" do
      line = "this is a very, very, very long long line full of text\n"
      subject.description = line * 20

      expect(subject.short_description).to eq((line * 5).chomp + ' [...]')
    end
  end

  describe "multiple authors" do
    authors = %w{John Paul George Ringo}

    context "in the DSL" do
      it "should support multiple authors" do

        authors.each {|name| subject.author name }
        expect(subject.authors).to match_array(authors)

        expect(subject.author).to eq(authors.join("\n"))
      end

      it "should reject author as an array" do
        expect { subject.author ["Foo", "Bar"] }.
          to raise_error ArgumentError, /author must be a string/
      end
    end

    context "#author=" do
      it "should accept a single name" do
        subject.author = "Fred"
        expect(subject.author).to eq("Fred")
      end

      it "should accept an array of names" do
        subject.author = authors
        expect(subject.authors).to match_array(authors)
        expect(subject.author).to eq(authors.join("\n"))
      end

      it "should not append when set multiple times" do
        subject.author = "Fred"
        subject.author = "John"
        expect(subject.author).to eq("John")
      end

      it "should reject arrays with embedded newlines" do
        expect { subject.author = ["Fred\nJohn"] }.
          to raise_error ArgumentError, /author should be a single line/
      end
    end
  end

  describe "#license" do
    it "should default to reserving rights" do
      expect(subject.license).to match(/All Rights Reserved/)
    end

    it "should accept an arbitrary license string on the object" do
      subject.license = "foo"
      expect(subject.license).to eq("foo")
    end
  end

  describe "#copyright" do
    it "should fail with just a name" do
      expect { subject.copyright("invalid") }.
        to raise_error ArgumentError, /copyright takes the owners names, then the years covered/
    end

    [1997, "1997"].each do |year|
      it "should accept an entity name and a #{year.class.name} year" do
        subject.copyright("me", year)
        expect(subject.copyright).to match(/\bme\b/)
        expect(subject.copyright).to match(/#{year}/)
      end

      it "should accept multiple entity names and a #{year.class.name} year" do
        subject.copyright ["me", "you"], year
        expect(subject.copyright).to match(/\bme\b/)
        expect(subject.copyright).to match(/\byou\b/)
        expect(subject.copyright).to match(/#{year}/)
      end
    end

    ["1997-2003", "1997 - 2003", 1997..2003].each do |range|
      it "should accept a #{range.class.name} range of years" do
        subject.copyright("me", range)
        expect(subject.copyright).to match(/\bme\b/)
        expect(subject.copyright).to match(/1997-2003/)
      end

      it "should accept a #{range.class.name} range of years" do
        subject.copyright ["me", "you"], range
        expect(subject.copyright).to match(/\bme\b/)
        expect(subject.copyright).to match(/\byou\b/)
        expect(subject.copyright).to match(/1997-2003/)
      end
    end

    [[1997, 2003], ["1997", 2003], ["1997", "2003"]].each do |input|
      it "should accept the set of years #{input.inspect} in an array" do
        subject.copyright "me", input
        expect(subject.copyright).to match(/\bme\b/)
        expect(subject.copyright).to match(/1997, 2003/)
      end

      it "should accept the set of years #{input.inspect} in an array" do
        subject.copyright ["me", "you"], input
        expect(subject.copyright).to match(/\bme\b/)
        expect(subject.copyright).to match(/\byou\b/)
        expect(subject.copyright).to match(/1997, 2003/)
      end
    end

    it "should warn if someone does math accidentally on the range of years" do
      expect { subject.copyright "me", 1997-2003 }.
        to raise_error ArgumentError, /copyright with a year before 1970 is very strange; did you accidentally add or subtract two years\?/
    end

    it "should accept complex copyright years" do
      years = [1997, 1999, 2000..2002, 2005].reverse
      subject.copyright "me", years
      expect(subject.copyright).to match(/\bme\b/)
      expect(subject.copyright).to match(/1997, 1999, 2000-2002, 2005/)
    end
  end

  # Things that are automatically generated.
  [:name, :options, :synopsis].each do |attr|
    describe "##{attr}" do
      it "should not allow you to set #{attr}" do
        expect(subject).not_to respond_to :"#{attr}="
      end

      it "should have a #{attr}" do
        expect(subject.send(attr)).not_to be_nil
      end

      it "'s #{attr} should not be empty..." do
        expect(subject.send(attr)).not_to eq('')
      end
    end
  end
end
