From 0e316966af3f281b3e297cae2d5f4c0c3054f8ed Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Tue, 15 Oct 2024 11:18:14 -0400 Subject: [PATCH] misc: implement webdriver on top of geckodriver in order to reduce overhead maintenance and code (#30324) * misc: implement webdriver npm package as the client for the webdriver specification * misc: go back to xulstore to save browser preferences [run ci] * add changelog [run ci] * chore: fix screenshot resolution [run ci] * fix check-ts issues [run ci] * run windows ci [run ci] * run ci * add comments [run ci] * build binaries for webdriver impl [run ci] * fix Cypress namespace missing issue. see https://github.com/cypress-io/cypress/pull/30331/commits/6d7ba97c2562054e11983e8bfed576f0fa3ee37b * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * patch edgedriver and preserve dependency paths for webdriver so they are included in the binary [run ci] * fix issues with firefox profile not being created in open mode when old profile exists from previous cypress versions where they are no longer compatible [run ci] * address comments that came up in review [run ci] * see if this fixes extension test [run ci] * make sure process kill emits the exit event [run ci] * update geckodriver to include contribution patch to types [run ci] * fix misapplication of geckodriver package (accidentally deleted) [run ci] * empty commit to trigger ci [run ci] * address comments from code review [run ci] --------- Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> --- .circleci/cache-version.txt | 2 +- .circleci/workflows.yml | 10 +- cli/CHANGELOG.md | 1 + package.json | 2 + packages/data-context/src/DataContext.ts | 2 +- .../src/graphql/urqlFetchSocketAdapter.ts | 2 +- packages/server/lib/browsers/firefox-util.ts | 18 +- packages/server/lib/browsers/firefox.ts | 219 ++++--- .../README.md => firefox_automation.md} | 28 +- .../server/lib/browsers/geckodriver/index.ts | 122 ---- .../lib/browsers/webdriver-classic/index.ts | 263 --------- .../server/lib/browsers/webdriver/index.ts | 15 + packages/server/package.json | 12 +- .../patches/@wdio+protocols+9.0.0.patch | 79 +++ .../server/patches/@wdio+utils+9.0.0.patch | 31 + .../server/patches/edgedriver+5.6.1.patch | 240 ++++++++ ...er+4.4.2.patch => geckodriver+4.5.1.patch} | 167 ++++-- packages/server/patches/webdriver+9.0.0.patch | 199 +++++++ .../server/test/unit/browsers/firefox_spec.ts | 557 +++++++++++------- .../unit/browsers/geckodriver/index_spec.ts | 242 -------- .../browsers/webdriver-classic/index_spec.ts | 316 ---------- packages/types/src/spec.ts | 3 +- scripts/binary/binary-cleanup.js | 1 + system-tests/lib/pluginUtils.js | 5 +- .../cache/darwin/snapshot-meta.json | 104 ++-- .../cache/linux/snapshot-meta.json | 17 +- .../cache/win32/snapshot-meta.json | 104 ++-- yarn.lock | 295 +++++++--- 28 files changed, 1506 insertions(+), 1550 deletions(-) rename packages/server/lib/browsers/{geckodriver/README.md => firefox_automation.md} (55%) delete mode 100644 packages/server/lib/browsers/geckodriver/index.ts delete mode 100644 packages/server/lib/browsers/webdriver-classic/index.ts create mode 100644 packages/server/lib/browsers/webdriver/index.ts create mode 100644 packages/server/patches/@wdio+protocols+9.0.0.patch create mode 100644 packages/server/patches/@wdio+utils+9.0.0.patch create mode 100644 packages/server/patches/edgedriver+5.6.1.patch rename packages/server/patches/{geckodriver+4.4.2.patch => geckodriver+4.5.1.patch} (65%) create mode 100644 packages/server/patches/webdriver+9.0.0.patch delete mode 100644 packages/server/test/unit/browsers/geckodriver/index_spec.ts delete mode 100644 packages/server/test/unit/browsers/webdriver-classic/index_spec.ts diff --git a/.circleci/cache-version.txt b/.circleci/cache-version.txt index deafa871233e..4bb51c5873a8 100644 --- a/.circleci/cache-version.txt +++ b/.circleci/cache-version.txt @@ -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 diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 7c5cdac5165e..7cc7af7935e1 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -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 >> @@ -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 diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 50b468dd75ca..3ab6f48f4221 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -10,6 +10,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:** diff --git a/package.json b/package.json index 8f6aeb311e83..ae8e9e8119af 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/data-context/src/DataContext.ts b/packages/data-context/src/DataContext.ts index 3b4db97e276a..895dc4d12e4e 100644 --- a/packages/data-context/src/DataContext.ts +++ b/packages/data-context/src/DataContext.ts @@ -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), diff --git a/packages/frontend-shared/src/graphql/urqlFetchSocketAdapter.ts b/packages/frontend-shared/src/graphql/urqlFetchSocketAdapter.ts index 1db4ac9f551b..c592434196e9 100644 --- a/packages/frontend-shared/src/graphql/urqlFetchSocketAdapter.ts +++ b/packages/frontend-shared/src/graphql/urqlFetchSocketAdapter.ts @@ -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((resolve, reject) => { // Handle aborted requests if (fetchOptions.signal) { diff --git a/packages/server/lib/browsers/firefox-util.ts b/packages/server/lib/browsers/firefox-util.ts index 40d0828c24f3..07ec85c45caa 100644 --- a/packages/server/lib/browsers/firefox-util.ts +++ b/packages/server/lib/browsers/firefox-util.ts @@ -6,9 +6,9 @@ import Foxdriver from '@benmalka/foxdriver' import * as protocol from './protocol' 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') @@ -20,7 +20,7 @@ let timings = { collections: [] as any[], } -let webDriverClassic: WebDriverClassic +let webdriverClient: WebDriverClient const getTabId = (tab) => { return _.get(tab, 'browsingContextID') @@ -103,11 +103,11 @@ async function connectToNewTabClassic () { // 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) { @@ -140,7 +140,7 @@ async function setupCDP (remotePort: number, automation: Automation, onError?: ( } async function navigateToUrlClassic (url: string) { - await webDriverClassic.navigate(url) + await webdriverClient.navigateTo(url) } const logGcDetails = () => { @@ -213,17 +213,17 @@ export default { url, foxdriverPort, remotePort, - webDriverClassic: wdcInstance, + webdriverClient: wdInstance, }: { automation: Automation onError?: (err: Error) => void url: string foxdriverPort: number remotePort: number - webDriverClassic: WebDriverClassic + webdriverClient: WebDriverClient }): Promise { // set the WebDriver classic instance instantiated from geckodriver - webDriverClassic = wdcInstance + webdriverClient = wdInstance const [, browserCriClient] = await Promise.all([ this.setupFoxdriver(foxdriverPort), setupCDP(remotePort, automation, onError), diff --git a/packages/server/lib/browsers/firefox.ts b/packages/server/lib/browsers/firefox.ts index c1cfc9a88139..6f0a85efcc03 100644 --- a/packages/server/lib/browsers/firefox.ts +++ b/packages/server/lib/browsers/firefox.ts @@ -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 @@ -443,16 +451,30 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc const [ foxdriverPort, marionettePort, - geckoDriverPort, webDriverBiDiPort, - ] = await Promise.all([getPort(), getPort(), getPort(), getPort()]) + ] = await Promise.all([getPort(), getPort(), getPort()]) defaultLaunchOptions.preferences['devtools.debugger.remote-port'] = foxdriverPort 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', { foxdriverPort, 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, @@ -471,23 +493,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') @@ -513,26 +548,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 @@ -544,85 +579,119 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc ...process.env, }, }, - }) + jsdebugger: Debug.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) || false, + log: Debug.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) ? 'debug' : 'error', + logNoTruncate: Debug.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE), + } - const wdcInstance = new WebDriverClassic('127.0.0.1', geckoDriverPort) + // 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() - debug('launch in firefox', { url, args: launchOptions.args }) + browserInstanceWrapper.kill = () => undefined - 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, + try { + /** + * 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/* + * @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions + */ + const base64EncodedProfile = await new Promise((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, }, - // @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, + firstMatch: [], }, - }, - } + } - try { - debugVerbose(`creating session with capabilities %s`, JSON.stringify(capabilitiesToSend.capabilities)) + 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() - - // 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() - } - - // install the browser extensions - await Promise.all(_.map(launchOptions.extensions, (path) => { - debug(`installing extension at path: ${path}`) + const driverPID: number = webdriverClient.capabilities['wdio:driverPID'] as number - return wdcInstance!.installAddOn({ - path, - temporary: true, - }) - })) - - debug('setting up firefox utils') - browserCriClient = await firefoxUtil.setup({ automation, url, foxdriverPort, webDriverClassic: wdcInstance, remotePort: cdpPort, onError: options.onError }) + debug(`webdriver running on pid: ${driverPID}`) - // 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 and webdriver') + const driverReturnStatus = process.kill(driverPID) - debug('closing geckodriver') + // needed for closing the browser when switching browsers in open mode to signal + // the browser is done closing + browserInstanceWrapper.emit('exit') - return originalGeckoDriverKill.apply(geckoDriverInstance, args) + 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(), }) @@ -630,7 +699,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 () { diff --git a/packages/server/lib/browsers/geckodriver/README.md b/packages/server/lib/browsers/firefox_automation.md similarity index 55% rename from packages/server/lib/browsers/geckodriver/README.md rename to packages/server/lib/browsers/firefox_automation.md index fb635abd7203..0c7e57222fe7 100644 --- a/packages/server/lib/browsers/geckodriver/README.md +++ b/packages/server/lib/browsers/firefox_automation.md @@ -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. diff --git a/packages/server/lib/browsers/geckodriver/index.ts b/packages/server/lib/browsers/geckodriver/index.ts deleted file mode 100644 index 8120ddb70736..000000000000 --- a/packages/server/lib/browsers/geckodriver/index.ts +++ /dev/null @@ -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 - download: (geckodriverVersion?: string, cacheDir?: string) => Promise - } = () => { - /** - * 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 { - 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) - } - } -} diff --git a/packages/server/lib/browsers/webdriver-classic/index.ts b/packages/server/lib/browsers/webdriver-classic/index.ts deleted file mode 100644 index 81eb62e06b87..000000000000 --- a/packages/server/lib/browsers/webdriver-classic/index.ts +++ /dev/null @@ -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} - the results of the Webdriver Session (enabled through remote.active-protocols) - */ - async createSession (args: { - capabilities: {[key: string]: any} - }): Promise { - 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} All the available top-level contexts/handles - */ - async getWindowHandles (): Promise { - 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} - */ - async switchToWindow (handle: string): Promise { - 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} - */ - async maximizeWindow (): Promise { - 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} - */ - async navigate (url: string): Promise { - 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 - } - } -} diff --git a/packages/server/lib/browsers/webdriver/index.ts b/packages/server/lib/browsers/webdriver/index.ts new file mode 100644 index 000000000000..e586119b9f75 --- /dev/null +++ b/packages/server/lib/browsers/webdriver/index.ts @@ -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] })) + } +} diff --git a/packages/server/package.json b/packages/server/package.json index 6374ba01e5f5..ba5cf7f3ffeb 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -61,6 +61,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", @@ -68,10 +69,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", @@ -107,7 +108,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", @@ -131,6 +131,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" }, @@ -217,10 +218,11 @@ "nohoist": [ "@benmalka/foxdriver", "devtools-protocol", + "edgedriver", "geckodriver", "http-proxy", - "pump", - "tsconfig-paths" + "tsconfig-paths", + "webdriver" ] }, "optionalDependencies": { diff --git a/packages/server/patches/@wdio+protocols+9.0.0.patch b/packages/server/patches/@wdio+protocols+9.0.0.patch new file mode 100644 index 000000000000..f07bd7459da8 --- /dev/null +++ b/packages/server/patches/@wdio+protocols+9.0.0.patch @@ -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 + }, + { diff --git a/packages/server/patches/@wdio+utils+9.0.0.patch b/packages/server/patches/@wdio+utils+9.0.0.patch new file mode 100644 index 000000000000..9e1f8eac9b56 --- /dev/null +++ b/packages/server/patches/@wdio+utils+9.0.0.patch @@ -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; + } + }); diff --git a/packages/server/patches/edgedriver+5.6.1.patch b/packages/server/patches/edgedriver+5.6.1.patch new file mode 100644 index 000000000000..ec35c3a94c95 --- /dev/null +++ b/packages/server/patches/edgedriver+5.6.1.patch @@ -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`
+-Default: `latest` +- +-### port +-The port on which the driver should run. +- +-Example: `9515`
+-Type: `number` +- +-### adbPort +-The port on which the ADB driver should run. +- +-Example: `9515`
+-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[]`
+-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[]`
+-Default: `['*']` +- +-### cacheDir +-The path to the root of the cache directory. +- +-Type: `string`
+-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`
+-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 diff --git a/packages/server/patches/geckodriver+4.4.2.patch b/packages/server/patches/geckodriver+4.5.1.patch similarity index 65% rename from packages/server/patches/geckodriver+4.4.2.patch rename to packages/server/patches/geckodriver+4.5.1.patch index 18628d957f8d..f8c6b8cd0132 100644 --- a/packages/server/patches/geckodriver+4.4.2.patch +++ b/packages/server/patches/geckodriver+4.5.1.patch @@ -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`
-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`
+-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 diff --git a/packages/server/patches/webdriver+9.0.0.patch b/packages/server/patches/webdriver+9.0.0.patch new file mode 100644 index 000000000000..658561f221b4 --- /dev/null +++ b/packages/server/patches/webdriver+9.0.0.patch @@ -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`
+-Required: `true` +- +-### logLevel +-Level of logging verbosity. +- +-Type: `String`
+-Default: *info*
+-Options: *trace* | *debug* | *info* | *warn* | *error* | *silent* +- +-### protocol +-Protocol to use when communicating with the Selenium standalone server (or driver). +- +-Type: `String`
+-Default: *http* +-Options: *http* | *https* +- +-### hostname +-Host of your WebDriver server. +- +-Type: `String`
+-Default: *localhost* +- +-### port +-Port your WebDriver server is on. +- +-Type: `Number`
+-Default: `undefined` +- +-### path +-Path to WebDriver endpoint or grid server. +- +-Type: `String`
+-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`
+-Default: *120000* +- +-### connectionRetryCount +-Count of request retries to the Selenium server. +- +-Type: `Number`
+-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`
+-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`
+-Default: *none* +- +-### transformResponse +-Function intercepting HTTP response objects after a WebDriver response has arrived. +- +-Type: `(Response, RequestOptions) => Response`
+-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"); diff --git a/packages/server/test/unit/browsers/firefox_spec.ts b/packages/server/test/unit/browsers/firefox_spec.ts index 54efd09c2f51..f492d6269671 100644 --- a/packages/server/test/unit/browsers/firefox_spec.ts +++ b/packages/server/test/unit/browsers/firefox_spec.ts @@ -1,22 +1,22 @@ 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 Foxdriver from '@benmalka/foxdriver' 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 protocol = require('../../../lib/browsers/protocol') @@ -26,7 +26,8 @@ describe('lib/browsers/firefox', () => { const port = 3333 let foxdriver: any let foxdriverTab: any - let wdcInstance: sinon.SinonStubbedInstance + let wdInstance: sinon.SinonStubbedInstance + let browserCriClient: BrowserCriClient const stubFoxdriver = () => { foxdriverTab = { @@ -67,46 +68,26 @@ describe('lib/browsers/firefox', () => { sinon.stub(protocol, '_connectAsync').resolves(null) - this.browserInstance = { - // should be high enough to not kill any real PIDs - pid: Number.MAX_SAFE_INTEGER, - } - - 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() }) @@ -115,7 +96,7 @@ describe('lib/browsers/firefox', () => { 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({}), } @@ -132,14 +113,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') @@ -156,171 +141,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(webdriver.newSession).to.have.been.calledWith(sinon.match({ + capabilities: { + alwaysMatch: { + 'moz:firefoxOptions': { + prefs: { + 'foo': 'bar', + }, }, - }), - }) - - 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'] : []), - ], + }, + firstMatch: [], + }, + })) + }) + + 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', }, - 'moz:debuggerAddress': true, - }, + }), + jsdebugger: false, + log: 'error', + logNoTruncate: false, + }, }, - )) + firstMatch: [], + }), + }) - expect(wdcInstance.installAddOn).to.have.been.calledWith(sinon.match({ - path: '/path/to/ext', - temporary: true, - })) + expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true) - expect(wdcInstance.navigate).to.have.been.calledWith('http://') + 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 }) - }) + // 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 () { - this.browser.isHeadless = true - - return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => { - expect(wdcInstance.maximizeWindow).not.to.have.been.called + describe('debugging', () => { + afterEach(() => { + debug.disable() }) - }) - 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: {}, - }) + 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: [], + }), + }) - return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => { - expect(wdcInstance.maximizeWindow).not.to.have.been.called + 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('maximizes the browser if headed and no "-width" or "-height" arg is set', function () { - this.browser.isHeadless = false + 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).to.have.been.called - }) + await firefox.open(this.browser, 'http://', this.options, this.automation) + expect(wdInstance.maximizeWindow).not.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) - 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) - }) + expect(webdriver.newSession).to.have.been.calledWith(sinon.match({ + capabilities: { + alwaysMatch: { + 'moz:firefoxOptions': { + prefs: { + 'general.useragent.override': 'User Agent', + }, + }, + }, + firstMatch: [], + }, + })) }) - it('writes extension and ensure write access', function () { - // TODO: Test is failing locally, figure out why?? - if (!process.env.CI) { - return - } + 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', async function () { mockfs({ [path.resolve(`${__dirname }../../../../../extension/dist/v2`)]: { 'background.js': mockfs.file({ @@ -341,57 +412,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(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: [], + }, + })) + }) - expect(FirefoxProfile.prototype.setPreference).to.be.calledWith('network.proxy.no_proxies_on') - }) + 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.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('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('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.setPreference).not.to.be.calledWith('network.proxy.no_proxies_on') - }) + 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. @@ -399,31 +485,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) - it('updates the preferences', function () { - return firefox.open(this.browser, 'http://', this.options, this.automation).then(() => { - expect(FirefoxProfile.prototype.updatePreferences).to.be.called - }) + expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({ + preferences: { + 'remote.active-protocols': 2, + }, + }), this.options) }) - 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('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('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]', @@ -431,22 +512,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 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 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('clears browser cache', function () { + 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', async function () { mockfs({ '/path/to/appData/firefox-stable/interactive/': { 'CypressCache': { 'foo': 'bar' }, @@ -455,12 +548,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 retrying socket fails', async function () { @@ -475,30 +564,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') }) }) }) diff --git a/packages/server/test/unit/browsers/geckodriver/index_spec.ts b/packages/server/test/unit/browsers/geckodriver/index_spec.ts deleted file mode 100644 index b0d52505a361..000000000000 --- a/packages/server/test/unit/browsers/geckodriver/index_spec.ts +++ /dev/null @@ -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' - }) - }) - }) -}) diff --git a/packages/server/test/unit/browsers/webdriver-classic/index_spec.ts b/packages/server/test/unit/browsers/webdriver-classic/index_spec.ts deleted file mode 100644 index d69cedf120a1..000000000000 --- a/packages/server/test/unit/browsers/webdriver-classic/index_spec.ts +++ /dev/null @@ -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') - }) - }) -}) diff --git a/packages/types/src/spec.ts b/packages/types/src/spec.ts index 1725e6723c24..8003d2c2b6ff 100644 --- a/packages/types/src/spec.ts +++ b/packages/types/src/spec.ts @@ -13,7 +13,8 @@ export interface SpecFile extends BaseSpec { export interface FoundSpec extends SpecFile { specFileExtension: string fileExtension: string - specType: Cypress.CypressSpecType + // see: ./cli/types/cypress.d.ts `Cypress.CypressSpecType` + specType: 'integration' | 'component' } export interface SpecWithRelativeRoot extends FoundSpec { diff --git a/scripts/binary/binary-cleanup.js b/scripts/binary/binary-cleanup.js index da20a87c3f88..ec4729504193 100644 --- a/scripts/binary/binary-cleanup.js +++ b/scripts/binary/binary-cleanup.js @@ -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([ diff --git a/system-tests/lib/pluginUtils.js b/system-tests/lib/pluginUtils.js index b6f455e45647..0ffdd3f6a5da 100644 --- a/system-tests/lib/pluginUtils.js +++ b/system-tests/lib/pluginUtils.js @@ -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 diff --git a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json index 957bef5499d1..aee9bac711d4 100644 --- a/tooling/v8-snapshot/cache/darwin/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/darwin/snapshot-meta.json @@ -225,9 +225,6 @@ "./node_modules/async/dist/async.js", "./node_modules/basic-auth/node_modules/safe-buffer/index.js", "./node_modules/black-hole-stream/index.js", - "./node_modules/body-parser/index.js", - "./node_modules/body-parser/node_modules/debug/src/browser.js", - "./node_modules/body-parser/node_modules/debug/src/index.js", "./node_modules/buffer-from/index.js", "./node_modules/chalk/index.js", "./node_modules/chrome-remote-interface/index.js", @@ -270,19 +267,9 @@ "./node_modules/express-graphql/node_modules/depd/index.js", "./node_modules/express-graphql/node_modules/http-errors/index.js", "./node_modules/express-graphql/parseBody.js", - "./node_modules/express/lib/application.js", - "./node_modules/express/lib/request.js", - "./node_modules/express/lib/response.js", - "./node_modules/express/lib/router/index.js", - "./node_modules/express/lib/router/route.js", - "./node_modules/express/node_modules/debug/src/browser.js", - "./node_modules/express/node_modules/debug/src/index.js", - "./node_modules/express/node_modules/send/index.js", "./node_modules/fast-glob/out/settings.js", "./node_modules/fast-glob/out/utils/path.js", "./node_modules/file-uri-to-path/index.js", - "./node_modules/finalhandler/node_modules/debug/src/browser.js", - "./node_modules/finalhandler/node_modules/debug/src/index.js", "./node_modules/firefox-profile/node_modules/fs-extra/lib/fs/index.js", "./node_modules/firefox-profile/node_modules/fs-extra/lib/index.js", "./node_modules/firefox-profile/node_modules/fs-extra/lib/json/index.js", @@ -344,8 +331,6 @@ "./node_modules/lodash/isBuffer.js", "./node_modules/lodash/lodash.js", "./node_modules/make-dir/node_modules/semver/semver.js", - "./node_modules/marionette-client/lib/marionette/index.js", - "./node_modules/marionette-client/lib/marionette/marionette.js", "./node_modules/methods/index.js", "./node_modules/mime/mime.js", "./node_modules/mocha-7.0.1/index.js", @@ -522,10 +507,6 @@ "./node_modules/send/index.js", "./node_modules/send/node_modules/debug/src/browser.js", "./node_modules/send/node_modules/debug/src/index.js", - "./node_modules/serve-static/node_modules/debug/src/browser.js", - "./node_modules/serve-static/node_modules/debug/src/index.js", - "./node_modules/serve-static/node_modules/debug/src/node.js", - "./node_modules/serve-static/node_modules/send/index.js", "./node_modules/shell-env/node_modules/execa/lib/errname.js", "./node_modules/shell-env/node_modules/get-stream/buffer-stream.js", "./node_modules/shell-env/node_modules/semver/semver.js", @@ -667,11 +648,16 @@ "./packages/errors/index.js", "./packages/errors/src/errTemplate.ts", "./packages/graphql/index.js", + "./packages/graphql/node_modules/body-parser/index.js", "./packages/graphql/node_modules/chalk/node_modules/supports-color/index.js", "./packages/graphql/node_modules/chalk/source/index.js", "./packages/graphql/node_modules/debug/src/browser.js", "./packages/graphql/node_modules/debug/src/index.js", - "./packages/graphql/node_modules/supports-color/index.js", + "./packages/graphql/node_modules/express/lib/application.js", + "./packages/graphql/node_modules/express/lib/request.js", + "./packages/graphql/node_modules/express/lib/response.js", + "./packages/graphql/node_modules/express/lib/router/index.js", + "./packages/graphql/node_modules/express/lib/router/route.js", "./packages/graphql/src/makeGraphQLServer.ts", "./packages/graphql/src/plugins/index.ts", "./packages/graphql/src/plugins/nexusDebugFieldPlugin.ts", @@ -1591,18 +1577,6 @@ "./node_modules/binary-extensions/index.js", "./node_modules/binaryextensions/edition-es5/index.js", "./node_modules/bindings/bindings.js", - "./node_modules/body-parser/lib/read.js", - "./node_modules/body-parser/lib/types/json.js", - "./node_modules/body-parser/lib/types/raw.js", - "./node_modules/body-parser/lib/types/text.js", - "./node_modules/body-parser/lib/types/urlencoded.js", - "./node_modules/body-parser/node_modules/debug/src/debug.js", - "./node_modules/body-parser/node_modules/ms/index.js", - "./node_modules/body-parser/node_modules/qs/lib/formats.js", - "./node_modules/body-parser/node_modules/qs/lib/index.js", - "./node_modules/body-parser/node_modules/qs/lib/parse.js", - "./node_modules/body-parser/node_modules/qs/lib/stringify.js", - "./node_modules/body-parser/node_modules/qs/lib/utils.js", "./node_modules/braces/index.js", "./node_modules/braces/lib/compile.js", "./node_modules/braces/lib/constants.js", @@ -1761,7 +1735,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/deep-is/index.js", @@ -1861,24 +1834,6 @@ "./node_modules/express-graphql/node_modules/statuses/index.js", "./node_modules/express-graphql/node_modules/toidentifier/index.js", "./node_modules/express-graphql/renderGraphiQL.js", - "./node_modules/express/index.js", - "./node_modules/express/lib/express.js", - "./node_modules/express/lib/middleware/init.js", - "./node_modules/express/lib/middleware/query.js", - "./node_modules/express/lib/router/layer.js", - "./node_modules/express/lib/utils.js", - "./node_modules/express/lib/view.js", - "./node_modules/express/node_modules/cookie/index.js", - "./node_modules/express/node_modules/debug/src/debug.js", - "./node_modules/express/node_modules/merge-descriptors/index.js", - "./node_modules/express/node_modules/ms/index.js", - "./node_modules/express/node_modules/path-to-regexp/index.js", - "./node_modules/express/node_modules/qs/lib/formats.js", - "./node_modules/express/node_modules/qs/lib/index.js", - "./node_modules/express/node_modules/qs/lib/parse.js", - "./node_modules/express/node_modules/qs/lib/stringify.js", - "./node_modules/express/node_modules/qs/lib/utils.js", - "./node_modules/express/node_modules/send/node_modules/ms/index.js", "./node_modules/ext-list/index.js", "./node_modules/ext-name/index.js", "./node_modules/extend/index.js", @@ -1908,9 +1863,6 @@ "./node_modules/fast-glob/out/utils/string.js", "./node_modules/fastq/queue.js", "./node_modules/fill-range/index.js", - "./node_modules/finalhandler/index.js", - "./node_modules/finalhandler/node_modules/debug/src/debug.js", - "./node_modules/finalhandler/node_modules/ms/index.js", "./node_modules/find-process/index.js", "./node_modules/find-process/lib/find.js", "./node_modules/find-process/lib/find_pid.js", @@ -1918,8 +1870,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", @@ -1933,17 +1884,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", @@ -2494,7 +2443,6 @@ "./node_modules/lodash/toString.js", "./node_modules/make-dir/node_modules/pify/index.js", "./node_modules/make-error/index.js", - "./node_modules/marionette-client/lib/marionette/message.js", "./node_modules/md5/md5.js", "./node_modules/media-typer/index.js", "./node_modules/merge-descriptors/index.js", @@ -3311,9 +3259,6 @@ "./node_modules/semver/ranges/valid.js", "./node_modules/send/node_modules/debug/node_modules/ms/index.js", "./node_modules/send/node_modules/debug/src/debug.js", - "./node_modules/serve-static/index.js", - "./node_modules/serve-static/node_modules/debug/node_modules/ms/index.js", - "./node_modules/serve-static/node_modules/debug/src/debug.js", "./node_modules/server-destroy/index.js", "./node_modules/set-function-length/index.js", "./node_modules/setprototypeof/index.js", @@ -3444,7 +3389,6 @@ "./node_modules/trash/node_modules/uuid/lib/rng.js", "./node_modules/trash/node_modules/uuid/v1.js", "./node_modules/trash/node_modules/uuid/v4.js", - "./node_modules/tree-kill/index.js", "./node_modules/truncate-utf8-bytes/lib/truncate.js", "./node_modules/ts-node/dist-raw/node-internal-constants.js", "./node_modules/ts-node/dist-raw/node-internal-errors.js", @@ -3849,10 +3793,28 @@ "./packages/extension/lib/util.js", "./packages/frontend-shared/cypress/e2e/prod-dependencies.ts", "./packages/frontend-shared/cypress/e2e/v8-snapshot-entry.ts", + "./packages/graphql/node_modules/body-parser/lib/read.js", + "./packages/graphql/node_modules/body-parser/lib/types/json.js", + "./packages/graphql/node_modules/body-parser/lib/types/raw.js", + "./packages/graphql/node_modules/body-parser/lib/types/text.js", + "./packages/graphql/node_modules/body-parser/lib/types/urlencoded.js", "./packages/graphql/node_modules/chalk/source/templates.js", "./packages/graphql/node_modules/chalk/source/util.js", - "./packages/graphql/node_modules/debug/src/common.js", + "./packages/graphql/node_modules/cookie/index.js", + "./packages/graphql/node_modules/debug/node_modules/ms/index.js", + "./packages/graphql/node_modules/debug/src/debug.js", + "./packages/graphql/node_modules/encodeurl/index.js", + "./packages/graphql/node_modules/express/index.js", + "./packages/graphql/node_modules/express/lib/express.js", + "./packages/graphql/node_modules/express/lib/middleware/init.js", + "./packages/graphql/node_modules/express/lib/middleware/query.js", + "./packages/graphql/node_modules/express/lib/router/layer.js", + "./packages/graphql/node_modules/express/lib/utils.js", + "./packages/graphql/node_modules/express/lib/view.js", + "./packages/graphql/node_modules/finalhandler/index.js", "./packages/graphql/node_modules/has-flag/index.js", + "./packages/graphql/node_modules/path-to-regexp/index.js", + "./packages/graphql/node_modules/serve-static/index.js", "./packages/graphql/src/index.ts", "./packages/https-proxy/lib/proxy.js", "./packages/https-proxy/lib/server.js", @@ -3982,11 +3944,13 @@ "./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/index.ts", "./packages/server/lib/browsers/webkit-automation.ts", "./packages/server/lib/browsers/webkit.ts", "./packages/server/lib/cloud/api/scrub_url.ts", "./packages/server/lib/cloud/artifacts/artifact.ts", "./packages/server/lib/cloud/artifacts/file_upload_strategy.ts", + "./packages/server/lib/cloud/artifacts/print_protocol_upload_error.ts", "./packages/server/lib/cloud/artifacts/upload_artifacts.ts", "./packages/server/lib/cloud/encryption.ts", "./packages/server/lib/cloud/environment.ts", @@ -3999,6 +3963,7 @@ "./packages/server/lib/cloud/protocol.ts", "./packages/server/lib/cloud/upload/send_file.ts", "./packages/server/lib/cloud/upload/stream_activity_monitor.ts", + "./packages/server/lib/cloud/upload/stream_stalled_error.ts", "./packages/server/lib/cohorts.ts", "./packages/server/lib/controllers/client.js", "./packages/server/lib/controllers/files.js", @@ -4209,6 +4174,7 @@ "./packages/server/node_modules/p-queue/dist/priority-queue.js", "./packages/server/node_modules/path-key/index.js", "./packages/server/node_modules/path-to-regexp/index.js", + "./packages/server/node_modules/pump/index.js", "./packages/server/node_modules/readable-stream/lib/internal/streams/async_iterator.js", "./packages/server/node_modules/readable-stream/lib/internal/streams/destroy.js", "./packages/server/node_modules/readable-stream/lib/internal/streams/end-of-stream.js", @@ -4271,5 +4237,5 @@ "./tooling/v8-snapshot/cache/darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "9ab51832162435dec2de359d222b063dfdce4b3d555581562e6f2c4255bc1e9b" + "deferredHash": "4c1da762a851baab24ef5d1ba9d878ad207147c7703383f4796c0c9c7910240f" } \ No newline at end of file diff --git a/tooling/v8-snapshot/cache/linux/snapshot-meta.json b/tooling/v8-snapshot/cache/linux/snapshot-meta.json index 3de9bf87eb70..5ab13a730c9a 100644 --- a/tooling/v8-snapshot/cache/linux/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/linux/snapshot-meta.json @@ -1760,7 +1760,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/deep-is/index.js", @@ -1917,8 +1916,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", @@ -1932,17 +1930,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", @@ -3981,6 +3977,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/index.ts", "./packages/server/lib/browsers/webkit-automation.ts", "./packages/server/lib/browsers/webkit.ts", "./packages/server/lib/cloud/api/scrub_url.ts", @@ -4270,5 +4267,5 @@ "./tooling/v8-snapshot/cache/linux/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "9ab51832162435dec2de359d222b063dfdce4b3d555581562e6f2c4255bc1e9b" + "deferredHash": "4c1da762a851baab24ef5d1ba9d878ad207147c7703383f4796c0c9c7910240f" } \ No newline at end of file diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index bee31a50c414..6176f11d7a00 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -224,9 +224,6 @@ "./node_modules/async/dist/async.js", "./node_modules/basic-auth/node_modules/safe-buffer/index.js", "./node_modules/black-hole-stream/index.js", - "./node_modules/body-parser/index.js", - "./node_modules/body-parser/node_modules/debug/src/browser.js", - "./node_modules/body-parser/node_modules/debug/src/index.js", "./node_modules/buffer-from/index.js", "./node_modules/chalk/index.js", "./node_modules/chrome-remote-interface/index.js", @@ -269,19 +266,9 @@ "./node_modules/express-graphql/node_modules/depd/index.js", "./node_modules/express-graphql/node_modules/http-errors/index.js", "./node_modules/express-graphql/parseBody.js", - "./node_modules/express/lib/application.js", - "./node_modules/express/lib/request.js", - "./node_modules/express/lib/response.js", - "./node_modules/express/lib/router/index.js", - "./node_modules/express/lib/router/route.js", - "./node_modules/express/node_modules/debug/src/browser.js", - "./node_modules/express/node_modules/debug/src/index.js", - "./node_modules/express/node_modules/send/index.js", "./node_modules/fast-glob/out/settings.js", "./node_modules/fast-glob/out/utils/path.js", "./node_modules/file-uri-to-path/index.js", - "./node_modules/finalhandler/node_modules/debug/src/browser.js", - "./node_modules/finalhandler/node_modules/debug/src/index.js", "./node_modules/firefox-profile/node_modules/fs-extra/lib/fs/index.js", "./node_modules/firefox-profile/node_modules/fs-extra/lib/index.js", "./node_modules/firefox-profile/node_modules/fs-extra/lib/json/index.js", @@ -342,8 +329,6 @@ "./node_modules/lodash/isBuffer.js", "./node_modules/lodash/lodash.js", "./node_modules/make-dir/node_modules/semver/semver.js", - "./node_modules/marionette-client/lib/marionette/index.js", - "./node_modules/marionette-client/lib/marionette/marionette.js", "./node_modules/methods/index.js", "./node_modules/mime/mime.js", "./node_modules/mocha-7.0.1/index.js", @@ -521,10 +506,6 @@ "./node_modules/send/index.js", "./node_modules/send/node_modules/debug/src/browser.js", "./node_modules/send/node_modules/debug/src/index.js", - "./node_modules/serve-static/node_modules/debug/src/browser.js", - "./node_modules/serve-static/node_modules/debug/src/index.js", - "./node_modules/serve-static/node_modules/debug/src/node.js", - "./node_modules/serve-static/node_modules/send/index.js", "./node_modules/shell-env/node_modules/execa/lib/errname.js", "./node_modules/shell-env/node_modules/get-stream/buffer-stream.js", "./node_modules/shell-env/node_modules/semver/semver.js", @@ -668,11 +649,16 @@ "./packages/errors/index.js", "./packages/errors/src/errTemplate.ts", "./packages/graphql/index.js", + "./packages/graphql/node_modules/body-parser/index.js", "./packages/graphql/node_modules/chalk/node_modules/supports-color/index.js", "./packages/graphql/node_modules/chalk/source/index.js", "./packages/graphql/node_modules/debug/src/browser.js", "./packages/graphql/node_modules/debug/src/index.js", - "./packages/graphql/node_modules/supports-color/index.js", + "./packages/graphql/node_modules/express/lib/application.js", + "./packages/graphql/node_modules/express/lib/request.js", + "./packages/graphql/node_modules/express/lib/response.js", + "./packages/graphql/node_modules/express/lib/router/index.js", + "./packages/graphql/node_modules/express/lib/router/route.js", "./packages/graphql/src/makeGraphQLServer.ts", "./packages/graphql/src/plugins/index.ts", "./packages/graphql/src/plugins/nexusDebugFieldPlugin.ts", @@ -1593,18 +1579,6 @@ "./node_modules/binary-extensions/index.js", "./node_modules/binaryextensions/edition-es5/index.js", "./node_modules/bindings/bindings.js", - "./node_modules/body-parser/lib/read.js", - "./node_modules/body-parser/lib/types/json.js", - "./node_modules/body-parser/lib/types/raw.js", - "./node_modules/body-parser/lib/types/text.js", - "./node_modules/body-parser/lib/types/urlencoded.js", - "./node_modules/body-parser/node_modules/debug/src/debug.js", - "./node_modules/body-parser/node_modules/ms/index.js", - "./node_modules/body-parser/node_modules/qs/lib/formats.js", - "./node_modules/body-parser/node_modules/qs/lib/index.js", - "./node_modules/body-parser/node_modules/qs/lib/parse.js", - "./node_modules/body-parser/node_modules/qs/lib/stringify.js", - "./node_modules/body-parser/node_modules/qs/lib/utils.js", "./node_modules/braces/index.js", "./node_modules/braces/lib/compile.js", "./node_modules/braces/lib/constants.js", @@ -1763,7 +1737,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/deep-is/index.js", @@ -1863,24 +1836,6 @@ "./node_modules/express-graphql/node_modules/statuses/index.js", "./node_modules/express-graphql/node_modules/toidentifier/index.js", "./node_modules/express-graphql/renderGraphiQL.js", - "./node_modules/express/index.js", - "./node_modules/express/lib/express.js", - "./node_modules/express/lib/middleware/init.js", - "./node_modules/express/lib/middleware/query.js", - "./node_modules/express/lib/router/layer.js", - "./node_modules/express/lib/utils.js", - "./node_modules/express/lib/view.js", - "./node_modules/express/node_modules/cookie/index.js", - "./node_modules/express/node_modules/debug/src/debug.js", - "./node_modules/express/node_modules/merge-descriptors/index.js", - "./node_modules/express/node_modules/ms/index.js", - "./node_modules/express/node_modules/path-to-regexp/index.js", - "./node_modules/express/node_modules/qs/lib/formats.js", - "./node_modules/express/node_modules/qs/lib/index.js", - "./node_modules/express/node_modules/qs/lib/parse.js", - "./node_modules/express/node_modules/qs/lib/stringify.js", - "./node_modules/express/node_modules/qs/lib/utils.js", - "./node_modules/express/node_modules/send/node_modules/ms/index.js", "./node_modules/ext-list/index.js", "./node_modules/ext-name/index.js", "./node_modules/extend/index.js", @@ -1910,9 +1865,6 @@ "./node_modules/fast-glob/out/utils/string.js", "./node_modules/fastq/queue.js", "./node_modules/fill-range/index.js", - "./node_modules/finalhandler/index.js", - "./node_modules/finalhandler/node_modules/debug/src/debug.js", - "./node_modules/finalhandler/node_modules/ms/index.js", "./node_modules/find-process/index.js", "./node_modules/find-process/lib/find.js", "./node_modules/find-process/lib/find_pid.js", @@ -1920,8 +1872,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", @@ -1935,17 +1886,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", @@ -2496,7 +2445,6 @@ "./node_modules/lodash/toString.js", "./node_modules/make-dir/node_modules/pify/index.js", "./node_modules/make-error/index.js", - "./node_modules/marionette-client/lib/marionette/message.js", "./node_modules/md5/md5.js", "./node_modules/media-typer/index.js", "./node_modules/merge-descriptors/index.js", @@ -3312,9 +3260,6 @@ "./node_modules/semver/ranges/valid.js", "./node_modules/send/node_modules/debug/node_modules/ms/index.js", "./node_modules/send/node_modules/debug/src/debug.js", - "./node_modules/serve-static/index.js", - "./node_modules/serve-static/node_modules/debug/node_modules/ms/index.js", - "./node_modules/serve-static/node_modules/debug/src/debug.js", "./node_modules/server-destroy/index.js", "./node_modules/set-function-length/index.js", "./node_modules/setprototypeof/index.js", @@ -3444,7 +3389,6 @@ "./node_modules/trash/node_modules/uuid/lib/rng.js", "./node_modules/trash/node_modules/uuid/v1.js", "./node_modules/trash/node_modules/uuid/v4.js", - "./node_modules/tree-kill/index.js", "./node_modules/truncate-utf8-bytes/lib/truncate.js", "./node_modules/ts-node/dist-raw/node-internal-constants.js", "./node_modules/ts-node/dist-raw/node-internal-errors.js", @@ -3848,10 +3792,28 @@ "./packages/extension/lib/util.js", "./packages/frontend-shared/cypress/e2e/prod-dependencies.ts", "./packages/frontend-shared/cypress/e2e/v8-snapshot-entry.ts", + "./packages/graphql/node_modules/body-parser/lib/read.js", + "./packages/graphql/node_modules/body-parser/lib/types/json.js", + "./packages/graphql/node_modules/body-parser/lib/types/raw.js", + "./packages/graphql/node_modules/body-parser/lib/types/text.js", + "./packages/graphql/node_modules/body-parser/lib/types/urlencoded.js", "./packages/graphql/node_modules/chalk/source/templates.js", "./packages/graphql/node_modules/chalk/source/util.js", - "./packages/graphql/node_modules/debug/src/common.js", + "./packages/graphql/node_modules/cookie/index.js", + "./packages/graphql/node_modules/debug/node_modules/ms/index.js", + "./packages/graphql/node_modules/debug/src/debug.js", + "./packages/graphql/node_modules/encodeurl/index.js", + "./packages/graphql/node_modules/express/index.js", + "./packages/graphql/node_modules/express/lib/express.js", + "./packages/graphql/node_modules/express/lib/middleware/init.js", + "./packages/graphql/node_modules/express/lib/middleware/query.js", + "./packages/graphql/node_modules/express/lib/router/layer.js", + "./packages/graphql/node_modules/express/lib/utils.js", + "./packages/graphql/node_modules/express/lib/view.js", + "./packages/graphql/node_modules/finalhandler/index.js", "./packages/graphql/node_modules/has-flag/index.js", + "./packages/graphql/node_modules/path-to-regexp/index.js", + "./packages/graphql/node_modules/serve-static/index.js", "./packages/graphql/src/index.ts", "./packages/https-proxy/lib/proxy.js", "./packages/https-proxy/lib/server.js", @@ -3981,11 +3943,13 @@ "./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/index.ts", "./packages/server/lib/browsers/webkit-automation.ts", "./packages/server/lib/browsers/webkit.ts", "./packages/server/lib/cloud/api/scrub_url.ts", "./packages/server/lib/cloud/artifacts/artifact.ts", "./packages/server/lib/cloud/artifacts/file_upload_strategy.ts", + "./packages/server/lib/cloud/artifacts/print_protocol_upload_error.ts", "./packages/server/lib/cloud/artifacts/upload_artifacts.ts", "./packages/server/lib/cloud/encryption.ts", "./packages/server/lib/cloud/environment.ts", @@ -3998,6 +3962,7 @@ "./packages/server/lib/cloud/protocol.ts", "./packages/server/lib/cloud/upload/send_file.ts", "./packages/server/lib/cloud/upload/stream_activity_monitor.ts", + "./packages/server/lib/cloud/upload/stream_stalled_error.ts", "./packages/server/lib/cohorts.ts", "./packages/server/lib/controllers/client.js", "./packages/server/lib/controllers/files.js", @@ -4208,6 +4173,7 @@ "./packages/server/node_modules/p-queue/dist/priority-queue.js", "./packages/server/node_modules/path-key/index.js", "./packages/server/node_modules/path-to-regexp/index.js", + "./packages/server/node_modules/pump/index.js", "./packages/server/node_modules/readable-stream/lib/internal/streams/async_iterator.js", "./packages/server/node_modules/readable-stream/lib/internal/streams/destroy.js", "./packages/server/node_modules/readable-stream/lib/internal/streams/end-of-stream.js", @@ -4270,5 +4236,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "f18ec1dec107c1830ca781417585733b0b6bd4ab18c20910588f5e7bf4de40fb" + "deferredHash": "13b4e2c024574673089f0bd15e763cc1db73256cafb63a98edb1e916317e3d3f" } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index a9c5a88e94cb..0461818d61c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5939,6 +5939,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" @@ -5952,6 +5959,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" @@ -7775,10 +7796,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" @@ -7795,6 +7818,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" @@ -8256,6 +8284,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" @@ -8263,10 +8296,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" "*" @@ -8730,16 +8763,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" @@ -9180,7 +9257,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== @@ -13306,12 +13383,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" @@ -13462,6 +13539,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" @@ -14262,6 +14344,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" @@ -15955,6 +16058,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" @@ -16359,16 +16469,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" @@ -16668,16 +16778,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" @@ -16841,16 +16941,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" @@ -16926,6 +17026,11 @@ get-port@^3.2.0: resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= +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" @@ -18254,7 +18359,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== @@ -18366,7 +18471,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== @@ -18615,7 +18720,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== @@ -20848,6 +20953,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" @@ -22582,7 +22696,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== @@ -25673,6 +25787,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" @@ -25732,7 +25860,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== @@ -27231,6 +27359,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" @@ -27537,7 +27670,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== @@ -28586,6 +28719,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" @@ -28691,7 +28829,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== @@ -30425,6 +30563,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" @@ -30646,7 +30789,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== @@ -30688,6 +30831,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" @@ -30821,11 +30969,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" @@ -31049,6 +31192,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" @@ -31503,7 +31651,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== @@ -31579,6 +31727,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" @@ -32241,10 +32404,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" @@ -32292,10 +32455,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" @@ -32616,7 +32779,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==