diff --git a/.github/workflows/ci-ruby.yml b/.github/workflows/ci-ruby.yml index e37fbef61adca..aef29721a4f02 100644 --- a/.github/workflows/ci-ruby.yml +++ b/.github/workflows/ci-ruby.yml @@ -208,7 +208,7 @@ jobs: - name: Setup Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: ${{ matrix.ruby }} - name: Cache Bazel artifacts uses: actions/cache@v2 with: diff --git a/rb/BUILD.bazel b/rb/BUILD.bazel index 227e25ca9c65d..3f718199ed9f0 100644 --- a/rb/BUILD.bazel +++ b/rb/BUILD.bazel @@ -22,6 +22,24 @@ copy_file( out = "lib/selenium/webdriver/atoms/findElements.js", ) +copy_file( + name = "manager-linux", + src = "//common/manager:linux/selenium-manager", + out = "bin/linux/selenium-manager", +) + +copy_file( + name = "manager-windows", + src = "//common/manager:windows/selenium-manager.exe", + out = "bin/windows/selenium-manager.exe", +) + +copy_file( + name = "manager-macos", + src = "//common/manager:macos/selenium-manager", + out = "bin/macos/selenium-manager", +) + copy_file( name = "get-attribute", src = "//javascript/webdriver/atoms:get-attribute.js", @@ -118,6 +136,9 @@ ruby_library( ":find-elements", ":get-attribute", ":is-displayed", + ":manager-linux", + ":manager-macos", + ":manager-windows", ":license", ], deps = [ diff --git a/rb/lib/selenium/webdriver/chrome/service.rb b/rb/lib/selenium/webdriver/chrome/service.rb index 42823a8127245..b38a2bd172e1e 100644 --- a/rb/lib/selenium/webdriver/chrome/service.rb +++ b/rb/lib/selenium/webdriver/chrome/service.rb @@ -26,7 +26,7 @@ class Service < WebDriver::Service MISSING_TEXT = <<~ERROR Unable to find chromedriver. Please download the server from https://chromedriver.storage.googleapis.com/index.html and place it somewhere on your PATH. - More info at https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver. + More info at https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/?language=ruby. ERROR SHUTDOWN_SUPPORTED = true diff --git a/rb/lib/selenium/webdriver/common.rb b/rb/lib/selenium/webdriver/common.rb index 89437ac489181..de8929a59a2d9 100644 --- a/rb/lib/selenium/webdriver/common.rb +++ b/rb/lib/selenium/webdriver/common.rb @@ -22,6 +22,7 @@ require 'selenium/webdriver/common/proxy' require 'selenium/webdriver/common/log_entry' require 'selenium/webdriver/common/file_reaper' +require 'selenium/webdriver/common/selenium_manager' require 'selenium/webdriver/common/service' require 'selenium/webdriver/common/service_manager' require 'selenium/webdriver/common/socket_lock' diff --git a/rb/lib/selenium/webdriver/common/selenium_manager.rb b/rb/lib/selenium/webdriver/common/selenium_manager.rb new file mode 100644 index 0000000000000..115ce460d1d01 --- /dev/null +++ b/rb/lib/selenium/webdriver/common/selenium_manager.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Selenium + module WebDriver + # + # Wrapper for getting information from the Selenium Manager binaries. + # @api private + # + class SeleniumManager + BIN_PATH = "../../../../../bin" + + class << self + # @param [String] driver_name which driver to use. + # @return [String] the path to the correct driver. + def driver_path(driver_name) + @driver_path ||= begin + unless %w[chromedriver geckodriver].include?(driver_name) + msg = "Unable to locate driver with name: #{driver_name}" + raise Error::WebDriverError, msg + end + + location = run("#{binary} --driver #{driver_name}").split("\t").last.strip + WebDriver.logger.debug("Driver found at #{location}") + Platform.assert_executable location + + location + end + end + + private + + # @return [String] the path to the correct selenium manager + def binary + @binary ||= begin + path = File.expand_path(BIN_PATH, __FILE__) + path << if Platform.windows? + '/windows/selenium-manager.exe' + elsif Platform.mac? + '/macos/selenium-manager' + elsif Platform.linux? + '/linux/selenium-manager' + end + location = File.expand_path(path, __FILE__) + unless location.is_a?(String) && File.exist?(location) && File.executable?(location) + raise Error::WebDriverError, "Unable to obtain Selenium Manager" + end + + WebDriver.logger.debug("Selenium Manager found at #{location}") + location + end + end + + def run(command) + WebDriver.logger.debug("Executing Process #{command}") + + begin + result = `#{command}` + return result if result.match?(/^INFO\t/) + rescue StandardError => e + raise Error::WebDriverError, "Unsuccessful command executed: #{command}; #{e.message}" + end + + raise Error::WebDriverError, "Unsuccessful command executed: #{command}" + end + end + end # SeleniumManager + end # WebDriver +end # Selenium diff --git a/rb/lib/selenium/webdriver/common/service.rb b/rb/lib/selenium/webdriver/common/service.rb index 97eb69e69132b..5b67d28eb818d 100644 --- a/rb/lib/selenium/webdriver/common/service.rb +++ b/rb/lib/selenium/webdriver/common/service.rb @@ -80,9 +80,7 @@ def initialize(path: nil, port: nil, args: nil) end def launch - sm = ServiceManager.new(self) - sm.start - sm + ServiceManager.new(self).tap(&:start) end def shutdown_supported @@ -101,6 +99,12 @@ def binary_path(path = nil) path = path.call if path.is_a?(Proc) path ||= Platform.find_binary(self.class::EXECUTABLE) + begin + path ||= SeleniumManager.driver_path(self.class::EXECUTABLE) + rescue Error::WebDriverError => e + WebDriver.logger.debug("Unable obtain driver using Selenium Manager; #{e.message}") + end + raise Error::WebDriverError, self.class::MISSING_TEXT unless path Platform.assert_executable path diff --git a/rb/spec/integration/selenium/webdriver/chrome/service_spec.rb b/rb/spec/integration/selenium/webdriver/chrome/service_spec.rb new file mode 100755 index 0000000000000..957c4e5d08921 --- /dev/null +++ b/rb/spec/integration/selenium/webdriver/chrome/service_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require_relative '../spec_helper' + +module Selenium + module WebDriver + module Chrome + describe Service, exclusive: {browser: :chrome} do + let(:service) { Service.new } + let(:service_manager) { service.launch } + + after { service_manager.stop } + + it 'auto uses chromedriver' do + allow(Platform).to receive(:find_binary) + expect(service_manager.uri).to be_a(URI) + end + end + end # Chrome + end # WebDriver +end # Selenium diff --git a/rb/spec/integration/selenium/webdriver/firefox/service_spec.rb b/rb/spec/integration/selenium/webdriver/firefox/service_spec.rb new file mode 100755 index 0000000000000..2c8683c4d9178 --- /dev/null +++ b/rb/spec/integration/selenium/webdriver/firefox/service_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require_relative '../spec_helper' + +module Selenium + module WebDriver + module Firefox + describe Service, exclusive: {browser: :firefox} do + let(:service) { Service.new } + let(:service_manager) { service.launch } + + after { service_manager.stop } + + it 'auto uses geckodriver' do + allow(Platform).to receive(:find_binary) + service_manager = service.launch + expect(service_manager.uri).to be_a(URI) + end + end + end # Chrome + end # WebDriver +end # Selenium diff --git a/rb/spec/unit/selenium/webdriver/chrome/service_spec.rb b/rb/spec/unit/selenium/webdriver/chrome/service_spec.rb index 489e676c0e560..c0ad78b297a04 100644 --- a/rb/spec/unit/selenium/webdriver/chrome/service_spec.rb +++ b/rb/spec/unit/selenium/webdriver/chrome/service_spec.rb @@ -29,6 +29,8 @@ module WebDriver allow(Platform).to receive(:assert_executable).and_return(true) end + after { Chrome::Service.driver_path = nil } + it 'uses default path and port' do allow(Platform).to receive(:find_binary).and_return(service_path) diff --git a/rb/spec/unit/selenium/webdriver/common/manager_spec.rb b/rb/spec/unit/selenium/webdriver/common/manager_spec.rb new file mode 100644 index 0000000000000..df3da5e8564ca --- /dev/null +++ b/rb/spec/unit/selenium/webdriver/common/manager_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require File.expand_path('../spec_helper', __dir__) + +module Selenium + module WebDriver + describe SeleniumManager do + describe 'self.binary' do + before do + SeleniumManager.instance_variable_set(:@binary, nil) + end + + it 'detects Windows' do + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:executable?).and_return(true) + allow(Platform).to receive(:windows?).and_return(true) + + expect(SeleniumManager.send(:binary)).to match(%r{/windows/selenium-manager\.exe$}) + end + + it 'detects Mac' do + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:executable?).and_return(true) + allow(Platform).to receive(:windows?).and_return(false) + allow(Platform).to receive(:mac?).and_return(true) + + expect(SeleniumManager.send(:binary)).to match(%r{/macos/selenium-manager$}) + end + + it 'detects Linux' do + allow(File).to receive(:exist?).and_return(true) + allow(File).to receive(:executable?).and_return(true) + allow(Platform).to receive(:windows?).and_return(false) + allow(Platform).to receive(:mac?).and_return(false) + allow(Platform).to receive(:linux?).and_return(true) + + expect(SeleniumManager.send(:binary)).to match(%r{/linux/selenium-manager$}) + end + + it 'errors if cannot find' do + allow(File).to receive(:exist?).and_return(false) + + expect { + SeleniumManager.send(:binary) + }.to raise_error(Error::WebDriverError, /Unable to obtain Selenium Manager/) + end + end + + describe 'self.run' do + it 'errors if a problem with command' do + expect { + SeleniumManager.send(:run, "anything") + }.to raise_error(Error::WebDriverError, /Unsuccessful command executed: /) + end + end + + describe 'self.driver_path' do + it 'errors if not one of 3 valid drivers' do + expect { + SeleniumManager.driver_path('something') + }.to raise_error(Error::WebDriverError, /Unable to locate driver with name/) + end + + it 'does not use if driver_path provided' do + allow(SeleniumManager).to receive(:driver_path) + allow(Platform).to receive(:assert_executable).and_return(false) + + WebDriver::Service.chrome(path: 'something') + + expect(SeleniumManager).not_to have_received(:driver_path) + end + + it 'not used if found on PATH' do + allow(SeleniumManager).to receive(:driver_path) + allow(Platform).to receive(:assert_executable).and_return(false) + allow(Platform).to receive(:find_binary).and_return("something") + + WebDriver::Service.chrome + + expect(SeleniumManager).not_to have_received(:driver_path) + end + + it 'gives original error if not found' do + allow(Platform).to receive(:find_binary) + allow(SeleniumManager).to receive(:driver_path) + + expect { + WebDriver::Service.chrome + }.to raise_error(WebDriver::Error::WebDriverError, /Unable to find chromedriver. Please download/) + end + end + end + end # WebDriver +end # Selenium