diff --git a/.github/workflows/ci-javascript.yml b/.github/workflows/ci-javascript.yml index 4b3a9c30ec721..b6ed591228d3b 100644 --- a/.github/workflows/ci-javascript.yml +++ b/.github/workflows/ci-javascript.yml @@ -53,16 +53,17 @@ jobs: java-version: 11 - name: Setup Fluxbox run: sudo apt-get -y install fluxbox - - name: Setup Firefox and GeckoDriver - uses: ./.github/actions/setup-firefox + - name: Setup Firefox + uses: abhi1693/setup-browser@v0.3.4 with: + browser: firefox version: ${{ matrix.version }} if: | matrix.browser == 'firefox' - - name: Setup Chrome and ChromeDriver - uses: ./.github/actions/setup-chrome + - name: Setup Chrome + uses: browser-actions/setup-chrome@latest with: - version: ${{ matrix.version }} + chrome-version: ${{ matrix.version }} if: | matrix.browser == 'chrome' - name: Start XVFB @@ -98,9 +99,10 @@ jobs: java-version: 11 - name: Setup Fluxbox run: sudo apt-get -y install fluxbox - - name: Setup Firefox and GeckoDriver - uses: ./.github/actions/setup-firefox + - name: Setup Firefox + uses: abhi1693/setup-browser@v0.3.4 with: + browser: firefox version: ${{ matrix.version }} - name: Start XVFB run: Xvfb :99 & diff --git a/common/manager/BUILD.bazel b/common/manager/BUILD.bazel index c27d1c9ca3c64..4e388d598f1bb 100644 --- a/common/manager/BUILD.bazel +++ b/common/manager/BUILD.bazel @@ -26,6 +26,7 @@ exports_files( "//java/test/org/openqa/selenium/firefox:__pkg__", "//py:__pkg__", "//rb:__pkg__", + "//javascript/node/selenium-webdriver:__pkg__", "//dotnet/src/webdriver:__pkg__", ], ) diff --git a/javascript/node/selenium-webdriver/BUILD.bazel b/javascript/node/selenium-webdriver/BUILD.bazel index 3a92bb12a5608..60c83fe52d508 100644 --- a/javascript/node/selenium-webdriver/BUILD.bazel +++ b/javascript/node/selenium-webdriver/BUILD.bazel @@ -23,6 +23,7 @@ SRC_FILES = [ "remote/*.js", "testing/*.js", "devtools/*.js", + "common/*.js" ]) pkg_npm( @@ -31,6 +32,9 @@ pkg_npm( srcs = SRC_FILES, deps = [ ":license", + ":manager-linux", + ":manager-macos", + ":manager-windows", "//javascript/node/selenium-webdriver/lib/atoms:find-elements", "//javascript/node/selenium-webdriver/lib/atoms:get_attribute", "//javascript/node/selenium-webdriver/lib/atoms:is_displayed", @@ -141,3 +145,22 @@ genrule( ":cdp-srcs-generator-" + n, ], ) for n in BROWSER_VERSIONS] + + +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", +) diff --git a/javascript/node/selenium-webdriver/chrome.js b/javascript/node/selenium-webdriver/chrome.js index f0ab9c14bb5b6..68e4e6a892705 100644 --- a/javascript/node/selenium-webdriver/chrome.js +++ b/javascript/node/selenium-webdriver/chrome.js @@ -130,6 +130,7 @@ const io = require('./io') const { Browser } = require('./lib/capabilities') const chromium = require('./chromium') +const { driverLocation } = require('./common/seleniumManager') /** * Name of the ChromeDriver executable. @@ -150,20 +151,35 @@ class ServiceBuilder extends chromium.ServiceBuilder { /** * @param {string=} opt_exe Path to the server executable to use. If omitted, * the builder will attempt to locate the chromedriver on the current - * PATH. + * PATH. If the chromedriver is not available in path, selenium-manager will + * download the chromedriver * @throws {Error} If provided executable does not exist, or the chromedriver * cannot be found on the PATH. */ constructor(opt_exe) { let exe = opt_exe || locateSynchronously() + + if (!exe) { + console.log( + ` The ChromeDriver could not be found on the current PATH, trying Selenium Manager` + ) + + try { + exe = driverLocation(Browser.CHROME) + } catch (err) { + console.log(`Unable to obtain driver using Selenium Manager: ${err}`) + } + } + if (!exe) { throw Error( - `The ChromeDriver could not be found on the current PATH. Please ` + - `download the latest version of the ChromeDriver from ` + - `http://chromedriver.storage.googleapis.com/index.html and ensure ` + - `it can be found on your PATH.` + `The ChromeDriver could not be found on the current PATH. + Please download the latest version of the ChromeDriver + from http://chromedriver.storage.googleapis.com/index.html + and ensure it can be found on your PATH.` ) } + super(exe) } } diff --git a/javascript/node/selenium-webdriver/common/seleniumManager.js b/javascript/node/selenium-webdriver/common/seleniumManager.js new file mode 100644 index 0000000000000..26206fb6525d2 --- /dev/null +++ b/javascript/node/selenium-webdriver/common/seleniumManager.js @@ -0,0 +1,91 @@ +/* + * 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. + */ + +/** + * Wrapper for getting information from the Selenium Manager binaries + */ + +const { platform } = require('process') +const path = require('path') +const fs = require('fs') +const execSync = require('child_process').execSync + +/** + * currently supported browsers for selenium-manager + * @type {string[]} + */ +const Browser = ['chrome', 'firefox', 'edge'] + +/** + * Determines the path of the correct Selenium Manager binary + * @returns {string} + */ +function getBinary() { + const directory = { + darwin: 'macos', + win32: 'windows', + cygwin: 'windows', + linux: 'linux', + }[platform] + + const file = + directory === 'windows' ? 'selenium-manager.exe' : 'selenium-manager' + + const filePath = path.join(__dirname, '..', '/bin', directory, file) + + if (!fs.existsSync(filePath)) { + throw new Error(`Unable to obtain Selenium Manager`) + } + + return filePath +} + +/** + * Determines the path of the correct driver + * @param {Browser|string} browser name to fetch the driver + * @returns {string} path of the driver location + */ + +function driverLocation(browser) { + if (!Browser.includes(browser.toLocaleString())) { + throw new Error( + `Unable to locate driver associated with browser name: ${browser}` + ) + } + + let args = [getBinary(), '--browser', browser] + let result + + try { + result = execSync(args.join(' ')).toString() + } catch (e) { + throw new Error( + `Error executing command with ${args} : Error ${e.stderr.toString()}` + ) + } + + if (!result.startsWith('INFO\t')) { + throw new Error(`Unsuccessful command executed ${args}}`) + } + + return result.replace('INFO\t', '').trim() +} + +// PUBLIC API +module.exports = { driverLocation } diff --git a/javascript/node/selenium-webdriver/edge.js b/javascript/node/selenium-webdriver/edge.js index 9dd54d5a516d1..2247a2b790434 100644 --- a/javascript/node/selenium-webdriver/edge.js +++ b/javascript/node/selenium-webdriver/edge.js @@ -80,6 +80,7 @@ const { Browser } = require('./lib/capabilities') const io = require('./io') const chromium = require('./chromium') +const { driverLocation } = require('./common/seleniumManager') /** * Name of the EdgeDriver executable. @@ -106,6 +107,19 @@ class ServiceBuilder extends chromium.ServiceBuilder { */ constructor(opt_exe) { let exe = opt_exe || locateSynchronously() + + if (!exe) { + console.log( + `The WebDriver for Edge could not be found on the current PATH, trying Selenium Manager` + ) + + try { + exe = driverLocation('edge') + } catch (err) { + console.log(`Unable to obtain driver using Selenium Manager: ${err}`) + } + } + if (!exe) { throw Error( `The WebDriver for Edge could not be found on the current PATH. Please download the ` + @@ -114,6 +128,7 @@ class ServiceBuilder extends chromium.ServiceBuilder { `and ensure it can be found on your PATH.` ) } + super(exe) this.setLoopback(true) } diff --git a/javascript/node/selenium-webdriver/firefox.js b/javascript/node/selenium-webdriver/firefox.js index cf5885afb3eb1..c796740f71412 100644 --- a/javascript/node/selenium-webdriver/firefox.js +++ b/javascript/node/selenium-webdriver/firefox.js @@ -127,6 +127,7 @@ const webdriver = require('./lib/webdriver') const zip = require('./io/zip') const { Browser, Capabilities } = require('./lib/capabilities') const { Zip } = require('./io/zip') +const { driverLocation } = require('./common/seleniumManager') /** * Thrown when there an add-on is malformed. @@ -459,16 +460,27 @@ function locateSynchronously() { */ function findGeckoDriver() { let exe = locateSynchronously() + + if (!exe) { + console.log( + `The ${GECKO_DRIVER_EXE} executable could not be found on the current PATH, trying Selenium Manager` + ) + + try { + exe = driverLocation(Browser.FIREFOX) + } catch (err) { + console.log(`Unable to obtain driver using Selenium Manager: ${err}`) + } + } + if (!exe) { throw Error( - 'The ' + - GECKO_DRIVER_EXE + - ' executable could not be found on the current ' + - 'PATH. Please download the latest version from ' + - 'https://github.com/mozilla/geckodriver/releases/ ' + - 'and ensure it can be found on your PATH.' + `The ${GECKO_DRIVER_EXE} executable could not be found on the current PATH. + Please download the latest version from https://github.com/mozilla/geckodriver/releases/ + and ensure it can be found on your PATH.` ) } + return exe } diff --git a/javascript/node/selenium-webdriver/test/lib/webdriver_test.js b/javascript/node/selenium-webdriver/test/lib/webdriver_test.js index 60261eac93c3a..a983d3bbe31dd 100644 --- a/javascript/node/selenium-webdriver/test/lib/webdriver_test.js +++ b/javascript/node/selenium-webdriver/test/lib/webdriver_test.js @@ -228,7 +228,7 @@ describe('WebDriver', function () { .expect(CName.NEW_SESSION) .withParameters({ capabilities: { - alwaysMatch: { browserName: 'firefox'}, + alwaysMatch: { browserName: 'firefox' }, firstMatch: [{}], }, })