node-geckodriver

An npm wrapper that downloads, manages, and launches Mozilla's Geckodriver binary for Firefox WebDriver automation in Node.js.

Tool
npm
v6.1.1
47stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
49/100Fair
Development Activity44
Maintenance48
Community32
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality82
Innovation55
Learning Curve55

geckodriver is the official npm-distributed wrapper around Mozilla’s Geckodriver, the WebDriver-BiDi proxy that lets automation tools drive Firefox. Rather than asking developers to manually download and pin a platform-specific Geckodriver binary, the package resolves the current release (or a pinned GECKODRIVER_VERSION), downloads the correct archive for the host OS and architecture, verifies and extracts it into a cache directory, and exposes both a CLI (npx geckodriver) and a programmatic start/download API.

It’s a foundational dependency for Node.js browser-automation stacks: WebdriverIO uses it internally to spin up Firefox sessions, and any project built on selenium-webdriver or a raw WebDriver client can use it the same way. The package handles the operational plumbing — proxy support via HTTPS_PROXY/HTTP_PROXY, safe concurrent downloads via per-operation staging directories, Zip-Slip-safe archive extraction, and CJS/ESM dual exports — so consuming test suites don’t have to.

Because it wraps a native binary rather than reimplementing WebDriver itself, its own codebase stays small and focused: a handful of TypeScript modules handle argument parsing, download-URL construction, and process spawning, while the actual browser automation logic lives in Geckodriver (Rust, maintained by Mozilla) and the WebDriver clients that speak to it.

What You Get

  • Automatic detection and download of the latest (or a pinned) Geckodriver release for the current OS and CPU architecture
  • A start() function returning a Node.js ChildProcess handle, so callers can manage the Geckodriver process lifecycle themselves
  • A download() function for pre-fetching a specific Geckodriver version outside of a test run, useful for CI image baking
  • A geckodriver CLI binary (npx geckodriver --port=4444) that behaves like invoking the native driver directly
  • Configurable cache directory, custom CDN/proxy support (GECKODRIVER_CDNURL, HTTPS_PROXY), and a customGeckoDriverPath escape hatch for already-cached or custom binaries
  • Dual CJS/ESM module exports so it drops into either module system without a build-config change

Common Use Cases

  • WebdriverIO Firefox sessions - WebdriverIO uses this package under the hood to provision Geckodriver automatically when a test run requests browserName: 'firefox'
  • Custom Selenium/WebDriver test runners - Projects that speak WebDriver directly (via selenium-webdriver or a hand-rolled HTTP client) use start() to launch a local Geckodriver instance before opening a session
  • CI pipelines that pin a driver version - Setting GECKODRIVER_VERSION (or calling download(version) in a setup script) ensures every CI run tests against the same Geckodriver build
  • Air-gapped or proxied CI environments - GECKODRIVER_CDNURL and HTTPS_PROXY/HTTP_PROXY let teams route the binary download through an internal mirror or corporate proxy

Under The Hood

Architecture The package is a thin orchestration layer around a native binary, not a WebDriver implementation itself. src/index.ts exposes start(), which resolves a Geckodriver binary path (explicit path, GECKODRIVER_PATH/GECKODRIVER_FILEPATH env vars, or a fresh download via install.ts), validates access, derives CLI arguments from a typed GeckodriverParameters object via parseParams() in utils.ts, and spawns the binary with node:child_process. install.ts owns the download path: it checks a versioned cache entry first, otherwise fetches the latest version string from Mozilla’s Cargo.toml on GitHub, resolves a platform/arch-specific download URL, and extracts the archive (tar.gz via modern-tar, zip via @zip.js/zip.js on Windows) into a unique per-operation staging directory before renaming the binary into its final cache slot — a pattern that specifically defends against races between concurrent parallel-worker downloads of the same version. The CLI entry point (bin/geckodriver.jssrc/cli.ts) is a much simpler pass-through: download, then spawn with inherited stdio and forwarded SIGTERM. What breaks if the core abstraction (binary-path resolution) changes: every consumer, including WebdriverIO’s Firefox driver management, depends on start() returning a live, already-listening-capable child process.

Tech Stack TypeScript targeting ES2020 with NodeNext module resolution, built via tsc -b into dual CJS/ESM output (dist/index.js and dist/cjs/index.js) declared through a conditional exports map. Runtime dependencies are deliberately minimal: @wdio/logger for structured logging, @zip.js/zip.js for zip extraction, modern-tar for tar.gz extraction, decamelize for CLI flag naming, and http-proxy-agent/https-proxy-agent for corporate-proxy support in fetch calls. Dev tooling includes Vitest for tests, ESLint via the shared @wdio/eslint config, Husky for git hooks, and release-it for publishing — consistent with the wider WebdriverIO organization’s toolchain, since this package now lives under the webdriverio-community GitHub org.

Code Quality Testing is thorough for a package this size: tests/unit.test.ts (282 lines) exercises getBinaryFilename, getDownloadUrl, parseParams, retryFetch, and the full download() flow with extensive vi.mock() doubles for node:fs/promises, node:os, node:stream/promises, and the zip/tar libraries — including a documented note about Vitest mock hoisting order. tests/start-unit.test.ts covers start()’s branching logic (missing binary, connectExisting vs websocketPort conflicts), and tests/test.e2e.ts runs a real end-to-end Firefox session via WebdriverIO. An interop test (tests/interop/cjs.test.ts) specifically guards the CJS/ESM dual-export contract. ESLint runs as part of npm test, and CI/Audit GitHub Actions badges are wired into the README. Error handling is explicit rather than swallowed — invalid version strings, failed downloads (non-200 status), and Zip-Slip path traversal attempts all throw named errors with context.

What Makes It Unique The package’s specific value is operational hardening around a task that looks trivial (“download a file, unzip it”) but has real edge cases at scale: concurrent test workers downloading the same version simultaneously (solved via per-operation staging directories and treating a rename EEXIST/EPERM race as success rather than failure), zip-archive path traversal from a malicious or corrupted archive (explicitly checked and rejected), and cross-platform binary naming/architecture detection (os.platform()/os.arch() branching for win32/darwin/linux and arm64/x64/x86). It isn’t reinventing WebDriver automation — its innovation is in being a dependable, race-safe binary manager that larger tools like WebdriverIO can build on without each reimplementing this plumbing themselves.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search