# frozen_string_literal: true

require 'spec_helper'

describe Webdrivers::Chromedriver do
  let(:chromedriver) { described_class }

  before { chromedriver.remove }

  describe '#update' do
    context 'when evaluating #correct_binary?' do
      it 'does not download when latest version and current version match' do
        allow(chromedriver).to receive(:latest_version).and_return(Gem::Version.new('72.0.0'))
        allow(chromedriver).to receive(:current_version).and_return(Gem::Version.new('72.0.0'))

        chromedriver.update

        expect(chromedriver.send(:exists?)).to be false
      end

      it 'does not download when offline, binary exists and is less than v70' do
        allow(Net::HTTP).to receive(:get_response).and_raise(SocketError)
        allow(chromedriver).to receive(:exists?).and_return(true)
        allow(chromedriver).to receive(:current_version).and_return(Gem::Version.new(69))

        chromedriver.update

        expect(File.exist?(chromedriver.driver_path)).to be false
      end

      it 'does not download when offline, binary exists and matches major browser version' do
        allow(Net::HTTP).to receive(:get_response).and_raise(SocketError)
        allow(chromedriver).to receive(:exists?).and_return(true)
        allow(chromedriver).to receive(:chrome_version).and_return(Gem::Version.new('73.0.3683.68'))
        allow(chromedriver).to receive(:current_version).and_return(Gem::Version.new('73.0.3683.20'))

        chromedriver.update

        expect(File.exist?(chromedriver.driver_path)).to be false
      end

      it 'does not download when get raises exception, binary exists and matches major browser version' do
        client_error = instance_double(Net::HTTPNotFound, class: Net::HTTPNotFound, code: 404, message: '')

        allow(Webdrivers::Network).to receive(:get_response).and_return(client_error)
        allow(chromedriver).to receive(:exists?).and_return(true)
        allow(chromedriver).to receive(:chrome_version).and_return(Gem::Version.new('73.0.3683.68'))
        allow(chromedriver).to receive(:current_version).and_return(Gem::Version.new('73.0.3683.20'))

        chromedriver.update

        expect(File.exist?(chromedriver.driver_path)).to be false
      end

      it 'raises ConnectionError when offline, and binary does not match major browser version' do
        allow(Net::HTTP).to receive(:get_response).and_raise(SocketError)
        allow(chromedriver).to receive(:exists?).and_return(true)
        allow(chromedriver).to receive(:chrome_version).and_return(Gem::Version.new('73.0.3683.68'))
        allow(chromedriver).to receive(:current_version).and_return(Gem::Version.new('72.0.0.0'))

        msg = %r{Can not reach https://chromedriver.storage.googleapis.com}
        expect { chromedriver.update }.to raise_error(Webdrivers::ConnectionError, msg)
      end

      it 'raises ConnectionError when offline, and no binary exists' do
        allow(Net::HTTP).to receive(:get_response).and_raise(SocketError)
        allow(chromedriver).to receive(:exists?).and_return(false)

        msg = %r{Can not reach https://chromedriver.storage.googleapis.com}
        expect { chromedriver.update }.to raise_error(Webdrivers::ConnectionError, msg)
      end
    end

    context 'when correct binary is found' do
      before { allow(chromedriver).to receive(:correct_binary?).and_return(true) }

      it 'does not download' do
        chromedriver.update

        expect(chromedriver.current_version).to be_nil
      end

      it 'does not raise exception if offline' do
        allow(Net::HTTP).to receive(:get_response).and_raise(SocketError)

        chromedriver.update

        expect(chromedriver.current_version).to be_nil
      end
    end

    context 'when correct binary is not found' do
      before { allow(chromedriver).to receive(:correct_binary?).and_return(false) }

      it 'downloads binary' do
        chromedriver.update

        expect(chromedriver.current_version).not_to be_nil
      end

      it 'raises ConnectionError if offline' do
        allow(Net::HTTP).to receive(:get_response).and_raise(SocketError)

        msg = %r{Can not reach https://chromedriver.storage.googleapis.com/}
        expect { chromedriver.update }.to raise_error(Webdrivers::ConnectionError, msg)
      end
    end

    it 'makes a network call if cached driver does not match the browser' do
      Webdrivers::System.cache_version('chromedriver', '71.0.3578.137')
      allow(chromedriver).to receive(:chrome_version).and_return(Gem::Version.new('73.0.3683.68'))
      allow(Webdrivers::Network).to receive(:get).and_return('73.0.3683.68')
      allow(Webdrivers::System).to receive(:download)

      chromedriver.update

      expect(Webdrivers::Network).to have_received(:get).twice
    end

    context 'when required version is 0' do
      it 'downloads the latest version' do
        allow(chromedriver).to receive(:latest_version).and_return(Gem::Version.new('72.0.3626.7'))
        chromedriver.required_version = 0
        chromedriver.update
        expect(chromedriver.current_version.version).to eq('72.0.3626.7')
      end
    end

    context 'when required version is nil' do
      it 'downloads the latest version' do
        allow(chromedriver).to receive(:latest_version).and_return(Gem::Version.new('72.0.3626.7'))
        chromedriver.required_version = nil
        chromedriver.update
        expect(chromedriver.current_version.version).to eq('72.0.3626.7')
      end
    end

    context 'when ENV variable WD_CACHE_TIME is set' do
      it 'uses cache time value from ENV variable' do
        allow(ENV).to receive(:[]).with('WD_CACHE_TIME').and_return('999')
        expect(Webdrivers.cache_time).to be(999)
      end
    end
  end

  describe '#current_version' do
    it 'returns nil if binary does not exist on the system' do
      allow(chromedriver).to receive(:driver_path).and_return('')

      expect(chromedriver.current_version).to be_nil
    end

    it 'returns a Gem::Version instance if binary is on the system' do
      allow(chromedriver).to receive(:exists?).and_return(true)
      allow(Webdrivers::System).to receive(:call)
        .with("#{chromedriver.driver_path} --version")
        .and_return '71.0.3578.137'

      expect(chromedriver.current_version).to eq Gem::Version.new('71.0.3578.137')
    end
  end

  describe '#latest_version' do
    it 'returns 2.41 if the browser version is less than 70' do
      allow(chromedriver).to receive(:chrome_version).and_return('69.0.0')

      expect(chromedriver.latest_version).to eq(Gem::Version.new('2.41'))
    end

    it 'returns the correct point release for a production version greater than 70' do
      allow(chromedriver).to receive(:chrome_version).and_return '71.0.3578.9999'

      expect(chromedriver.latest_version).to eq Gem::Version.new('71.0.3578.137')
    end

    it 'raises VersionError for beta version' do
      allow(chromedriver).to receive(:chrome_version).and_return('100.0.0')
      msg = 'Unable to find latest point release version for 100.0.0. '\
'You appear to be using a non-production version of Chrome. '\
'Please set `Webdrivers::Chromedriver.required_version = <desired driver version>` '\
'to a known chromedriver version: https://chromedriver.storage.googleapis.com/index.html'

      expect { chromedriver.latest_version }.to raise_exception(Webdrivers::VersionError, msg)
    end

    it 'raises VersionError for unknown version' do
      allow(chromedriver).to receive(:chrome_version).and_return('72.0.9999.0000')
      msg = 'Unable to find latest point release version for 72.0.9999. '\
'Please set `Webdrivers::Chromedriver.required_version = <desired driver version>` '\
'to a known chromedriver version: https://chromedriver.storage.googleapis.com/index.html'

      expect { chromedriver.latest_version }.to raise_exception(Webdrivers::VersionError, msg)
    end

    it 'raises ConnectionError when offline' do
      allow(Net::HTTP).to receive(:get_response).and_raise(SocketError)

      msg = %r{^Can not reach https://chromedriver.storage.googleapis.com}
      expect { chromedriver.latest_version }.to raise_error(Webdrivers::ConnectionError, msg)
    end

    it 'creates cached file' do
      allow(Webdrivers::Network).to receive(:get).and_return('71.0.3578.137')

      chromedriver.latest_version
      expect(File.exist?("#{Webdrivers::System.install_dir}/chromedriver.version")).to eq true
    end

    it 'does not make network call if cache is valid' do
      allow(Webdrivers).to receive(:cache_time).and_return(3600)
      Webdrivers::System.cache_version('chromedriver', '71.0.3578.137')
      allow(Webdrivers::Network).to receive(:get)

      expect(chromedriver.latest_version).to eq Gem::Version.new('71.0.3578.137')

      expect(Webdrivers::Network).not_to have_received(:get)
    end

    it 'makes a network call if cache is expired' do
      Webdrivers::System.cache_version('chromedriver', '71.0.3578.137')
      allow(Webdrivers::Network).to receive(:get).and_return('73.0.3683.68')
      allow(Webdrivers::System).to receive(:valid_cache?)

      expect(chromedriver.latest_version).to eq Gem::Version.new('73.0.3683.68')

      expect(Webdrivers::Network).to have_received(:get)
      expect(Webdrivers::System).to have_received(:valid_cache?)
    end
  end

  describe '#required_version=' do
    after { chromedriver.required_version = nil }

    it 'returns the version specified as a Float' do
      chromedriver.required_version = 73.0

      expect(chromedriver.required_version).to eq Gem::Version.new('73.0')
    end

    it 'returns the version specified as a String' do
      chromedriver.required_version = '73.0'

      expect(chromedriver.required_version).to eq Gem::Version.new('73.0')
    end
  end

  describe '#remove' do
    it 'removes existing chromedriver' do
      chromedriver.update

      chromedriver.remove
      expect(chromedriver.current_version).to be_nil
    end

    it 'does not raise exception if no chromedriver found' do
      expect { chromedriver.remove }.not_to raise_error
    end
  end

  describe '#install_dir' do
    it 'uses ~/.webdrivers as default value' do
      expect(Webdrivers.install_dir).to include('.webdriver')
    end

    it 'uses provided value' do
      begin
        install_dir            = File.expand_path(File.join(ENV['HOME'], '.webdrivers2'))
        Webdrivers.install_dir = install_dir

        expect(Webdrivers.install_dir).to eq install_dir
      ensure
        Webdrivers.install_dir = nil
      end
    end

    context 'when ENV variable WD_INSTALL_DIR is set' do
      it 'uses path from the ENV variable' do
        allow(ENV).to receive(:[]).with('WD_INSTALL_DIR').and_return('custom_dir')
        expect(Webdrivers.install_dir).to be('custom_dir')
      end
    end
  end

  describe '#driver_path' do
    it 'returns full location of binary' do
      expect(chromedriver.driver_path).to match("#{File.join(ENV['HOME'])}/.webdrivers/chromedriver")
    end
  end
end
