# frozen_string_literal: true

require 'fuubar'
require 'stringio'
require 'ostruct'

# rubocop:disable RSpec/MultipleMemoizedHelpers
describe ::Fuubar do
  let(:output) do
    io = ::StringIO.new

    allow(io).to receive(:tty?).
                   and_return(true)

    io
  end

  let(:formatter)            { ::Fuubar.new(output) }
  let(:example)              { self.class.example }
  let(:example_count)        { 2 }
  let(:start_notification)   { ::RSpec::Core::Notifications::StartNotification.new(example_count, ::Time.now) }
  let(:message_notification) { ::RSpec::Core::Notifications::MessageNotification.new('My Message') }
  let(:example_notification) { ::RSpec::Core::Notifications::ExampleNotification.for(example) }
  let(:pending_notification) { ::RSpec::Core::Notifications::ExampleNotification.for(pending_example) }
  let(:failed_notification)  { ::RSpec::Core::Notifications::ExampleNotification.for(failed_example) }

  let(:failed_example) do
    exception = ::RuntimeError.new('Test Fuubar Error')
    exception.set_backtrace [
                              "/my/filename.rb:4:in `some_method'"
                            ]

    example = self.class.example

    example.metadata[:file_path] = '/my/example/spec.rb'
    example.metadata[:execution_result].status = :failed
    example.metadata[:execution_result].exception = exception

    example
  end

  let(:pending_example) do
    example = self.class.example
    example.metadata[:execution_result].pending_fixed = true
    example
  end

  let(:fuubar_results) do
    output.rewind
    output.read
  end

  before(:each) do
    ::RSpec.configuration.fuubar_progress_bar_options = {
      :length        => 40,
      :throttle_rate => 0.0
    }

    ENV.delete('CONTINUOUS_INTEGRATION')
  end

  context 'when it is created' do
    it 'does not start the bar until the formatter is started' do
      expect(formatter.progress).not_to be_started

      formatter.start(start_notification)

      expect(formatter.progress).to be_started
    end

    it 'creates a new ProgressBar' do
      expect(formatter.progress).to be_instance_of ::ProgressBar::Base
    end

    it 'sets the format of the bar to the default' do
      expect(formatter.progress.instance_variable_get(:@format)).to eql ' %c/%C |%w>%i| %e '
    end

    it 'sets the total to the number of examples' do
      expect(formatter.progress.total).to be_zero
    end

    it 'sets the bar\'s output' do
      expect(formatter.progress.send(:output).stream).to            be_a ::Fuubar::Output
      expect(formatter.progress.send(:output).stream.__getobj__).to eql output
    end

    context 'and continuous integration is enabled' do
      before(:each) do
        allow(::RSpec.configuration).to receive(:color_mode).and_return(:on)
        ::RSpec.configuration.fuubar_progress_bar_options = { :length => 40 }
        ENV['CONTINUOUS_INTEGRATION'] = 'true'
      end

      it 'throttles the progress bar at one second' do
        throttle      = formatter.progress.__send__(:output).__send__(:throttle)
        throttle_rate = throttle.__send__(:rate)

        expect(throttle_rate).to be 1.0
      end

      context 'when processing an example' do
        before(:each) do
          formatter.start(start_notification)

          throttle       = formatter.progress.__send__(:output).__send__(:throttle)
          _throttle_rate = throttle.__send__(:rate=, 0.0)

          output.rewind

          formatter.example_passed(example)
        end

        it 'does not output color codes' do
          expect(fuubar_results).to start_with " 1/2 |== 50 ==>        |  ETA: 00:00:00 \r"
        end
      end
    end

    context 'and continuous integration is not enabled' do
      before(:each) do
        allow(::RSpec.configuration).to receive(:color_mode).and_return(:on)
        ::RSpec.configuration.fuubar_progress_bar_options = { :length => 40 }
        ENV['CONTINUOUS_INTEGRATION'] = 'false'
      end

      it 'throttles the progress bar at the default rate' do
        throttle      = formatter.progress.__send__(:output).__send__(:throttle)
        throttle_rate = throttle.__send__(:rate)

        expect(throttle_rate).to be 0.01
      end

      context 'when processing an example' do
        before(:each) do
          formatter.start(start_notification)

          throttle       = formatter.progress.__send__(:output).__send__(:throttle)
          _throttle_rate = throttle.__send__(:rate=, 0.0)

          output.rewind

          formatter.example_passed(example)
        end

        it 'outputs color codes' do
          expect(fuubar_results).to start_with "\e[32m 1/2 |== 50 ==>        |  ETA: 00:00:00 \r\e[0m"
        end
      end
    end
  end

  context 'when custom options are set after the formatter is created' do
    before(:each) do
      formatter
      ::RSpec.configuration.fuubar_progress_bar_options = {
        :length        => 40,
        :throttle_rate => 0.0,
        :format        => '%c'
      }
    end

    context 'when the bar is started' do
      before(:each) { formatter.start(start_notification) }

      it 'properly creates the bar' do
        expect(formatter.progress.instance_variable_get(:@format)).to eql '%c'
      end
    end
  end

  context 'when it is started' do
    before(:each) do
      allow(::RSpec.configuration).to receive(:color_mode).and_return(:on)

      formatter.start(start_notification)
    end

    it 'sets the total to the number of examples' do
      expect(formatter.progress.total).to be 2
    end

    context 'and no custom options are passed in' do
      it 'sets the format of the bar to the default' do
        expect(formatter.progress.instance_variable_get(:@format)).to eql ' %c/%C |%w>%i| %e '
      end
    end

    context 'and an example passes' do
      before(:each) do
        output.rewind

        formatter.example_passed(example)
      end

      it 'outputs the proper bar information' do
        expect(fuubar_results).to start_with "\e[32m 1/2 |== 50 ==>        |  ETA: 00:00:00 \r\e[0m"
      end
    end

    context 'and an example pends' do
      before(:each) do
        output.rewind

        formatter.example_pending(pending_example)
      end

      it 'outputs the proper bar information' do
        formatter.progress.increment
        expect(fuubar_results).to start_with "\e[33m 1/2 |== 50 ==>        |  ETA: 00:00:00 \r\e[0m"
      end

      context 'and then an example succeeds' do
        before(:each) do
          output.rewind

          formatter.example_pending(pending_notification)
        end

        it 'outputs the pending bar' do
          expect(fuubar_results).to start_with "\e[33m 2/2 |===== 100 ======>| Time: 00:00:00 \n\e[0m"
        end
      end
    end

    context 'and an example fails' do
      it 'outputs the proper bar information' do
        output.rewind

        formatter.example_failed(failed_notification)

        expect(fuubar_results).to end_with "\e[31m 1/2 |== 50 ==>        |  ETA: 00:00:00 \r\e[0m"
      end

      context 'and then an example succeeds' do
        before(:each) do
          formatter.example_failed(failed_notification)

          output.rewind

          formatter.example_passed(example)
        end

        it 'outputs the failed bar' do
          expect(fuubar_results).to start_with "\e[31m 2/2 |===== 100 ======>| Time: 00:00:00 \n\e[0m"
        end
      end

      context 'and then an example pends' do
        before(:each) do
          formatter.example_failed(failed_notification)

          output.rewind

          formatter.example_pending(example_notification)
        end

        it 'outputs the failed bar' do
          expect(fuubar_results).to start_with "\e[31m 2/2 |===== 100 ======>| Time: 00:00:00 \n\e[0m"
        end
      end
    end

    it 'can properly log messages' do
      formatter.message message_notification

      expect(fuubar_results).to end_with "My Message\n 0/2 |>                |  ETA: ??:??:?? \r"
    end
  end
end
# rubocop:enable RSpec/MultipleMemoizedHelpers
