chore: merge develop

This commit is contained in:
Ryan Manuel
2024-10-18 13:13:40 -05:00
28 changed files with 1469 additions and 1448 deletions

View File

@@ -1,3 +1,3 @@
# Bump this version to force CI to re-create the cache from scratch.
09-30-24-windows-patch-server-2022
10-08-24-webdriver2

View File

@@ -30,7 +30,7 @@ mainBuildFilters: &mainBuildFilters
- /^release\/\d+\.\d+\.\d+$/
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- 'update-v8-snapshot-cache-on-develop'
- 'ryanm/fix/find-process'
- 'misc/use_webdriver'
- 'publish-binary'
# usually we don't build Mac app - it takes a long time
@@ -42,7 +42,7 @@ macWorkflowFilters: &darwin-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'ryanm/fix/find-process', << pipeline.git.branch >> ]
- equal: [ 'misc/use_webdriver', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -53,7 +53,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'ryanm/fix/find-process', << pipeline.git.branch >> ]
- equal: [ 'misc/use_webdriver', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -76,7 +76,7 @@ windowsWorkflowFilters: &windows-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'ryanm/fix/find-process', << pipeline.git.branch >> ]
- equal: [ 'misc/use_webdriver', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -85,7 +85,7 @@ executors:
# the Docker image with Cypress dependencies and Chrome browser
cy-doc:
docker:
- image: cypress/browsers-internal:node18.17.1-chrome128-ff130
- image: cypress/browsers-internal:node18.17.1-chrome128-ff131
# by default, we use "medium" to balance performance + CI costs. bump or reduce on a per-job basis if needed.
resource_class: medium
environment:
@@ -94,7 +94,7 @@ executors:
kitchensink-executor:
docker:
- image: cypress/browsers-internal:node20.15.0-chrome126-ff127
- image: cypress/browsers-internal:node20.15.0-chrome126-ff131
# by default, we use "medium" to balance performance + CI costs. bump or reduce on a per-job basis if needed.
resource_class: medium
environment:
@@ -104,7 +104,7 @@ executors:
# Docker image with non-root "node" user
non-root-docker-user:
docker:
- image: cypress/browsers-internal:node18.17.1-chrome128-ff130
- image: cypress/browsers-internal:node18.17.1-chrome128-ff131
user: node
environment:
PLATFORM: linux
@@ -152,7 +152,7 @@ commands:
name: Set environment variable to determine whether or not to persist artifacts
command: |
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "ryanm/fix/find-process" ]]; then
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "misc/use_webdriver" ]]; then
export SHOULD_PERSIST_ARTIFACTS=true
fi' >> "$BASH_ENV"
# You must run `setup_should_persist_artifacts` command and be using bash before running this command

View File

@@ -1,5 +1,5 @@
{
"chrome:beta": "130.0.6723.31",
"chrome:stable": "129.0.6668.100",
"chrome:beta": "130.0.6723.44",
"chrome:stable": "130.0.6723.58",
"chrome:minimum": "64.0.3282.0"
}

View File

@@ -20,6 +20,7 @@ _Released 10/1/2024 (PENDING)_
**Misc:**
- Cypress now consumes [geckodriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/index.html) to help automate the Firefox browser instead of [marionette-client](https://github.com/cypress-io/marionette-client). Addresses [#30217](https://github.com/cypress-io/cypress/issues/30217).
- Cypress now consumes [webdriver](https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver) to help automate the Firefox browser and [firefox-profile](https://github.com/saadtazi/firefox-profile-js) to create a firefox profile and convert it to Base64 to save user screen preferences via `xulstore.json`. Addresses [#30300](https://github.com/cypress-io/cypress/issues/30300) and [#30301](https://github.com/cypress-io/cypress/issues/30301).
- Pass spec information to protocol's `beforeSpec` to improve troubleshooting when reporting on errors. Addressed in [#30316](https://github.com/cypress-io/cypress/pull/30316).
**Dependency Updates:**

View File

@@ -245,6 +245,7 @@
"scripts"
],
"nohoist": [
"**/@wdio/*",
"**/webpack-preprocessor/babel-loader",
"**/webpack-preprocessor/**/merge-source-map",
"**/webpack-preprocessor/**/patch-package",
@@ -262,6 +263,7 @@
"**/@types/cheerio": "0.22.21",
"**/@types/enzyme": "3.10.5",
"**/@types/react": "16.9.50",
"**/@wdio/logger": "9.0.0",
"**/jquery": "3.4.1",
"**/pretty-format": "26.4.0",
"**/sharp": "0.29.3",

View File

@@ -181,7 +181,7 @@ export class DataContext {
@cached
get cloud () {
return new CloudDataSource({
fetch: (...args) => this.util.fetch(...args),
fetch: (...args: [RequestInfo | URL, (RequestInit | undefined)?]) => this.util.fetch(...args),
getUser: () => this.coreData.user,
logout: () => this.actions.auth.logout().catch(this.logTraceError),
invalidateClientUrqlCache: () => this.graphql.invalidateClientUrqlCache(this),

View File

@@ -3,7 +3,7 @@ import type { SocketShape } from '@packages/socket/lib/types'
import type { ClientOptions } from '@urql/core'
export const urqlFetchSocketAdapter = (io: SocketShape): ClientOptions['fetch'] => {
return (url, fetchOptions = {}) => {
return (url, fetchOptions: RequestInit = {}) => {
return new Promise<Response>((resolve, reject) => {
// Handle aborted requests
if (fetchOptions.signal) {

View File

@@ -1,24 +1,24 @@
import Debug from 'debug'
import { CdpAutomation } from './cdp_automation'
import { BrowserCriClient } from './browser-cri-client'
import type { Client as WebDriverClient } from 'webdriver'
import type { Automation } from '../automation'
import type { CypressError } from '@packages/errors'
import type { WebDriverClassic } from './webdriver-classic'
const debug = Debug('cypress:server:browsers:firefox-util')
let webDriverClassic: WebDriverClassic
let webdriverClient: WebDriverClient
async function connectToNewTabClassic () {
// Firefox keeps a blank tab open in versions of Firefox 123 and lower when the last tab is closed.
// For versions 124 and above, a new tab is not created, so @packages/extension creates one for us.
// Since the tab is always available on our behalf,
// we can connect to it here and navigate it to about:blank to set it up for CDP connection
const handles = await webDriverClassic.getWindowHandles()
const handles = await webdriverClient.getWindowHandles()
await webDriverClassic.switchToWindow(handles[0])
await webdriverClient.switchToWindow(handles[0])
await webDriverClassic.navigate('about:blank')
await webdriverClient.navigateTo('about:blank')
}
async function connectToNewSpec (options, automation: Automation, browserCriClient: BrowserCriClient) {
@@ -51,26 +51,29 @@ async function setupCDP (remotePort: number, automation: Automation, onError?: (
}
async function navigateToUrlClassic (url: string) {
await webDriverClassic.navigate(url)
await webdriverClient.navigateTo(url)
}
export default {
async setup ({
automation,
url,
webDriverClassic: wdcInstance,
foxdriverPort,
remotePort,
onError,
webdriverClient: wdInstance,
}: {
automation: Automation
url: string
webDriverClassic: WebDriverClassic
foxdriverPort: number
remotePort: number
onError?: (err: Error) => void
webdriverClient: WebDriverClient
}): Promise<BrowserCriClient> {
// set the WebDriver classic instance instantiated from geckodriver
webDriverClassic = wdcInstance
const browserCriClient = await setupCDP(remotePort, automation, onError)
webdriverClient = wdInstance
const [, browserCriClient] = await Promise.all([
this.setupFoxdriver(foxdriverPort),
setupCDP(remotePort, automation, onError),
])
await navigateToUrlClassic(url)

View File

@@ -1,4 +1,5 @@
import _ from 'lodash'
import EventEmitter from 'events'
import fs from 'fs-extra'
import Debug from 'debug'
import getPort from 'get-port'
@@ -18,12 +19,19 @@ import type { Automation } from '../automation'
import { getCtx } from '@packages/data-context'
import { getError } from '@packages/errors'
import type { BrowserLaunchOpts, BrowserNewTabOpts, RunModeVideoApi } from '@packages/types'
import { GeckoDriver } from './geckodriver'
import { WebDriverClassic } from './webdriver-classic'
import type { RemoteConfig } from 'webdriver'
import type { GeckodriverParameters } from 'geckodriver'
import { WebDriver } from './webdriver'
const debug = Debug('cypress:server:browsers:firefox')
const debugVerbose = Debug('cypress-verbose:server:browsers:firefox')
// These debug variables have an impact on the 3rd-party webdriver and geckodriver
// packages. To see verbose logs from Firefox, set both of these options to the
// DEBUG variable.
const WEBDRIVER_DEBUG_NAMESPACE_VERBOSE = 'cypress-verbose:server:browsers:webdriver'
const GECKODRIVER_DEBUG_NAMESPACE_VERBOSE = 'cypress-verbose:server:browsers:geckodriver'
// used to prevent the download prompt for the specified file types.
// this should cover most/all file types, but if it's necessary to
// discover more, open Firefox DevTools, download the file yourself
@@ -440,15 +448,29 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
const [
marionettePort,
geckoDriverPort,
webDriverBiDiPort,
] = await Promise.all([getPort(), getPort(), getPort(), getPort()])
] = await Promise.all([getPort(), getPort(), getPort()])
defaultLaunchOptions.preferences['marionette.port'] = marionettePort
// NOTE: we get the BiDi port and set it inside of geckodriver, but BiDi is not currently enabled (see remote.active-protocols above).
// this is so the BiDi websocket port does not get set to 0, which is the default for the geckodriver package.
debug('available ports: %o', { marionettePort, geckoDriverPort, webDriverBiDiPort })
debug('available ports: %o', { foxdriverPort, marionettePort, webDriverBiDiPort })
const profileDir = utils.getProfileDir(browser, options.isTextTerminal)
// Delete the legacy profile directory if in open mode.
// Cypress does this because profiles are sourced and created differently with geckodriver/webdriver.
// the profile creation method before 13.15.0 will no longer work with geckodriver/webdriver
// and actually corrupts the profile directory from being able to be encoded. Hence, we delete it to prevent any conflicts.
// This is critical to make sure different Cypress versions do not corrupt the firefox profile, which can fail silently.
if (!options.isTextTerminal) {
const doesPathExist = await fs.pathExists(profileDir)
if (doesPathExist) {
await fs.remove(profileDir)
}
}
const [
cacheDir,
@@ -467,23 +489,36 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
launchOptions.extensions = [extensionDest]
}
const profileDir = utils.getProfileDir(browser, options.isTextTerminal)
const profile = new FirefoxProfile({
destinationDirectory: profileDir,
})
// make sure the profile that is ported into the session is destroyed when the browser is closed
profile.shouldDeleteOnExit(true)
debug('firefox directories %o', { path: profile.path(), cacheDir, extensionDest })
launchOptions.preferences['browser.cache.disk.parent_directory'] = cacheDir
for (const pref in launchOptions.preferences) {
const value = launchOptions.preferences[pref]
const xulStorePath = path.join(profile.path(), 'xulstore.json')
profile.setPreference(pref, value)
// if user has set custom window.sizemode pref or it's the first time launching on this profile, write to xulStore.
if (!await fs.pathExists(xulStorePath)) {
// this causes the browser to launch maximized, which chrome does by default
// otherwise an arbitrary size will be picked for the window size
// this used to not have an effect after first launch in 'interactive' mode.
// However, since Cypress 13.15.1,
// geckodriver creates unique profile names that copy over the xulstore.json to the used profile.
// The copy is ultimately updated on the unique profile name and is destroyed when the browser is torn down,
// so the values are not persisted. Cypress could hypothetically determine the profile is in use, copy the xulstore.json
// out of the profile and try to persist it in the next created profile, but this method is likely error prone as it requires
// moving/copying of files while creation/deletion of profiles occur, plus the ability to correlate the correct profile to the current run,
// which there are not guarantees we can deterministically do this in open mode.
const sizemode = 'maximized'
await fs.writeJSON(xulStorePath, { 'chrome://browser/content/browser.xhtml': { 'main-window': { 'width': 1280, 'height': 1024, sizemode } } })
}
// TODO: fix this - synchronous FS operation
profile.updatePreferences()
launchOptions.preferences['browser.cache.disk.parent_directory'] = cacheDir
const userCSSPath = path.join(profileDir, 'chrome')
@@ -509,26 +544,26 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
}
// resolution of exactly 1280x720
// (height must account for firefox url bar, which we can only shrink to 1px ,
// and the total size of the window url and tab bar, which is 85 pixels for a total offset of 86 pixels)
// (height must account for firefox url bar, which we can only shrink to 2px)
const BROWSER_ENVS = {
MOZ_REMOTE_SETTINGS_DEVTOOLS: '1',
MOZ_HEADLESS_WIDTH: '1280',
MOZ_HEADLESS_HEIGHT: '806',
MOZ_HEADLESS_HEIGHT: '722',
...launchOptions.env,
}
debug('launching geckodriver with browser envs %o', BROWSER_ENVS)
// create the geckodriver process, which we will use WebDriver Classic to open the browser
const geckoDriverInstance = await GeckoDriver.create({
debug('launch in firefox', { url, args: launchOptions.args })
const geckoDriverOptions: GeckodriverParameters = {
host: '127.0.0.1',
port: geckoDriverPort,
// geckodriver port is assigned under the hood by @wdio/utils
// @see https://github.com/webdriverio/webdriverio/blob/v9.1.1/packages/wdio-utils/src/node/startWebDriver.ts#L65
marionetteHost: '127.0.0.1',
marionettePort,
webdriverBidiPort: webDriverBiDiPort,
profilePath: profile.path(),
binaryPath: browser.path,
websocketPort: webDriverBiDiPort,
profileRoot: profile.path(),
// To pass env variables into the firefox process, we CANNOT do it through capabilities when starting the browser.
// Since geckodriver spawns the firefox process, we can pass the env variables directly to geckodriver, which in turn will
// pass them to the firefox process
@@ -540,85 +575,119 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
...process.env,
},
},
})
const wdcInstance = new WebDriverClassic('127.0.0.1', geckoDriverPort)
debug('launch in firefox', { url, args: launchOptions.args })
const capabilitiesToSend = {
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
// @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
'moz:firefoxOptions': {
binary: browser.path,
args: launchOptions.args,
prefs: launchOptions.preferences,
},
// @see https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html#moz-debuggeraddress
// we specify the debugger address option for Webdriver, which will return us the CDP address when the capability is returned.
'moz:debuggerAddress': true,
},
},
jsdebugger: Debug.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) || false,
log: Debug.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) ? 'debug' : 'error',
logNoTruncate: Debug.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE),
}
// since we no longer directly control the browser with webdriver, we need to make the browserInstance
// a simulated wrapper that kills the process IDs that come back from webdriver
// @ts-expect-error
let browserInstanceWrapper: BrowserInstance = new EventEmitter()
browserInstanceWrapper.kill = () => undefined
try {
debugVerbose(`creating session with capabilities %s`, JSON.stringify(capabilitiesToSend.capabilities))
/**
* To set the profile, we use the profile capabilities in firefoxOptions which
* requires the profile to be base64 encoded. The profile will be copied over to whatever
* profile is created by geckodriver stemming from the root profile path.
*
* For example, if the profileRoot in geckodriver is /usr/foo/firefox-stable/run-12345, the new webdriver session
* will take the base64 encoded profile contents we created in /usr/foo/firefox-stable/run-12345/* (via firefox-profile npm package) and
* copy it to a profile created in the profile root, which would look something like /usr/foo/firefox-stable/run-12345/rust_mozprofile<HASH>/*
* @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
*/
const base64EncodedProfile = await new Promise<string>((resolve, reject) => {
profile.encoded(function (err, encodedProfile) {
err ? reject(err) : resolve(encodedProfile)
})
})
const newSessionCapabilities: RemoteConfig = {
logLevel: Debug.enabled(WEBDRIVER_DEBUG_NAMESPACE_VERBOSE) ? 'info' : 'silent',
capabilities: {
alwaysMatch: {
browserName: 'firefox',
acceptInsecureCerts: true,
// @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
'moz:firefoxOptions': {
profile: base64EncodedProfile,
binary: browser.path,
args: launchOptions.args,
prefs: launchOptions.preferences,
},
// @see https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html#moz-debuggeraddress
// we specify the debugger address option for Webdriver, which will return us the CDP address when the capability is returned.
// @ts-expect-error
'moz:debuggerAddress': true,
// @see https://webdriver.io/docs/capabilities/#wdiogeckodriveroptions
// webdriver starts geckodriver with the correct options on behalf of Cypress
'wdio:geckodriverOptions': geckoDriverOptions,
},
firstMatch: [],
},
}
debugVerbose(`creating session with capabilities %s`, JSON.stringify(newSessionCapabilities.capabilities))
const WD = WebDriver.getWebDriverPackage()
// this command starts the webdriver session and actually opens the browser
const { capabilities } = await wdcInstance.createSession(capabilitiesToSend)
// to debug geckodriver, set the DEBUG=cypress-verbose:server:browsers:geckodriver (debugs a third-party patched package geckodriver to enable console output)
// to debug webdriver, set the DEBUG=cypress-verbose:server:browsers:webdriver (debugs a third-party patched package webdriver to enable console output)
// @see ./firefox_automation.md
const webdriverClient = await WD.newSession(newSessionCapabilities)
debugVerbose(`received capabilities %o`, capabilities)
debugVerbose(`received capabilities %o`, webdriverClient.capabilities)
const cdpPort = parseInt(new URL(`ws://${capabilities['moz:debuggerAddress']}`).port)
const cdpPort = parseInt(new URL(`ws://${webdriverClient.capabilities['moz:debuggerAddress']}`).port)
debug(`CDP running on port ${cdpPort}`)
const browserPID = capabilities['moz:processID']
const browserPID: number = webdriverClient.capabilities['moz:processID']
debug(`firefox running on pid: ${browserPID}`)
// makes it so get getRemoteDebuggingPort() is calculated correctly
process.env.CYPRESS_REMOTE_DEBUGGING_PORT = cdpPort.toString()
const driverPID: number = webdriverClient.capabilities['wdio:driverPID'] as number
// maximize the window if running headful and no width or height args are provided.
// NOTE: We used to do this with xulstore.json, but this is no longer possible with geckodriver
// as firefox will create the profile under the profile root that we cannot control and we cannot consistently provide
// a base 64 encoded profile.
if (!browser.isHeadless && (!launchOptions.args.includes('-width') || !launchOptions.args.includes('-height'))) {
await wdcInstance.maximizeWindow()
}
debug(`webdriver running on pid: ${driverPID}`)
// install the browser extensions
await Promise.all(_.map(launchOptions.extensions, (path) => {
debug(`installing extension at path: ${path}`)
return wdcInstance!.installAddOn({
path,
temporary: true,
})
}))
debug('setting up firefox utils')
browserCriClient = await firefoxUtil.setup({ automation, url, webDriverClassic: wdcInstance, remotePort: cdpPort, onError: options.onError })
// monkey-patch the .kill method to that the CDP connection is closed
const originalGeckoDriverKill = geckoDriverInstance.kill
geckoDriverInstance.kill = (...args) => {
// now that we have the driverPID and browser PID
browserInstanceWrapper.kill = (...args) => {
// Do nothing on failure here since we're shutting down anyway
clearInstanceState({ gracefulShutdown: true })
debug('closing firefox')
process.kill(browserPID)
const browserReturnStatus = process.kill(browserPID)
debug('closing geckodriver')
debug('closing geckodriver and webdriver')
const driverReturnStatus = process.kill(driverPID)
return originalGeckoDriverKill.apply(geckoDriverInstance, args)
// needed for closing the browser when switching browsers in open mode to signal
// the browser is done closing
browserInstanceWrapper.emit('exit')
return browserReturnStatus || driverReturnStatus
}
// makes it so get getRemoteDebuggingPort() is calculated correctly
process.env.CYPRESS_REMOTE_DEBUGGING_PORT = cdpPort.toString()
// install the browser extensions
await Promise.all(_.map(launchOptions.extensions, async (path) => {
debug(`installing extension at path: ${path}`)
const id = await webdriverClient.installAddOn(path, true)
debug(`extension with id ${id} installed!`)
return
}))
debug('setting up firefox utils')
browserCriClient = await firefoxUtil.setup({ automation, url, foxdriverPort, webdriverClient, remotePort: cdpPort, onError: options.onError })
await utils.executeAfterBrowserLaunch(browser, {
webSocketDebuggerUrl: browserCriClient.getWebSocketDebuggerUrl(),
})
@@ -626,7 +695,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
errors.throwErr('FIREFOX_COULD_NOT_CONNECT', err)
}
return geckoDriverInstance
return browserInstanceWrapper
}
export async function closeExtraTargets () {

View File

@@ -1,35 +1,39 @@
# GeckoDriver
# Firefox Automation
## Purpose
## GeckoDriver / WebDriver Classic
Cypress uses [GeckoDriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/index.html) to drive [classic WebDriver](https://w3c.github.io/webdriver.) methods, as well as interface with Firefox's [Marionette Protocol](https://firefox-source-docs.mozilla.org/testing/marionette/Intro.html). This is necessary to automate the Firefox browser in the following cases:
### Purpose
* Navigating to the current/next spec URL via [WebDriver Classic](https://w3c.github.io/webdriver.).
Cypress uses [GeckoDriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/index.html) to drive [classic WebDriver](https://w3c.github.io/webdriver) methods, as well as interface with Firefox's [Marionette Protocol](https://firefox-source-docs.mozilla.org/testing/marionette/Intro.html). This is necessary to automate the Firefox browser in the following cases:
* Navigating to the current/next spec URL via [WebDriver Classic](https://w3c.github.io/webdriver).
* Installing the [Cypress web extension](https://github.com/cypress-io/cypress/tree/develop/packages/extension) via the [Marionette Protocol](https://firefox-source-docs.mozilla.org/testing/marionette/Intro.html), which is critical to automating Firefox.
The [WebDriver](https://w3c.github.io/webdriver) methods used come from the [webdriver](https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver) package to drive WebDriver (and in the future, WebDriver BiDi), which also starts the [geckodriver](https://github.com/webdriverio-community/node-geckodriver#readme) for us using the [`wdio:geckodriverOptions`](https://webdriver.io/docs/capabilities/#wdiogeckodriveroptions) capability.
Currently, [Chrome Devtools Protocol](https://chromedevtools.github.io/devtools-protocol/) automates most of our browser interactions with Firefox. However, [CDP will be removed towards the end of 2024](https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/) now that [WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) is fully supported in Firefox 130 and up. [GeckoDriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/index.html) will be the entry point in which Cypress implements [WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) for Firefox.
## Historical Context
### Historical Context
Previously, Cypress was using an older package called the [marionette-client](https://github.com/cypress-io/marionette-client), which is near identical to the [mozilla b2g marionette client](https://github.com/mozilla-b2g/gaia/tree/master/tests/jsmarionette/client/marionette-client/lib/marionette). The b2g client hasn't had active development since 2014 and there have been changes to Marionette's server implementation since then. This means the [marionette-client](https://github.com/cypress-io/marionette-client) could break at any time, hence why we have migrated away from it. See [Cypress' migration to WebDriver BiDi within Firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=1604723) bugzilla ticket for more details.
## Implementation
### Implementation
To consume [`GeckoDriver`](https://firefox-source-docs.mozilla.org/testing/geckodriver/index.html), Cypress installs the [`geckodriver`](https://github.com/webdriverio-community/node-geckodriver#readme) package, a lightweight wrapper around the [geckodriver binary](https://github.com/mozilla/geckodriver), to connect to the Firefox browser. Once connected, `GeckoDriver` is able to send `WebDriver` commands, as well as `Marionette` commands, to the Firefox browser. It is also capable of creating a `WebDriver BiDi` session to send `WebDriver BiDi` commands and receive `WebDriver BiDi` events.
It is worth noting that Cypress patches the [`geckodriver`](https://github.com/webdriverio-community/node-geckodriver#readme) package for a few reasons:
* To coincide with our debug logs in order to not print extraneous messages to the console that could disrupt end user experience as well as impact our system tests.
It is worth noting that Cypress patches the [`geckodriver`](https://github.com/webdriverio-community/node-geckodriver#readme) and [`webdriver`](https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver) packages for a few reasons:
* To coincide with our debug logs in order to not print extraneous messages to the console that could disrupt end user experience as well as impact our system tests. These can be turned on by setting the `DEBUG` variable to `cypress-verbose:server:browsers:geckodriver` or `cypress-verbose:server:browsers:webdriver` depending on what needs to be debugged
* Patch top-level awaits to correctly build the app.
* Pass process spawning arguments to the geckodriver process (which is a child process of the main process) in order to set `MOZ_` prefixed environment variables in the browser. These cannot be set through the [env capability](https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions#env_object) and must be done through `geckodriver` as `geckodriver` spawns the `firefox` process. Please see [this comment](https://bugzilla.mozilla.org/show_bug.cgi?id=1604723#c20) for more details.
Currently, since the use of WebDriver Classic is so miniscule, the methods are implemented using direct fetch calls inside the [WebDriver Classic Class](../webdriver-classic/index.ts). It's important to note that, unlike Chrome, Firefox is launched via the WebDriver [newSession command](https://w3c.github.io/webdriver.#new-session) As BiDi development occurs and to reduce maintenance cost, using an already implemented client like [`webdriver`](https://www.npmjs.com/package/webdriver) will be explored.
Currently, the use of WebDriver Classic is small. To prepare for the implementation of WebDriver BiDi and reduce the need for maintenance code, the methods are implemented via the [webdriver](https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver) package. It's important to note that, unlike Chrome, Firefox is launched via the WebDriver [newSession command](https://w3c.github.io/webdriver#new-session) (via `webdriver` [package](https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver#webdriver-example)).
Since we patch the [`geckodriver`](https://github.com/webdriverio-community/node-geckodriver#readme) package, we [`nohoist`](https://classic.yarnpkg.com/blog/2018/02/15/nohoist/) the dependency. This mostly works with sub-dependencies, but one of the dependencies, `pump@^3.0.0` (from `geckodriver` -> `tar-fs` -> `pump`) is missing from the binary. To workaround this, we install `pump` in `@packages/server` and `nohoist` the dependency so it is available in the binary as a production dependency.
Since we patch the [`geckodriver`](https://github.com/webdriverio-community/node-geckodriver#readme) and the related [`webdriver`](https://github.com/webdriverio/webdriverio/tree/main/packages) packages, we [`nohoist`](https://classic.yarnpkg.com/blog/2018/02/15/nohoist/) the dependencies. We keep all dependencies related to `webdriver` in the packherd `binary-cleanup` file. One of these preserved dependencies, `edgedriver@5.6.1` has the same top-level await that `geckodriver` uses, so we need to patch it. We do not consume this package directly. This is just to make sure the binary can build.
## Debugging
To help debug `geckodriver` and `firefox`, the `DEBUG=cypress-verbose:server:browsers:geckodriver` can be used when launching `cypress`. This will
To help debug `geckodriver` and `firefox`, the `DEBUG=cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver` can be used when launching `cypress`. This will
* Enable full `stdout` and `stderr` for `geckodriver` (including startup time) and the `firefox` process. The logs will **NOT** be truncated so they may get quite long.
* Enables the debugger terminal, which will additionally print browser information to the debugger terminal process.
* enables `webdriver` debug logs for all commands and events.
If you are having trouble running firefox, turning on this debug option can be quite useful.

View File

@@ -1,122 +0,0 @@
import Bluebird from 'bluebird'
import debugModule from 'debug'
import errors from '@packages/errors'
import type { ChildProcess } from 'child_process'
const geckoDriverPackageName = 'geckodriver'
const GECKODRIVER_DEBUG_NAMESPACE = 'cypress:server:browsers:geckodriver'
const GECKODRIVER_DEBUG_NAMESPACE_VERBOSE = 'cypress-verbose:server:browsers:geckodriver'
const debug = debugModule(GECKODRIVER_DEBUG_NAMESPACE)
const debugVerbose = debugModule(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE)
type SpawnOpt = { [key: string]: string | string[] | SpawnOpt }
export type StartGeckoDriverArgs = {
host: string
port: number
marionetteHost: string
marionettePort: number
webdriverBidiPort: number
profilePath: string
binaryPath: string
spawnOpts?: SpawnOpt
}
/**
* Class with static methods that serve as a wrapper around GeckoDriver
*/
export class GeckoDriver {
// We resolve this package in such a way that packherd can discover it, meaning we are re-declaring the types here to get typings support =(
// the only reason a static method is used here is so we can stub the class method more easily while under unit-test
private static getGeckoDriverPackage: () => {
start: (args: {
allowHosts?: string[]
allowOrigins?: string[]
binary?: string
connectExisting?: boolean
host?: string
jsdebugger?: boolean
log?: 'fatal' | 'error' | 'warn' | 'info' | 'config' | 'debug' | 'trace'
logNoTruncate?: boolean
marionetteHost?: string
marionettePort?: number
port?: number
websocketPort?: number
profileRoot?: string
geckoDriverVersion?: string
customGeckoDriverPath?: string
cacheDir?: string
spawnOpts: SpawnOpt
}) => Promise<ChildProcess>
download: (geckodriverVersion?: string, cacheDir?: string) => Promise<string>
} = () => {
/**
* NOTE: geckodriver is an ESM package and does not play well with mksnapshot.
* Requiring the package in this way, dynamically, will
* make it undiscoverable by mksnapshot
*/
return require(require.resolve(geckoDriverPackageName, { paths: [__dirname] }))
}
/**
* creates an instance of the GeckoDriver server. This is needed to start WebDriver
* @param {StartGeckoDriverArgs} opts - arguments needed to start GeckoDriver
* @returns {ChildProcess} - the child process in which the geckodriver is running
*/
static async create (opts: StartGeckoDriverArgs, timeout: number = 5000): Promise<ChildProcess> {
debug('no geckodriver instance exists. starting geckodriver...')
let geckoDriverChildProcess: ChildProcess | null = null
try {
const { start: startGeckoDriver } = GeckoDriver.getGeckoDriverPackage()
geckoDriverChildProcess = await startGeckoDriver({
host: opts.host,
port: opts.port,
marionetteHost: opts.marionetteHost,
marionettePort: opts.marionettePort,
websocketPort: opts.webdriverBidiPort,
profileRoot: opts.profilePath,
binary: opts.binaryPath,
jsdebugger: debugModule.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) || false,
log: debugModule.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) ? 'debug' : 'error',
logNoTruncate: debugModule.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE),
spawnOpts: opts.spawnOpts || {},
})
// using a require statement to make this easier to test with mocha/mockery
const waitPort = require('wait-port')
await Bluebird.resolve(waitPort({
port: opts.port,
// add 1 second to the timeout so the timeout throws first if the limit is reached
timeout: timeout + 1000,
output: debugModule.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) ? 'dots' : 'silent',
})).timeout(timeout)
debug('geckodriver started!')
// For whatever reason, we NEED to bind to stderr/stdout in order
// for the geckodriver process not to hang, even though the event effectively
// isn't doing anything without debug logs enabled.
geckoDriverChildProcess.stdout?.on('data', (buf) => {
debugVerbose('firefox stdout: %s', String(buf).trim())
})
geckoDriverChildProcess.stderr?.on('data', (buf) => {
debugVerbose('firefox stderr: %s', String(buf).trim())
})
geckoDriverChildProcess.on('exit', (code, signal) => {
debugVerbose('firefox exited: %o', { code, signal })
})
return geckoDriverChildProcess
} catch (err) {
geckoDriverChildProcess?.kill()
debug(`geckodriver failed to start from 'geckodriver:start' for reason: ${err}`)
throw errors.get('FIREFOX_GECKODRIVER_FAILURE', 'geckodriver:start', err)
}
}
}

View File

@@ -1,263 +0,0 @@
import debugModule from 'debug'
// using cross fetch to make unit testing easier to mock
import crossFetch from 'cross-fetch'
const debug = debugModule('cypress:server:browsers:webdriver')
type InstallAddOnArgs = {
path: string
temporary: boolean
}
namespace WebDriver {
export namespace Session {
export type NewResult = {
capabilities: {
acceptInsecureCerts: boolean
browserName: string
browserVersion: string
platformName: string
pageLoadStrategy: 'normal'
strictFileInteractability: boolean
timeouts: {
implicit: number
pageLoad: number
script: number
}
'moz:accessibilityChecks': boolean
'moz:buildID': string
'moz:geckodriverVersion': string
'moz:debuggerAddress': string
'moz:headless': boolean
'moz:platformVersion': string
'moz:processID': number
'moz:profile': string
'moz:shutdownTimeout': number
'moz:webdriverClick': boolean
'moz:windowless': boolean
unhandledPromptBehavior: string
userAgent: string
sessionId: string
}
}
}
}
export class WebDriverClassic {
#host: string
#port: number
private sessionId: string = ''
constructor (host: string, port: number) {
this.#host = host
this.#port = port
}
/**
* Creates a new WebDriver Session through GeckoDriver. Capabilities are predetermined
* @see https://w3c.github.io/webdriver.#new-session
* @returns {Promise<WebDriver.Session.NewResult>} - the results of the Webdriver Session (enabled through remote.active-protocols)
*/
async createSession (args: {
capabilities: {[key: string]: any}
}): Promise<WebDriver.Session.NewResult> {
const getSessionUrl = `http://${this.#host}:${this.#port}/session`
const body = {
capabilities: args.capabilities,
}
try {
const createSessionResp = await crossFetch(getSessionUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
if (!createSessionResp.ok) {
const error = new Error(`${createSessionResp.status}: ${createSessionResp.statusText}.`)
try {
const resp = await createSessionResp.json()
error.message = `${error.message } ${resp.value.error}. ${resp.value.message}.`
} finally {
// if for some reason we can't parse the response, continue to throw with some information.
throw error
}
}
const createSessionRespBody = await createSessionResp.json()
this.sessionId = createSessionRespBody.value.sessionId
return createSessionRespBody.value
} catch (e) {
debug(`unable to create new Webdriver session: ${e}`)
throw e
}
}
/**
* Gets available windows handles in the browser. The order in which the window handles are returned is arbitrary.
* @see https://w3c.github.io/webdriver.#get-window-handles
*
* @returns {Promise<string[]>} All the available top-level contexts/handles
*/
async getWindowHandles (): Promise<string[]> {
const getWindowHandles = `http://${this.#host}:${this.#port}/session/${this.sessionId}/window/handles`
try {
const getWindowHandlesResp = await crossFetch(getWindowHandles)
if (!getWindowHandlesResp.ok) {
throw new Error(`${getWindowHandlesResp.status}: ${getWindowHandlesResp.statusText}`)
}
const getWindowHandlesRespBody = await getWindowHandlesResp.json()
return getWindowHandlesRespBody.value
} catch (e) {
debug(`unable to get classic webdriver window handles: ${e}`)
throw e
}
}
/**
* Switching windows will select the session's current top-level browsing context as the target for all subsequent commands.
* In a tabbed browser, this will typically make the tab containing the browsing context the selected tab.
* @see https://w3c.github.io/webdriver.#dfn-switch-to-window
*
* @param {string} handle - the context ID of the window handle
* @returns {Promise<null>}
*/
async switchToWindow (handle: string): Promise<null> {
const switchToWindowUrl = `http://${this.#host}:${this.#port}/session/${this.sessionId}/window`
const body = {
handle,
}
try {
const switchToWindowResp = await crossFetch(switchToWindowUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
if (!switchToWindowResp.ok) {
throw new Error(`${switchToWindowResp.status}: ${switchToWindowResp.statusText}`)
}
const switchToWindowRespBody = await switchToWindowResp.json()
return switchToWindowRespBody.value
} catch (e) {
debug(`unable to switch to window via classic webdriver : ${e}`)
throw e
}
}
/**
* maximizes the current window
* @see https://w3c.github.io/webdriver.#maximize-window
*
* @returns {Promise<null>}
*/
async maximizeWindow (): Promise<null> {
const maximizeWindowUrl = `http://${this.#host}:${this.#port}/session/${this.sessionId}/window/maximize`
try {
const maximizeWindowResp = await crossFetch(maximizeWindowUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
if (!maximizeWindowResp.ok) {
throw new Error(`${maximizeWindowResp.status}: ${maximizeWindowResp.statusText}`)
}
const maximizeWindowRespBody = await maximizeWindowResp.json()
return maximizeWindowRespBody.value
} catch (e) {
debug(`unable to maximize window via classic webdriver : ${e}`)
throw e
}
}
/**
* causes the user agent to navigate the session's current top-level browsing context to a new location.
* @see https://w3c.github.io/webdriver.#navigate-to
*
* @param url - the url of where the context handle is navigating to
* @returns {Promise<null>}
*/
async navigate (url: string): Promise<null> {
const navigateUrl = `http://${this.#host}:${this.#port}/session/${this.sessionId}/url`
const body = {
url,
}
try {
const navigateResp = await crossFetch(navigateUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
if (!navigateResp.ok) {
throw new Error(`${navigateResp.status}: ${navigateResp.statusText}`)
}
const navigateRespBody = await navigateResp.json()
return navigateRespBody.value
} catch (e) {
debug(`unable to navigate via classic webdriver : ${e}`)
throw e
}
}
/**
* Installs a web extension on the given WebDriver session
* @see https://searchfox.org/mozilla-central/rev/cc01f11adfacca9cd44a75fd140d2fdd8f9a48d4/testing/geckodriver/src/command.rs#33-36
* @param {InstallAddOnArgs} opts - options needed to install a web extension.
*/
async installAddOn (opts: InstallAddOnArgs) {
const body = {
path: opts.path,
temporary: opts.temporary,
}
// If the webdriver session is created, we can now install our extension through geckodriver
const url = `http://${this.#host}:${this.#port}/session/${this.sessionId}/moz/addon/install`
try {
const resp = await crossFetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
if (!resp.ok) {
throw new Error(`${resp.status}: ${resp.statusText}`)
}
} catch (e) {
debug(`unable to install extension: ${e}`)
throw e
}
}
}

View File

@@ -0,0 +1,15 @@
import type WebDriverPackage from 'webdriver'
const webDriverPackageName = 'webdriver'
export class WebDriver {
// We resolve this package in such a way to packherd can discover it.
static getWebDriverPackage: () => typeof WebDriverPackage = () => {
/**
* NOTE: webdriver is an ESM package and does not play well with mksnapshot.
* Requiring the package in this way, dynamically, will
* make it undiscoverable by mksnapshot
*/
return require(require.resolve(webDriverPackageName, { paths: [__dirname] }))
}
}

View File

@@ -60,6 +60,7 @@
"debug": "^4.3.4",
"dirt-simple-file-cache": "^0.4.0",
"duplexify": "4.1.2",
"edgedriver": "5.6.1",
"electron-context-menu": "3.6.1",
"errorhandler": "1.5.1",
"evil-dns": "0.2.0",
@@ -67,10 +68,10 @@
"express": "4.21.0",
"fetch-retry-ts": "^1.3.1",
"find-process": "1.4.7",
"firefox-profile": "4.6.0",
"firefox-profile": "4.7.0",
"fluent-ffmpeg": "2.1.2",
"fs-extra": "9.1.0",
"geckodriver": "4.4.2",
"geckodriver": "4.5.1",
"get-port": "5.1.1",
"getos": "3.2.1",
"glob": "7.1.3",
@@ -106,7 +107,6 @@
"pidusage": "3.0.2",
"pluralize": "8.0.0",
"pretty-bytes": "^5.6.0",
"pump": "^3.0.2",
"randomstring": "1.3.0",
"recast": "0.20.4",
"resolve": "1.17.0",
@@ -130,6 +130,7 @@
"url-parse": "1.5.10",
"uuid": "8.3.2",
"wait-port": "1.1.0",
"webdriver": "9.0.0",
"webpack-virtual-modules": "0.5.0",
"widest-line": "3.1.0"
},
@@ -215,10 +216,11 @@
"workspaces": {
"nohoist": [
"devtools-protocol",
"edgedriver",
"geckodriver",
"http-proxy",
"pump",
"tsconfig-paths"
"tsconfig-paths",
"webdriver"
]
},
"optionalDependencies": {

View File

@@ -0,0 +1,79 @@
diff --git a/node_modules/@wdio/protocols/README.md b/node_modules/@wdio/protocols/README.md
deleted file mode 100644
index eea42bf..0000000
--- a/node_modules/@wdio/protocols/README.md
+++ /dev/null
@@ -1,55 +0,0 @@
-WebdriverIO Protocol Helper
-===========================
-
-This package stores the definition for various automation protocols such as [WebDriver](https://w3c.github.io/webdriver/) or vendor specific protocol extensions like for [SauceLabs](https://saucelabs.com/). Unless you are interested in generating a WebDriver client there should be no reason why you should need this package. This package holds the definition of the following protocols:
-
-- [WebDriver](https://w3c.github.io/webdriver/)
-- [JSON Wire Protocol](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol)
-- [Appium](http://appium.io/)
-- [Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md)
-- [Sauce Labs](https://saucelabs.com/)
-- Chrome (WebDriver extension when running Chromedriver)
-- Selenium (when running Selenium Standalone Server)
-
-## Install
-
-To install the package, run:
-
-```sh
-npm install @wdio/protocols
-```
-
-## Usage
-
-You can get data by importing the package as follows:
-
-```js
-import { WebDriverProtocol, MJsonWProtocol, AppiumProtocol, ChromiumProtocol, SauceLabsProtocol, SeleniumProtocol } from '@wdio/protocols'
-
-/**
- * get description of session command
- */
-console.log(WebDriverProtocol['/session'].POST.description)
-```
-
-## TypeScript Interfaces
-
-The package exposes TypeScript interfaces for all protocols. You can use them for your own project as follows:
-
-```ts
-import type { WebDriverCommands } from '@wdio/protocol'
-
-import { WebDriverCommands, WebDriverCommandsAsync } from './src'
-
-const browser = {} as WebDriverCommands
-browser.sendAlertText(true)
-// fails with "Argument of type 'boolean' is not assignable to parameter of type 'string'.ts(2345)"
-
-const asyncBrowser = {} as WebDriverCommandsAsync
-const a = await asyncBrowser.getTitle()
-type foo = typeof a // string
-```
-
-----
-
-For more information on WebdriverIO see the [homepage](https://webdriver.io).
diff --git a/node_modules/@wdio/protocols/build/index.js b/node_modules/@wdio/protocols/build/index.js
index 05e55cc..9ea546e 100644
--- a/node_modules/@wdio/protocols/build/index.js
+++ b/node_modules/@wdio/protocols/build/index.js
@@ -5165,9 +5165,11 @@ var gecko_default = {
],
parameters: [
{
- name: "addon",
+ // cypress uses 'path' over 'addon' to avoid zipping the extension,
+ // which is easier for cypress to encorporate and doesn't break users
+ name: "path",
type: "string",
- description: "base64 string of the add on file",
+ description: "path to the extension",
required: true
},
{

View File

@@ -0,0 +1,31 @@
diff --git a/node_modules/@wdio/utils/README.md b/node_modules/@wdio/utils/README.md
deleted file mode 100644
index 3e8048a..0000000
--- a/node_modules/@wdio/utils/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-WDIO Repl
-=========
-
-> A WDIO helper utility to provide a repl interface WebdriverIO
diff --git a/node_modules/@wdio/utils/build/index.js b/node_modules/@wdio/utils/build/index.js
index 1540cb6..07bd02e 100644
--- a/node_modules/@wdio/utils/build/index.js
+++ b/node_modules/@wdio/utils/build/index.js
@@ -571,6 +571,7 @@ import cp2 from "node:child_process";
import getPort from "get-port";
import waitPort from "wait-port";
import logger3 from "@wdio/logger";
+import debugModule from 'debug'
import split2 from "split2";
import { deepmerge } from "deepmerge-ts";
import { start as startSafaridriver } from "safaridriver";
@@ -681,6 +682,8 @@ var init_startWebDriver = __esm({
init_utils();
init_constants();
log2 = logger3("@wdio/utils");
+ // wrap in cypress debugger statement to avoid extraneous messages to the console
+ log2.setLevel(debugModule.enabled('cypress-verbose:server:browsers:webdriver') ? 'info' : 'silent')
DRIVER_WAIT_TIMEOUT = 10 * 1e3;
}
});

View File

@@ -0,0 +1,240 @@
diff --git a/node_modules/edgedriver/README.md b/node_modules/edgedriver/README.md
deleted file mode 100644
index 3b361a7..0000000
--- a/node_modules/edgedriver/README.md
+++ /dev/null
@@ -1,218 +0,0 @@
-EdgeDriver [![CI](https://github.com/webdriverio-community/node-edgedriver/actions/workflows/ci.yml/badge.svg)](https://github.com/webdriverio-community/node-edgedriver/actions/workflows/ci.yml) [![Audit](https://github.com/webdriverio-community/node-edgedriver/actions/workflows/audit.yml/badge.svg)](https://github.com/webdriverio-community/node-edgedriver/actions/workflows/audit.yml)
-==========
-
-An NPM wrapper for Microsofts' [EdgeDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/). It manages to download various (or the latest) Edgedriver versions and provides a programmatic interface to start and stop it within Node.js. __Note:__ this is a wrapper module. If you discover any bugs with EdgeDriver, please report them in the [official repository](https://github.com/MicrosoftEdge/EdgeWebDriver).
-
-# Installing
-
-You can install this package via:
-
-```sh
-npm install edgedriver
-```
-
-Once installed you can start Edgedriver via:
-
-```sh
-npx edgedriver --port=4444
-```
-
-By default, this package downloads Edgedriver when used for the first time through the CLI or the programmatical interface. If you like to download it as part of the NPM install process, set the `EDGEDRIVER_AUTO_INSTALL` environment flag, e.g.:
-
-```sh
-EDGEDRIVER_AUTO_INSTALL=1 npm i
-```
-
-To get a list of available CLI options run `npx edgedriver --help`. By default this package tries to find the Mircosoft Edge version installed on a given system. If you prefer to have it install a custom EdgeDriver version you can define the environment variable `EDGEDRIVER_VERSION` when running in CLI, e.g.:
-
-```sh
-$ npm i edgedriver
-$ EDGEDRIVER_VERSION=105.0.1343.33 npx edgedriver --version
-Microsoft Edge WebDriver 105.0.1343.33 (4122bb4646b33f33bca5d269490b9caadfc452b2)
-```
-
-# Programmatic Interface
-
-You can import this package with Node.js and start the driver as part of your script and use it e.g. with [WebdriverIO](https://webdriver.io).
-
-## Exported Methods
-
-The package exports a `start`, `findEdgePath` and `download` method.
-
-### `start`
-
-Starts an EdgeDriver instance and returns a [`ChildProcess`](https://nodejs.org/api/child_process.html#class-childprocess). If EdgeDriver is not downloaded it will download it for you.
-
-__Params:__ `EdgedriverParameters` - options to pass into EdgeDriver (see below)
-
-__Example:__
-
-```js
-import { start } from 'edgedriver';
-import { remote } from 'webdriverio';
-import waitPort from 'wait-port';
-
-/**
- * first start EdgeDriver
- */
-const cp = await start({ port: 4444 });
-
-/**
- * wait for EdgeDriver to be up
- */
-await waitPort({ port: 4444 });
-
-/**
- * then start WebdriverIO session
- */
-const browser = await remote({ capabilities: { browserName: 'msedge' } });
-await browser.url('https://webdriver.io');
-console.log(await browser.getTitle()); // prints "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO"
-
-/**
- * kill Edgedriver process
- */
-cp.kill();
-```
-
-__Note:__ as you can see in the example above this package does not wait for the driver to be up, you have to manage this yourself through packages like [`wait-on`](https://github.com/jeffbski/wait-on).
-
-### `download`
-
-Method to download an EdgeDriver with a particular version. If version parameter is omitted it tries to detect the version based on the Edge browser installed on the system.
-
-__Params:__ `string` - version of Edgedriver to download (optional)
-__Returns:__ `string` - path to Edgedriver binary
-
-### `findEdgePath`
-
-The `findEdgePath` is a helper method to find the Microsoft Egde binary on given system. If there is a `EDGE_BINARY_PATH` environment set, it will return that value.
-
-__Returns:__ `string` - path to Microsoft Edge binary
-
-## CJS Support
-
-In case your module uses CJS you can use this package as follows:
-
-```js
-const { start } = require('edgedriver')
-// see example above
-```
-
-## Custom CDN URL
-
-This allows you to use your own endpoints for downloading Edgedriver binaries. It is useful in air gapped scenarios or if you have download restrictions, such as firewalls. You can either set an environment variable:
-
-```sh
-$ npm i edgedriver
-$ EDGEDRIVER_CDNURL=https://msedgedriver.azureedge.net npx edgedriver --version
-```
-
-or create a [`.npmrc`](https://docs.npmjs.com/cli/configuring-npm/npmrc) with the following content:
-
-```toml
-edgedriver_cdnurl=https://msedgedriver.azureedge.net
-```
-
-## Options
-
-The `start` method offers the following options to be passed on to the actual Edgedriver CLI.
-
-### edgeDriverVersion
-
-The version of EdgeDriver to start. See [Egdedriver directory list](https://msedgewebdriverstorage.z22.web.core.windows.net/) for all available versions, platforms and architecture.
-
-Type: `number`<br />
-Default: `latest`
-
-### port
-The port on which the driver should run.
-
-Example: `9515`<br >
-Type: `number`
-
-### adbPort
-The port on which the ADB driver should run.
-
-Example: `9515`<br >
-Type: `number`
-
-### baseUrl
-Base URL path prefix for commands, e.g. `wd/url`.
-
-Example: `/`
-
-Type: `string`
-
-### logPath
-Write server log to file instead of stderr, increases log level to `INFO`
-
-Type: `string`
-
-### logLevel
-Set log level. Possible options `ALL`, `DEBUG`, `INFO`, `WARNING`, `SEVERE`, `OFF`.
-
-Type: `string`
-
-### verbose
-Log verbosely (equivalent to `--log-level=ALL`)
-
-Type: `boolean`
-
-### silent
-Log nothing (equivalent to `--log-level=OFF`)
-
-Type: `boolean`
-
-### appendLog
-Append log file instead of rewriting.
-
-Type: `boolean`
-
-### replayable
-Log verbosely and don't truncate long strings so that the log can be replayed (experimental).
-
-Type: `boolean`
-
-### readableTimestamp
-Add readable timestamps to log.
-
-Type: `boolean`
-
-### enableChromeLogs
-Show logs from the browser (overrides other logging options).
-
-Type: `boolean`
-
-### bidiMapperPath
-Custom bidi mapper path.
-
-Type: `string`
-
-### allowedIps
-Comma-separated allowlist of remote IP addresses which are allowed to connect to EdgeDriver.
-
-Type: `string[]`<br />
-Default: `['']`
-
-### allowedOrigins
-Comma-separated allowlist of request origins which are allowed to connect to EdgeDriver. Using `*` to allow any host origin is dangerous!
-
-Type: `string[]`<br />
-Default: `['*']`
-
-### cacheDir
-The path to the root of the cache directory.
-
-Type: `string`<br />
-Default: `process.env.EDGEDRIVER_CACHE_DIR || os.tmpdir()`
-
-### customEdgeDriverPath
-Don't download EdgeDriver, instead use a custom path to it, e.g. a cached binary.
-
-Type: `string`<br />
-Default: `process.env.EDGEDRIVER_PATH`
-
----
-
-For more information on WebdriverIO see the [homepage](https://webdriver.io).
diff --git a/node_modules/edgedriver/dist/install.js b/node_modules/edgedriver/dist/install.js
index 99730a0..30e63c0 100644
--- a/node_modules/edgedriver/dist/install.js
+++ b/node_modules/edgedriver/dist/install.js
@@ -205,6 +205,9 @@ function sanitizeVersion(version) {
* download on install
*/
if (process.argv[1] && process.argv[1].endsWith('/dist/install.js') && Boolean(process.env.EDGEDRIVER_AUTO_INSTALL)) {
- await download().then(() => log.info('Success!'), (err) => log.error(`Failed to install Edgedriver: ${err.stack}`));
+ // removing the await here as packherd cannot bundle with a top-level await.
+ // This only has an impact if invoking from a CLI context, which cypress is not.
+ // Cypress actually does not directly use this package in any way.
+ download().then(() => log.info('Success!'), (err) => log.error(`Failed to install Edgedriver: ${err.stack}`));
}
//# sourceMappingURL=install.js.map
\ No newline at end of file

View File

@@ -1,9 +1,15 @@
diff --git a/node_modules/geckodriver/AUTHORS b/node_modules/geckodriver/AUTHORS
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/LICENSE b/node_modules/geckodriver/LICENSE
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/README.md b/node_modules/geckodriver/README.md
deleted file mode 100644
index 8634875..0000000
index 95e3ef0..0000000
--- a/node_modules/geckodriver/README.md
+++ /dev/null
@@ -1,230 +0,0 @@
@@ -1,239 +0,0 @@
-Geckodriver [![CI](https://github.com/webdriverio-community/node-geckodriver/actions/workflows/ci.yml/badge.svg)](https://github.com/webdriverio-community/node-geckodriver/actions/workflows/ci.yml) [![Audit](https://github.com/webdriverio-community/node-geckodriver/actions/workflows/audit.yml/badge.svg)](https://github.com/webdriverio-community/node-geckodriver/actions/workflows/audit.yml)
-==========
-
@@ -223,6 +229,15 @@ index 8634875..0000000
-Type: `string`<br />
-Default: `process.env.GECKODRIVER_CACHE_DIR || os.tmpdir()`
-
-### `spawnOpts`
-Options to pass into the geckodriver process. This can be useful if needing
-Firefox to spawn with `MOZ_` prefix variables, such as `MOZ_HEADLESS_WIDTH`.
-See https://nodejs.org/api/child_process.html#child_processspawncommand-args-options for
-all options.
-
-Type: `SpawnOptionsWithoutStdio | SpawnOptionsWithStdioTuple`<br />
-Default: `undefined`
-
-# Other Browser Driver
-
-If you also look for other browser driver NPM wrappers, you can find them here:
@@ -234,70 +249,84 @@ index 8634875..0000000
----
-
-For more information on WebdriverIO see the [homepage](https://webdriver.io).
diff --git a/node_modules/geckodriver/dist/cjs/index.d.ts b/node_modules/geckodriver/dist/cjs/index.d.ts
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/cjs/index.d.ts.map b/node_modules/geckodriver/dist/cjs/index.d.ts.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/cjs/index.js b/node_modules/geckodriver/dist/cjs/index.js
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/cjs/index.js.map b/node_modules/geckodriver/dist/cjs/index.js.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/cli.d.ts b/node_modules/geckodriver/dist/cli.d.ts
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/cli.d.ts.map b/node_modules/geckodriver/dist/cli.d.ts.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/cli.js b/node_modules/geckodriver/dist/cli.js
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/cli.js.map b/node_modules/geckodriver/dist/cli.js.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/constants.d.ts b/node_modules/geckodriver/dist/constants.d.ts
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/constants.d.ts.map b/node_modules/geckodriver/dist/constants.d.ts.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/constants.js b/node_modules/geckodriver/dist/constants.js
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/constants.js.map b/node_modules/geckodriver/dist/constants.js.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/index.d.ts b/node_modules/geckodriver/dist/index.d.ts
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/index.d.ts.map b/node_modules/geckodriver/dist/index.d.ts.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/index.js b/node_modules/geckodriver/dist/index.js
index 2367e0b..18be0e1 100644
old mode 100644
new mode 100755
index 2edc6d2..d0b456e
--- a/node_modules/geckodriver/dist/index.js
+++ b/node_modules/geckodriver/dist/index.js
@@ -1,11 +1,11 @@
@@ -1,10 +1,14 @@
import cp from 'node:child_process';
-import logger from '@wdio/logger';
+import debugModule from 'debug';
import logger from '@wdio/logger';
+import debugModule from 'debug'
import { download as downloadDriver } from './install.js';
import { hasAccess, parseParams } from './utils.js';
import { DEFAULT_HOSTNAME } from './constants.js';
-const log = logger('geckodriver');
+const debug = debugModule('cypress-verbose:server:browsers:geckodriver');
const log = logger('geckodriver');
export async function start(params) {
- const { cacheDir, customGeckoDriverPath, ...startArgs } = params;
+ const { cacheDir, customGeckoDriverPath, spawnOpts, ...startArgs } = params;
+ // wrap in cypress debugger statement to avoid extraneous messages to the console
+ log.setLevel(debugModule.enabled('cypress-verbose:server:browsers:geckodriver') ? 'info' : 'silent')
+
const { cacheDir, customGeckoDriverPath, spawnOpts, ...startArgs } = params;
let geckoDriverPath = (customGeckoDriverPath ||
process.env.GECKODRIVER_PATH ||
// deprecated
@@ -23,8 +23,8 @@ export async function start(params) {
// Otherwise all instances try to connect to the default port and fail
startArgs.websocketPort = startArgs.websocketPort ?? 0;
const args = parseParams(startArgs);
- log.info(`Starting Geckodriver at ${geckoDriverPath} with params: ${args.join(' ')}`);
- return cp.spawn(geckoDriverPath, args);
+ debug(`Starting Geckodriver at ${geckoDriverPath} with params: ${args.join(' ')}`);
+ return cp.spawn(geckoDriverPath, args, spawnOpts);
}
export const download = downloadDriver;
export * from './types.js';
diff --git a/node_modules/geckodriver/dist/index.js.map b/node_modules/geckodriver/dist/index.js.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/install.d.ts b/node_modules/geckodriver/dist/install.d.ts
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/install.d.ts.map b/node_modules/geckodriver/dist/install.d.ts.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/install.js b/node_modules/geckodriver/dist/install.js
index c27c805..d230983 100644
old mode 100644
new mode 100755
index c27c805..ac53518
--- a/node_modules/geckodriver/dist/install.js
+++ b/node_modules/geckodriver/dist/install.js
@@ -4,14 +4,14 @@ import util from 'node:util';
import stream from 'node:stream';
import fsp, { writeFile } from 'node:fs/promises';
import zlib from 'node:zlib';
-import logger from '@wdio/logger';
+import debugModule from 'debug';
import tar from 'tar-fs';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { HttpProxyAgent } from 'http-proxy-agent';
import { BINARY_FILE, GECKODRIVER_CARGO_YAML } from './constants.js';
import { hasAccess, getDownloadUrl, retryFetch } from './utils.js';
import { BlobReader, BlobWriter, ZipReader } from '@zip.js/zip.js';
-const log = logger('geckodriver');
+const debug = debugModule('cypress-verbose:server:browsers:geckodriver');
const streamPipeline = util.promisify(stream.pipeline);
const fetchOpts = {};
if (process.env.HTTPS_PROXY) {
@@ -36,10 +36,10 @@ export async function download(geckodriverVersion = process.env.GECKODRIVER_VERS
throw new Error(`Couldn't find version property in Cargo.toml file: ${JSON.stringify(toml)}`);
}
geckodriverVersion = version.split(' = ').pop().slice(1, -1);
- log.info(`Detected Geckodriver v${geckodriverVersion} to be latest`);
+ debug(`Detected Geckodriver v${geckodriverVersion} to be latest`);
}
const url = getDownloadUrl(geckodriverVersion);
- log.info(`Downloading Geckodriver from ${url}`);
+ debug(`Downloading Geckodriver from ${url}`);
const res = await retryFetch(url, fetchOpts);
if (res.status !== 200) {
throw new Error(`Failed to download binary (statusCode ${res.status}): ${res.statusText}`);
@@ -70,6 +70,8 @@ async function downloadZip(res, cacheDir) {
* download on install
*/
@@ -305,7 +334,37 @@ index c27c805..d230983 100644
- await download().then(() => log.info('Success!'), (err) => log.error(`Failed to install Geckodriver: ${err.stack}`));
+ // removing the await here as packherd cannot bundle with a top-level await.
+ // This only has an impact if invoking from a CLI context, which cypress is not.
+ download().then(() => debug('Success!'), (err) => debug(`Failed to install Geckodriver: ${err.stack}`));
+ download().then(() => log.info('Success!'), (err) => log.error(`Failed to install Geckodriver: ${err.stack}`));
}
//# sourceMappingURL=install.js.map
\ No newline at end of file
diff --git a/node_modules/geckodriver/dist/install.js.map b/node_modules/geckodriver/dist/install.js.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/types.d.ts b/node_modules/geckodriver/dist/types.d.ts
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/types.d.ts.map b/node_modules/geckodriver/dist/types.d.ts.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/types.js b/node_modules/geckodriver/dist/types.js
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/types.js.map b/node_modules/geckodriver/dist/types.js.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/utils.d.ts b/node_modules/geckodriver/dist/utils.d.ts
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/utils.d.ts.map b/node_modules/geckodriver/dist/utils.d.ts.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/utils.js b/node_modules/geckodriver/dist/utils.js
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/dist/utils.js.map b/node_modules/geckodriver/dist/utils.js.map
old mode 100644
new mode 100755
diff --git a/node_modules/geckodriver/tsconfig.tsbuildinfo b/node_modules/geckodriver/tsconfig.tsbuildinfo
old mode 100644
new mode 100755

View File

@@ -0,0 +1,199 @@
diff --git a/node_modules/webdriver/README.md b/node_modules/webdriver/README.md
deleted file mode 100644
index c3ed38d..0000000
--- a/node_modules/webdriver/README.md
+++ /dev/null
@@ -1,170 +0,0 @@
-WebDriver
-=========
-
-> A lightweight, non-opinionated implementation of the [WebDriver](https://w3c.github.io/webdriver/webdriver-spec.html) and [WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) specification including mobile commands supported by [Appium](http://appium.io/)
-
-There are [tons](https://github.com/christian-bromann/awesome-selenium#javascript) of Selenium and WebDriver binding implementations in the Node.js world. Every one of them has an opinionated API and recommended way to use it. This binding is the most non-opinionated you will find as it just represents the [WebDriver specification](https://w3c.github.io/webdriver/webdriver-spec.html) and doesn't come with any extra or higher-level abstraction. It is lightweight and comes with support for the [WebDriver specification](https://w3c.github.io/webdriver/webdriver-spec.html) and Appium's [Mobile JSONWire Protocol](https://github.com/appium/appium-base-driver/blob/master/docs/mjsonwp/protocol-methods.md).
-
-The package supports the following protocols:
-
-- [WebDriver](https://w3c.github.io/webdriver/)
-- [WebDriver Bidi](https://w3c.github.io/webdriver-bidi/)
-- [Appium](http://appium.io/)
-- [Chromium](http://chromedriver.chromium.org/) (additional Chromedriver specific commands)
-- [Selenium](https://www.selenium.dev/) (additional Selenium WebDriver specific commands)
-- [Sauce Labs](https://saucelabs.com/) (Sauce Labs specific WebDriver extensions)
-- [JSONWireProtocol](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol) (depcrecated)
-- [Mobile JSONWireProtocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) (depcrecated)
-
-Commands are added to the client's protocol based on assumptions of provided capabilities. You can find more details about the commands by checking out the [`@wdio/protocols`](https://www.npmjs.com/package/@wdio/protocols) package. All commands come with TypeScript support.
-
-## Install
-
-To install this package from NPM run:
-
-```sh
-npm i webdriver
-```
-
-## WebDriver Example
-
-The following example demonstrates a simple Google Search scenario:
-
-```js
-import WebDriver from 'webdriver';
-
-const client = await WebDriver.newSession({
- path: '/',
- capabilities: { browserName: 'firefox' }
-})
-
-await client.navigateTo('https://www.google.com/ncr')
-
-const searchInput = await client.findElement('css selector', '#lst-ib')
-await client.elementSendKeys(searchInput['element-6066-11e4-a52e-4f735466cecf'], 'WebDriver')
-
-const searchBtn = await client.findElement('css selector', 'input[value="Google Search"]')
-await client.elementClick(searchBtn['element-6066-11e4-a52e-4f735466cecf'])
-
-console.log(await client.getTitle()) // outputs "WebDriver - Google Search"
-
-await client.deleteSession()
-```
-
-## WebDriver Bidi Example
-
-To connect to the WebDriver Bidi protocol you have to send along a `webSocketUrl` flag to tell the browser driver to opt-in to the protocol:
-
-```js
-import WebDriver from 'webdriver'
-
-const browser = await WebDriver.newSession({
- capabilities: {
- webSocketUrl: true,
- browserName: 'firefox'
- }
-})
-
-await browser.sessionSubscribe({ events: ['log.entryAdded'] })
-
-/**
- * returns: {"type":"console","method":"log","realm":null,"args":[{"type":"string","value":"Hello Bidi"}],"level":"info","text":"Hello Bidi","timestamp":1657282076037}
- */
-browser.on('log.entryAdded', (entryAdded) => console.log('received %s', entryAdded))
-
-await browser.executeScript('console.log("Hello Bidi")', [])
-await browser.deleteSession()
-```
-
-# Configuration
-
-To create a WebDriver session call the `newSession` method on the `WebDriver` class and pass in your configurations:
-
-```js
-import WebDriver from 'webdriver'
-const client = await WebDriver.newSession(options)
-```
-
-The following options are available:
-
-### capabilities
-Defines the [capabilities](https://w3c.github.io/webdriver/webdriver-spec.html#capabilities) you want to run in your WebDriver session. Note: by default, it will automatically set the `webSocketUrl` to establish a [WebDriver Bidi](https://w3c.github.io/webdriver-bidi/) session, if you don't want this, make sure to set `'wdio:enforceWebDriverClassic': true` in your capabilities.
-
-Type: `Object`<br />
-Required: `true`
-
-### logLevel
-Level of logging verbosity.
-
-Type: `String`<br />
-Default: *info*<br />
-Options: *trace* | *debug* | *info* | *warn* | *error* | *silent*
-
-### protocol
-Protocol to use when communicating with the Selenium standalone server (or driver).
-
-Type: `String`<br />
-Default: *http*
-Options: *http* | *https*
-
-### hostname
-Host of your WebDriver server.
-
-Type: `String`<br />
-Default: *localhost*
-
-### port
-Port your WebDriver server is on.
-
-Type: `Number`<br />
-Default: `undefined`
-
-### path
-Path to WebDriver endpoint or grid server.
-
-Type: `String`<br />
-Default: */*
-
-### queryParams
-Query parameters that are propagated to the driver server.
-
-Type: `Object`
-Default: `undefined`
-
-### connectionRetryTimeout
-Timeout for any WebDriver request to a driver or grid.
-
-Type: `Number`<br />
-Default: *120000*
-
-### connectionRetryCount
-Count of request retries to the Selenium server.
-
-Type: `Number`<br />
-Default: *3*
-
-### agent
-
-Allows you to use a custom` http`/`https`/`http2` [agent](https://www.npmjs.com/package/got#agent) to make requests.
-
-Type: `Object`<br />
-Default:
-
-```js
-{
- http: new http.Agent({ keepAlive: true }),
- https: new https.Agent({ keepAlive: true })
-}
-```
-
-### transformRequest
-Function intercepting [HTTP request options](https://github.com/sindresorhus/got#options) before a WebDriver request is made to a driver.
-
-Type: `(RequestOptions) => RequestOptions`<br />
-Default: *none*
-
-### transformResponse
-Function intercepting HTTP response objects after a WebDriver response has arrived.
-
-Type: `(Response, RequestOptions) => Response`<br />
-Default: *none*
diff --git a/node_modules/webdriver/build/index.js b/node_modules/webdriver/build/index.js
index 3be438a..ecad188 100644
--- a/node_modules/webdriver/build/index.js
+++ b/node_modules/webdriver/build/index.js
@@ -41,6 +41,10 @@ import { CAPABILITY_KEYS } from "@wdio/protocols";
// src/bidi/core.ts
import logger from "@wdio/logger";
+// removing the await here as packherd cannot bundle with a top-level await.
+// Since we are using the package in a node context, we can just import 'ws'
+import socket_default from 'ws'
+
// src/bidi/socket.ts
var BrowserSocket = class {
#callbacks = /* @__PURE__ */ new Set();
@@ -78,7 +82,6 @@ var BrowserSocket = class {
this.#ws.close();
}
};
-var socket_default = globalThis.window ? BrowserSocket : (await import("ws")).default;
// src/bidi/core.ts
var log = logger("webdriver");

View File

@@ -1,28 +1,29 @@
require('../../spec_helper')
import 'chai-as-promised'
import { expect } from 'chai'
import debug from 'debug'
import os from 'os'
import sinon from 'sinon'
import fsExtra from 'fs-extra'
import * as firefox from '../../../lib/browsers/firefox'
import firefoxUtil from '../../../lib/browsers/firefox-util'
import { CdpAutomation } from '../../../lib/browsers/cdp_automation'
import { BrowserCriClient } from '../../../lib/browsers/browser-cri-client'
import { ICriClient } from '../../../lib/browsers/cri-client'
import { GeckoDriver } from '../../../lib/browsers/geckodriver'
import * as webDriverClassicImport from '../../../lib/browsers/webdriver-classic'
import { type Client as WebDriverClient, default as webdriver } from 'webdriver'
import { EventEmitter } from 'stream'
const path = require('path')
const _ = require('lodash')
const mockfs = require('mock-fs')
const FirefoxProfile = require('firefox-profile')
const launch = require('@packages/launcher/lib/browsers')
const utils = require('../../../lib/browsers/utils')
const plugins = require('../../../lib/plugins')
const specUtil = require('../../specUtils')
describe('lib/browsers/firefox', () => {
const port = 3333
let wdcInstance: sinon.SinonStubbedInstance<webDriverClassicImport.WebDriverClassic>
let wdInstance: sinon.SinonStubbedInstance<WebDriverClient>
let browserCriClient: BrowserCriClient
afterEach(() => {
return mockfs.restore()
@@ -35,53 +36,37 @@ describe('lib/browsers/firefox', () => {
'/path/to/appData/firefox-stable/interactive': {},
})
this.browserInstance = {
// should be high enough to not kill any real PIDs
pid: Number.MAX_SAFE_INTEGER,
}
sinon.stub(protocol, '_connectAsync').resolves(null)
sinon.stub(GeckoDriver, 'create').resolves(this.browserInstance)
wdcInstance = sinon.createStubInstance(webDriverClassicImport.WebDriverClassic)
wdcInstance.createSession.resolves({
wdInstance = {
maximizeWindow: sinon.stub(),
installAddOn: sinon.stub(),
getWindowHandles: sinon.stub(),
switchToWindow: sinon.stub(),
navigateTo: sinon.stub(),
capabilities: {
'moz:debuggerAddress': '127.0.0.1:12345',
acceptInsecureCerts: false,
browserName: '',
browserVersion: '',
platformName: '',
pageLoadStrategy: 'normal',
strictFileInteractability: false,
timeouts: {
implicit: 0,
pageLoad: 0,
script: 0,
},
'moz:accessibilityChecks': false,
'moz:buildID': '',
'moz:geckodriverVersion': '',
'moz:headless': false,
'moz:platformVersion': '',
'moz:processID': 0,
'moz:profile': '',
'moz:shutdownTimeout': 0,
'moz:webdriverClick': false,
'moz:windowless': false,
unhandledPromptBehavior: '',
userAgent: '',
sessionId: '',
// @ts-expect-error
'moz:processID': 1234,
'wdio:driverPID': 5678,
},
})
}
sinon.stub(webDriverClassicImport, 'WebDriverClassic').callsFake(() => wdcInstance)
wdInstance.maximizeWindow.resolves(undefined)
wdInstance.installAddOn.resolves(undefined)
wdInstance.switchToWindow.resolves(undefined)
wdInstance.navigateTo.resolves(undefined)
sinon.stub(webdriver, 'newSession').resolves(wdInstance)
stubFoxdriver()
})
context('#open', () => {
beforeEach(function () {
// majorVersion >= 86 indicates CDP support for Firefox, which provides
// the CDP debugger URL for the after:browser:launch tests
this.browser = { name: 'firefox', channel: 'stable', majorVersion: 100 }
this.browser = { name: 'firefox', channel: 'stable', majorVersion: 100, path: '/path/to/binary' }
this.automation = {
use: sinon.stub().returns({}),
}
@@ -96,14 +81,18 @@ describe('lib/browsers/firefox', () => {
sinon.stub(plugins, 'has')
sinon.stub(plugins, 'execute')
sinon.stub(launch, 'launch').returns(this.browserInstance)
sinon.stub(utils, 'writeExtension').resolves('/path/to/ext')
sinon.stub(utils, 'getPort').resolves(1234)
sinon.spy(FirefoxProfile.prototype, 'setPreference')
sinon.spy(FirefoxProfile.prototype, 'updatePreferences')
sinon.spy(FirefoxProfile.prototype, 'shouldDeleteOnExit')
sinon.spy(FirefoxProfile.prototype, 'path')
sinon.stub(FirefoxProfile.prototype, 'encoded').callsFake((cb: Function) => {
cb(undefined, 'abcdef')
})
const browserCriClient: BrowserCriClient = sinon.createStubInstance(BrowserCriClient)
sinon.stub(fsExtra, 'writeJSON').resolves(undefined)
sinon.stub(fsExtra, 'writeFile').returns(undefined)
browserCriClient = sinon.createStubInstance(BrowserCriClient)
browserCriClient.attachToTargetUrl = sinon.stub().resolves({})
browserCriClient.getWebSocketDebuggerUrl = sinon.stub().returns('ws://debugger')
@@ -120,171 +109,257 @@ describe('lib/browsers/firefox', () => {
})
it('calls connectToNewSpec in firefoxUtil', async function () {
wdcInstance.getWindowHandles.resolves(['mock-context-id'])
wdInstance.getWindowHandles.resolves(['mock-context-id'])
await firefox.open(this.browser, 'http://', this.options, this.automation)
this.options.url = 'next-spec-url'
await firefox.connectToNewSpec(this.browser, this.options, this.automation)
expect(this.options.onInitializeNewBrowserTab).to.have.been.called
expect(wdcInstance.getWindowHandles).to.have.been.called
expect(wdcInstance.switchToWindow).to.have.been.calledWith('mock-context-id')
expect(wdInstance.getWindowHandles).to.have.been.called
expect(wdInstance.switchToWindow).to.have.been.calledWith('mock-context-id')
// first time when connecting a new tab
expect(wdcInstance.navigate).to.have.been.calledWith('about:blank')
expect(wdInstance.navigateTo).to.have.been.calledWith('about:blank')
// second time when navigating to the spec
expect(wdcInstance.navigate).to.have.been.calledWith('next-spec-url')
expect(wdInstance.navigateTo).to.have.been.calledWith('next-spec-url')
})
})
it('executes before:browser:launch if registered', function () {
it('executes before:browser:launch if registered', async function () {
plugins.has.withArgs('before:browser:launch').returns(true)
plugins.execute.resolves(null)
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(plugins.execute).to.be.calledWith('before:browser:launch')
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(plugins.execute).to.be.calledWith('before:browser:launch')
})
it('does not execute before:browser:launch if not registered', function () {
it('does not execute before:browser:launch if not registered', async function () {
plugins.has.withArgs('before:browser:launch').returns(false)
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(plugins.execute).not.to.be.calledWith('before:browser:launch')
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(plugins.execute).not.to.be.calledWith('before:browser:launch')
})
it('uses default preferences if before:browser:launch returns falsy value', function () {
it('uses default preferences if before:browser:launch returns falsy value', async function () {
plugins.has.withArgs('before:browser:launch').returns(true)
plugins.execute.resolves(null)
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.type', 1)
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(webdriver.newSession).to.have.been.calledWith(sinon.match({
capabilities: {
alwaysMatch: {
'moz:firefoxOptions': {
prefs: {
'network.proxy.type': 1,
},
},
},
firstMatch: [],
},
}))
})
it('uses default preferences if before:browser:launch returns object with non-object preferences', function () {
it('uses default preferences if before:browser:launch returns object with non-object preferences', async function () {
plugins.has.withArgs('before:browser:launch').returns(true)
plugins.execute.resolves({
preferences: [],
})
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.type', 1)
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(webdriver.newSession).to.have.been.calledWith(sinon.match({
capabilities: {
alwaysMatch: {
'moz:firefoxOptions': {
prefs: {
'network.proxy.type': 1,
},
},
},
firstMatch: [],
},
}))
})
it('sets preferences if returned by before:browser:launch', function () {
it('sets preferences if returned by before:browser:launch', async function () {
plugins.has.withArgs('before:browser:launch').returns(true)
plugins.execute.resolves({
preferences: { 'foo': 'bar' },
})
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('foo', 'bar')
})
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
it('creates the geckodriver, the creation of the WebDriver session, installs the extension, and passes the correct port to CDP', function () {
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(GeckoDriver.create).to.have.been.calledWith({
host: '127.0.0.1',
port: sinon.match(Number),
marionetteHost: '127.0.0.1',
marionettePort: sinon.match(Number),
webdriverBidiPort: sinon.match(Number),
profilePath: '/path/to/appData/firefox-stable/interactive',
binaryPath: undefined,
spawnOpts: sinon.match({
stdio: ['ignore', 'pipe', 'pipe'],
env: {
MOZ_REMOTE_SETTINGS_DEVTOOLS: '1',
MOZ_HEADLESS_WIDTH: '1280',
MOZ_HEADLESS_HEIGHT: '806',
},
}),
})
expect(wdcInstance.createSession).to.have.been.calledWith(sinon.match(
{
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
'moz:firefoxOptions': {
args: [
'-new-instance',
'-start-debugger-server',
'-no-remote',
...(os.platform() !== 'linux' ? ['-foreground'] : []),
],
},
'moz:debuggerAddress': true,
expect(webdriver.newSession).to.have.been.calledWith(sinon.match({
capabilities: {
alwaysMatch: {
'moz:firefoxOptions': {
prefs: {
'foo': 'bar',
},
},
},
))
firstMatch: [],
},
}))
})
expect(wdcInstance.installAddOn).to.have.been.calledWith(sinon.match({
path: '/path/to/ext',
temporary: true,
}))
it('creates the WebDriver session and geckodriver instance through capabilities, installs the extension, and passes the correct port to CDP', async function () {
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(webdriver.newSession).to.have.been.calledWith({
logLevel: 'silent',
capabilities: sinon.match({
alwaysMatch: {
browserName: 'firefox',
acceptInsecureCerts: true,
// @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
'moz:firefoxOptions': {
binary: '/path/to/binary',
args: [
'-new-instance',
'-start-debugger-server',
'-no-remote',
...(os.platform() !== 'linux' ? ['-foreground'] : []),
],
// only partially match the preferences object because it is so large
prefs: sinon.match({
'remote.active-protocols': 2,
'remote.enabled': true,
}),
},
'moz:debuggerAddress': true,
'wdio:geckodriverOptions': {
host: '127.0.0.1',
marionetteHost: '127.0.0.1',
marionettePort: sinon.match(Number),
websocketPort: sinon.match(Number),
profileRoot: '/path/to/appData/firefox-stable/interactive',
binaryPath: undefined,
spawnOpts: sinon.match({
stdio: ['ignore', 'pipe', 'pipe'],
env: {
MOZ_REMOTE_SETTINGS_DEVTOOLS: '1',
MOZ_HEADLESS_WIDTH: '1280',
MOZ_HEADLESS_HEIGHT: '722',
},
}),
jsdebugger: false,
log: 'error',
logNoTruncate: false,
expect(wdcInstance.navigate).to.have.been.calledWith('http://')
},
},
firstMatch: [],
}),
})
expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true)
expect(wdInstance.navigateTo).to.have.been.calledWith('http://')
// make sure CDP gets the expected port
expect(BrowserCriClient.create).to.be.calledWith({ hosts: ['127.0.0.1', '::1'], port: 12345, browserName: 'Firefox', onAsynchronousError: undefined, onServiceWorkerClientEvent: undefined })
})
describe('debugging', () => {
afterEach(() => {
debug.disable()
})
it('sets additional arguments if "DEBUG=cypress-verbose:server:browsers:geckodriver" and "DEBUG=cypress-verbose:server:browsers:webdriver" is set', async function () {
debug.enable('cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver')
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(webdriver.newSession).to.have.been.calledWith({
logLevel: 'info',
capabilities: sinon.match({
alwaysMatch: {
browserName: 'firefox',
acceptInsecureCerts: true,
// @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
'moz:firefoxOptions': {
binary: '/path/to/binary',
args: [
'-new-instance',
'-start-debugger-server',
'-no-remote',
...(os.platform() !== 'linux' ? ['-foreground'] : []),
],
// only partially match the preferences object because it is so large
prefs: sinon.match({
'remote.active-protocols': 2,
'remote.enabled': true,
}),
},
'moz:debuggerAddress': true,
'wdio:geckodriverOptions': {
host: '127.0.0.1',
marionetteHost: '127.0.0.1',
marionettePort: sinon.match(Number),
websocketPort: sinon.match(Number),
profileRoot: '/path/to/appData/firefox-stable/interactive',
binaryPath: undefined,
spawnOpts: sinon.match({
stdio: ['ignore', 'pipe', 'pipe'],
env: {
MOZ_REMOTE_SETTINGS_DEVTOOLS: '1',
MOZ_HEADLESS_WIDTH: '1280',
MOZ_HEADLESS_HEIGHT: '722',
},
}),
jsdebugger: true,
log: 'debug',
logNoTruncate: true,
},
},
firstMatch: [],
}),
})
expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true)
expect(wdInstance.navigateTo).to.have.been.calledWith('http://')
// make sure CDP gets the expected port
expect(BrowserCriClient.create).to.be.calledWith({ hosts: ['127.0.0.1', '::1'], port: 12345, browserName: 'Firefox', onAsynchronousError: undefined, onServiceWorkerClientEvent: undefined })
})
})
it('does not maximize the browser if headless', function () {
it('does not maximize the browser if headless', async function () {
this.browser.isHeadless = true
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(wdcInstance.maximizeWindow).not.to.have.been.called
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(wdInstance.maximizeWindow).not.to.have.been.called
})
it('does not maximize the browser if "-width" or "-height" arg is set', function () {
this.browser.isHeadless = false
sinon.stub(utils, 'executeBeforeBrowserLaunch').resolves({
args: ['-width', '1280', '-height', '720'],
extensions: [],
preferences: {},
})
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(wdcInstance.maximizeWindow).not.to.have.been.called
})
})
it('maximizes the browser if headed and no "-width" or "-height" arg is set', function () {
this.browser.isHeadless = false
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(wdcInstance.maximizeWindow).to.have.been.called
})
})
it('sets user-agent preference if specified', function () {
it('sets user-agent preference if specified', async function () {
this.options.userAgent = 'User Agent'
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('general.useragent.override', 'User Agent')
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(webdriver.newSession).to.have.been.calledWith(sinon.match({
capabilities: {
alwaysMatch: {
'moz:firefoxOptions': {
prefs: {
'general.useragent.override': 'User Agent',
},
},
},
firstMatch: [],
},
}))
})
it('writes extension', function () {
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(utils.writeExtension).to.be.calledWith(this.options.browser, this.options.isTextTerminal, this.options.proxyUrl, this.options.socketIoRoute)
})
it('writes extension', async function () {
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(utils.writeExtension).to.be.calledWith(this.options.browser, this.options.isTextTerminal, this.options.proxyUrl, this.options.socketIoRoute)
})
it('writes extension and ensure write access', function () {
// TODO: Test is failing locally, figure out why??
if (!process.env.CI) {
return
}
it('writes extension and ensure write access', async function () {
mockfs({
[path.resolve(`${__dirname }../../../../../extension/dist/v2`)]: {
'background.js': mockfs.file({
@@ -305,57 +380,72 @@ describe('lib/browsers/firefox', () => {
})
utils.writeExtension.restore()
// @ts-expect-error
fsExtra.writeFile.restore()
sinon.spy(fsExtra, 'chmod')
const getFile = function (path) {
return _.reduce(_.compact(_.split(path, '/')), (acc, item) => {
return acc.getItem(item)
}, mockfs.getMockRoot())
}
// bypass the extension clearing that happens in open mode, which is tested at the system test level
this.options.isTextTerminal = true
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(getFile(`${process.env.HOME }/.config/Cypress/cy/test/browsers/firefox-stable/interactive/CypressExtension/background.js`).getMode()).to.be.equals(0o644)
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(fsExtra.chmod).to.have.been.calledWith(sinon.match(/CypressExtension\/background\.js/), 0o644)
})
it('sets proxy-related preferences if specified', function () {
it('sets proxy-related preferences if specified', async function () {
this.options.proxyServer = 'http://proxy-server:1234'
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.http', 'proxy-server')
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.ssl', 'proxy-server')
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.http_port', 1234)
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.ssl_port', 1234)
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.no_proxies_on')
})
expect(webdriver.newSession).to.have.been.calledWith(sinon.match({
capabilities: {
alwaysMatch: {
'moz:firefoxOptions': {
prefs: {
'network.proxy.http': 'proxy-server',
'network.proxy.ssl': 'proxy-server',
'network.proxy.http_port': 1234,
'network.proxy.ssl_port': 1234,
'network.proxy.no_proxies_on': '',
},
},
},
firstMatch: [],
},
}))
})
it('does not set proxy-related preferences if not specified', function () {
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.http', 'proxy-server')
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.https', 'proxy-server')
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.http_port', 1234)
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.https_port', 1234)
it('does not set proxy-related preferences if not specified', async function () {
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.no_proxies_on')
})
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.http', 'proxy-server')
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.https', 'proxy-server')
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.http_port', 1234)
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.https_port', 1234)
expect(FirefoxProfile.prototype.setPreference).not.to.be.calledWith('network.proxy.no_proxies_on')
})
it('tears down the temporary profile when the browser is destroyed', async function () {
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(FirefoxProfile.prototype.shouldDeleteOnExit).to.be.calledWith(true)
})
// @see https://github.com/cypress-io/cypress/issues/17896
it('escapes the downloadsFolders path correctly when running on Windows OS', function () {
it('escapes the downloadsFolders path correctly when running on Windows OS', async function () {
this.options.proxyServer = 'http://proxy-server:1234'
this.options.downloadsFolder = 'C:/Users/test/Downloads/My_Test_Downloads_Folder'
sinon.stub(os, 'platform').returns('win32')
const executeBeforeBrowserLaunchSpy = sinon.spy(utils, 'executeBeforeBrowserLaunch')
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({
preferences: {
// NOTE: sinon.match treats the string itself as a regular expression. The backslashes need to be escaped.
'browser.download.dir': 'C:\\\\Users\\\\test\\\\Downloads\\\\My_Test_Downloads_Folder',
},
}), this.options)
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({
preferences: {
// NOTE: sinon.match treats the string itself as a regular expression. The backslashes need to be escaped.
'browser.download.dir': 'C:\\\\Users\\\\test\\\\Downloads\\\\My_Test_Downloads_Folder',
},
}), this.options)
})
// CDP is deprecated in Firefox 129 and up.
@@ -363,31 +453,26 @@ describe('lib/browsers/firefox', () => {
// remote.active-protocol=2
// @see https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/
// @see https://github.com/cypress-io/cypress/issues/29713
it('sets "remote.active-protocols"=2 to keep CDP enabled for firefox versions 129 and up', function () {
it('sets "remote.active-protocols"=2 to keep CDP enabled for firefox versions 129 and up', async function () {
const executeBeforeBrowserLaunchSpy = sinon.spy(utils, 'executeBeforeBrowserLaunch')
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({
preferences: {
'remote.active-protocols': 2,
},
}), this.options)
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({
preferences: {
'remote.active-protocols': 2,
},
}), this.options)
})
it('updates the preferences', function () {
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(FirefoxProfile.prototype.updatePreferences).to.be.called
})
it('resolves the browser instance as an event emitter', async function () {
const result = await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(result).to.be.an.instanceof(EventEmitter)
expect(result.kill).to.be.an.instanceof(Function)
})
it('resolves the browser instance', function () {
return firefox.open(this.browser, 'http://', this.options, this.automation).then((result) => {
expect(result).to.equal(this.browserInstance)
})
})
it('does not clear user profile if already exists', function () {
it('always clear user profile if it already exists', async function () {
mockfs({
'/path/to/appData/firefox-stable/interactive/': {
'xulstore.json': '[foo xulstore.json]',
@@ -395,22 +480,34 @@ describe('lib/browsers/firefox', () => {
},
})
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
// @ts-ignore
expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).containSubset({
'xulstore.json': '[foo xulstore.json]',
'chrome': { 'userChrome.css': '[foo userChrome.css]' },
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined
})
it('creates xulstore.json if not exist', async function () {
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(fsExtra.writeJSON).to.have.been.calledWith('/path/to/appData/firefox-stable/interactive/xulstore.json', {
'chrome://browser/content/browser.xhtml':
{
'main-window':
{
'width': 1280,
'height': 1024,
'sizemode': 'maximized',
},
},
})
})
it('creates chrome/userChrome.css if not exist', function () {
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive/chrome/userChrome.css')).ok
})
it('creates chrome/userChrome.css if not exist', async function () {
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(fsExtra.writeFile).to.have.been.calledWith('/path/to/appData/firefox-stable/interactive/chrome/userChrome.css')
})
it('clears browser cache', function () {
it('clears browser cache', async function () {
mockfs({
'/path/to/appData/firefox-stable/interactive/': {
'CypressCache': { 'foo': 'bar' },
@@ -419,12 +516,8 @@ describe('lib/browsers/firefox', () => {
this.options.isTextTerminal = false
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
// @ts-ignore
expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).containSubset({
'CypressCache': {},
})
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined
})
it('wraps errors when failing to connect to firefox (CDP failure)', async function () {
@@ -443,30 +536,46 @@ describe('lib/browsers/firefox', () => {
})
})
it('executes after:browser:launch if registered', function () {
it('executes after:browser:launch if registered', async function () {
plugins.has.withArgs('after:browser:launch').returns(true)
plugins.execute.resolves(null)
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(plugins.execute).to.be.calledWith('after:browser:launch', this.browser, {
webSocketDebuggerUrl: 'ws://debugger',
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(plugins.execute).to.be.calledWith('after:browser:launch', this.browser, {
webSocketDebuggerUrl: 'ws://debugger',
})
})
it('does not execute after:browser:launch if not registered', function () {
it('does not execute after:browser:launch if not registered', async function () {
plugins.has.withArgs('after:browser:launch').returns(false)
return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => {
expect(plugins.execute).not.to.be.calledWith('after:browser:launch')
})
await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(plugins.execute).not.to.be.calledWith('after:browser:launch')
})
context('returns BrowserInstance', function () {
context('returns BrowserInstanceWrapper as EventEmitter', function () {
it('from browsers.launch', async function () {
const instance = await firefox.open(this.browser, 'http://', this.options, this.automation)
expect(instance).to.eq(this.browserInstance)
expect(instance).to.be.an.instanceof(EventEmitter)
})
it('kills the driver and browser PIDs when the kill method is called and emits the exit event', async function () {
sinon.stub(process, 'kill').returns(true)
const instance = await firefox.open(this.browser, 'http://', this.options, this.automation)
sinon.spy(instance, 'emit')
const killResult = instance.kill()
expect(killResult).to.be.true
// kills the browser
expect(process.kill).to.have.been.calledWith(1234)
// kills the webdriver process/ geckodriver process
expect(process.kill).to.have.been.calledWith(5678)
expect(browserCriClient.close).to.have.been.called
// makes sure the exit event is called to signal to the rest of cypress server that the processes are killed
expect(instance.emit).to.have.been.calledWith('exit')
})
})
})

View File

@@ -1,242 +0,0 @@
import Bluebird from 'bluebird'
import debug from 'debug'
import mockery from 'mockery'
import EventEmitter from 'events'
import { expect, sinon } from '../../../spec_helper'
import { GeckoDriver, type StartGeckoDriverArgs } from '../../../../lib/browsers/geckodriver'
import type Sinon from 'sinon'
describe('lib/browsers/geckodriver', () => {
let geckoDriverMockProcess: any
let geckoDriverMockStart: Sinon.SinonStub
let waitPortPackageStub: Sinon.SinonStub
let mockOpts: StartGeckoDriverArgs
beforeEach(() => {
geckoDriverMockProcess = new EventEmitter()
geckoDriverMockStart = sinon.stub()
waitPortPackageStub = sinon.stub()
geckoDriverMockProcess.stdout = new EventEmitter()
geckoDriverMockProcess.stderr = new EventEmitter()
geckoDriverMockProcess.kill = sinon.stub().returns(true)
mockOpts = {
host: '127.0.0.1',
port: 3000,
marionetteHost: '127.0.0.1',
marionettePort: 3001,
webdriverBidiPort: 3002,
profilePath: 'path/to/profile',
binaryPath: 'path/to/binary',
}
mockery.enable()
mockery.warnOnUnregistered(false)
mockery.registerMock('wait-port', waitPortPackageStub)
// we stub the dynamic require on the Class to make this easier to test
// @ts-expect-error
GeckoDriver.getGeckoDriverPackage = () => {
return {
start: geckoDriverMockStart,
}
}
})
afterEach(() => {
mockery.deregisterMock('geckodriver')
mockery.deregisterMock('wait-port')
mockery.disable()
})
describe('GeckoDriver.create', () => {
it('starts the geckodriver', async () => {
geckoDriverMockStart.resolves(geckoDriverMockProcess)
waitPortPackageStub.resolves()
const geckoDriverInstanceWrapper = await GeckoDriver.create(mockOpts)
expect(geckoDriverInstanceWrapper).to.equal(geckoDriverMockProcess)
expect(geckoDriverMockStart).to.have.been.called.once
expect(geckoDriverMockStart).to.have.been.calledWith(sinon.match({
host: '127.0.0.1',
port: 3000,
marionetteHost: '127.0.0.1',
marionettePort: 3001,
websocketPort: 3002,
profileRoot: 'path/to/profile',
binary: 'path/to/binary',
jsdebugger: false,
logNoTruncate: false,
log: 'error',
spawnOpts: {},
}))
expect(waitPortPackageStub).to.have.been.called.once
expect(waitPortPackageStub).to.have.been.calledWith(sinon.match({
port: 3000,
timeout: 6000,
output: 'silent',
}))
})
it('allows overriding of default props when starting', async () => {
geckoDriverMockStart.resolves(geckoDriverMockProcess)
waitPortPackageStub.resolves()
mockOpts.spawnOpts = {
MOZ_FOO: 'BAR',
}
const geckoDriverInstanceWrapper = await GeckoDriver.create(mockOpts, 10000)
expect(geckoDriverInstanceWrapper).to.equal(geckoDriverMockProcess)
expect(geckoDriverMockStart).to.have.been.called.once
expect(geckoDriverMockStart).to.have.been.calledWith(sinon.match({
host: '127.0.0.1',
port: 3000,
marionetteHost: '127.0.0.1',
marionettePort: 3001,
websocketPort: 3002,
profileRoot: 'path/to/profile',
binary: 'path/to/binary',
jsdebugger: false,
logNoTruncate: false,
log: 'error',
spawnOpts: {
MOZ_FOO: 'BAR',
},
}))
expect(waitPortPackageStub).to.have.been.called.once
expect(waitPortPackageStub).to.have.been.calledWith(sinon.match({
port: 3000,
timeout: 11000,
output: 'silent',
}))
})
describe('debugging', () => {
afterEach(() => {
debug.disable()
})
it('sets additional arguments if "DEBUG=cypress-verbose:server:browsers:geckodriver" is set', async () => {
debug.enable('cypress-verbose:server:browsers:geckodriver')
geckoDriverMockStart.resolves(geckoDriverMockProcess)
waitPortPackageStub.resolves()
mockOpts.spawnOpts = {
MOZ_FOO: 'BAR',
}
const geckoDriverInstanceWrapper = await GeckoDriver.create(mockOpts)
expect(geckoDriverInstanceWrapper).to.equal(geckoDriverMockProcess)
expect(geckoDriverMockStart).to.have.been.called.once
expect(geckoDriverMockStart).to.have.been.calledWith(sinon.match({
host: '127.0.0.1',
port: 3000,
marionetteHost: '127.0.0.1',
marionettePort: 3001,
websocketPort: 3002,
profileRoot: 'path/to/profile',
binary: 'path/to/binary',
jsdebugger: true,
logNoTruncate: true,
log: 'debug',
spawnOpts: {
MOZ_FOO: 'BAR',
},
}))
expect(waitPortPackageStub).to.have.been.called.once
expect(waitPortPackageStub).to.have.been.calledWith(sinon.match({
port: 3000,
timeout: 6000,
output: 'dots',
}))
})
})
describe('throws if', () => {
it('geckodriver failed to start', async () => {
geckoDriverMockStart.rejects(new Error('I FAILED TO START'))
try {
await GeckoDriver.create(mockOpts)
} catch (err) {
expect(err.isCypressErr).to.be.true
expect(err.type).to.equal('FIREFOX_GECKODRIVER_FAILURE')
// what the debug logs will show
expect(err.details).to.contain('Error: I FAILED TO START')
// what the user sees
expect(err.messageMarkdown).to.equal('Cypress could not connect to Firefox.\n\nAn unexpected error was received from GeckoDriver: `geckodriver:start`\n\nTo avoid this error, ensure that there are no other instances of Firefox launched by Cypress running.')
return
}
throw 'test did not enter catch as expected'
})
it('geckodriver failed to attach or took to long to register', async () => {
geckoDriverMockStart.resolves(geckoDriverMockProcess)
waitPortPackageStub.rejects(new Error('I DID NOT ATTACH OR TOOK TOO LONG!'))
try {
await GeckoDriver.create(mockOpts)
} catch (err) {
expect(err.isCypressErr).to.be.true
expect(err.type).to.equal('FIREFOX_GECKODRIVER_FAILURE')
// what the debug logs will show
expect(err.details).to.contain('Error: I DID NOT ATTACH OR TOOK TOO LONG!')
// what the user sees
expect(err.messageMarkdown).to.equal('Cypress could not connect to Firefox.\n\nAn unexpected error was received from GeckoDriver: `geckodriver:start`\n\nTo avoid this error, ensure that there are no other instances of Firefox launched by Cypress running.')
expect(geckoDriverMockProcess.kill).to.have.been.called.once
return
}
throw 'test did not enter catch as expected'
})
it('geckodriver times out starting', async () => {
geckoDriverMockStart.resolves(geckoDriverMockProcess)
// return a promise that does not resolve so the timeout is reached
waitPortPackageStub.resolves(new Bluebird(() => {}))
try {
// timeout after 0 seconds
await GeckoDriver.create(mockOpts, 0)
} catch (err) {
expect(err.isCypressErr).to.be.true
expect(err.type).to.equal('FIREFOX_GECKODRIVER_FAILURE')
// what the debug logs will show
expect(err.details).to.contain('TimeoutError: operation timed out')
// what the user sees
expect(err.messageMarkdown).to.equal('Cypress could not connect to Firefox.\n\nAn unexpected error was received from GeckoDriver: `geckodriver:start`\n\nTo avoid this error, ensure that there are no other instances of Firefox launched by Cypress running.')
expect(geckoDriverMockProcess.kill).to.have.been.called.once
return
}
throw 'test did not enter catch as expected'
})
})
})
})

View File

@@ -1,316 +0,0 @@
import nock from 'nock'
import { expect } from '../../../spec_helper'
import { WebDriverClassic } from '../../../../lib/browsers/webdriver-classic'
describe('lib/browsers/webdriver-classic', () => {
let mockSessionId: string
let mockOpts: {
host: string
port: number
}
let nockContext: nock.Scope
beforeEach(() => {
mockSessionId = `123456-abcdef`
mockOpts = {
host: '127.0.0.1',
port: 3000,
}
nockContext = nock(`http://${mockOpts.host}:${mockOpts.port}`)
})
afterEach(() => {
nock.cleanAll()
})
describe('WebDriverClassic.createSession', () => {
it('can create a session', async () => {
const newSessionScope = nockContext.post('/session', {
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
binary: '/path/to/binary',
'moz:firefoxOptions': {
args: ['-headless', '-new-instance'],
env: {
foo: 'bar',
},
prefs: {
'remote.active-protocols': 1,
},
},
'moz:debuggerAddress': true,
},
},
}).reply(200, {
value: {
capabilities: {
acceptInsecureCerts: true,
browserName: 'firefox',
browserVersion: '130.0',
'moz:accessibilityChecks': false,
'moz:buildID': '20240829075237',
'moz:geckodriverVersion': '0.35.0',
'moz:headless': false,
'moz:platformVersion': '23.3.0',
'moz:profile': '/path/to/profile',
'moz:processID': 12345,
'moz:shutdownTimeout': 60000,
'moz:windowless': false,
'moz:webdriverClick': true,
'pageLoadStrategy': 'normal',
platformName: 'mac',
proxy: {},
setWindowRect: true,
strictFileInteractability: false,
timeouts: {
implicit: 0,
pageLoad: 300000,
script: 30000,
},
unhandledPromptBehavior: 'dismiss and notify',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:130.0) Gecko/20100101 Firefox/130.0',
'moz:debuggerAddress': '127.0.0.1:3001',
},
sessionId: mockSessionId,
},
})
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
const { capabilities } = await wdc.createSession({
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
binary: '/path/to/binary',
'moz:firefoxOptions': {
args: ['-headless', '-new-instance'],
env: {
foo: 'bar',
},
prefs: {
'remote.active-protocols': 1,
},
},
'moz:debuggerAddress': true,
},
},
})
// test a few expected capabilities from the response
expect(capabilities.acceptInsecureCerts).to.be.true
expect(capabilities['moz:debuggerAddress']).to.equal('127.0.0.1:3001')
expect(capabilities.platformName).to.equal('mac')
newSessionScope.done()
})
it('throws if session cannot be created (detailed)', () => {
nockContext.post('/session', {
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
},
},
}).reply(500, {
value: {
error: 'session not created',
message: 'failed to set preferences: unknown error',
},
})
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
expect(wdc.createSession({
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
},
},
})).to.be.rejectedWith('500: Internal Server Error. session not created. failed to set preferences: unknown error.')
})
it('throws if session cannot be created (generic)', () => {
nockContext.post('/session', {
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
},
},
}).reply(500)
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
expect(wdc.createSession({
capabilities: {
alwaysMatch: {
acceptInsecureCerts: true,
},
},
})).to.be.rejectedWith('500: Internal Server Error.')
})
})
describe('WebDriverClassic.installAddOn', () => {
it('can install extensions', async () => {
const installExtensionScope = nockContext.post(`/session/${mockSessionId}/moz/addon/install`, {
path: '/path/to/ext',
temporary: true,
}).reply(200)
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
await wdc.installAddOn({
path: '/path/to/ext',
temporary: true,
})
installExtensionScope.done()
})
it('throws if extension cannot be installed', () => {
nockContext.post(`/session/${mockSessionId}/moz/addon/install`, {
path: '/path/to/ext',
temporary: true,
}).reply(500)
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
expect(wdc.installAddOn({
path: '/path/to/ext',
temporary: true,
})).to.be.rejectedWith('500: Internal Server Error')
})
})
describe('WebDriverClassic.getWindowHandles', () => {
it('returns the page contexts when the requests succeeds', async () => {
const expectedContexts = ['mock-context-id-1']
nockContext.get(`/session/${mockSessionId}/window/handles`).reply(200, {
value: expectedContexts,
})
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
const contexts = await wdc.getWindowHandles()
expect(contexts).to.deep.equal(expectedContexts)
})
it('throws an error if the request fails', async () => {
nockContext.get(`/session/${mockSessionId}/window/handles`).reply(500)
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
expect(wdc.getWindowHandles()).to.be.rejectedWith('500: Internal Server Error')
})
})
describe('WebDriverClassic.switchToWindow', () => {
it('returns null when the requests succeeds', async () => {
nockContext.post(`/session/${mockSessionId}/window`, {
handle: 'mock-context-id',
}).reply(200, {
value: null,
})
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
const payload = await wdc.switchToWindow('mock-context-id')
expect(payload).to.equal(null)
})
it('throws an error if the request fails', async () => {
nockContext.post(`/session/${mockSessionId}/window`, {
handle: 'mock-context-id',
}).reply(500)
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
expect(wdc.switchToWindow('mock-context-id')).to.be.rejectedWith('500: Internal Server Error')
})
})
describe('WebDriverClassic.navigate', () => {
let mockNavigationUrl = 'http://localhost:8080'
it('returns null when the requests succeeds', async () => {
nockContext.post(`/session/${mockSessionId}/url`, {
url: mockNavigationUrl,
}).reply(200, {
value: null,
})
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
const payload = await wdc.navigate(mockNavigationUrl)
expect(payload).to.equal(null)
})
it('throws an error if the request fails', async () => {
nockContext.post(`/session/${mockSessionId}/url`, {
url: mockNavigationUrl,
}).reply(500)
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
expect(wdc.navigate(mockNavigationUrl)).to.be.rejectedWith('500: Internal Server Error')
})
})
describe('WebDriverClassic.maximizeWindow', () => {
it('returns null when the requests succeeds', async () => {
nockContext.post(`/session/${mockSessionId}/window/maximize`).reply(200, {
value: null,
})
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
const payload = await wdc.maximizeWindow()
expect(payload).to.equal(null)
})
it('throws an error if the request fails', async () => {
nockContext.post(`/session/${mockSessionId}/window/maximize`).reply(500)
const wdc = new WebDriverClassic(mockOpts.host, mockOpts.port)
// @ts-expect-error
wdc.sessionId = mockSessionId
expect(wdc.maximizeWindow()).to.be.rejectedWith('500: Internal Server Error')
})
})
})

View File

@@ -50,6 +50,7 @@ const getDependencyPathsToKeep = async (buildAppDir) => {
'node_modules/html-webpack-plugin-4/index.js',
'node_modules/html-webpack-plugin-5/index.js',
'node_modules/mocha-7.0.1/index.js',
'packages/server/node_modules/webdriver/build/index.js',
]
let entryPoints = new Set([

View File

@@ -5,10 +5,9 @@ module.exports = {
if (browser.family === 'firefox') {
// this is needed to ensure correct error screenshot / video recording
// resolution of exactly 1280x720
// (height must account for firefox url bar, which we can only shrink to 1px ,
// and the total size of the window url and tab bar, which is 85 pixels for a total offset of 86 pixels)
// (height must account for firefox url bar, which we can only shrink to 2px)
options.args.push(
'-width', '1280', '-height', '806',
'-width', '1280', '-height', '722',
)
} else if (browser.name === 'electron') {
options.preferences.width = 1280

View File

@@ -1723,7 +1723,6 @@
"./node_modules/dayjs/plugin/duration.js",
"./node_modules/dayjs/plugin/relativeTime.js",
"./node_modules/dayjs/plugin/updateLocale.js",
"./node_modules/debug/node_modules/ms/index.js",
"./node_modules/debug/src/common.js",
"./node_modules/dedent/dist/dedent.js",
"./node_modules/define-data-property/index.js",
@@ -1858,8 +1857,7 @@
"./node_modules/find-process/lib/utils.js",
"./node_modules/firefox-profile/lib/firefox_profile.js",
"./node_modules/firefox-profile/lib/profile_finder.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy-sync/copy-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy-sync/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/copy-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/copy.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/empty/index.js",
@@ -1873,17 +1871,15 @@
"./node_modules/firefox-profile/node_modules/fs-extra/lib/json/output-json.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/make-dir.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move-sync/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move-sync/move-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/utils.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/move-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/move.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/output/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/output-file/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/remove/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/remove/rimraf.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/util/stat.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/util/utimes.js",
"./node_modules/firefox-profile/node_modules/ini/ini.js",
"./node_modules/firefox-profile/node_modules/jsonfile/node_modules/universalify/index.js",
"./node_modules/firefox-profile/node_modules/ini/lib/ini.js",
"./node_modules/firefox-profile/node_modules/jsonfile/utils.js",
"./node_modules/firefox-profile/node_modules/universalify/index.js",
"./node_modules/fluent-ffmpeg/lib/capabilities.js",
@@ -3938,7 +3934,7 @@
"./packages/server/lib/browsers/memory/cgroup-v1.ts",
"./packages/server/lib/browsers/memory/default.ts",
"./packages/server/lib/browsers/protocol.ts",
"./packages/server/lib/browsers/webdriver-classic/index.ts",
"./packages/server/lib/browsers/webdriver/index.ts",
"./packages/server/lib/browsers/webkit-automation.ts",
"./packages/server/lib/browsers/webkit.ts",
"./packages/server/lib/cloud/api/scrub_url.ts",
@@ -4196,5 +4192,5 @@
"./tooling/v8-snapshot/cache/darwin/snapshot-entry.js"
],
"deferredHashFile": "yarn.lock",
"deferredHash": "f170955b504058c9220ca27cc0e8cdc5ba4672137e3c373a2fca8fa058215acd"
"deferredHash": "4c1da762a851baab24ef5d1ba9d878ad207147c7703383f4796c0c9c7910240f"
}

View File

@@ -1722,7 +1722,6 @@
"./node_modules/dayjs/plugin/duration.js",
"./node_modules/dayjs/plugin/relativeTime.js",
"./node_modules/dayjs/plugin/updateLocale.js",
"./node_modules/debug/node_modules/ms/index.js",
"./node_modules/debug/src/common.js",
"./node_modules/dedent/dist/dedent.js",
"./node_modules/define-data-property/index.js",
@@ -1857,8 +1856,7 @@
"./node_modules/find-process/lib/utils.js",
"./node_modules/firefox-profile/lib/firefox_profile.js",
"./node_modules/firefox-profile/lib/profile_finder.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy-sync/copy-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy-sync/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/copy-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/copy.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/empty/index.js",
@@ -1872,17 +1870,15 @@
"./node_modules/firefox-profile/node_modules/fs-extra/lib/json/output-json.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/make-dir.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move-sync/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move-sync/move-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/utils.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/move-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/move.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/output/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/output-file/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/remove/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/remove/rimraf.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/util/stat.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/util/utimes.js",
"./node_modules/firefox-profile/node_modules/ini/ini.js",
"./node_modules/firefox-profile/node_modules/jsonfile/node_modules/universalify/index.js",
"./node_modules/firefox-profile/node_modules/ini/lib/ini.js",
"./node_modules/firefox-profile/node_modules/jsonfile/utils.js",
"./node_modules/firefox-profile/node_modules/universalify/index.js",
"./node_modules/fluent-ffmpeg/lib/capabilities.js",
@@ -3937,7 +3933,7 @@
"./packages/server/lib/browsers/memory/cgroup-v1.ts",
"./packages/server/lib/browsers/memory/default.ts",
"./packages/server/lib/browsers/protocol.ts",
"./packages/server/lib/browsers/webdriver-classic/index.ts",
"./packages/server/lib/browsers/webdriver/index.ts",
"./packages/server/lib/browsers/webkit-automation.ts",
"./packages/server/lib/browsers/webkit.ts",
"./packages/server/lib/cloud/api/scrub_url.ts",
@@ -4195,5 +4191,5 @@
"./tooling/v8-snapshot/cache/linux/snapshot-entry.js"
],
"deferredHashFile": "yarn.lock",
"deferredHash": "f170955b504058c9220ca27cc0e8cdc5ba4672137e3c373a2fca8fa058215acd"
"deferredHash": "4c1da762a851baab24ef5d1ba9d878ad207147c7703383f4796c0c9c7910240f"
}

View File

@@ -1725,7 +1725,6 @@
"./node_modules/dayjs/plugin/duration.js",
"./node_modules/dayjs/plugin/relativeTime.js",
"./node_modules/dayjs/plugin/updateLocale.js",
"./node_modules/debug/node_modules/ms/index.js",
"./node_modules/debug/src/common.js",
"./node_modules/dedent/dist/dedent.js",
"./node_modules/define-data-property/index.js",
@@ -1860,8 +1859,7 @@
"./node_modules/find-process/lib/utils.js",
"./node_modules/firefox-profile/lib/firefox_profile.js",
"./node_modules/firefox-profile/lib/profile_finder.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy-sync/copy-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy-sync/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/copy-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/copy.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/copy/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/empty/index.js",
@@ -1875,17 +1873,15 @@
"./node_modules/firefox-profile/node_modules/fs-extra/lib/json/output-json.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/make-dir.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move-sync/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move-sync/move-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/mkdirs/utils.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/move-sync.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/move/move.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/output/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/output-file/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/remove/index.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/remove/rimraf.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/util/stat.js",
"./node_modules/firefox-profile/node_modules/fs-extra/lib/util/utimes.js",
"./node_modules/firefox-profile/node_modules/ini/ini.js",
"./node_modules/firefox-profile/node_modules/jsonfile/node_modules/universalify/index.js",
"./node_modules/firefox-profile/node_modules/ini/lib/ini.js",
"./node_modules/firefox-profile/node_modules/jsonfile/utils.js",
"./node_modules/firefox-profile/node_modules/universalify/index.js",
"./node_modules/fluent-ffmpeg/lib/capabilities.js",
@@ -3937,7 +3933,7 @@
"./packages/server/lib/browsers/memory/cgroup-v1.ts",
"./packages/server/lib/browsers/memory/default.ts",
"./packages/server/lib/browsers/protocol.ts",
"./packages/server/lib/browsers/webdriver-classic/index.ts",
"./packages/server/lib/browsers/webdriver/index.ts",
"./packages/server/lib/browsers/webkit-automation.ts",
"./packages/server/lib/browsers/webkit.ts",
"./packages/server/lib/cloud/api/scrub_url.ts",
@@ -4195,5 +4191,5 @@
"./tooling/v8-snapshot/cache/win32/snapshot-entry.js"
],
"deferredHashFile": "yarn.lock",
"deferredHash": "472108b7086bfe04dfc755373cfb3f35ad51acd2bc8a36d9b2ed6dba033c9954"
"deferredHash": "13b4e2c024574673089f0bd15e763cc1db73256cafb63a98edb1e916317e3d3f"
}

295
yarn.lock
View File

@@ -5927,6 +5927,13 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
"@promptbook/utils@0.70.0-1":
version "0.70.0-1"
resolved "https://registry.npmjs.org/@promptbook/utils/-/utils-0.70.0-1.tgz#a950f1db9397f1b21b72cae9e0b147ffb9a209da"
integrity sha512-qd2lLRRN+sE6UuNMi2tEeUUeb4zmXnxY5EMdfHVXNE+bqBDpUC7/aEfXgA3jnUXEr+xFjQ8PTFQgWvBMaKvw0g==
dependencies:
spacetrim "0.11.39"
"@puppeteer/browsers@1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.7.1.tgz#04f1e3aec4b87f50a7acc8f64be2149bda014f0a"
@@ -5940,6 +5947,20 @@
unbzip2-stream "1.4.3"
yargs "17.7.1"
"@puppeteer/browsers@^2.2.0":
version "2.4.0"
resolved "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz#a0dd0f4e381e53f509109ae83b891db5972750f5"
integrity sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==
dependencies:
debug "^4.3.6"
extract-zip "^2.0.1"
progress "^2.0.3"
proxy-agent "^6.4.0"
semver "^7.6.3"
tar-fs "^3.0.6"
unbzip2-stream "^1.4.3"
yargs "^17.7.2"
"@purge-icons/generated@0.8.1":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@purge-icons/generated/-/generated-0.8.1.tgz#15544a9c9b2436e436d884828077f9d88df660e7"
@@ -7763,10 +7784,12 @@
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@>=10.0.0", "@types/node@^18.11.18", "@types/node@^18.17.5":
version "18.18.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.3.tgz#e5188135fc2909b46530c798ef49be65083be3fd"
integrity sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==
"@types/node@*", "@types/node@>=10.0.0", "@types/node@^20.1.0":
version "20.16.9"
resolved "https://registry.npmjs.org/@types/node/-/node-20.16.9.tgz#1217c6cc77c4f3aaf4a6c76fb56b790e81e48120"
integrity sha512-rkvIVJxsOfBejxK7I0FO5sa2WxFmJCzoDwcd88+fq/CUfynNywTo/1/T6hyFz22CyztsnLS9nVlHOnTI36RH5w==
dependencies:
undici-types "~6.19.2"
"@types/node@16.9.1":
version "16.9.1"
@@ -7783,6 +7806,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
"@types/node@^18.11.18", "@types/node@^18.17.5":
version "18.18.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.3.tgz#e5188135fc2909b46530c798ef49be65083be3fd"
integrity sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==
"@types/node@^8.0.7":
version "8.10.66"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3"
@@ -8244,6 +8272,11 @@
dependencies:
"@types/node" "*"
"@types/which@^2.0.1":
version "2.0.2"
resolved "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz#54541d02d6b1daee5ec01ac0d1b37cecf37db1ae"
integrity sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==
"@types/ws@^7.4.7":
version "7.4.7"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
@@ -8251,10 +8284,10 @@
dependencies:
"@types/node" "*"
"@types/ws@^8.5.10", "@types/ws@^8.5.5":
version "8.5.10"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"
integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==
"@types/ws@^8.5.10", "@types/ws@^8.5.3", "@types/ws@^8.5.5":
version "8.5.12"
resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e"
integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==
dependencies:
"@types/node" "*"
@@ -8718,16 +8751,60 @@
dependencies:
vue-demi "*"
"@wdio/logger@^8.28.0":
version "8.38.0"
resolved "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz#a96406267e800bef9c58ac95de00f42ab0d3ac5c"
integrity sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==
"@wdio/config@9.0.0":
version "9.0.0"
resolved "https://registry.npmjs.org/@wdio/config/-/config-9.0.0.tgz#4022e996928e3f1b29436c1a3a446a464c319a39"
integrity sha512-OeRSEO3fTDMeKcGWoS39YO5lrMNT8qn+/E7ZcsG6NAbXu2o0ZfLDgDh1Guhe/a8s3LKc6dck5GxgFEuAylwlAw==
dependencies:
"@wdio/logger" "9.0.0"
"@wdio/types" "9.0.0"
"@wdio/utils" "9.0.0"
decamelize "^6.0.0"
deepmerge-ts "^7.0.3"
glob "^10.2.2"
import-meta-resolve "^4.0.0"
"@wdio/logger@9.0.0", "@wdio/logger@^8.38.0", "@wdio/logger@^9.0.0":
version "9.0.0"
resolved "https://registry.npmjs.org/@wdio/logger/-/logger-9.0.0.tgz#f13ebacfce2903f31b8c6894bb152407de3cba79"
integrity sha512-DmmkVjxcCFUCFJVymca4/gQF4uTtevG4AF+jCzPUA4NByTnyjVtg8x83K0sG3/YX5SOxgc+JUhSdH8g5wceWSA==
dependencies:
chalk "^5.1.2"
loglevel "^1.6.0"
loglevel-plugin-prefix "^0.8.4"
strip-ansi "^7.1.0"
"@wdio/protocols@9.0.0":
version "9.0.0"
resolved "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.0.0.tgz#bda1a6c02770711f5cf429b800da0af991b117e1"
integrity sha512-qM+TwCvFjmomX8oi0Ns8LXfG5dvXiqQKLaJt9Nj+JXxczSh6XsLnFTLQAcG9ynnU7uz2v1TqM11M5enj74WEAA==
"@wdio/types@9.0.0":
version "9.0.0"
resolved "https://registry.npmjs.org/@wdio/types/-/types-9.0.0.tgz#0bc2e3dcc3a718a9042779ecbf953f30882a9bc8"
integrity sha512-XDqIjNCw2ftWKXWn8vTqqZAMtjSxwJwO8IAEQczgRB0fUCvvipkeT3ZobT5Z4Mo5uvLLTaOqJxb4nwPXOfVt/A==
dependencies:
"@types/node" "^20.1.0"
"@wdio/utils@9.0.0":
version "9.0.0"
resolved "https://registry.npmjs.org/@wdio/utils/-/utils-9.0.0.tgz#4613dcd01944165a5c6004bb557ef20a51acde5b"
integrity sha512-MGtMGHf/rbMChD/qtPLpnZuF685W5gBnMhP5b8hNzB+riDJIFbl/e41Jqhf4scrkpXHtjBM8kdvXZEI6SW3AuA==
dependencies:
"@puppeteer/browsers" "^2.2.0"
"@wdio/logger" "9.0.0"
"@wdio/types" "9.0.0"
decamelize "^6.0.0"
deepmerge-ts "^7.0.3"
edgedriver "^5.6.1"
geckodriver "^4.3.3"
get-port "^7.0.0"
import-meta-resolve "^4.0.0"
locate-app "^2.2.24"
safaridriver "^0.1.2"
split2 "^4.2.0"
wait-port "^1.1.0"
"@webassemblyjs/ast@1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
@@ -9168,7 +9245,7 @@
resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.36.0.tgz#7a1b53f4091e18d0b404873ea3e3c83589c765f2"
integrity sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==
"@zip.js/zip.js@^2.7.44":
"@zip.js/zip.js@^2.7.48":
version "2.7.52"
resolved "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.52.tgz#bc11de93b41f09e03155bc178e7f9c2e2612671d"
integrity sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==
@@ -13294,12 +13371,12 @@ debug@3.2.6:
dependencies:
ms "^2.1.1"
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@~4.3.1:
version "4.3.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@~4.3.1:
version "4.3.7"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
dependencies:
ms "2.1.2"
ms "^2.1.3"
debug@4.1.1:
version "4.1.1"
@@ -13450,6 +13527,11 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge-ts@^7.0.3:
version "7.1.0"
resolved "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.0.tgz#c1e0f11f64465b3e04ca4e03658235fba1cce07b"
integrity sha512-q6bNsfNBtgr8ZOQqmZbl94MmYWm+QcDNIkqCxVWiw1vKvf+y/N2dZQKdnDXn4c5Ygt/y63tDof6OCN+2YwWVEg==
deepmerge@^4.2.2, deepmerge@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
@@ -14250,6 +14332,27 @@ ecstatic@^3.3.2:
minimist "^1.1.0"
url-join "^2.0.5"
edge-paths@^3.0.5:
version "3.0.5"
resolved "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz#9a35361d701d9b5dc07f641cebe8da01ede80937"
integrity sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==
dependencies:
"@types/which" "^2.0.1"
which "^2.0.2"
edgedriver@5.6.1, edgedriver@^5.6.1:
version "5.6.1"
resolved "https://registry.npmjs.org/edgedriver/-/edgedriver-5.6.1.tgz#36971f000aee8756c11f3fb1dc5273f109e860d5"
integrity sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==
dependencies:
"@wdio/logger" "^8.38.0"
"@zip.js/zip.js" "^2.7.48"
decamelize "^6.0.0"
edge-paths "^3.0.5"
fast-xml-parser "^4.4.1"
node-fetch "^3.3.2"
which "^4.0.0"
editorconfig@^0.15.3:
version "0.15.3"
resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
@@ -15943,6 +16046,13 @@ fast-xml-parser@4.2.5:
dependencies:
strnum "^1.0.5"
fast-xml-parser@^4.4.1:
version "4.5.0"
resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37"
integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==
dependencies:
strnum "^1.0.5"
fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.16:
version "1.0.16"
resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
@@ -16347,16 +16457,16 @@ fined@^1.0.1:
object.pick "^1.2.0"
parse-filepath "^1.0.1"
firefox-profile@4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/firefox-profile/-/firefox-profile-4.6.0.tgz#e819b2f75a05b4d215d0c30a74d5b9d2d9ba8ae1"
integrity sha512-I9rAm1w8U3CdhgO4EzTJsCvgcbvynZn9lOySkZf78wUdUIQH2w9QOKf3pAX+THt2XMSSR3kJSuM8P7bYux9j8g==
firefox-profile@4.7.0:
version "4.7.0"
resolved "https://registry.npmjs.org/firefox-profile/-/firefox-profile-4.7.0.tgz#97087b17a9a38fea58ec0acf2bca19a5cc121cb7"
integrity sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==
dependencies:
adm-zip "~0.5.x"
fs-extra "~9.0.1"
ini "~2.0.0"
minimist "^1.2.5"
xml2js "^0.5.0"
fs-extra "^11.2.0"
ini "^4.1.3"
minimist "^1.2.8"
xml2js "^0.6.2"
flagged-respawn@^1.0.0:
version "1.0.1"
@@ -16656,16 +16766,6 @@ fs-extra@^7.0.1:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@~9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==
dependencies:
at-least-node "^1.0.0"
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^1.0.0"
fs-minipass@^2.0.0, fs-minipass@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@@ -16829,16 +16929,16 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
geckodriver@4.4.2:
version "4.4.2"
resolved "https://registry.npmjs.org/geckodriver/-/geckodriver-4.4.2.tgz#b5b72b3e5deb905947151f214b96f52505c2dd3a"
integrity sha512-/JFJ7DJPJUvDhLjzQk+DwjlkAmiShddfRHhZ/xVL9FWbza5Bi3UMGmmerEKqD69JbRs7R81ZW31co686mdYZyA==
geckodriver@4.5.1, geckodriver@^4.3.3:
version "4.5.1"
resolved "https://registry.npmjs.org/geckodriver/-/geckodriver-4.5.1.tgz#624fc01815c1aa498dd3f717f7bd4c6cca0c57b8"
integrity sha512-lGCRqPMuzbRNDWJOQcUqhNqPvNsIFu6yzXF8J/6K3WCYFd2r5ckbeF7h1cxsnjA7YLSEiWzERCt6/gjZ3tW0ug==
dependencies:
"@wdio/logger" "^8.28.0"
"@zip.js/zip.js" "^2.7.44"
"@wdio/logger" "^9.0.0"
"@zip.js/zip.js" "^2.7.48"
decamelize "^6.0.0"
http-proxy-agent "^7.0.2"
https-proxy-agent "^7.0.4"
https-proxy-agent "^7.0.5"
node-fetch "^3.3.2"
tar-fs "^3.0.6"
which "^4.0.0"
@@ -16909,6 +17009,11 @@ get-port@5.1.1:
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
get-port@^7.0.0:
version "7.1.0"
resolved "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz#d5a500ebfc7aa705294ec2b83cc38c5d0e364fec"
integrity sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==
get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -18237,7 +18342,7 @@ http-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.2:
http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1, http-proxy-agent@^7.0.2:
version "7.0.2"
resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
@@ -18349,7 +18454,7 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.4:
https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5:
version "7.0.5"
resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2"
integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==
@@ -18598,7 +18703,7 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@2.0.0, ini@~2.0.0:
ini@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
@@ -20812,6 +20917,15 @@ local-pkg@^0.5.0:
mlly "^1.4.2"
pkg-types "^1.0.3"
locate-app@^2.2.24:
version "2.4.43"
resolved "https://registry.npmjs.org/locate-app/-/locate-app-2.4.43.tgz#5686a0e308b86889e7c88463a507186ca03ec1d5"
integrity sha512-BX6NEdECUGcDQw8aqqg02qLyF9rF8V+dAfyAnBzL2AofIlIvf4Q6EGXnzVWpWot9uBE+x/o8CjXHo7Zlegu91Q==
dependencies:
"@promptbook/utils" "0.70.0-1"
type-fest "2.13.0"
userhome "1.0.0"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -22546,7 +22660,7 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2:
ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@@ -25637,6 +25751,20 @@ proxy-agent@6.3.1:
proxy-from-env "^1.1.0"
socks-proxy-agent "^8.0.2"
proxy-agent@^6.4.0:
version "6.4.0"
resolved "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d"
integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==
dependencies:
agent-base "^7.0.2"
debug "^4.3.4"
http-proxy-agent "^7.0.1"
https-proxy-agent "^7.0.3"
lru-cache "^7.14.1"
pac-proxy-agent "^7.0.1"
proxy-from-env "^1.1.0"
socks-proxy-agent "^8.0.2"
proxy-from-env@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
@@ -25696,7 +25824,7 @@ pump@^2.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
pump@^3.0.0, pump@^3.0.2:
pump@^3.0.0:
version "3.0.2"
resolved "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8"
integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==
@@ -27195,6 +27323,11 @@ rxjs@^7.1.0, rxjs@^7.5.5:
dependencies:
tslib "^2.1.0"
safaridriver@^0.1.2:
version "0.1.2"
resolved "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz#166571d5881c7d6f884900d92d51ee1309c05aa4"
integrity sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==
safe-array-concat@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb"
@@ -27501,7 +27634,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2:
semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3:
version "7.6.3"
resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
@@ -28550,6 +28683,11 @@ sourcemap-codec@^1.4.8:
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
spacetrim@0.11.39:
version "0.11.39"
resolved "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.39.tgz#9f606970741512c705fe4969139b0116e239deb3"
integrity sha512-S/baW29azJ7py5ausQRE2S6uEDQnlxgMHOEEq4V770ooBDD1/9kZnxRcco/tjZYuDuqYXblCk/r3N13ZmvHZ2g==
sparkles@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c"
@@ -28655,7 +28793,7 @@ split2@^3.0.0:
dependencies:
readable-stream "^3.0.0"
split2@^4.0.0:
split2@^4.0.0, split2@^4.2.0:
version "4.2.0"
resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
@@ -30381,6 +30519,11 @@ type-detect@^1.0.0:
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
integrity sha1-diIXzAbbJY7EiQihKY6LlRIejqI=
type-fest@2.13.0:
version "2.13.0"
resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.13.0.tgz#d1ecee38af29eb2e863b22299a3d68ef30d2abfb"
integrity sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
@@ -30602,7 +30745,7 @@ unbox-primitive@^1.0.2:
has-symbols "^1.0.3"
which-boxed-primitive "^1.0.2"
unbzip2-stream@1.4.3:
unbzip2-stream@1.4.3, unbzip2-stream@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
@@ -30644,6 +30787,11 @@ undertaker@^1.2.1:
object.reduce "^1.0.0"
undertaker-registry "^1.0.0"
undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
unfetch@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db"
@@ -30777,11 +30925,6 @@ universalify@^0.2.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
universalify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==
universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
@@ -31005,6 +31148,11 @@ user-home@^2.0.0:
dependencies:
os-homedir "^1.0.0"
userhome@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz#b6491ff12d21a5e72671df9ccc8717e1c6688c0b"
integrity sha512-ayFKY3H+Pwfy4W98yPdtH1VqH4psDeyW8lYYFzfecR9d6hqLpqhecktvYR3SEEXt7vG0S1JEpciI3g94pMErig==
utf8-byte-length@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
@@ -31459,7 +31607,7 @@ vuex@^4.0.0:
resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.0.0.tgz#ac877aa76a9c45368c979471e461b520d38e6cf5"
integrity sha512-56VPujlHscP5q/e7Jlpqc40sja4vOhC4uJD1llBCWolVI8ND4+VzisDVkUMl+z5y0MpIImW6HjhNc+ZvuizgOw==
wait-port@1.1.0:
wait-port@1.1.0, wait-port@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz#e5d64ee071118d985e2b658ae7ad32b2ce29b6b5"
integrity sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==
@@ -31535,6 +31683,21 @@ web-streams-polyfill@^3.0.3:
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
webdriver@9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/webdriver/-/webdriver-9.0.0.tgz#9e07c1c5f963c09131c9ab3dbc5551cacb03d905"
integrity sha512-wcysboeZ1Ax6K7WPTF/vCNzegcdg+f453GOJd86ARE+wf3xRSQsvCZDwnEO8/7wvj/3xV9KMYPeuhPJ6b5VFvw==
dependencies:
"@types/node" "^20.1.0"
"@types/ws" "^8.5.3"
"@wdio/config" "9.0.0"
"@wdio/logger" "9.0.0"
"@wdio/protocols" "9.0.0"
"@wdio/types" "9.0.0"
"@wdio/utils" "9.0.0"
deepmerge-ts "^7.0.3"
ws "^8.8.0"
webextension-polyfill@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.4.0.tgz#9cc5a60f0f2bf907a6b349fdd7e61701f54956f9"
@@ -32197,10 +32360,10 @@ ws@^6.2.1:
dependencies:
async-limiter "~1.0.0"
ws@^8.0.0, ws@^8.13.0, ws@^8.16.0, ws@^8.5.0:
version "8.16.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
ws@^8.0.0, ws@^8.13.0, ws@^8.16.0, ws@^8.5.0, ws@^8.8.0:
version "8.18.0"
resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
ws@~8.11.0:
version "8.11.0"
@@ -32248,10 +32411,10 @@ xml2js@^0.4.5:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xml2js@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7"
integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==
xml2js@^0.6.2:
version "0.6.2"
resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499"
integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
@@ -32572,7 +32735,7 @@ yargs@^15.1.0, yargs@^15.3.1:
y18n "^4.0.0"
yargs-parser "^18.1.2"
yargs@^17.0.0, yargs@^17.0.1, yargs@^17.5.1, yargs@^17.6.2:
yargs@^17.0.0, yargs@^17.0.1, yargs@^17.5.1, yargs@^17.6.2, yargs@^17.7.2:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==