require 'spec_helper'

RSpec.describe SvgHeartsYou do

  let(:test_svg_path)          { File.join File.dirname(__FILE__), 'svgs' }
  let!(:svg_file)              { 'sapphire.svg' }
  let!(:svg_file2)             { 'circle.svg' }
  let!(:svg_folder)            { 'shapes' }
  let!(:svg_folder2)           { 'logos' }
  let!(:nonexistent_svg_file)  { 'nope-nope.svg' }

  let!(:unconfigured_message)  { "File #{svg_file} not found" }
  let!(:missing_file_message)  { "File #{nonexistent_svg_file} not found" }


  # Singleton module is messy for random order tests, so wipe state after each
  after(:each) do
    SvgHeartsYou.reset
  end

  # Generically tests methods (passed as symbol) that are
  # expected to throw runtime errors
  shared_examples 'methods that throw error on file not found' do |method|
    it 'throws a RuntimeError when not file not found' do
      expect{subject.send(method, svg_file)}.to raise_error(RuntimeError, unconfigured_message)
    end
  end

  # Examples for methods that may throw error when files are missing
  shared_examples 'method using file' do |method|
    it 'throws a RuntimeError when file is not found' do
      expect{subject.send(method, nonexistent_svg_file)}.to raise_error(RuntimeError, missing_file_message)
    end
  end

  # Tests that are used in both unconfigured and configured contexts
  shared_examples 'svg use' do
    it 'returns SVG use statement' do
      svg_content = subject.svg_use '#id'
      expect(svg_content).to include '<use xlink:href="#id">'
    end

    it 'adds hash at beginning if not given or external link' do
      svg_content = subject.svg_use 'id'
      expect(svg_content).to include '<use xlink:href="#id">'
    end

    it 'adds classes and ids to svg tag' do
      svg_content = subject.svg_use '#id', id: 'hearts', class: 'love'
      expect(svg_content).to have_tag('svg', with: { id: 'hearts', class: 'love' })
    end

    it 'adds any attribute to svg tag' do
      attributes = { width: '64px', height: '48px', viewport: '0, 0, 12, 24' }
      svg_content = subject.svg_use '#id', attributes
      expect(svg_content).to have_tag('svg', with: attributes)
    end

    it 'strips .svg extension if necessary' do
      svg_content = subject.svg_use '#circle.svg'
      expect(svg_content).to include '<use xlink:href="#circle">'
      expect(svg_content).to_not include '<use xlink:href="#circle.svg">'
    end
  end


  describe 'without configuration' do
    describe 'self.configuration' do
      it 'defaults attributes to nil' do
        expect(SvgHeartsYou.configuration.svg_paths).to eq([])
      end
    end

    describe '#svg_inline' do
      it_behaves_like 'methods that throw error on file not found', :svg_inline
    end

    describe '#svg_use' do
      include_examples 'svg use'
    end

    describe '#svg_symbol' do
      it_behaves_like 'methods that throw error on file not found', :svg_symbol
    end
  end

  describe 'with configuration' do
    before do
      SvgHeartsYou.configure do |config|
        config.svg_paths << test_svg_path
      end
    end

    describe 'self.configuration' do
      it 'is configured to the the current directory' do
        expect(SvgHeartsYou.configuration.svg_paths).to eq([test_svg_path])
      end
    end

    describe '#svg_inline' do
      it_behaves_like 'method using file', :svg_inline

      it 'returns contents of SVG file without XML headers' do
        svg_content = subject.svg_inline 'sapphire.svg'

        expect(svg_content).to have_tag('svg')
        expect(svg_content).not_to include('<xml')
        expect(svg_content).not_to include('DOCTYPE')
      end

      it 'can be called without the .svg extension' do
        svg_content = subject.svg_inline 'sapphire'
        expect(svg_content).to have_tag('svg')
      end
    end

    describe '#svg_use' do
      include_examples 'svg use'
    end

    describe '#svg_symbol' do
      let(:sapphire_svg_attributes) {{
        x: '0',
        y: '0',
        width: '64',
        height: '52',
        viewbox: '0, 0, 64, 52'
        }}

      it_behaves_like 'method using file', :svg_symbol

      it 'returns the SVG contents in a symbol' do
        svg_content = subject.svg_symbol svg_file

        expect(svg_content).not_to have_tag('svg', with: sapphire_svg_attributes)
        expect(svg_content).to have_tag('svg>symbol', with: sapphire_svg_attributes)
        expect(svg_content).to have_tag('svg>symbol', with: { id: 'sapphire' })
        expect(svg_content).not_to have_tag('svg>*:not(symbol)')
        expect(svg_content).to have_tag('svg>symbol>*')
      end

      it 'applies extra attributes to top level svg' do
        new_id = 'hey-there'

        svg_content = subject.svg_symbol svg_file, id: new_id
        expect(svg_content).to have_tag('svg', with: {id: new_id})
      end

      it 'applies or replaces attributes on each symbol with the `each` parameter' do
        new_viewbox = '0, 0, 100, 100'
        new_class = 'shape'

        updated_attributes = sapphire_svg_attributes.clone
        updated_attributes[:viewbox] = new_viewbox
        updated_attributes[:class] = new_class

        svg_content = subject.svg_symbol svg_file, each: { viewbox: new_viewbox, class: new_class }
        expect(svg_content).to have_tag('svg>symbol', with: updated_attributes)
      end

      it 'pulls in multiple files' do
        svg_content = subject.svg_symbol [svg_file, svg_file2]
        expect(svg_content).to have_tag('svg>symbol', count: 2)
      end

      it 'pulls in a given folder with `folder` parameter' do
        svg_content = subject.svg_symbol svg_folder, folder: true
        expect(svg_content).to have_tag('svg>symbol', with: { id: 'polygon' })
        expect(svg_content).to have_tag('svg>symbol', with: { id: 'star' })
        expect(svg_content).to have_tag('svg>symbol', with: { id: 'triangle' })
      end

      it 'pulls in multiple folders with `folder` parameter' do
        svg_content = subject.svg_symbol [svg_folder, svg_folder2], folder: true
        expect(svg_content).to have_tag('svg>symbol', count: 8)
      end

      it 'takes a block that can modify each symbol' do
        new_class = 'shape'

        svg_content = subject.svg_symbol svg_folder, folder: true do |attributes|
          attributes[:id] = attributes[:id] + '-extra'
          attributes[:class] = 'shape'
        end

        expect(svg_content).to have_tag('svg>symbol', with: { id: 'polygon-extra',  class: new_class })
        expect(svg_content).to have_tag('svg>symbol', with: { id: 'star-extra',     class: new_class })
        expect(svg_content).to have_tag('svg>symbol', with: { id: 'triangle-extra', class: new_class })
      end
    end
  end

  # Meta test to make sure state is wiped
  it 'correctly resets configuration at end of tests' do
    expect(SvgHeartsYou.configuration.svg_paths).to eq([])
  end
end