Skip to content

[javascript] Feat 14045/nodejs env var driver location #15930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from
40 changes: 38 additions & 2 deletions javascript/selenium-webdriver/chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@
const { Browser } = require('./lib/capabilities')
const chromium = require('./chromium')
const CHROME_CAPABILITY_KEY = 'goog:chromeOptions'
const path = require('node:path')
let runfiles = null
try {
runfiles = require('@bazel/runfiles').runfiles
} catch (e) {
// Ignore if @bazel/runfiles is not available
}

/**
* Environment variable that defines the location of the ChromeDriver executable.
* @const {string}
*/
const CHROME_DRIVER_EXE_ENV_VAR = 'SE_CHROMEDRIVER'

// Try to load @bazel/runfiles for resolving paths in Bazel environments
let runfiles = null
try {
runfiles = require('@bazel/runfiles').runfiles
} catch (e) {
// Ignore if @bazel/runfiles is not available
}

/** @type {remote.DriverService} */

Expand All @@ -138,14 +159,29 @@ const CHROME_CAPABILITY_KEY = 'goog:chromeOptions'
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
* the builder will attempt to use the chromedriver path from the
* SE_CHROMEDRIVER environment variable, then locate the chromedriver on the current
* 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) {
super(opt_exe)
let exePath = opt_exe || process.env[CHROME_DRIVER_EXE_ENV_VAR]

// If path is from env variable and appears to be a relative path, try to resolve it using runfiles
if (!opt_exe && exePath && !path.isAbsolute(exePath) && runfiles) {
try {
const resolvedPath = runfiles.resolve(exePath)
if (resolvedPath) {
exePath = resolvedPath
}
} catch (e) {
// If resolution fails, use the original path
}
}

super(exePath)
}
}

Expand Down
11 changes: 9 additions & 2 deletions javascript/selenium-webdriver/edge.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ const { Browser } = require('./lib/capabilities')
const chromium = require('./chromium')
const EDGE_CAPABILITY_KEY = 'ms:edgeOptions'

/**
* Environment variable that defines the location of the MSEdgeDriver executable.
* @const {string}
*/
const EDGE_DRIVER_EXE_ENV_VAR = 'SE_EDGEDRIVER'

/** @type {remote.DriverService} */

/**
Expand All @@ -93,13 +99,14 @@ const EDGE_CAPABILITY_KEY = 'ms:edgeOptions'
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 msedgedriver on the current
* the builder will attempt to use the msedgedriver path from the
* SE_EDGEDRIVER environment variable, then locate the msedgedriver on the current
* PATH.
* @throws {Error} If provided executable does not exist, or the msedgedriver
* cannot be found on the PATH.
*/
constructor(opt_exe) {
super(opt_exe)
super(opt_exe || process.env[EDGE_DRIVER_EXE_ENV_VAR])
this.setLoopback(true)
}
}
Expand Down
31 changes: 29 additions & 2 deletions javascript/selenium-webdriver/firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ const { Zip } = require('./io/zip')
const { getBinaryPaths } = require('./common/driverFinder')
const { findFreePort } = require('./net/portprober')
const FIREFOX_CAPABILITY_KEY = 'moz:firefoxOptions'
let runfiles = null
try {
runfiles = require('@bazel/runfiles').runfiles
} catch (e) {
// Ignore if @bazel/runfiles is not available
}

/**
* Environment variable that defines the location of the GeckoDriver executable.
* @const {string}
*/
const GECKO_DRIVER_EXE_ENV_VAR = 'SE_GECKODRIVER'

/**
* Thrown when there an add-on is malformed.
Expand Down Expand Up @@ -489,10 +501,25 @@ function configureExecutor(executor) {
class ServiceBuilder extends remote.DriverService.Builder {
/**
* @param {string=} opt_exe Path to the server executable to use. If omitted,
* the builder will attempt to locate the geckodriver on the system PATH.
* the builder will attempt to use the geckodriver path from the
* SE_GECKODRIVER environment variable, then locate the geckodriver on the system PATH.
*/
constructor(opt_exe) {
super(opt_exe)
let exePath = opt_exe || process.env[GECKO_DRIVER_EXE_ENV_VAR]

// If path is from env variable and appears to be a relative path, try to resolve it using runfiles
if (!opt_exe && exePath && !path.isAbsolute(exePath) && runfiles) {
try {
const resolvedPath = runfiles.resolve(exePath)
if (resolvedPath) {
exePath = resolvedPath
}
} catch (e) {
// If resolution fails, use the original path
}
}

super(exePath)
this.setLoopback(true) // Required.
}

Expand Down
6 changes: 4 additions & 2 deletions javascript/selenium-webdriver/ie.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const error = require('./lib/error')
const { getBinaryPaths } = require('./common/driverFinder')

const OPTIONS_CAPABILITY_KEY = 'se:ieOptions'
const IE_DRIVER_EXE_ENV_VAR = 'SE_IEDRIVER'
const SCROLL_BEHAVIOUR = {
BOTTOM: 1,
TOP: 0,
Expand Down Expand Up @@ -422,10 +423,11 @@ function createServiceFromCapabilities(capabilities) {
class ServiceBuilder extends remote.DriverService.Builder {
/**
* @param {string=} opt_exe Path to the server executable to use. If omitted,
* the builder will attempt to locate the IEDriverServer on the system PATH.
* the builder will attempt to use the IEDriverServer path from the
* SE_IEDRIVER environment variable, then locate the IEDriverServer on the system PATH.
*/
constructor(opt_exe) {
super(opt_exe)
super(opt_exe || process.env[IE_DRIVER_EXE_ENV_VAR])
this.setLoopback(true) // Required.
}
}
Expand Down
11 changes: 9 additions & 2 deletions javascript/selenium-webdriver/safari.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ const webdriver = require('./lib/webdriver')
const { Browser, Capabilities } = require('./lib/capabilities')
const { getBinaryPaths } = require('./common/driverFinder')

/**
* Environment variable that defines the location of the SafariDriver executable.
* @const {string}
*/
const SAFARI_DRIVER_EXE_ENV_VAR = 'SE_SAFARIDRIVER'

/**
* Creates {@link remote.DriverService} instances that manage
* a [safaridriver] server in a child process.
Expand All @@ -38,10 +44,11 @@ const { getBinaryPaths } = require('./common/driverFinder')
class ServiceBuilder extends remote.DriverService.Builder {
/**
* @param {string=} opt_exe Path to the server executable to use. If omitted,
* the builder will attempt to locate the safaridriver on the system PATH.
* the builder will attempt to use the safaridriver path from the
* SE_SAFARIDRIVER environment variable, then locate the safaridriver on the system PATH.
*/
constructor(opt_exe) {
super(opt_exe)
super(opt_exe || process.env[SAFARI_DRIVER_EXE_ENV_VAR])
this.setLoopback(true) // Required.
}
}
Expand Down
45 changes: 45 additions & 0 deletions javascript/selenium-webdriver/test/chrome/service_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,51 @@ test.suite(
assert.ok(url.endsWith('/foo/bar/baz'), 'unexpected url: ' + url)
})
})

describe('environment variable support', function () {
let originalEnvValue

beforeEach(function () {
originalEnvValue = process.env.SE_CHROMEDRIVER
})

afterEach(function () {
if (originalEnvValue) {
process.env.SE_CHROMEDRIVER = originalEnvValue
} else {
delete process.env.SE_CHROMEDRIVER
}
})

it('uses SE_CHROMEDRIVER environment variable when set', function () {
const testPath = '/custom/path/to/chromedriver'
process.env.SE_CHROMEDRIVER = testPath

const serviceBuilder = new chrome.ServiceBuilder()
const service = serviceBuilder.build()
assert.strictEqual(service.getExecutable(), testPath)
})

it('explicit path overrides environment variable', function () {
const envPath = '/env/path/to/chromedriver'
const explicitPath = '/explicit/path/to/chromedriver'

process.env.SE_CHROMEDRIVER = envPath
const serviceBuilder = new chrome.ServiceBuilder(explicitPath)
const service = serviceBuilder.build()

assert.strictEqual(service.getExecutable(), explicitPath)
})

it('falls back to default behavior when environment variable is not set', function () {
delete process.env.SE_CHROMEDRIVER

const serviceBuilder = new chrome.ServiceBuilder()
const service = serviceBuilder.build()
// Should be null/undefined when no explicit path and no env var
assert.ok(!service.getExecutable())
})
})
})
},
{ browsers: ['chrome'] },
Expand Down
45 changes: 45 additions & 0 deletions javascript/selenium-webdriver/test/edge/service_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,51 @@ test.suite(
let url = await service.start()
assert(/127\.0\.0\.1/.test(url), `unexpected url: ${url}`)
})

describe('environment variable support', function () {
let originalEnvValue

beforeEach(function () {
originalEnvValue = process.env.SE_EDGEDRIVER
})

afterEach(function () {
if (originalEnvValue) {
process.env.SE_EDGEDRIVER = originalEnvValue
} else {
delete process.env.SE_EDGEDRIVER
}
})

it('uses SE_EDGEDRIVER environment variable when set', function () {
const testPath = '/custom/path/to/edgedriver'
process.env.SE_EDGEDRIVER = testPath

const serviceBuilder = new edge.ServiceBuilder()
const service = serviceBuilder.build()
assert.strictEqual(service.getExecutable(), testPath)
})

it('explicit path overrides environment variable', function () {
const envPath = '/env/path/to/edgedriver'
const explicitPath = '/explicit/path/to/edgedriver'

process.env.SE_EDGEDRIVER = envPath
const serviceBuilder = new edge.ServiceBuilder(explicitPath)
const service = serviceBuilder.build()

assert.strictEqual(service.getExecutable(), explicitPath)
})

it('falls back to default behavior when environment variable is not set', function () {
delete process.env.SE_EDGEDRIVER

const serviceBuilder = new edge.ServiceBuilder()
const service = serviceBuilder.build()
// Should be null/undefined when no explicit path and no env var
assert.ok(!service.getExecutable())
})
})
})
},
{ browsers: ['MicrosoftEdge'] },
Expand Down
90 changes: 90 additions & 0 deletions javascript/selenium-webdriver/test/firefox/service_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 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.

'use strict'

const assert = require('node:assert')
const firefox = require('selenium-webdriver/firefox')
const test = require('../../lib/test')
const { getBinaryPaths } = require('selenium-webdriver/common/driverFinder')

test.suite(
function (_env) {
describe('geckodriver', function () {
let service

afterEach(function () {
if (service) {
return service.kill()
}
})

it('can start geckodriver', async function () {
service = new firefox.ServiceBuilder().build()
service.setExecutable(getBinaryPaths(new firefox.Options()).driverPath)
let url = await service.start()
assert(/127\.0\.0\.1/.test(url), `unexpected url: ${url}`)
})

describe('environment variable support', function () {
let originalEnvValue

beforeEach(function () {
originalEnvValue = process.env.SE_GECKODRIVER
})

afterEach(function () {
if (originalEnvValue) {
process.env.SE_GECKODRIVER = originalEnvValue
} else {
delete process.env.SE_GECKODRIVER
}
})

it('uses SE_GECKODRIVER environment variable when set', function () {
const testPath = '/custom/path/to/geckodriver'
process.env.SE_GECKODRIVER = testPath

const serviceBuilder = new firefox.ServiceBuilder()
const service = serviceBuilder.build()
assert.strictEqual(service.getExecutable(), testPath)
})

it('explicit path overrides environment variable', function () {
const envPath = '/env/path/to/geckodriver'
const explicitPath = '/explicit/path/to/geckodriver'

process.env.SE_GECKODRIVER = envPath
const serviceBuilder = new firefox.ServiceBuilder(explicitPath)
const service = serviceBuilder.build()

assert.strictEqual(service.getExecutable(), explicitPath)
})

it('falls back to default behavior when environment variable is not set', function () {
delete process.env.SE_GECKODRIVER

const serviceBuilder = new firefox.ServiceBuilder()
const service = serviceBuilder.build()
// Should be null/undefined when no explicit path and no env var
assert.ok(!service.getExecutable())
})
})
})
},
{ browsers: ['firefox'] },
)
Loading