From 8733383ac54a1b7b6565451fa7f0525fb3a99f17 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Wed, 11 May 2022 16:55:47 +0200 Subject: [PATCH] Get rid of Cliver --- .../gemfiles/websocket-driver-0.6.x.gemfile | 1 - .../gemfiles/websocket-driver-0.7.x.gemfile | 1 - README.md | 1 - ferrum.gemspec | 1 - lib/ferrum/browser.rb | 5 +- lib/ferrum/browser/binary.rb | 42 ++++++++ lib/ferrum/browser/command.rb | 2 +- lib/ferrum/browser/options/base.rb | 10 +- lib/ferrum/browser/options/chrome.rb | 5 + lib/ferrum/browser/options/firefox.rb | 5 + lib/ferrum/browser/process.rb | 1 - lib/ferrum/browser/xvfb.rb | 8 +- lib/ferrum/errors.rb | 5 +- lib/ferrum/utils/platform.rb | 7 ++ spec/browser/binary_spec.rb | 98 +++++++++++++++++++ spec/browser_spec.rb | 2 + spec/xvfb_spec.rb | 2 +- 17 files changed, 169 insertions(+), 27 deletions(-) create mode 100644 lib/ferrum/browser/binary.rb create mode 100644 spec/browser/binary_spec.rb diff --git a/.github/gemfiles/websocket-driver-0.6.x.gemfile b/.github/gemfiles/websocket-driver-0.6.x.gemfile index ce0f2140..6680c1b9 100644 --- a/.github/gemfiles/websocket-driver-0.6.x.gemfile +++ b/.github/gemfiles/websocket-driver-0.6.x.gemfile @@ -3,6 +3,5 @@ source "https://rubygems.org" gem "websocket-driver", "~> 0.6.5" -gem "cliver", github: "route/cliver", branch: "develop" gemspec path: "../../" diff --git a/.github/gemfiles/websocket-driver-0.7.x.gemfile b/.github/gemfiles/websocket-driver-0.7.x.gemfile index cadfa90d..88a76ec5 100644 --- a/.github/gemfiles/websocket-driver-0.7.x.gemfile +++ b/.github/gemfiles/websocket-driver-0.7.x.gemfile @@ -3,6 +3,5 @@ source "https://rubygems.org" gem "websocket-driver", "~> 0.7.1" -gem "cliver", github: "route/cliver", branch: "develop" gemspec path: "../../" diff --git a/README.md b/README.md index dd521dfa..6310264e 100644 --- a/README.md +++ b/README.md @@ -731,7 +731,6 @@ Returns bitfield for a given keys Returns cookies hash - ```ruby browser.cookies.all # => {"NID"=>#"NID", "value"=>"...", "domain"=>".google.com", "path"=>"/", "expires"=>1583211046.575681, "size"=>178, "httpOnly"=>true, "secure"=>false, "session"=>false}>} ``` diff --git a/ferrum.gemspec b/ferrum.gemspec index 86becd20..69eb5622 100644 --- a/ferrum.gemspec +++ b/ferrum.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.6.0" s.add_runtime_dependency "addressable", "~> 2.5" - s.add_runtime_dependency "cliver", "~> 0.3" s.add_runtime_dependency "concurrent-ruby", "~> 1.1" s.add_runtime_dependency "webrick", "~> 1.7" s.add_runtime_dependency "websocket-driver", ">= 0.6", "< 0.8" diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb index ec266a81..a410912a 100644 --- a/lib/ferrum/browser.rb +++ b/lib/ferrum/browser.rb @@ -8,6 +8,7 @@ require "ferrum/browser/xvfb" require "ferrum/browser/process" require "ferrum/browser/client" +require "ferrum/browser/binary" module Ferrum class Browser @@ -18,7 +19,7 @@ class Browser extend Forwardable delegate %i[default_context] => :contexts delegate %i[targets create_target page pages windows] => :default_context - delegate %i[go_to back forward refresh reload stop wait_for_reload + delegate %i[go_to goto go back forward refresh reload stop wait_for_reload at_css at_xpath css xpath current_url current_title url title body doctype content= headers cookies network @@ -27,7 +28,7 @@ class Browser frames frame_by main_frame evaluate evaluate_on evaluate_async execute evaluate_func add_script_tag add_style_tag bypass_csp - on goto position position= + on position position= playback_rate playback_rate=] => :page delegate %i[default_user_agent] => :process diff --git a/lib/ferrum/browser/binary.rb b/lib/ferrum/browser/binary.rb new file mode 100644 index 00000000..733db009 --- /dev/null +++ b/lib/ferrum/browser/binary.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Ferrum + class Browser + module Binary + module_function + + def find(commands) + enum(commands).first + end + + def all(commands) + enum(commands).force + end + + def enum(commands) + paths, exts = prepare_paths + cmds = Array(commands).product(paths, exts) + lazy_find(cmds) + end + + def prepare_paths + exts = (ENV.key?("PATHEXT") ? ENV.fetch("PATHEXT").split(";") : []) << "" + paths = ENV["PATH"].split(File::PATH_SEPARATOR) + raise EmptyPathError if paths.empty? + + [paths, exts] + end + + def lazy_find(cmds) + cmds.lazy.filter_map do |cmd, path, ext| + cmd = File.expand_path("#{cmd}#{ext}", path) unless File.absolute_path?(cmd) + + next unless File.executable?(cmd) + next if File.directory?(cmd) + + cmd + end + end + end + end +end diff --git a/lib/ferrum/browser/command.rb b/lib/ferrum/browser/command.rb index 187e1c8c..6fa7ed26 100644 --- a/lib/ferrum/browser/command.rb +++ b/lib/ferrum/browser/command.rb @@ -30,7 +30,7 @@ def initialize(defaults, options, user_data_dir) @options = options @user_data_dir = user_data_dir @path = options[:browser_path] || ENV.fetch("BROWSER_PATH", nil) || defaults.detect_path - raise Cliver::Dependency::NotFound, NOT_FOUND unless @path + raise BinaryNotFoundError, NOT_FOUND unless @path merge_options end diff --git a/lib/ferrum/browser/options/base.rb b/lib/ferrum/browser/options/base.rb index f8e6b045..3006fee8 100644 --- a/lib/ferrum/browser/options/base.rb +++ b/lib/ferrum/browser/options/base.rb @@ -24,15 +24,7 @@ def except(*keys) end def detect_path - if Utils::Platform.mac? - self.class::MAC_BIN_PATH.find { |n| File.exist?(n) } - elsif Utils::Platform.windows? - self.class::WINDOWS_BIN_PATH.find { |path| File.exist?(path) } - else - self.class::LINUX_BIN_PATH.find do |name| - path = Cliver.detect(name) and break(path) - end - end + Binary.find(self.class::PLATFORM_PATH[Utils::Platform.name]) end def merge_required(flags, options, user_data_dir) diff --git a/lib/ferrum/browser/options/chrome.rb b/lib/ferrum/browser/options/chrome.rb index d3171c84..06ceaf10 100644 --- a/lib/ferrum/browser/options/chrome.rb +++ b/lib/ferrum/browser/options/chrome.rb @@ -52,6 +52,11 @@ class Chrome < Base "C:/Program Files/Google/Chrome/Application/chrome.exe", "C:/Program Files/Google/Chrome Dev/Application/chrome.exe" ].freeze + PLATFORM_PATH = { + mac: MAC_BIN_PATH, + windows: WINDOWS_BIN_PATH, + linux: LINUX_BIN_PATH + }.freeze def merge_required(flags, options, user_data_dir) port = options.fetch(:port, BROWSER_PORT) diff --git a/lib/ferrum/browser/options/firefox.rb b/lib/ferrum/browser/options/firefox.rb index 77c08f5a..6b22383a 100644 --- a/lib/ferrum/browser/options/firefox.rb +++ b/lib/ferrum/browser/options/firefox.rb @@ -16,6 +16,11 @@ class Firefox < Base "C:/Program Files/Firefox Developer Edition/firefox.exe", "C:/Program Files/Mozilla Firefox/firefox.exe" ].freeze + PLATFORM_PATH = { + mac: MAC_BIN_PATH, + windows: WINDOWS_BIN_PATH, + linux: LINUX_BIN_PATH + }.freeze def merge_required(flags, options, user_data_dir) port = options.fetch(:port, BROWSER_PORT) diff --git a/lib/ferrum/browser/process.rb b/lib/ferrum/browser/process.rb index 03d115e3..08704b20 100644 --- a/lib/ferrum/browser/process.rb +++ b/lib/ferrum/browser/process.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "cliver" require "net/http" require "json" require "addressable" diff --git a/lib/ferrum/browser/xvfb.rb b/lib/ferrum/browser/xvfb.rb index 73d06a29..76c6b8db 100644 --- a/lib/ferrum/browser/xvfb.rb +++ b/lib/ferrum/browser/xvfb.rb @@ -10,15 +10,11 @@ def self.start(*args) new(*args).tap(&:start) end - def self.xvfb_path - Cliver.detect("Xvfb") - end - attr_reader :screen_size, :display_id, :pid def initialize(options) - @path = self.class.xvfb_path - raise Cliver::Dependency::NotFound, NOT_FOUND unless @path + @path = Binary.find("Xvfb") + raise BinaryNotFoundError, NOT_FOUND unless @path @screen_size = "#{options.fetch(:window_size, [1024, 768]).join('x')}x24" @display_id = (Time.now.to_f * 1000).to_i % 100_000_000 diff --git a/lib/ferrum/errors.rb b/lib/ferrum/errors.rb index 0d479add..2c46071f 100644 --- a/lib/ferrum/errors.rb +++ b/lib/ferrum/errors.rb @@ -2,12 +2,11 @@ module Ferrum class Error < StandardError; end - class NoSuchPageError < Error; end - class NoSuchTargetError < Error; end - class NotImplementedError < Error; end + class BinaryNotFoundError < Error; end + class EmptyPathError < Error; end class StatusError < Error def initialize(url, message = nil) diff --git a/lib/ferrum/utils/platform.rb b/lib/ferrum/utils/platform.rb index f80f491e..d9adcf51 100644 --- a/lib/ferrum/utils/platform.rb +++ b/lib/ferrum/utils/platform.rb @@ -5,6 +5,13 @@ module Utils module Platform module_function + def name + return :mac if mac? + return :windows if windows? + + :linux + end + def windows? RbConfig::CONFIG["host_os"] =~ /mingw|mswin|cygwin/ end diff --git a/spec/browser/binary_spec.rb b/spec/browser/binary_spec.rb new file mode 100644 index 00000000..011bcd44 --- /dev/null +++ b/spec/browser/binary_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Ferrum + class Browser + describe Binary do + let(:tmp_bin) { "#{PROJECT_ROOT}/spec/tmp/bin" } + let(:bin1) { File.join(tmp_bin, "bin1") } + let(:bin1_exe) { File.join(tmp_bin, "bin1.exe") } + let(:bin2_no_x) { File.join(tmp_bin, "/bin2") } + let(:bin3) { File.join(tmp_bin, "/bin3") } + let(:bin4_exe) { File.join(tmp_bin, "/bin4.exe") } + + before do + FileUtils.mkdir_p(tmp_bin) + FileUtils.touch([bin1, bin1_exe, bin2_no_x, bin3, bin4_exe]) + FileUtils.chmod("u=rwx,go=rx", bin1) + FileUtils.chmod("u=rwx,go=rx", bin1_exe) + FileUtils.chmod("u=rw,go=r", bin2_no_x) + FileUtils.chmod("u=rwx,go=rx", bin3) + FileUtils.chmod("u=rwx,go=rx", bin4_exe) + + @original_env_path = ENV.fetch("PATH", nil) + @original_env_pathext = ENV.fetch("PATHEXT", nil) + ENV["PATH"] = "#{tmp_bin}#{File::PATH_SEPARATOR}#{@original_env_path}" + end + + after do + FileUtils.rm_rf(tmp_bin) + ENV["PATH"] = @original_env_path + ENV["PATHEXT"] = @original_env_pathext + end + + context "#find" do + it "finds one binary" do + expect(Binary.find("bin1")).to eq(bin1) + end + + it "finds first binary when list is passed" do + expect(Binary.find(%w[bin1 bin3])).to eq(bin1) + end + + it "finds binary with PATHEXT" do + ENV["PATHEXT"] = ".com;.exe" + + expect(Binary.find(%w[bin4])).to eq(bin4_exe) + end + + it "finds binary without ext" do + ENV["PATHEXT"] = ".com;.exe" + + expect(Binary.find("bin1")).to eq(bin1_exe) + FileUtils.rm_rf(bin1_exe) + expect(Binary.find("bin1")).to eq(bin1) + end + + it "raises an error" do + ENV["PATH"] = "" + + expect { Binary.find(%w[bin1]) }.to raise_error(Ferrum::EmptyPathError) + end + end + + context "#all" do + it "finds one binary" do + expect(Binary.all("bin1")).to eq([bin1]) + end + + it "finds multiple binaries with ext" do + ENV["PATHEXT"] = ".com;.exe" + + expect(Binary.all("bin1")).to eq([bin1_exe, bin1]) + end + + it "finds all binary when list passed" do + expect(Binary.all(%w[bin1 bin3])).to eq([bin1, bin3]) + end + + it "finds binary with PATHEXT" do + ENV["PATHEXT"] = ".com;.exe" + + expect(Binary.all(%w[bin4])).to eq([bin4_exe]) + end + + it "raises an error" do + ENV["PATH"] = "" + + expect { Binary.all(%w[bin1]) }.to raise_error(Ferrum::EmptyPathError) + end + end + + it "works lazily" do + enum = Binary.lazy_find(%w[ls which none]) + + expect(enum.instance_of?(Enumerator::Lazy)).to be_truthy + end + end + end +end diff --git a/spec/browser_spec.rb b/spec/browser_spec.rb index ca3e4826..f8d1b896 100644 --- a/spec/browser_spec.rb +++ b/spec/browser_spec.rb @@ -120,6 +120,8 @@ module Ferrum end it "allows the window to be positioned" do + skip if Utils::Platform.mac? + expect do browser.position = { left: 10, top: 20 } end.to change { diff --git a/spec/xvfb_spec.rb b/spec/xvfb_spec.rb index 3743328d..e5cc39bd 100644 --- a/spec/xvfb_spec.rb +++ b/spec/xvfb_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ferrum - describe Browser::Xvfb, skip: !Browser::Xvfb.xvfb_path do + describe Browser::Xvfb, skip: !Browser::Binary.find("Xvfb") do let(:process) { xvfb_browser.process } let(:xvfb_browser) { Browser.new(default_options.merge(options)) } let(:default_options) { Hash(headless: true, xvfb: true) }