This monorepo contains Node.js wrappers for managing browser driver binaries (Geckodriver, Edgedriver, and Safaridriver). These packages facilitate downloading, starting, and managing driver server processes for WebdriverIO and other test automation frameworks.
WebdriverIO installs the driver packages it needs through @wdio/utils. If you only use WebdriverIO, you do not install them yourself.
| WebdriverIO | geckodriver |
edgedriver |
safaridriver |
Node.js | @wdio/logger |
|---|---|---|---|---|---|
| 10 | 8.x | 8.x | 3.x | 22.19.0 or newer | 10, peer dependency |
| 9 | 6.x or 7.x | 6.x or 7.x | 1.x or 2.x | 20 or newer (safaridriver: 18) |
9, dependency |
From geckodriver 8 and edgedriver 8, @wdio/logger is a peer dependency, so the drivers use the logger of your WebdriverIO install. A second copy of the logger empties the WebdriverIO log file (outputDir).
- Use Node.js 22.19.0 or newer.
- If you use
geckodriveroredgedriverwithout WebdriverIO and install with Yarn, add@wdio/loggerto your dependencies. npm and pnpm install it for you. geckodriverno longer readsGECKODRIVER_FILEPATH. UseGECKODRIVER_PATH.require()now returns every export of the package, for examplefindEdgePathfromedgedriver, with CommonJS types.- With
require('safaridriver'),start()returns theChildProcessandstop()returns nothing, as in ESM: before, both returned a Promise.await start()still works;start().then(...)does not. HTTPS_PROXYandHTTP_PROXYnow apply to downloads; 6.x and 7.x ignored them. If a proxy is set in your environment but the CDN must be reached directly, add its host toNO_PROXY.edgedriver'sstart()resolves to aChildProcess(it was typedChildProcessWithoutNullStreams): itsstdoutandstderrarenullwhen you passspawnOpts: { stdio: 'ignore' }.safaridrivernow pipes the driver output like the other drivers: readstdoutandstderr, or passspawnOpts: { stdio: 'ignore' }. Before, the output was buffered and the driver was killed after 1 MB of it.- The
edgedriverandgeckodriverCLIs exit with code 1 when a signal kills the driver; they exited with 0. - On Windows,
EDGEDRIVER_AUTO_INSTALLandGECKODRIVER_AUTO_INSTALLnow download the driver during the install; before, the install always skipped the download there. findEdgePath()on Windows returnsundefinedwhen Edge is not installed, as on macOS and Linux; it threw.spawnOptsis a new option ofedgedriverandsafaridriver. The rest of the API, the CLI and the options did not change.
Install the driver package needed for your target browser:
# Firefox
npm install geckodriver --save-dev
# Microsoft Edge
npm install edgedriver --save-dev
# Safari (macOS only)
npm install safaridriver --save-dev
Drivers can be executed directly using npx:
# Start Geckodriver
npx geckodriver --port=4444
# Start Edgedriver
npx edgedriver --port=4444
The safaridriver package has no CLI: Safaridriver is part of macOS, so run /usr/bin/safaridriver --port=4444 directly (once, run safaridriver --enable to allow remote automation).
By default, binaries download when initialized via CLI or API. To download them during npm install, pass the respective flag:
- Geckodriver:
GECKODRIVER_AUTO_INSTALL=1 npm i - Edgedriver:
EDGEDRIVER_AUTO_INSTALL=1 npm i
The download runs in each package's postinstall script. pnpm 10 or newer and Bun run it only for packages you approve (pnpm approve-builds, or trustedDependencies in Bun), and recent npm versions warn until you approve them with npm install-scripts approve <package>. If the download fails, the install does not fail: the driver then downloads on first use.
-
Custom Driver Version:
-
GECKODRIVER_VERSION="0.31.0" -
EDGEDRIVER_VERSION="114.0.1823.18" -
EDGE_BINARY_PATH=/path/to/msedge: the Edge binary whose version selects the Edgedriver download whenEDGEDRIVER_VERSIONis not set.findEdgePath()returns it. -
Custom CDN URL:
-
GECKODRIVER_CDNURL=https://INTERNAL_CDN/geckodriver/download -
EDGEDRIVER_CDNURL=https://INTERNAL_CDN/edgedriver/download -
CDN credentials: a CDN URL can carry them, for example
https://user:password@INTERNAL_CDN. They are sent as a BasicAuthorizationheader and kept out of the logs; percent-encode special characters (@is%40,%is%25). Usehttps://: overhttp://, Basic credentials travel in clear text. A redirect to another host does not receive them. -
HTTP/HTTPS Proxy:
HTTPS_PROXYandHTTP_PROXYapply to downloads, andNO_PROXYlists the hosts that skip the proxy. The lower-case forms work too and win over the upper-case ones. A dispatcher set with undici'ssetGlobalDispatcher(as in the WebdriverIO proxy docs) wins over these variables.
A global install on Windows adds geckodriver.cmd and edgedriver.cmd to your PATH, but selenium-webdriver looks for geckodriver.exe and msedgedriver.exe. The packages download the binary into their cache folder, not into the package folder: geckodriver-<version>.exe in GECKODRIVER_CACHE_DIR, msedgedriver.exe in EDGEDRIVER_CACHE_DIR (both default to the system temporary folder). Set the cache folder and the version, run the driver once, then copy the binary to a folder on your PATH:
set GECKODRIVER_CACHE_DIR=%USERPROFILE%\geckodriver
set GECKODRIVER_VERSION=0.37.1
geckodriver --version
copy /Y %GECKODRIVER_CACHE_DIR%\geckodriver-%GECKODRIVER_VERSION%.exe %APPDATA%\npm\geckodriver.exeName the exact version: with a wildcard, copy joins all cached versions into one broken file.
Recent selenium-webdriver versions can also download the drivers themselves with Selenium Manager.
All driver packages export methods to programmatically control browser driver instances within Node.js scripts.
import { start } from 'geckodriver'; // or 'edgedriver'
import { remote } from 'webdriverio';
import waitPort from 'wait-port';
// 1. Start driver process
const cp = await start({ port: 4444 });
// 2. Wait for driver port to open
await waitPort({ port: 4444 });
// 3. Connect WebdriverIO session
// without `port`, WebdriverIO starts a driver of its own; geckodriver accepts
// `127.0.0.1` but not `localhost` (the default) unless you pass `allowHosts`
const browser = await remote({
hostname: '127.0.0.1',
port: 4444,
capabilities: {
browserName: 'firefox' // or 'MicrosoftEdge'
}
});
await browser.url('https://webdriver.io');
console.log(await browser.getTitle());
// 4. Terminate process when finished
cp.kill();import safaridriver from 'safaridriver';
import { remote } from 'webdriverio';
import waitPort from 'wait-port';
// 1. Start Safaridriver server
safaridriver.start({ port: 4444 });
// 2. Wait for driver port to open
await waitPort({ port: 4444 });
// 3. Connect WebdriverIO session
// without `port`, WebdriverIO starts a driver of its own
const browser = await remote({
port: 4444,
capabilities: {
browserName: 'safari'
}
});
await browser.url('https://webdriver.io');
console.log(await browser.getTitle());
// 4. Stop Safaridriver process
safaridriver.stop();Passed into the start(options) method:
| Option | Type | Default | Description |
|---|---|---|---|
port |
number |
— | Port to listen on. |
host |
string |
0.0.0.0 |
Host IP address to bind server. |
customGeckoDriverPath |
string |
process.env.GECKODRIVER_PATH |
Path to custom/cached driver binary. |
cacheDir |
string |
process.env.GECKODRIVER_CACHE_DIR || os.tmpdir() |
Root directory for caching downloaded binaries. |
spawnOpts |
object |
undefined |
Spawn options passed directly to Node.js child_process.spawn. |
allowHosts |
string[] |
[] |
List of explicit host names allowed to connect. |
allowOrigins |
string[] |
[] |
List of allowed request origins (scheme://host:port). |
See the geckodriver README for the full list of options.
Passed into the start(options) method:
| Option | Type | Default | Description |
|---|---|---|---|
port |
number |
— | Port to listen on. |
customEdgeDriverPath |
string |
process.env.EDGEDRIVER_PATH |
Path to custom/cached driver binary. |
cacheDir |
string |
process.env.EDGEDRIVER_CACHE_DIR || os.tmpdir() |
Root directory for caching downloaded binaries. |
allowedIps |
string[] |
[''] |
List of remote IP addresses allowed to connect. |
allowedOrigins |
string[] |
['*'] |
List of allowed request origins. Using * to allow any origin is dangerous! |
spawnOpts |
object |
undefined |
Spawn options passed directly to Node.js child_process.spawn, e.g. { stdio: 'ignore' } if you don't read the driver output. |
See the edgedriver README for the full list of options.
Passed into safaridriver.start(options):
| Option | Type | Default | Description |
|---|---|---|---|
port |
number |
4444 |
Port for HTTP server listening. |
path |
string |
/usr/bin/safaridriver |
Path to system safaridriver binary. |
useTechnologyPreview |
boolean |
false |
Enables Safari Technology Preview driver binary. |
enable |
boolean |
false |
Configures macOS permissions ("Enable Remote Automation") and exits immediately. |
diagnose |
boolean |
false |
Enables diagnostic log output for driver sessions. |
spawnOpts |
object |
undefined |
Spawn options passed directly to Node.js child_process.spawn, e.g. { stdio: 'ignore' } if you don't read the driver output. |
See the safaridriver README for more details.