selenium-webdriver
Advanced tools
Comparing version 4.22.0 to 4.23.0
@@ -20,2 +20,7 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
/** | ||
* @deprecated | ||
* in favor of LocalValue methods for all argument values. | ||
* This extra wrapper is not required. | ||
*/ | ||
class ArgumentValue { | ||
@@ -28,3 +33,3 @@ constructor(value) { | ||
if (this.value instanceof LocalValue) { | ||
return this.value.toJson() | ||
return this.value.asMap() | ||
} else { | ||
@@ -31,0 +36,0 @@ // ReferenceValue |
@@ -141,2 +141,25 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
} | ||
async close() { | ||
if ( | ||
this._browsingContextIds !== null && | ||
this._browsingContextIds !== undefined && | ||
this._browsingContextIds.length > 0 | ||
) { | ||
await this.bidi.unsubscribe( | ||
'browsingContext.contextCreated', | ||
'browsingContext.contextDestroyed', | ||
'browsingContext.fragmentNavigated', | ||
'browsingContext.userPromptClosed', | ||
this._browsingContextIds, | ||
) | ||
} else { | ||
await this.bidi.unsubscribe( | ||
'browsingContext.contextCreated', | ||
'browsingContext.contextDestroyed', | ||
'browsingContext.fragmentNavigated', | ||
'browsingContext.userPromptClosed', | ||
) | ||
} | ||
} | ||
} | ||
@@ -143,0 +166,0 @@ |
@@ -160,2 +160,4 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
this.events.push(...eventsArray) | ||
await this.send(params) | ||
@@ -171,8 +173,11 @@ } | ||
async unsubscribe(events, browsingContexts) { | ||
if (typeof events === 'string') { | ||
this.events = this.events.filter((event) => event !== events) | ||
} else if (Array.isArray(events)) { | ||
this.events = this.events.filter((event) => !events.includes(event)) | ||
} | ||
const eventsToRemove = typeof events === 'string' ? [events] : events | ||
// Check if the eventsToRemove are in the subscribed events array | ||
// Filter out events that are not in this.events before filtering | ||
const existingEvents = eventsToRemove.filter((event) => this.events.includes(event)) | ||
// Remove the events from the subscribed events array | ||
this.events = this.events.filter((event) => !existingEvents.includes(event)) | ||
if (typeof browsingContexts === 'string') { | ||
@@ -184,6 +189,9 @@ this.browsingContexts.pop() | ||
if (existingEvents.length === 0) { | ||
return | ||
} | ||
const params = { | ||
method: 'session.unsubscribe', | ||
params: { | ||
events: this.events, | ||
events: existingEvents, | ||
}, | ||
@@ -190,0 +198,0 @@ } |
@@ -20,2 +20,4 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
const { Source } = require('./scriptTypes') | ||
/** | ||
@@ -29,2 +31,3 @@ * Represents a base log entry. | ||
* @param {string} level - The log level. | ||
* @param {string} text - The log source. | ||
* @param {string} text - The log text. | ||
@@ -34,4 +37,5 @@ * @param {number} timeStamp - The log timestamp. | ||
*/ | ||
constructor(level, text, timeStamp, stackTrace) { | ||
constructor(level, source, text, timeStamp, stackTrace) { | ||
this._level = level | ||
this._source = new Source(source) | ||
this._text = text | ||
@@ -73,2 +77,6 @@ this._timeStamp = timeStamp | ||
} | ||
get source() { | ||
return this._source | ||
} | ||
} | ||
@@ -90,4 +98,4 @@ | ||
*/ | ||
constructor(level, text, timeStamp, type, stackTrace) { | ||
super(level, text, timeStamp, stackTrace) | ||
constructor(level, source, text, timeStamp, type, stackTrace) { | ||
super(level, source, text, timeStamp, stackTrace) | ||
this._type = type | ||
@@ -111,6 +119,5 @@ } | ||
class ConsoleLogEntry extends GenericLogEntry { | ||
constructor(level, text, timeStamp, type, method, realm, args, stackTrace) { | ||
super(level, text, timeStamp, type, stackTrace) | ||
constructor(level, source, text, timeStamp, type, method, args, stackTrace) { | ||
super(level, source, text, timeStamp, type, stackTrace) | ||
this._method = method | ||
this._realm = realm | ||
this._args = args | ||
@@ -126,12 +133,3 @@ } | ||
} | ||
/** | ||
* Gets the realm associated with the log entry. | ||
* @returns {string} The realm associated with the log entry. | ||
*/ | ||
get realm() { | ||
return this._realm | ||
} | ||
/** | ||
* Gets the arguments associated with the log entry. | ||
@@ -151,4 +149,4 @@ * @returns {Array} The arguments associated with the log entry. | ||
class JavascriptLogEntry extends GenericLogEntry { | ||
constructor(level, text, timeStamp, type, stackTrace) { | ||
super(level, text, timeStamp, type, stackTrace) | ||
constructor(level, source, text, timeStamp, type, stackTrace) { | ||
super(level, source, text, timeStamp, type, stackTrace) | ||
} | ||
@@ -155,0 +153,0 @@ } |
@@ -131,2 +131,3 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
params.level, | ||
params.source, | ||
params.text, | ||
@@ -136,3 +137,2 @@ params.timestamp, | ||
params.method, | ||
params.realm, | ||
params.args, | ||
@@ -183,2 +183,3 @@ params.stackTrace, | ||
params.level, | ||
params.source, | ||
params.text, | ||
@@ -218,2 +219,3 @@ params.timestamp, | ||
params.level, | ||
params.source, | ||
params.text, | ||
@@ -257,2 +259,3 @@ params.timestamp, | ||
params.level, | ||
params.source, | ||
params.text, | ||
@@ -287,2 +290,3 @@ params.timestamp, | ||
params.level, | ||
params.source, | ||
params.text, | ||
@@ -292,3 +296,2 @@ params.timestamp, | ||
params.method, | ||
params.realm, | ||
params.args, | ||
@@ -312,2 +315,3 @@ params.stackTrace, | ||
params.level, | ||
params.source, | ||
params.text, | ||
@@ -341,3 +345,11 @@ params.timestamp, | ||
async close() { | ||
await this.bidi.unsubscribe('log.entryAdded', this._browsingContextIds) | ||
if ( | ||
this._browsingContextIds !== null && | ||
this._browsingContextIds !== undefined && | ||
this._browsingContextIds.length > 0 | ||
) { | ||
await this.bidi.unsubscribe('log.entryAdded', this._browsingContextIds) | ||
} else { | ||
await this.bidi.unsubscribe('log.entryAdded') | ||
} | ||
} | ||
@@ -344,0 +356,0 @@ } |
@@ -316,8 +316,22 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
async close() { | ||
await this.bidi.unsubscribe( | ||
'network.beforeRequestSent', | ||
'network.responseStarted', | ||
'network.responseCompleted', | ||
'network.authRequired', | ||
) | ||
if ( | ||
this._browsingContextIds !== null && | ||
this._browsingContextIds !== undefined && | ||
this._browsingContextIds.length > 0 | ||
) { | ||
await this.bidi.unsubscribe( | ||
'network.beforeRequestSent', | ||
'network.responseStarted', | ||
'network.responseCompleted', | ||
'network.authRequired', | ||
this._browsingContextIds, | ||
) | ||
} else { | ||
await this.bidi.unsubscribe( | ||
'network.beforeRequestSent', | ||
'network.responseStarted', | ||
'network.responseCompleted', | ||
'network.authRequired', | ||
) | ||
} | ||
} | ||
@@ -324,0 +338,0 @@ } |
@@ -188,3 +188,7 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
toJson() { | ||
static createReferenceValue(handle, sharedId) { | ||
return new ReferenceValue(handle, sharedId) | ||
} | ||
asMap() { | ||
let toReturn = {} | ||
@@ -191,0 +195,0 @@ toReturn[TYPE_CONSTANT] = this.type |
@@ -30,2 +30,8 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
const ScriptEvent = { | ||
MESSAGE: 'script.message', | ||
REALM_CREATED: 'script.realmCreated', | ||
REALM_DESTROYED: 'script.realmDestroyed', | ||
} | ||
/** | ||
@@ -37,6 +43,44 @@ * Represents class to run events and commands of Script module. | ||
class ScriptManager { | ||
#callbackId = 0 | ||
#listener | ||
constructor(driver) { | ||
this._driver = driver | ||
this.#listener = new Map() | ||
this.#listener.set(ScriptEvent.MESSAGE, new Map()) | ||
this.#listener.set(ScriptEvent.REALM_CREATED, new Map()) | ||
this.#listener.set(ScriptEvent.REALM_DESTROYED, new Map()) | ||
} | ||
addCallback(eventType, callback) { | ||
const id = ++this.#callbackId | ||
const eventCallbackMap = this.#listener.get(eventType) | ||
eventCallbackMap.set(id, callback) | ||
return id | ||
} | ||
removeCallback(id) { | ||
let hasId = false | ||
for (const [, callbacks] of this.#listener) { | ||
if (callbacks.has(id)) { | ||
callbacks.delete(id) | ||
hasId = true | ||
} | ||
} | ||
if (!hasId) { | ||
throw Error(`Callback with id ${id} not found`) | ||
} | ||
} | ||
invokeCallbacks(eventType, data) { | ||
const callbacks = this.#listener.get(eventType) | ||
if (callbacks) { | ||
for (const [, callback] of callbacks) { | ||
callback(data) | ||
} | ||
} | ||
} | ||
async init(browsingContextIds) { | ||
@@ -256,2 +300,10 @@ if (!(await this._driver.getCapabilities()).get('webSocketUrl')) { | ||
if (argumentValueList != null) { | ||
let argumentParams = [] | ||
argumentValueList.forEach((argumentValue) => { | ||
argumentParams.push(argumentValue.asMap()) | ||
}) | ||
params['arguments'] = argumentParams | ||
} | ||
const command = { | ||
@@ -439,3 +491,3 @@ method: 'script.addPreloadScript', | ||
async onMessage(callback) { | ||
await this.subscribeAndHandleEvent('script.message', callback) | ||
return await this.subscribeAndHandleEvent(ScriptEvent.MESSAGE, callback) | ||
} | ||
@@ -450,3 +502,3 @@ | ||
async onRealmCreated(callback) { | ||
await this.subscribeAndHandleEvent('script.realmCreated', callback) | ||
return await this.subscribeAndHandleEvent(ScriptEvent.REALM_CREATED, callback) | ||
} | ||
@@ -461,15 +513,14 @@ | ||
async onRealmDestroyed(callback) { | ||
await this.subscribeAndHandleEvent('script.realmDestroyed', callback) | ||
return await this.subscribeAndHandleEvent(ScriptEvent.REALM_DESTROYED, callback) | ||
} | ||
async subscribeAndHandleEvent(eventType, callback) { | ||
if (this.browsingContextIds != null) { | ||
await this.bidi.subscribe(eventType, this.browsingContextIds) | ||
if (this._browsingContextIds != null) { | ||
await this.bidi.subscribe(eventType, this._browsingContextIds) | ||
} else { | ||
await this.bidi.subscribe(eventType) | ||
} | ||
await this._on(callback) | ||
} | ||
async _on(callback) { | ||
let id = this.addCallback(eventType, callback) | ||
this.ws = await this.bidi.socket | ||
@@ -491,6 +542,25 @@ this.ws.on('message', (event) => { | ||
} | ||
callback(response) | ||
this.invokeCallbacks(eventType, response) | ||
} | ||
}) | ||
return id | ||
} | ||
async close() { | ||
if ( | ||
this._browsingContextIds !== null && | ||
this._browsingContextIds !== undefined && | ||
this._browsingContextIds.length > 0 | ||
) { | ||
await this.bidi.unsubscribe( | ||
'script.message', | ||
'script.realmCreated', | ||
'script.realmDestroyed', | ||
this._browsingContextIds, | ||
) | ||
} else { | ||
await this.bidi.unsubscribe('script.message', 'script.realmCreated', 'script.realmDestroyed') | ||
} | ||
} | ||
} | ||
@@ -497,0 +567,0 @@ |
@@ -0,1 +1,13 @@ | ||
## 4.23.0 | ||
- Expose pnpm as a tool we can use | ||
- [bidi] Fix the event unsubscribe method. Update modules to have close methods. (#14192) | ||
- Run Node browser tests on the RBE (#14194) | ||
- [bidi] Add methods to add/remove handlers in Script module (#14230) | ||
- [bidi] Add source type to log entry (#14244) | ||
- [bidi] Add dom mutation handlers (#14238) | ||
- [bidi] Add high-level script pinning APIs (#14250) | ||
- [bidi] Deprecate argument value wrapper class (#14251) | ||
- Add CDP for Chrome 127 and remove 124 | ||
## 4.22.0 | ||
@@ -2,0 +14,0 @@ |
@@ -23,2 +23,3 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
this.scriptSource_ = script | ||
// eslint-disable-next-line | ||
this.scriptHandle_ = crypto.randomUUID().replace(/-/gi, '') | ||
@@ -25,0 +26,0 @@ } |
@@ -19,2 +19,7 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
const logInspector = require('../bidi/logInspector') | ||
const scriptManager = require('../bidi//scriptManager') | ||
const { LocalValue, ChannelValue } = require('../bidi/protocolValue') | ||
const fs = require('node:fs') | ||
const path = require('node:path') | ||
const by = require('./by') | ||
@@ -24,2 +29,3 @@ class Script { | ||
#logInspector | ||
#script | ||
@@ -43,2 +49,9 @@ constructor(driver) { | ||
async #initScript() { | ||
if (this.#script !== undefined) { | ||
return | ||
} | ||
this.#script = await scriptManager([], this.#driver) | ||
} | ||
async addJavaScriptErrorHandler(callback) { | ||
@@ -64,4 +77,54 @@ await this.#init() | ||
} | ||
async addDomMutationHandler(callback) { | ||
await this.#initScript() | ||
let argumentValues = [] | ||
let value = LocalValue.createChannelValue(new ChannelValue('channel_name')) | ||
argumentValues.push(value) | ||
const filePath = path.join(__dirname, 'atoms', 'bidi-mutation-listener.js') | ||
let mutationListener = fs.readFileSync(filePath, 'utf-8').toString() | ||
await this.#script.addPreloadScript(mutationListener, argumentValues) | ||
let id = await this.#script.onMessage(async (message) => { | ||
let payload = JSON.parse(message['data']['value']) | ||
let elements = await this.#driver.findElements({ | ||
css: '*[data-__webdriver_id=' + by.escapeCss(payload['target']) + ']', | ||
}) | ||
if (elements.length === 0) { | ||
return | ||
} | ||
let event = { | ||
element: elements[0], | ||
attribute_name: payload['name'], | ||
current_value: payload['value'], | ||
old_value: payload['oldValue'], | ||
} | ||
callback(event) | ||
}) | ||
return id | ||
} | ||
async removeDomMutationHandler(id) { | ||
await this.#initScript() | ||
await this.#script.removeCallback(id) | ||
} | ||
async pin(script) { | ||
await this.#initScript() | ||
return await this.#script.addPreloadScript(script) | ||
} | ||
async unpin(id) { | ||
await this.#initScript() | ||
await this.#script.removePreloadScript(id) | ||
} | ||
} | ||
module.exports = Script |
{ | ||
"name": "selenium-webdriver", | ||
"version": "4.22.0", | ||
"version": "4.23.0", | ||
"description": "The official WebDriver JavaScript bindings from the Selenium project", | ||
@@ -26,25 +26,27 @@ "license": "Apache-2.0", | ||
"dependencies": { | ||
"@bazel/runfiles": "^5.8.1", | ||
"jszip": "^3.10.1", | ||
"tmp": "^0.2.3", | ||
"ws": ">=8.16.0" | ||
"ws": "^8.17.1" | ||
}, | ||
"devDependencies": { | ||
"@bazel/runfiles": "^5.8.1", | ||
"@eslint/js": "^9.1.1", | ||
"@eslint/js": "^9.5.0", | ||
"clean-jsdoc-theme": "^4.3.0", | ||
"eslint": "^9.1.0", | ||
"eslint": "^9.5.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-mocha": "^10.4.3", | ||
"eslint-plugin-n": "^17.2.1", | ||
"eslint-plugin-n": "^17.9.0", | ||
"eslint-plugin-no-only-tests": "^3.1.0", | ||
"eslint-plugin-prettier": "^5.1.3", | ||
"express": "^4.19.2", | ||
"globals": "^15.0.0", | ||
"globals": "^15.6.0", | ||
"has-flag": "^4.0.0", | ||
"jsdoc": "^4.0.3", | ||
"mocha": "^10.4.0", | ||
"mocha": "^10.5.1", | ||
"mocha-junit-reporter": "^2.2.1", | ||
"multer": "^1.4.5-lts.1", | ||
"prettier": "^3.2.5", | ||
"multer": "1.4.5-lts.1", | ||
"prettier": "^3.3.2", | ||
"serve-index": "^1.9.1", | ||
"sinon": "^17.0.1" | ||
"sinon": "^17.0.1", | ||
"supports-color": "^9.4.0" | ||
}, | ||
@@ -54,4 +56,3 @@ "scripts": { | ||
"lint:fix": "eslint . --fix", | ||
"test": "npm run lint && mocha -t 600000 --recursive test", | ||
"test-jasmine": "bazel test //javascript/node/selenium-webdriver:tests", | ||
"test": "bazel test //javascript/node/selenium-webdriver/...", | ||
"generate-docs": "jsdoc --configure jsdoc_conf.json --verbose" | ||
@@ -58,0 +59,0 @@ }, |
@@ -35,3 +35,6 @@ // Licensed to the Software Freedom Conservancy (SFC) under one | ||
const fs = require('node:fs') | ||
const path = require('node:path') | ||
const { isatty } = require('node:tty') | ||
const { runfiles } = require('@bazel/runfiles') | ||
const chrome = require('../chrome') | ||
@@ -292,2 +295,33 @@ const edge = require('../edge') | ||
const builder = new Builder() | ||
// Sniff the environment variables for paths to use for the common browsers | ||
// Chrome | ||
if ('SE_CHROMEDRIVER' in process.env) { | ||
const found = locate(process.env.SE_CHROMEDRIVER) | ||
const service = new chrome.ServiceBuilder(found) | ||
builder.setChromeService(service) | ||
} | ||
if ('SE_CHROME' in process.env) { | ||
const binary = locate(process.env.SE_CHROME) | ||
const options = new chrome.Options() | ||
options.setChromeBinaryPath(binary) | ||
options.setAcceptInsecureCerts(true) | ||
options.addArguments('disable-infobars', 'disable-breakpad', 'disable-dev-shm-usage', 'no-sandbox') | ||
builder.setChromeOptions(options) | ||
} | ||
// Edge | ||
// Firefox | ||
if ('SE_GECKODRIVER' in process.env) { | ||
const found = locate(process.env.SE_GECKODRIVER) | ||
const service = new firefox.ServiceBuilder(found) | ||
builder.setFirefoxService(service) | ||
} | ||
if ('SE_FIREFOX' in process.env) { | ||
const binary = locate(process.env.SE_FIREFOX) | ||
const options = new firefox.Options() | ||
options.enableBidi() | ||
options.setBinary(binary) | ||
builder.setFirefoxOptions(options) | ||
} | ||
builder.disableEnvironmentOverrides() | ||
@@ -510,2 +544,55 @@ | ||
function locate(fileLike) { | ||
if (fs.existsSync(fileLike)) { | ||
return fileLike | ||
} | ||
try { | ||
return runfiles.resolve(fileLike) | ||
} catch { | ||
// Fall through | ||
} | ||
// Is the item in the workspace? | ||
try { | ||
return runfiles.resolveWorkspaceRelative(fileLike) | ||
} catch { | ||
// Fall through | ||
} | ||
// Find the repo mapping file | ||
let repoMappingFile | ||
try { | ||
repoMappingFile = runfiles.resolve('_repo_mapping') | ||
} catch { | ||
throw new Error('Unable to locate (no repo mapping file): ' + fileLike) | ||
} | ||
const lines = fs.readFileSync(repoMappingFile, { encoding: 'utf8' }).split('\n') | ||
// Build a map of "repo we declared we need" to "path" | ||
const mapping = {} | ||
for (const line of lines) { | ||
if (line.startsWith(',')) { | ||
const parts = line.split(',', 3) | ||
mapping[parts[1]] = parts[2] | ||
} | ||
} | ||
// Get the first segment of the path | ||
const pathSegments = fileLike.split('/') | ||
if (!pathSegments.length) { | ||
throw new Error('Unable to locate ' + fileLike) | ||
} | ||
pathSegments[0] = mapping[pathSegments[0]] ? mapping[pathSegments[0]] : '_main' | ||
try { | ||
return runfiles.resolve(path.join(...pathSegments)) | ||
} catch { | ||
// Fall through | ||
} | ||
throw new Error('Unable to find ' + fileLike) | ||
} | ||
// PUBLIC API | ||
@@ -512,0 +599,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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
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 4 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
18025662
95
20822
1
4
19
32
+ Added@bazel/runfiles@^5.8.1
+ Added@bazel/runfiles@5.8.1(transitive)
Updatedws@^8.17.1