selenium-webdriver
Advanced tools
+112
-21
@@ -36,9 +36,98 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
| this.connected = false | ||
| this._closed = false | ||
| this._pending = new Map() | ||
| this._connectWaiters = new Set() | ||
| this._ws = new WebSocket(_webSocketUrl) | ||
| this._ws.on('open', () => { | ||
| // The handshake can complete after close()/_failPending() has already | ||
| // marked the connection closed. Don't flip connected back to true and | ||
| // proactively close the now-orphan socket so it does not leak. | ||
| if (this._closed) { | ||
| try { | ||
| this._ws.close() | ||
| } catch { | ||
| /* socket already closing */ | ||
| } | ||
| return | ||
| } | ||
| this.connected = true | ||
| for (const { resolve } of this._connectWaiters) { | ||
| resolve() | ||
| } | ||
| this._connectWaiters.clear() | ||
| }) | ||
| // Single shared response dispatcher. Avoids attaching a new 'message' | ||
| // listener for every in-flight send(), which previously caused | ||
| // MaxListenersExceededWarning under concurrent BiDi traffic | ||
| // (e.g. network interception during a page navigation). | ||
| this._ws.on('message', (data) => { | ||
| // Frames can arrive after close() has cleared _pending; ignore them | ||
| // rather than re-emitting parse errors or dispatching to nothing. | ||
| if (this._closed) { | ||
| return | ||
| } | ||
| let payload | ||
| try { | ||
| payload = JSON.parse(data.toString()) | ||
| } catch (err) { | ||
| // Surface protocol parse failures rather than silently dropping — | ||
| // otherwise callers see misleading send() timeouts. | ||
| const wrapped = new Error(`Failed to parse BiDi message: ${err.message}`) | ||
| if (this.listenerCount('error') > 0) { | ||
| this.emit('error', wrapped) | ||
| } else { | ||
| process.emitWarning(wrapped.message, 'BiDiProtocolWarning') | ||
| } | ||
| return | ||
| } | ||
| // Messages without a numeric id are BiDi events, not command | ||
| // responses; they are routed via subscribe/EventEmitter elsewhere | ||
| // and intentionally ignored by this dispatcher. | ||
| if (payload == null || typeof payload.id !== 'number') { | ||
| return | ||
| } | ||
| const entry = this._pending.get(payload.id) | ||
| if (entry === undefined) { | ||
| return | ||
| } | ||
| clearTimeout(entry.timeoutId) | ||
| this._pending.delete(payload.id) | ||
| entry.resolve(payload) | ||
| }) | ||
| // Fail any in-flight send() calls promptly when the peer disconnects | ||
| // or the socket errors, instead of waiting for RESPONSE_TIMEOUT. | ||
| this._ws.on('close', () => { | ||
| this._failPending(new Error('BiDi connection closed unexpectedly')) | ||
| }) | ||
| this._ws.on('error', (err) => { | ||
| this._failPending(new Error(`BiDi connection error: ${err.message}`)) | ||
| }) | ||
| } | ||
| /** | ||
| * Reject any in-flight sends and mark the connection failed. Idempotent so | ||
| * that close() and the underlying 'close'/'error' events do not double-reject. | ||
| * @param {Error} error | ||
| * @private | ||
| */ | ||
| _failPending(error) { | ||
| if (this._closed) { | ||
| return | ||
| } | ||
| this._closed = true | ||
| this.connected = false | ||
| for (const { reject, timeoutId } of this._pending.values()) { | ||
| clearTimeout(timeoutId) | ||
| reject(error) | ||
| } | ||
| this._pending.clear() | ||
| // Reject any callers parked in waitForConnection() so close() (or an | ||
| // unexpected disconnect) cannot leave them hanging forever. | ||
| for (const { reject } of this._connectWaiters) { | ||
| reject(error) | ||
| } | ||
| this._connectWaiters.clear() | ||
| } | ||
| /** | ||
| * @returns {WebSocket} | ||
@@ -73,10 +162,15 @@ */ | ||
| async waitForConnection() { | ||
| return new Promise((resolve) => { | ||
| return new Promise((resolve, reject) => { | ||
| if (this._closed) { | ||
| reject(new Error('BiDi connection is closed')) | ||
| return | ||
| } | ||
| if (this.connected) { | ||
| resolve() | ||
| } else { | ||
| this._ws.once('open', () => { | ||
| resolve() | ||
| }) | ||
| return | ||
| } | ||
| // Park the waiter in a Set so the constructor's 'open' handler can | ||
| // resolve it and _failPending() can reject it. Avoids attaching socket | ||
| // listeners that close()'s removeAllListeners('close') would strip. | ||
| this._connectWaiters.add({ resolve, reject }) | ||
| }) | ||
@@ -91,5 +185,14 @@ } | ||
| async send(params) { | ||
| if (this._closed) { | ||
| throw new Error('BiDi connection is closed') | ||
| } | ||
| if (!this.connected) { | ||
| await this.waitForConnection() | ||
| } | ||
| // Defense in depth: even after waitForConnection() resolves, the socket | ||
| // may have transitioned to CLOSING/CLOSED (e.g. caller closed the raw | ||
| // socket). Refuse rather than throwing from inside ws.send(). | ||
| if (this._ws.readyState !== WebSocket.OPEN) { | ||
| throw new Error('BiDi connection is not open') | ||
| } | ||
@@ -102,21 +205,7 @@ const id = ++this.id | ||
| const timeoutId = setTimeout(() => { | ||
| this._pending.delete(id) | ||
| reject(new Error(`Request with id ${id} timed out`)) | ||
| handler.off('message', listener) | ||
| }, RESPONSE_TIMEOUT) | ||
| const listener = (data) => { | ||
| try { | ||
| const payload = JSON.parse(data.toString()) | ||
| if (payload.id === id) { | ||
| clearTimeout(timeoutId) | ||
| handler.off('message', listener) | ||
| resolve(payload) | ||
| } | ||
| } catch (err) { | ||
| // eslint-disable-next-line no-undef | ||
| log.error(`Failed parse message: ${err.message}`) | ||
| } | ||
| } | ||
| const handler = this._ws.on('message', listener) | ||
| this._pending.set(id, { resolve, reject, timeoutId }) | ||
| }) | ||
@@ -213,2 +302,4 @@ } | ||
| close() { | ||
| this._failPending(new Error('BiDi connection closed before response was received')) | ||
| const closeWebSocket = (callback) => { | ||
@@ -215,0 +306,0 @@ // don't close if it's already closed |
+6
-6
| { | ||
| "name": "selenium-webdriver", | ||
| "version": "4.43.0", | ||
| "version": "4.44.0", | ||
| "description": "The official WebDriver JavaScript bindings from the Selenium project", | ||
@@ -29,7 +29,7 @@ "license": "Apache-2.0", | ||
| "tmp": "^0.2.5", | ||
| "ws": "^8.20.0" | ||
| "ws": "^8.20.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@eslint/js": "^9.39.4", | ||
| "clean-jsdoc-theme": "^4.3.0", | ||
| "clean-jsdoc-theme": "^4.3.2", | ||
| "eslint": "^9.39.4", | ||
@@ -39,5 +39,5 @@ "eslint-config-prettier": "^10.1.8", | ||
| "eslint-plugin-n": "^17.24.0", | ||
| "eslint-plugin-no-only-tests": "^3.3.0", | ||
| "eslint-plugin-no-only-tests": "^3.4.0", | ||
| "eslint-plugin-prettier": "^5.5.5", | ||
| "express": "^4.22.1", | ||
| "express": "^4.22.2", | ||
| "globals": "^15.15.0", | ||
@@ -49,3 +49,3 @@ "has-flag": "^5.0.1", | ||
| "multer": "2.0.2", | ||
| "prettier": "^3.8.1", | ||
| "prettier": "^3.8.3", | ||
| "serve-index": "^1.9.2", | ||
@@ -52,0 +52,0 @@ "sinon": "^19.0.5", |
+55
-183
| # selenium-webdriver | ||
| Selenium is a browser automation library. Most often used for testing | ||
| web-applications, Selenium may be used for any task that requires automating | ||
| interaction with the browser. | ||
| JavaScript language bindings for [Selenium WebDriver](https://www.selenium.dev). | ||
| Selenium automates browsers for testing and web-based task automation. | ||
| Requires Node.js >= 20. | ||
| ## Installation | ||
| Selenium may be installed via npm with | ||
| ```bash | ||
| npm install selenium-webdriver | ||
| ``` | ||
| npm install selenium-webdriver | ||
| ## Quick Start | ||
| You will need to download additional components to work with each of the major | ||
| browsers. The drivers for Chrome, Firefox, and Microsoft's IE and Edge web | ||
| browsers are all standalone executables that should be placed on your system | ||
| [PATH]. Apple's safaridriver (v10 and above) can be found at the | ||
| following path – /usr/bin/safaridriver. To enable automation on safari, | ||
| you need to run command `safaridriver --enable`. | ||
| | Browser | Component | | ||
| | :---------------- | :------------------------------- | | ||
| | Chrome | [chromedriver(.exe)][chrome] | | ||
| | Internet Explorer | [IEDriverServer.exe][release] | | ||
| | Edge | [MicrosoftWebDriver.msi][edge] | | ||
| | Firefox | [geckodriver(.exe)][geckodriver] | | ||
| | Opera | [operadriver(.exe)][operadriver] | | ||
| | Safari | [safaridriver] | | ||
| ## Usage | ||
| The sample below and others are included in the `example` directory. You may | ||
| also find the tests for selenium-webdriver informative. | ||
| ```javascript | ||
| const { Builder, Browser, By, Key, until } = require('selenium-webdriver') | ||
| const { Builder, Browser } = require('selenium-webdriver') | ||
| ;(async function example() { | ||
| let driver = await new Builder().forBrowser(Browser.FIREFOX).build() | ||
| let driver = await new Builder().forBrowser(Browser.CHROME).build() | ||
| try { | ||
| await driver.get('https://www.google.com/ncr') | ||
| await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN) | ||
| await driver.wait(until.titleIs('webdriver - Google Search'), 1000) | ||
| await driver.get('https://www.selenium.dev') | ||
| console.log(await driver.getTitle()) | ||
| } finally { | ||
@@ -49,183 +30,74 @@ await driver.quit() | ||
| ### Using the Builder API | ||
| Selenium Manager automatically handles browser driver installation — no manual driver setup required. | ||
| The `Builder` class is your one-stop shop for configuring new WebDriver | ||
| instances. Rather than clutter your code with branches for the various browsers, | ||
| the builder lets you set all options in one flow. When you call | ||
| `Builder#build()`, all options irrelevant to the selected browser are dropped: | ||
| ## Configuring the Builder | ||
| The `Builder` sets default options for all browsers in a single chain; options | ||
| for non-selected browsers are dropped at `build()` time. The target browser can | ||
| be swapped at runtime via the `SELENIUM_BROWSER` environment variable. | ||
| ```javascript | ||
| const webdriver = require('selenium-webdriver') | ||
| const { Builder, Browser } = require('selenium-webdriver') | ||
| const chrome = require('selenium-webdriver/chrome') | ||
| const firefox = require('selenium-webdriver/firefox') | ||
| let driver = new webdriver.Builder() | ||
| .forBrowser(webdriver.Browser.FIREFOX) | ||
| .setChromeOptions(/* ... */) | ||
| .setFirefoxOptions(/* ... */) | ||
| let driver = new Builder() | ||
| .forBrowser(Browser.FIREFOX) | ||
| .setChromeOptions(new chrome.Options()) | ||
| .setFirefoxOptions(new firefox.Options()) | ||
| .build() | ||
| ``` | ||
| Why would you want to configure options irrelevant to the target browser? The | ||
| `Builder`'s API defines your _default_ configuration. You can change the target | ||
| browser at runtime through the `SELENIUM_BROWSER` environment variable. For | ||
| example, the `example/google_search.js` script is configured to run against | ||
| Firefox. You can run the example against other browsers just by changing the | ||
| runtime environment | ||
| ## Running Against a Remote Server | ||
| # cd node_modules/selenium-webdriver | ||
| node example/google_search | ||
| SELENIUM_BROWSER=chrome node example/google_search | ||
| SELENIUM_BROWSER=safari node example/google_search | ||
| To run scripts against a [Selenium Grid](https://www.selenium.dev/documentation/grid/) | ||
| or standalone server, point the Builder at the server URL, or set | ||
| `SELENIUM_REMOTE_URL`: | ||
| ### The Standalone Selenium Server | ||
| The standalone Selenium Server acts as a proxy between your script and the | ||
| browser-specific drivers. The server may be used when running locally, but it's | ||
| not recommend as it introduces an extra hop for each request and will slow | ||
| things down. The server is required, however, to use a browser on a remote host | ||
| (most browser drivers, like the IEDriverServer, do not accept remote | ||
| connections). | ||
| To use the Selenium Server, you will need to install the | ||
| [JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and | ||
| download the latest server from [Selenium][release]. Once downloaded, run the | ||
| server with | ||
| java -jar selenium-server-4.27.0.jar standalone | ||
| You may configure your tests to run against a remote server through the Builder | ||
| API: | ||
| ```javascript | ||
| let driver = new webdriver.Builder() | ||
| .forBrowser(webdriver.Browser.FIREFOX) | ||
| .usingServer('http://localhost:4444/wd/hub') | ||
| .build() | ||
| let driver = new Builder().forBrowser(Browser.CHROME).usingServer('http://localhost:4444').build() | ||
| ``` | ||
| Or change the Builder's configuration at runtime with the `SELENIUM_REMOTE_URL` | ||
| environment variable: | ||
| ```bash | ||
| SELENIUM_REMOTE_URL="http://localhost:4444" node script.js | ||
| ``` | ||
| SELENIUM_REMOTE_URL="http://localhost:4444/wd/hub" node script.js | ||
| You can experiment with these options using the `example/google_search.js` | ||
| script provided with `selenium-webdriver`. | ||
| ## Documentation | ||
| API documentation is available online from the [Selenium project][api]. | ||
| Additional resources include | ||
| - the #selenium channel on Libera IRC | ||
| - the [selenium-users@googlegroups.com][users] list | ||
| - [SeleniumHQ](https://selenium.dev/documentation/) documentation | ||
| ## Contributing | ||
| Contributions are accepted either through [GitHub][gh] pull requests or patches | ||
| via the [Selenium issue tracker][issues]. | ||
| ## Node Support Policy | ||
| Each version of selenium-webdriver will support the latest _semver-minor_ | ||
| version of the [LTS] and stable Node releases. All _semver-major_ & | ||
| _semver-minor_ versions between the LTS and stable release will have "best | ||
| effort" support. Following a Selenium release, any _semver-minor_ Node releases | ||
| will also have "best effort" support. Releases older than the latest LTS, | ||
| _semver-major_ releases, and all unstable release branches (e.g. "v.Next") | ||
| are considered strictly unsupported. | ||
| Each `selenium-webdriver` release targets the latest _semver-minor_ of Node's | ||
| [LTS and Current releases](https://github.com/nodejs/release#release-schedule). | ||
| For example, suppose the current LTS and stable releases are v22.13.0 and | ||
| v23.6.0, | ||
| respectively. Then a Selenium release would have the following support levels: | ||
| | Level | Guarantee | | ||
| | :------------ | :------------------------------------------------------------------------ | | ||
| | _supported_ | API compatible without runtime flags; bugs investigated and fixed. | | ||
| | _best effort_ | Bugs investigated as time permits; API compatibility only where required. | | ||
| | _unsupported_ | Bug reports closed as will-not-fix; API compatibility not guaranteed. | | ||
| | Version | Support | | ||
| | :--------: | :-----------: | | ||
| | <= 16.20.2 | _unsupported_ | | ||
| | 16.20.2 | supported | | ||
| | 18.8.0 | supported | | ||
| | >= 22.13.0 | best effort | | ||
| | v.Next | _unsupported_ | | ||
| Versions older than the active LTS, unstable release branches (e.g. `v.Next`), | ||
| and _semver-major_ Node releases outside the LTS / Current pair are _unsupported_. | ||
| ### Support Level Definitions | ||
| ## Documentation | ||
| - _supported:_ A selenium-webdriver release will be API compatible with the | ||
| platform API, without the use of runtime flags. | ||
| - [Getting Started](https://www.selenium.dev/documentation/webdriver/getting_started/) | ||
| - [JavaScript API Docs](https://www.selenium.dev/selenium/docs/api/javascript/) | ||
| - [Selenium Manager](https://www.selenium.dev/documentation/selenium_manager/) | ||
| - [Selenium Grid](https://www.selenium.dev/documentation/grid/) | ||
| - _best effort:_ Bugs will be investigated as time permits. API compatibility is | ||
| only guaranteed where required by a _supported_ release. This effectively | ||
| means the adoption of new JS features, such as ES2015 modules, will depend | ||
| on what is supported in Node's LTS. | ||
| ## Support | ||
| - _unsupported:_ Bug submissions will be closed as will-not-fix and API | ||
| compatibility is not guaranteed. | ||
| - [Selenium Chat](https://www.selenium.dev/support/#ChatRoom) | ||
| - [GitHub Issues](https://github.com/SeleniumHQ/selenium/issues) | ||
| ### Projected Support Schedule | ||
| ## Contributing | ||
| If Node releases a new [LTS] each October and a new major version every 6 | ||
| months, the support window for selenium-webdriver will be roughly: | ||
| Contributions are welcome via [GitHub](https://github.com/SeleniumHQ/selenium/) pull requests. | ||
| See the [source code](https://github.com/SeleniumHQ/selenium/tree/trunk/javascript/selenium-webdriver) for this binding. | ||
| | Release | Status | END-OF-LIFE | | ||
| | :-----: | :-------------: | :---------: | | ||
| | v18.x | Maintenance LTS | 2025-04-30 | | ||
| | v19.x | End-of-Life | 2023-06-01 | | ||
| | v20.x | Maintenance LTS | 2026-04-30 | | ||
| | v21.x | End-of-Life | 2024-06-01 | | ||
| | V22.x | Active LTS | 2027-04-30 | | ||
| | V23.x | Current | 2025-06-01 | | ||
| ## Links | ||
| ## Issues | ||
| - [npm](https://www.npmjs.com/package/selenium-webdriver) | ||
| - [Documentation](https://www.selenium.dev/documentation/?tab=javascript) | ||
| Please report any issues using the [Selenium issue tracker][issues]. When using | ||
| the issue tracker | ||
| - **Do** include a detailed description of the problem. | ||
| - **Do** include a link to a [gist](http://gist.github.com/) with any | ||
| interesting stack traces/logs (you may also attach these directly to the bug | ||
| report). | ||
| - **Do** include a [reduced test case][reduction]. Reporting "unable to find | ||
| element on the page" is _not_ a valid report - there's nothing for us to | ||
| look into. Expect your bug report to be closed if you do not provide enough | ||
| information for us to investigate. | ||
| - **Do not** use the issue tracker to submit basic help requests. All help | ||
| inquiries should be directed to the [user forum][users] or #selenium IRC | ||
| channel. | ||
| - **Do not** post empty "I see this too" or "Any updates?" comments. These | ||
| provide no additional information and clutter the log. | ||
| - **Do not** report regressions on closed bugs as they are not actively | ||
| monitored for updates (especially bugs that are >6 months old). Please open a | ||
| new issue and reference the original bug in your report. | ||
| ## License | ||
| 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. | ||
| [LTS]: https://github.com/nodejs/LTS | ||
| [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29 | ||
| [api]: https://www.selenium.dev/selenium/docs/api/javascript/ | ||
| [chrome]: https://googlechromelabs.github.io/chrome-for-testing/#stable | ||
| [gh]: https://github.com/SeleniumHQ/selenium/ | ||
| [issues]: https://github.com/SeleniumHQ/selenium/issues | ||
| [edge]: http://go.microsoft.com/fwlink/?LinkId=619687 | ||
| [geckodriver]: https://github.com/mozilla/geckodriver/releases/ | ||
| [reduction]: http://www.webkit.org/quality/reduction.html | ||
| [release]: https://www.selenium.dev/downloads/ | ||
| [users]: https://groups.google.com/forum/#!forum/selenium-users | ||
| [safaridriver]: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28 | ||
| [operadriver]: https://github.com/operasoftware/operachromiumdriver/releases | ||
| Licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 14 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 14 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
18092410
0.86%21847
0.41%103
-55.41%Updated