diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 7dc5af95fa3..ac41c6cffdc 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -656,11 +656,6 @@ commands: description: run subset of tests with injectDocumentDomain config enabled type: boolean default: false - is-firefox-cdp: - description: whether or not the group should be associated to the firefox CDP - run or not. This is determined by the browser version. - type: boolean - default: false steps: - restore_cached_workspace @@ -701,9 +696,6 @@ commands: if << parameters.inject-document-domain >> ; then YARN_CMD="cypress:run:inject-document-domain" PARALLEL="--parallel --group 5x-driver-inject-document-domain-<>" - elif << parameters.is-firefox-cdp >> ; then - YARN_CMD="cypress:run" - PARALLEL="--parallel --group 5x-driver-cdp-<>" else YARN_CMD="cypress:run" PARALLEL="--parallel --group 5x-driver-<>" @@ -2136,18 +2128,6 @@ jobs: - run-driver-integration-tests: browser: firefox - # Runs the driver tests using firefox 134, which does NOT use WebDriver BiDi - # This is to test and make sure there aren't regressions with the old CDP driver - driver-integration-tests-firefox-cdp: - <<: *defaults - resource_class: medium+ - parallelism: 5 - steps: - - run-driver-integration-tests: - browser: firefox - firefox-version: "134.0.2" - is-firefox-cdp: true - driver-integration-tests-electron: <<: *defaults parallelism: 5 @@ -2888,7 +2868,6 @@ linux-x64-workflow: &linux-x64-workflow - run-webpack-dev-server-integration-tests - run-vite-dev-server-integration-tests - driver-integration-tests-firefox - - driver-integration-tests-firefox-cdp - driver-integration-tests-chrome - driver-integration-tests-chrome-inject-document-domain - driver-integration-tests-chrome-beta-inject-document-domain @@ -2964,10 +2943,6 @@ linux-x64-workflow: &linux-x64-workflow context: test-runner:cypress-record-key requires: - build - - driver-integration-tests-firefox-cdp: - context: test-runner:cypress-record-key - requires: - - build - driver-integration-tests-electron: context: test-runner:cypress-record-key requires: @@ -3110,7 +3085,6 @@ linux-x64-workflow: &linux-x64-workflow - linux-lint - percy-finalize - driver-integration-tests-firefox - - driver-integration-tests-firefox-cdp - driver-integration-tests-chrome - driver-integration-tests-chrome-beta - driver-integration-tests-chrome-inject-document-domain @@ -3366,10 +3340,6 @@ linux-x64-contributor-workflow: &linux-x64-contributor-workflow context: test-runner:cypress-record-key requires: - contributor-pr - - driver-integration-tests-firefox-cdp: - context: test-runner:cypress-record-key - requires: - - contributor-pr - driver-integration-tests-electron: context: test-runner:cypress-record-key requires: @@ -3511,7 +3481,6 @@ linux-x64-contributor-workflow: &linux-x64-contributor-workflow - linux-lint - percy-finalize - driver-integration-tests-firefox - - driver-integration-tests-firefox-cdp - driver-integration-tests-chrome - driver-integration-tests-chrome-beta - driver-integration-tests-electron diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 3816f3e9e75..403c189a195 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,11 +1,12 @@ ## 15.0.0 -_Released 7/1/2025 (PENDING)_ +_Released 07/01/2025 (PENDING)_ **Breaking Changes:** - Removed support for Node.js 18 and Node.js 23. Addresses [#31302](https://github.com/cypress-io/cypress/issues/31302). +- Removed support for [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol) with the [firefox](https://www.mozilla.org/) browser. Addresses [#31189](https://github.com/cypress-io/cypress/issues/31189). ## 14.2.2 diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 0bad56e294a..cb178f75b71 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -1193,9 +1193,6 @@ export const AllCypressErrors = { CDP_RETRYING_CONNECTION: (attempt: string | number, browserName: string, connectRetryThreshold: number) => { return errTemplate`Still waiting to connect to ${fmt.off(_.capitalize(browserName))}, retrying in 1 second ${fmt.meta(`(attempt ${attempt}/${connectRetryThreshold})`)}` }, - CDP_FIREFOX_DEPRECATED: () => { - return errTemplate`Since Firefox 129, Chrome DevTools Protocol (CDP) has been deprecated in Firefox. In Firefox 135 and above, Cypress defaults to automating the Firefox browser with WebDriver BiDi. Cypress will no longer support CDP within Firefox in the future and is planned for removal in Cypress 15.` - }, BROWSER_PROCESS_CLOSED_UNEXPECTEDLY: (browserName: string) => { return errTemplate`\ We detected that the ${fmt.highlight(browserName)} browser process closed unexpectedly. diff --git a/packages/errors/test/unit/visualSnapshotErrors_spec.ts b/packages/errors/test/unit/visualSnapshotErrors_spec.ts index 5f9512adf1d..aa77d2a7032 100644 --- a/packages/errors/test/unit/visualSnapshotErrors_spec.ts +++ b/packages/errors/test/unit/visualSnapshotErrors_spec.ts @@ -1112,11 +1112,6 @@ describe('visual error templates', () => { default: [1, 'chrome', 62], } }, - CDP_FIREFOX_DEPRECATED: () => { - return { - default: [], - } - }, BROWSER_PROCESS_CLOSED_UNEXPECTEDLY: () => { return { default: ['chrome'], diff --git a/packages/extension/app/v2/background.js b/packages/extension/app/v2/background.js index 7fd06328893..5a3bb0ca17c 100644 --- a/packages/extension/app/v2/background.js +++ b/packages/extension/app/v2/background.js @@ -31,23 +31,6 @@ const checkIfFirefox = async () => { return name === 'Firefox' } -// this check only applies to firefox versioning! -const isBiDiEnabled = async (config) => { - if (!browser || !get(browser, 'runtime.getBrowserInfo') || config.IS_CDP_FORCED_FOR_FIREFOX) { - return false - } - - const { version } = await browser.runtime.getBrowserInfo() - - if (version) { - const [majorVersion] = version.split('.').map(Number) - - return majorVersion >= 135 - } - - return false -} - const connect = function (host, path, extraOpts) { const listenToCookieChanges = once(() => { return browser.cookies.onChanged.addListener((info) => { @@ -84,30 +67,6 @@ const connect = function (host, path, extraOpts) { }) }) - const listenToOnBeforeHeaders = once(() => { - // adds a header to the request to mark it as a request for the AUT frame - // itself, so the proxy can utilize that for injection purposes - browser.webRequest.onBeforeSendHeaders.addListener((details) => { - if ( - // parentFrameId: 0 means the parent is the top-level, so if it isn't - // 0, it's nested inside the AUT and can't be the AUT itself - details.parentFrameId !== 0 - // is the spec frame, not the AUT - || details.url.includes('__cypress') - ) return - - return { - requestHeaders: [ - ...details.requestHeaders, - { - name: 'X-Cypress-Is-AUT-Frame', - value: 'true', - }, - ], - } - }, { urls: [''], types: ['sub_frame'] }, ['blocking', 'requestHeaders']) - }) - const fail = (id, err) => { return ws.emit('automation:response', id, { __error: err.message, @@ -167,13 +126,6 @@ const connect = function (host, path, extraOpts) { if (isFirefox) { // Non-Firefox browsers use CDP for this instead listenToDownloads() - // if BiDi is enabled, BiDi will handle the network interception. - // Otherwise, CDP does not support it for Firefox and we need to listen for it here. - const isBiDiTurnedOn = await isBiDiEnabled(config) - - if (!isBiDiTurnedOn) { - listenToOnBeforeHeaders() - } } }) diff --git a/packages/extension/test/integration/v2/background_spec.js b/packages/extension/test/integration/v2/background_spec.js index 11c1fc7ea59..1cd0e3591a3 100644 --- a/packages/extension/test/integration/v2/background_spec.js +++ b/packages/extension/test/integration/v2/background_spec.js @@ -293,120 +293,6 @@ describe('app/background', () => { }) }) - context('add header to aut iframe requests', () => { - beforeEach(() => { - browser.runtime.getBrowserInfo = sinon.stub().resolves({ name: 'Firefox', version: '135.0.1' }) - }) - - it('allows for CDP to be used as an escape hatch if BiDi would otherwise be enabled', async function () { - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect({ - IS_CDP_FORCED_FOR_FIREFOX: true, - }) - - expect(browser.webRequest.onBeforeSendHeaders.addListener).to.be.called - }) - - context('BiDi enabled', () => { - it('does not attach onBeforeSendHeaders listener if BiDi is enabled', async function () { - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - expect(browser.webRequest.onBeforeSendHeaders.addListener).not.to.be.called - }) - }) - - context('CDP enabled', () => { - beforeEach(() => { - browser.runtime.getBrowserInfo = sinon.stub().resolves({ name: 'Firefox', version: '134' }) - }) - - it('does not add header if it is the top frame', async function () { - const details = { - parentFrameId: -1, - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.be.undefined - }) - - it('does not add header if it is a nested frame', async function () { - const details = { - parentFrameId: 12345, - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.be.undefined - }) - - it('does not add header if it is a spec frame request', async function () { - const details = { - parentFrameId: 0, - type: 'sub_frame', - url: '/__cypress/integration/spec.js', - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.be.undefined - }) - - it('appends X-Cypress-Is-AUT-Frame header to AUT iframe request', async function () { - const details = { - parentFrameId: 0, - type: 'sub_frame', - url: 'http://localhost:3000/index.html', - requestHeaders: [ - { name: 'X-Foo', value: 'Bar' }, - ], - } - - sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - const result = browser.webRequest.onBeforeSendHeaders.addListener.lastCall.args[0](details) - - expect(result).to.deep.equal({ - requestHeaders: [ - { - name: 'X-Foo', - value: 'Bar', - }, - { - name: 'X-Cypress-Is-AUT-Frame', - value: 'true', - }, - ], - }) - }) - - it('does not add before-headers listener if in non-Firefox browser', async function () { - browser.runtime.getBrowserInfo = undefined - - const onBeforeSendHeaders = sinon.stub(browser.webRequest.onBeforeSendHeaders, 'addListener') - - await this.connect() - - expect(onBeforeSendHeaders).not.to.be.called - }) - }) - }) - context('.getAll', () => { it('resolves with specific cookie properties', () => { sinon.stub(browser.cookies, 'getAll').resolves([ diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 5480f28267a..45cc21d69e3 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -1129,7 +1129,6 @@ enum ErrorTypeEnum { CANNOT_TRASH_ASSETS CDP_COULD_NOT_CONNECT CDP_COULD_NOT_RECONNECT - CDP_FIREFOX_DEPRECATED CDP_RETRYING_CONNECTION CDP_VERSION_TOO_OLD CHROME_WEB_SECURITY_NOT_SUPPORTED diff --git a/packages/launcher/lib/known-browsers.ts b/packages/launcher/lib/known-browsers.ts index 0d1fa044fa2..ea6b9fca19a 100644 --- a/packages/launcher/lib/known-browsers.ts +++ b/packages/launcher/lib/known-browsers.ts @@ -1,5 +1,24 @@ import type { Browser, BrowserValidatorResult, FoundBrowser } from '@packages/types' +const firefoxValidatorFn = (browser: FoundBrowser, platform: NodeJS.Platform): BrowserValidatorResult => { + try { + if (browser.majorVersion) { + const majorVersion = Number(browser.majorVersion) + + if (majorVersion < 135) { + return { + isSupported: false, + warningMessage: `Cypress does not support running ${browser.displayName} version ${browser.majorVersion} due to lack of WebDriver BiDi support. To use ${browser.displayName} with Cypress, install version 135 or newer.`, + } + } + } + } catch (e) { /* empty */ } + + return { + isSupported: true, + } +} + /** list of the browsers we can detect and use by default */ export const knownBrowsers: Browser[] = [ { @@ -62,9 +81,10 @@ export const knownBrowsers: Browser[] = [ family: 'firefox', channel: 'stable', displayName: 'Firefox', - // Mozilla Firefox 70.0.1 + // Mozilla Firefox 135.0.1 versionRegex: /^Mozilla Firefox ([^\sab]+)$/m, binary: 'firefox', + validator: firefoxValidatorFn, }, { name: 'firefox', @@ -75,6 +95,7 @@ export const knownBrowsers: Browser[] = [ versionRegex: /^Mozilla Firefox (\S+b\S*)$/m, // ubuntu PPAs install it as firefox binary: ['firefox-developer-edition', 'firefox'], + validator: firefoxValidatorFn, }, { name: 'firefox', @@ -85,6 +106,7 @@ export const knownBrowsers: Browser[] = [ versionRegex: /^Mozilla Firefox (\S+a\S*)$/m, // ubuntu PPAs install it as firefox-trunk binary: ['firefox-nightly', 'firefox-trunk'], + validator: firefoxValidatorFn, }, { name: 'edge', diff --git a/packages/launcher/test/unit/browsers_spec.ts b/packages/launcher/test/unit/browsers_spec.ts index 79ec5514b3c..19f8f9ba808 100644 --- a/packages/launcher/test/unit/browsers_spec.ts +++ b/packages/launcher/test/unit/browsers_spec.ts @@ -71,6 +71,25 @@ describe('browsers', () => { expect(result.isSupported).to.be.true expect(result.warningMessage).to.be.undefined }) + + describe('firefox validation', () => { + const FIREFOX_KNOWN_BROWSER_CHANNELS = knownBrowsers.filter((browser) => { + return browser.family === 'firefox' + }) + + FIREFOX_KNOWN_BROWSER_CHANNELS.forEach((browser) => { + it(`${browser.channel}: fails validation when Firefox major version is below 135`, () => { + // @ts-expect-error + const result = browser.validator({ + majorVersion: '134', + displayName: 'Firefox', + }) + + expect(result.isSupported).to.be.false + expect(result.warningMessage).to.equal('Cypress does not support running Firefox version 134 due to lack of WebDriver BiDi support. To use Firefox with Cypress, install version 135 or newer.') + }) + }) + }) }) }) }) diff --git a/packages/launcher/test/unit/linux_spec.ts b/packages/launcher/test/unit/linux_spec.ts index 1f030b2e2b8..429894fc9d6 100644 --- a/packages/launcher/test/unit/linux_spec.ts +++ b/packages/launcher/test/unit/linux_spec.ts @@ -85,15 +85,15 @@ describe('linux browser detection', () => { name: 'firefox', family: 'firefox', displayName: 'Firefox', - majorVersion: '99', + majorVersion: '135', path: 'firefox', profilePath: '/home/foo/snap/firefox/current', - version: '99.2.3', + version: '135.0.1', } beforeEach(() => { execa.withArgs('firefox', ['--version']) - .resolves({ stdout: 'Mozilla Firefox 99.2.3' }) + .resolves({ stdout: 'Mozilla Firefox 135.0.1' }) sinon.stub(os, 'homedir').returns('/home/foo') }) diff --git a/packages/server/lib/browsers/firefox-util.ts b/packages/server/lib/browsers/firefox-util.ts index 620260224ce..f3a3dcf6842 100644 --- a/packages/server/lib/browsers/firefox-util.ts +++ b/packages/server/lib/browsers/firefox-util.ts @@ -1,10 +1,7 @@ import Debug from 'debug' -import { CdpAutomation } from './cdp_automation' import { BidiAutomation } from './bidi_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' const debug = Debug('cypress:server:browsers:firefox-util') @@ -27,36 +24,6 @@ async function connectToNewSpecBiDi (options, automation: Automation, browserBiD }) } -async function connectToNewSpecCDP (options, automation: Automation, browserCriClient: BrowserCriClient) { - debug('firefox: reconnecting to blank tab') - - // Firefox keeps a blank tab open in versions of Firefox 123 and lower when the last tab is closed. - // For versions 124 and above, a new tab is not created, so @packages/extension creates one for us. - // Since the tab is always available on our behalf, - // we can connect to it here and navigate it to about:blank to set it up for CDP connection - const handles = await webdriverClient.getWindowHandles() - - await webdriverClient.switchToWindow(handles[0]) - - await webdriverClient.navigateTo('about:blank') - - debug('firefox: reconnecting CDP') - - if (browserCriClient) { - await browserCriClient.currentlyAttachedTarget?.close().catch(() => {}) - // Strictly speaking this shouldn't ever happen in firefox, but to future proof adding it in case. - await browserCriClient.currentlyAttachedProtocolTarget?.close().catch(() => {}) - const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank') - - await CdpAutomation.create(pageCriClient.send, pageCriClient.on, pageCriClient.off, browserCriClient.resetBrowserTargets, automation) - } - - await options.onInitializeNewBrowserTab() - - debug(`firefox: navigating to ${options.url}`) - await webdriverClient.navigateTo(options.url) -} - async function setupBiDi (webdriverClient: WebDriverClient, automation: Automation) { // webdriver needs to subscribe to the correct BiDi events or else the events we are expecting to stream in will not be sent await webdriverClient.sessionSubscribe({ events: BidiAutomation.BIDI_EVENTS }) @@ -66,63 +33,39 @@ async function setupBiDi (webdriverClient: WebDriverClient, automation: Automati return biDiClient } -async function setupCDP (remotePort: number, automation: Automation, onError?: (err: Error) => void): Promise { - const browserCriClient = await BrowserCriClient.create({ hosts: ['127.0.0.1', '::1'], port: remotePort, browserName: 'Firefox', onAsynchronousError: onError as (err: CypressError) => void, onServiceWorkerClientEvent: automation.onServiceWorkerClientEvent }) - const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank') - - await CdpAutomation.create(pageCriClient.send, pageCriClient.on, pageCriClient.off, browserCriClient.resetBrowserTargets, automation) - - return browserCriClient -} - export default { async setup ({ automation, - onError, url, - remotePort, webdriverClient: wdInstance, - useWebDriverBiDi, }: { automation: Automation - onError?: (err: Error) => void url: string - remotePort: number | undefined webdriverClient: WebDriverClient - useWebDriverBiDi: boolean - }): Promise { + }): Promise { // set the WebDriver classic instance instantiated from geckodriver webdriverClient = wdInstance - let client: BrowserCriClient | BidiAutomation + let client: BidiAutomation - if (useWebDriverBiDi) { - client = await setupBiDi(webdriverClient, automation) - // use the BiDi commands to visit the url as opposed to classic webdriver - const { contexts } = await webdriverClient.browsingContextGetTree({}) + client = await setupBiDi(webdriverClient, automation) + // use the BiDi commands to visit the url as opposed to classic webdriver + const { contexts } = await webdriverClient.browsingContextGetTree({}) - // at this point there should only be one context: the top level context. - // we need to set this to bind our AUT intercepts correctly. Hopefully we can move this in the future on a more sure implementation - client.setTopLevelContextId(contexts[0].context) + // at this point there should only be one context: the top level context. + // we need to set this to bind our AUT intercepts correctly. Hopefully we can move this in the future on a more sure implementation + client.setTopLevelContextId(contexts[0].context) - await webdriverClient.browsingContextNavigate({ - context: contexts[0].context, - url, - }) - } else { - client = await setupCDP(remotePort as number, automation, onError) - // uses webdriver classic to navigate - await webdriverClient.navigateTo(url) - } + await webdriverClient.browsingContextNavigate({ + context: contexts[0].context, + url, + }) return client }, connectToNewSpecBiDi, - connectToNewSpecCDP, - setupBiDi, - setupCDP, } diff --git a/packages/server/lib/browsers/firefox.ts b/packages/server/lib/browsers/firefox.ts index abc57a9bbf7..aa5fe8ba818 100644 --- a/packages/server/lib/browsers/firefox.ts +++ b/packages/server/lib/browsers/firefox.ts @@ -14,28 +14,15 @@ import utils from './utils' import type { Browser, BrowserInstance, GracefulShutdownOptions } from './types' import os from 'os' import mimeDb from 'mime-db' -import { BrowserCriClient } from './browser-cri-client' import type { BidiAutomation } from './bidi_automation' import type { Automation } from '../automation' import { getCtx } from '@packages/data-context' -import { getError, SerializedError, CypressError } from '@packages/errors' +import { getError, CypressError } from '@packages/errors' import type { BrowserLaunchOpts, BrowserNewTabOpts, RunModeVideoApi } from '@packages/types' import type { RemoteConfig } from 'webdriver' import type { GeckodriverParameters } from 'geckodriver' import { WebDriver } from './webdriver' -export class CDPFailedToStartFirefox extends Error { - private static readonly ERROR_NAME = 'CDPFailedToStartFirefox' - constructor (message) { - super(message) - this.name = CDPFailedToStartFirefox.ERROR_NAME - } - - public static isCDPFailedToStartFirefoxError (error?: SerializedError): error is CDPFailedToStartFirefox { - return error?.name === CDPFailedToStartFirefox.ERROR_NAME - } -} - const debug = Debug('cypress:server:browsers:firefox') const debugVerbose = Debug('cypress-verbose:server:browsers:firefox') @@ -335,20 +322,18 @@ const defaultPreferences = { 'browser.helperApps.neverAsk.saveToDisk': downloadMimeTypes, } -// CDP is deprecated in Firefox 129 and up. +// CDP was deprecated in Firefox 129 and up and was removed in Firefox 141. // To enable BiDi (without CDP), we need to set // remote.active-protocol=1 -// In order to enable CDP (without BiDi), we need to set +// Cypress no longer supports CDP within Firefox. However, it can be enabled if needed (but only on Firefox 141 and lower) by setting // remote.active-protocol=2 // both can be enabled via // remote.active-protocol=3 // @see https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/ +// @see https://fxdx.dev/webdriver-bidi-becomes-the-default-for-cypress-in-firefox/ // @see https://github.com/cypress-io/cypress/issues/29713 const ACTIVE_PROTOCOLS = Object.freeze({ BIDI: 1, - CDP: 2, - // this key isn't actively used but checked in here if we need to turn it on for internal debugging - CDP_AND_BIDI: 3, }) const FIREFOX_HEADED_USERCSS = `\ @@ -387,7 +372,6 @@ toolbox { } ` -let browserCriClient: BrowserCriClient | undefined let browserBidiClient: BidiAutomation | undefined /** @@ -396,12 +380,6 @@ let browserBidiClient: BidiAutomation | undefined export function clearInstanceState (options: GracefulShutdownOptions = {}) { debug('clearing instance state') - if (browserCriClient) { - debug('closing remote interface client') - browserCriClient.close(options.gracefulShutdown).catch(() => {}) - browserCriClient = undefined - } - if (browserBidiClient) { debug('unbinding bidi client events') browserBidiClient.close() @@ -409,21 +387,8 @@ export function clearInstanceState (options: GracefulShutdownOptions = {}) { } } -function shouldUseBiDi (browser: Browser): boolean { - try { - // Gating on firefox version 135 to turn on BiDi as this is when all of our internal Cypress tests were able to pass. - return (browser.family === 'firefox' && !process.env.FORCE_FIREFOX_CDP && Number(browser.majorVersion) >= 135) - } catch (err: unknown) { - return false - } -} - export async function connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation) { - if (shouldUseBiDi(browser)) { - await firefoxUtil.connectToNewSpecBiDi(options, automation, browserBidiClient!) - } else { - await firefoxUtil.connectToNewSpecCDP(options, automation, browserCriClient!) - } + await firefoxUtil.connectToNewSpecBiDi(options, automation, browserBidiClient!) } export function connectToExisting () { @@ -445,12 +410,6 @@ async function recordVideo (videoApi: RunModeVideoApi) { } export async function open (browser: Browser, url: string, options: BrowserLaunchOpts, automation: Automation): Promise { - const USE_WEBDRIVER_BIDI = shouldUseBiDi(browser) - - if (!USE_WEBDRIVER_BIDI) { - errors.warning('CDP_FIREFOX_DEPRECATED') - } - const defaultLaunchOptions = utils.getDefaultLaunchOptions({ extensions: [] as string[], preferences: _.extend({}, defaultPreferences), @@ -463,7 +422,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc ], }) - defaultLaunchOptions.preferences['remote.active-protocols'] = USE_WEBDRIVER_BIDI ? ACTIVE_PROTOCOLS.BIDI : ACTIVE_PROTOCOLS.CDP + defaultLaunchOptions.preferences['remote.active-protocols'] = ACTIVE_PROTOCOLS.BIDI if (browser.isHeadless) { defaultLaunchOptions.args.push('-headless') @@ -558,26 +517,6 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc debug('firefox directories %o', { path: profile.path(), cacheDir, extensionDest }) - const xulStorePath = path.join(profile.path(), 'xulstore.json') - - // 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 } } }) - } - launchOptions.preferences['browser.cache.disk.parent_directory'] = cacheDir const userCSSPath = path.join(profileDir, 'chrome') @@ -669,7 +608,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc alwaysMatch: { browserName: 'firefox', acceptInsecureCerts: true, - webSocketUrl: USE_WEBDRIVER_BIDI, + webSocketUrl: true, // @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions 'moz:firefoxOptions': { profile: base64EncodedProfile, @@ -677,9 +616,6 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc args: launchOptions.args, prefs: launchOptions.preferences, }, - // @see https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html#moz-debuggeraddress - // we specify the debugger address option for Webdriver, which will return us the CDP address when the capability is returned. - 'moz:debuggerAddress': !USE_WEBDRIVER_BIDI, // @see https://webdriver.io/docs/capabilities/#wdiogeckodriveroptions // webdriver starts geckodriver with the correct options on behalf of Cypress 'wdio:geckodriverOptions': geckoDriverOptions, @@ -749,26 +685,12 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc return browserReturnStatus || driverReturnStatus } - let cdpPort: number | undefined - - if (!USE_WEBDRIVER_BIDI) { - // In some cases, the webdriver session will NOT return the moz:debuggerAddress capability even though - // we set it to true in the capabilities. This is out of our control, so when this happens, we fail the browser - // and gracefully terminate the related processes and attempt to relaunch the browser in the hopes we get a - // CDP address. @see https://github.com/cypress-io/cypress/issues/30352#issuecomment-2405701867 for more details. - if (!webdriverClient.capabilities['moz:debuggerAddress']) { - debugVerbose(`firefox failed to spawn with CDP connection. Failing current instance and retrying`) - // since this fails before the instance is created, we need to kill the processes here or else they will stay open - browserInstanceWrapper.kill() - throw new CDPFailedToStartFirefox(`webdriver session failed to start CDP even though "moz:debuggerAddress" was provided. Please try to relaunch the browser`) - } - - cdpPort = parseInt(new URL(`ws://${webdriverClient.capabilities['moz:debuggerAddress']}`).port) - - debug(`CDP running on port ${cdpPort}`) - - // 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 webdriverClient.maximizeWindow() } // install the browser extensions @@ -782,16 +704,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc })) debug('setting up firefox utils') - const client = await firefoxUtil.setup({ automation, url, webdriverClient, remotePort: cdpPort, useWebDriverBiDi: USE_WEBDRIVER_BIDI, onError: options.onError }) - - if (client instanceof BrowserCriClient) { - browserCriClient = client - await utils.executeAfterBrowserLaunch(browser, { - webSocketDebuggerUrl: browserCriClient.getWebSocketDebuggerUrl(), - }) - } else { - browserBidiClient = client - } + browserBidiClient = await firefoxUtil.setup({ automation, url, webdriverClient }) } catch (err: unknown) { errors.throwErr('FIREFOX_COULD_NOT_CONNECT', err as Error) } diff --git a/packages/server/lib/modes/run.ts b/packages/server/lib/modes/run.ts index 0f97e38e97a..12660cd83e0 100644 --- a/packages/server/lib/modes/run.ts +++ b/packages/server/lib/modes/run.ts @@ -27,7 +27,6 @@ import * as printResults from '../util/print-run' import { telemetry } from '@packages/telemetry' import { CypressRunResult, createPublicBrowser, createPublicConfig, createPublicRunResults, createPublicSpec, createPublicSpecResults } from './results' import { EarlyExitTerminator } from '../util/graceful_crash_handling' -import { CDPFailedToStartFirefox } from '../browsers/firefox' import type { CypressError } from '@packages/errors' type SetScreenshotMetadata = (data: TakeScreenshotProps) => void @@ -513,11 +512,7 @@ async function waitForBrowserToConnect (options: { project: Project, socketId: s // try again up to 3 attempts const word = browserLaunchAttempt === 1 ? 'Retrying...' : 'Retrying again...' - if (CDPFailedToStartFirefox.isCDPFailedToStartFirefoxError(err?.originalError)) { - errors.warning('FIREFOX_CDP_FAILED_TO_CONNECT', word) - } else { - errors.warning('TESTS_DID_NOT_START_RETRYING', word) - } + errors.warning('TESTS_DID_NOT_START_RETRYING', word) browserLaunchAttempt += 1 @@ -534,19 +529,7 @@ async function waitForBrowserToConnect (options: { project: Project, socketId: s waitForSocketConnection(project, socketId), // TODO: remove the need to extend options and coerce this type launchBrowser(options as typeof options & { setScreenshotMetadata: SetScreenshotMetadata }), - ]).catch((e: CypressError) => { - // if the error wrapped is a CDPFailedToStartFirefox, try to relaunch the browser - if (CDPFailedToStartFirefox.isCDPFailedToStartFirefoxError(e?.originalError)) { - // if CDP fails to connect, which is ultimately out of our control and in the hands of webdriver - // we retry launching the browser in the hopes the session is spawned correctly - debug(`Caught in launchBrowser: ${e.details}`) - - return retryOnError(e) - } - - // otherwise, fail - throw e - }) + ]) .timeout(browserTimeout) .then(() => { telemetry.getSpan(`waitForBrowserToConnect:attempt:${browserLaunchAttempt}`)?.end() diff --git a/packages/server/lib/socket-base.ts b/packages/server/lib/socket-base.ts index 7b14f55d94c..cd2a86e217f 100644 --- a/packages/server/lib/socket-base.ts +++ b/packages/server/lib/socket-base.ts @@ -226,9 +226,7 @@ export class SocketBase { debug('automation:client connected') // only send the necessary config - automationClient.emit('automation:config', { - IS_CDP_FORCED_FOR_FIREFOX: !!process.env.FORCE_FIREFOX_CDP, - }) + automationClient.emit('automation:config', {}) // if our automation disconnects then we're // in trouble and should probably bomb everything diff --git a/packages/server/test/unit/browsers/firefox_spec.ts b/packages/server/test/unit/browsers/firefox_spec.ts index a9b9e61ff74..41eb5534e8a 100644 --- a/packages/server/test/unit/browsers/firefox_spec.ts +++ b/packages/server/test/unit/browsers/firefox_spec.ts @@ -6,10 +6,6 @@ import os from 'os' import sinon from 'sinon' import fsExtra from 'fs-extra' import * as firefox from '../../../lib/browsers/firefox' -import firefoxUtil from '../../../lib/browsers/firefox-util' -import { CdpAutomation } from '../../../lib/browsers/cdp_automation' -import { BrowserCriClient } from '../../../lib/browsers/browser-cri-client' -import { ICriClient } from '../../../lib/browsers/cri-client' import { type Client as WebDriverClient, default as webdriver } from 'webdriver' import { EventEmitter } from 'stream' import { BidiAutomation } from '../../../lib/browsers/bidi_automation' @@ -22,23 +18,13 @@ const plugins = require('../../../lib/plugins') const specUtil = require('../../specUtils') describe('lib/browsers/firefox', () => { - const port = 3333 const mockContextId = '1234-5678' let wdInstance: sinon.SinonStubbedInstance - let browserCriClient: BrowserCriClient let bidiAutomationClient: BidiAutomation - afterEach(() => { - return mockfs.restore() - }) - beforeEach(function () { sinon.stub(utils, 'getProfileDir').returns('/path/to/appData/firefox-stable/interactive') - mockfs({ - '/path/to/appData/firefox-stable/interactive': {}, - }) - wdInstance = { maximizeWindow: sinon.stub(), installAddOn: sinon.stub(), @@ -49,7 +35,6 @@ describe('lib/browsers/firefox', () => { browsingContextGetTree: sinon.stub(), browsingContextNavigate: sinon.stub(), capabilities: { - 'moz:debuggerAddress': '127.0.0.1:12345', // @ts-expect-error 'moz:processID': 1234, 'wdio:driverPID': 5678, @@ -79,9 +64,8 @@ describe('lib/browsers/firefox', () => { context('#open', () => { beforeEach(function () { - // majorVersion >= 86 indicates CDP support for Firefox, which provides - // the CDP debugger URL for the after:browser:launch tests - this.browser = { name: 'firefox', channel: 'stable', majorVersion: 100, path: '/path/to/binary' } + // majorVersion >= 135 indicates BiDi support for Firefox + this.browser = { name: 'firefox', channel: 'stable', majorVersion: 135, path: '/path/to/binary' } this.automation = { use: sinon.stub().returns({}), } @@ -107,14 +91,6 @@ describe('lib/browsers/firefox', () => { 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') - browserCriClient.close = sinon.stub().resolves() - - sinon.stub(BrowserCriClient, 'create').resolves(browserCriClient) - sinon.stub(CdpAutomation, 'create').resolves() bidiAutomationClient = sinon.createStubInstance(BidiAutomation) bidiAutomationClient.setTopLevelContextId = sinon.stub().returns(undefined) @@ -122,32 +98,13 @@ describe('lib/browsers/firefox', () => { sinon.stub(BidiAutomation, 'create').returns(bidiAutomationClient) }) - context('#connectToNewSpec', () => { + context('#connectToNewSpecBiDi', () => { beforeEach(function () { this.options.onError = () => {} this.options.onInitializeNewBrowserTab = sinon.stub() }) - it('CDP: calls connectToNewSpecCDP in firefoxUtil', async function () { - 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(wdInstance.getWindowHandles).to.have.been.called - expect(wdInstance.switchToWindow).to.have.been.calledWith('mock-context-id') - - // first time when connecting a new tab - expect(wdInstance.navigateTo).to.have.been.calledWith('about:blank') - // second time when navigating to the spec - expect(wdInstance.navigateTo).to.have.been.calledWith('next-spec-url') - }) - it('BiDi: calls connectToNewSpecBiDi in firefoxUtil', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' await firefox.open(this.browser, 'http://', this.options, this.automation) this.options.url = 'next-spec-url' @@ -247,13 +204,10 @@ describe('lib/browsers/firefox', () => { describe(`webdriver capabilities`, () => { const getExpectedCapabilities = ({ - shouldUseBiDi, isDebugEnabled, }: { - shouldUseBiDi?: boolean isDebugEnabled?: boolean } = { - shouldUseBiDi: false, isDebugEnabled: false, }) => { return { @@ -261,7 +215,7 @@ describe('lib/browsers/firefox', () => { capabilities: sinon.match({ alwaysMatch: { browserName: 'firefox', - webSocketUrl: !!shouldUseBiDi, + webSocketUrl: true, acceptInsecureCerts: true, // @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions 'moz:firefoxOptions': { @@ -274,11 +228,10 @@ describe('lib/browsers/firefox', () => { ], // only partially match the preferences object because it is so large prefs: { - 'remote.active-protocols': shouldUseBiDi ? 1 : 2, + 'remote.active-protocols': 1, 'remote.enabled': true, }, }, - 'moz:debuggerAddress': !shouldUseBiDi, 'wdio:geckodriverOptions': { host: '127.0.0.1', marionetteHost: '127.0.0.1', @@ -304,71 +257,47 @@ describe('lib/browsers/firefox', () => { } } - describe(`creates the WebDriver session and geckodriver instance through capabilities, installs the extension, and passes the correct port to CDP`, function () { - it('for CDP', async function () { - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ shouldUseBiDi: false }))) - - expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true) + it('creates the WebDriver session and geckodriver instance through capabilities and installs the extension', async function () { + this.browser.family = 'firefox' + this.browser.majorVersion = '135' + await firefox.open(this.browser, 'http://', this.options, this.automation) + expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities())) - expect(wdInstance.navigateTo).to.have.been.calledWith('http://') + expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true) - // 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 }) - }) + expect(wdInstance.sessionSubscribe).to.be.calledWith({ events: [ + 'network.beforeRequestSent', + 'network.responseStarted', + 'network.responseCompleted', + 'network.fetchError', + 'browsingContext.contextCreated', + 'browsingContext.contextDestroyed', + ] }) - it('for BiDi', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ shouldUseBiDi: true }))) - - expect(wdInstance.installAddOn).to.have.been.calledWith('/path/to/ext', true) - - expect(wdInstance.sessionSubscribe).to.be.calledWith({ events: [ - 'network.beforeRequestSent', - 'network.responseStarted', - 'network.responseCompleted', - 'network.fetchError', - 'browsingContext.contextCreated', - 'browsingContext.contextDestroyed', - ] }) - - expect(wdInstance.browsingContextGetTree).to.be.calledWith({}) - - expect(wdInstance.browsingContextNavigate).to.have.been.calledWith({ - context: mockContextId, - url: 'http://', - }) - - // make sure Bidi gets created - expect(BidiAutomation.create).to.be.calledWith(wdInstance, this.automation) - expect(bidiAutomationClient.setTopLevelContextId).to.be.calledWith(mockContextId) - }) - }) + expect(wdInstance.browsingContextGetTree).to.be.calledWith({}) - describe('debugging: sets additional arguments if "DEBUG=cypress-verbose:server:browsers:geckodriver" and "DEBUG=cypress-verbose:server:browsers:webdriver" is set', () => { - afterEach(() => { - debug.disable() + expect(wdInstance.browsingContextNavigate).to.have.been.calledWith({ + context: mockContextId, + url: 'http://', }) - it('for CDP', async function () { - debug.enable('cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver') - - await firefox.open(this.browser, 'http://', this.options, this.automation) + // make sure Bidi gets created + expect(BidiAutomation.create).to.be.calledWith(wdInstance, this.automation) + expect(bidiAutomationClient.setTopLevelContextId).to.be.calledWith(mockContextId) + }) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ isDebugEnabled: true }))) - }) + afterEach(() => { + debug.disable() + }) - it('for BiDi', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' - debug.enable('cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver') + it('debugging: sets additional arguments if "DEBUG=cypress-verbose:server:browsers:geckodriver" and "DEBUG=cypress-verbose:server:browsers:webdriver" is set', async function () { + this.browser.family = 'firefox' + this.browser.majorVersion = '135' + debug.enable('cypress-verbose:server:browsers:geckodriver,cypress-verbose:server:browsers:webdriver') - await firefox.open(this.browser, 'http://', this.options, this.automation) + await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ isDebugEnabled: true, shouldUseBiDi: true }))) - }) + expect(webdriver.newSession).to.have.been.calledWith((getExpectedCapabilities({ isDebugEnabled: true }))) }) }) @@ -418,7 +347,6 @@ describe('lib/browsers/firefox', () => { }, [path.resolve(`${__dirname }/../../extension`)]: { 'abc': 'test' }, '/path/to/appData/firefox-stable/interactive': { - 'xulstore.json': '[foo xulstore.json]', 'chrome': { 'userChrome.css': '[foo userChrome.css]' }, }, }) @@ -493,26 +421,11 @@ describe('lib/browsers/firefox', () => { }) describe('sets "remote.active-protocols"', function () { - // CDP is deprecated in Firefox 129 and up. - // In order to enable CDP, we need to set - // remote.active-protocol=2 + // CDP was deprecated in Firefox 129 and up and was removed in Firefox 141. // @see https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/ + // @see https://fxdx.dev/webdriver-bidi-becomes-the-default-for-cypress-in-firefox/ // @see https://github.com/cypress-io/cypress/issues/29713 - it('=2 to enable only CDP', async function () { - const executeBeforeBrowserLaunchSpy = sinon.spy(utils, 'executeBeforeBrowserLaunch') - - await firefox.open(this.browser, 'http://', this.options, this.automation) - - expect(executeBeforeBrowserLaunchSpy).to.have.been.calledWith(this.browser, sinon.match({ - preferences: { - 'remote.active-protocols': 2, - }, - }), this.options) - }) - it('=1 to enable only BiDi', async function () { - this.browser.family = 'firefox' - this.browser.majorVersion = '135' const executeBeforeBrowserLaunchSpy = sinon.spy(utils, 'executeBeforeBrowserLaunch') await firefox.open(this.browser, 'http://', this.options, this.automation) @@ -532,78 +445,40 @@ describe('lib/browsers/firefox', () => { expect(result.kill).to.be.an.instanceof(Function) }) - it('always clear user profile if it already exists', async function () { - mockfs({ - '/path/to/appData/firefox-stable/interactive/': { - 'xulstore.json': '[foo xulstore.json]', - 'chrome': { 'userChrome.css': '[foo userChrome.css]' }, - }, + context('profile/extension', () => { + afterEach(() => { + return mockfs.restore() }) - await firefox.open(this.browser, 'http://', this.options, this.automation) - - expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined - }) - - it('creates xulstore.json if not exist', async function () { - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(fsExtra.writeJSON).to.have.been.calledWith('/path/to/appData/firefox-stable/interactive/xulstore.json', { - 'chrome://browser/content/browser.xhtml': - { - 'main-window': - { - 'width': 1280, - 'height': 1024, - 'sizemode': 'maximized', - }, + it('always clear user profile if it already exists', async function () { + mockfs({ + '/path/to/appData/firefox-stable/interactive/': { + 'chrome': { 'userChrome.css': '[foo userChrome.css]' }, }, + }) - }) - }) - - 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') - }) + await firefox.open(this.browser, 'http://', this.options, this.automation) - it('clears browser cache', async function () { - mockfs({ - '/path/to/appData/firefox-stable/interactive/': { - 'CypressCache': { 'foo': 'bar' }, - }, + expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined }) - this.options.isTextTerminal = false - - await firefox.open(this.browser, 'http://', this.options, this.automation) - expect(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined - }) - - it('wraps errors when failing to connect to firefox (CDP failure)', async function () { - const err = new Error('failed to connect to CDP') - - // BrowserCriClient.create is stubbed above. restore it and re-stub BrowserCriClient.create - // @ts-expect-error - BrowserCriClient.create.restore() - - sinon.stub(BrowserCriClient, 'create').rejects(err) + it('creates chrome/userChrome.css if not exist', async function () { + await firefox.open(this.browser, 'http://', this.options, this.automation) - await expect(firefox.open(this.browser, 'http://', this.options, this.automation)).to.be.rejectedWith() - .then((wrapperErr) => { - expect(wrapperErr.message).to.include('Cypress failed to make a connection to Firefox.') - expect(wrapperErr.details).to.include(err.message) + expect(fsExtra.writeFile).to.have.been.calledWith('/path/to/appData/firefox-stable/interactive/chrome/userChrome.css') }) - }) - it('executes after:browser:launch if registered', async function () { - plugins.has.withArgs('after:browser:launch').returns(true) - plugins.execute.resolves(null) + it('clears browser cache', async function () { + mockfs({ + '/path/to/appData/firefox-stable/interactive/': { + 'CypressCache': { 'foo': 'bar' }, + }, + }) - await firefox.open(this.browser, 'http://', this.options, this.automation) + this.options.isTextTerminal = false - 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(specUtil.getFsPath('/path/to/appData/firefox-stable/interactive')).to.be.undefined }) }) @@ -633,7 +508,6 @@ describe('lib/browsers/firefox', () => { 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') }) @@ -653,26 +527,9 @@ describe('lib/browsers/firefox', () => { 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') }) - - it('throws CDPFailedToStartFirefox if the mox:debuggerAddress capability is not returned by webdriver', function () { - delete wdInstance.capabilities['moz:debuggerAddress'] - sinon.stub(process, 'kill').returns(true) - - return firefox.open(this.browser, 'http://', this.options, this.automation).catch((err) => { - // make sure we through the correct error here to prompt @packages/server/lib/modes/run.ts - // to retry the browser connection - expect(err.details).to.include('CDPFailedToStartFirefox: webdriver session failed to start CDP even though "moz:debuggerAddress" was provided. Please try to relaunch the browser') - expect(err.type).to.equal('FIREFOX_COULD_NOT_CONNECT') - // 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) - }) - }) }) }) @@ -681,56 +538,4 @@ describe('lib/browsers/firefox', () => { expect(firefox.connectProtocolToBrowser).to.throw('Protocol is not yet supported in firefox.') }) }) - - context('#closeProtocolConnection', () => { - it('throws error', () => { - expect(firefox.closeProtocolConnection).to.throw('Protocol is not yet supported in firefox.') - }) - }) - - context('firefox-util', () => { - context('#setupCDP', function () { - it('correctly sets up the remote agent', async function () { - const criClientStub: ICriClient = { - targetId: '', - send: sinon.stub(), - on: sinon.stub(), - off: sinon.stub(), - close: sinon.stub(), - ws: sinon.stub() as any, - queue: { - enableCommands: [], - enqueuedCommands: [], - subscriptions: [], - }, - closed: false, - connected: false, - } - - const automationStub = { - onServiceWorkerClientEvent: sinon.stub(), - } - - const browserCriClient: BrowserCriClient = sinon.createStubInstance(BrowserCriClient) - - browserCriClient.attachToTargetUrl = sinon.stub().resolves(criClientStub) - - sinon.stub(BrowserCriClient, 'create').resolves(browserCriClient) - sinon.stub(CdpAutomation, 'create').resolves() - - const actual = await firefoxUtil.setupCDP(port, automationStub, null) - - expect(actual).to.equal(browserCriClient) - expect(browserCriClient.attachToTargetUrl).to.be.calledWith('about:blank') - expect(BrowserCriClient.create).to.be.calledWith({ hosts: ['127.0.0.1', '::1'], port, browserName: 'Firefox', onAsynchronousError: null, onServiceWorkerClientEvent: automationStub.onServiceWorkerClientEvent }) - expect(CdpAutomation.create).to.be.calledWith( - criClientStub.send, - criClientStub.on, - criClientStub.off, - browserCriClient.resetBrowserTargets, - automationStub, - ) - }) - }) - }) }) diff --git a/system-tests/__snapshots__/cdp_deprecated_firefox_spec.ts.js b/system-tests/__snapshots__/cdp_deprecated_firefox_spec.ts.js deleted file mode 100644 index e9c80eb32a4..00000000000 --- a/system-tests/__snapshots__/cdp_deprecated_firefox_spec.ts.js +++ /dev/null @@ -1,55 +0,0 @@ -exports['CDP deprecated in Firefox / logs a warning to the user that CDP is deprecated and will be removed in Cypress 15'] = ` - -==================================================================================================== - - (Run Starting) - - ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Cypress: 1.2.3 │ - │ Browser: FooBrowser 88 │ - │ Specs: 1 found (simple_passing.cy.js) │ - │ Searched: cypress/e2e/simple_passing.cy.js │ - └────────────────────────────────────────────────────────────────────────────────────────────────┘ - - -──────────────────────────────────────────────────────────────────────────────────────────────────── - - Running: simple_passing.cy.js (1 of 1) -Since Firefox 129, Chrome DevTools Protocol (CDP) has been deprecated in Firefox. In Firefox 135 and above, Cypress defaults to automating the Firefox browser with WebDriver BiDi. Cypress will no longer support CDP within Firefox in the future and is planned for removal in Cypress 15. - - - simple passing spec - ✓ passes - - - 1 passing - - - (Results) - - ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Tests: 1 │ - │ Passing: 1 │ - │ Failing: 0 │ - │ Pending: 0 │ - │ Skipped: 0 │ - │ Screenshots: 0 │ - │ Video: false │ - │ Duration: X seconds │ - │ Spec Ran: simple_passing.cy.js │ - └────────────────────────────────────────────────────────────────────────────────────────────────┘ - - -==================================================================================================== - - (Run Finished) - - - Spec Tests Passing Failing Pending Skipped - ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ ✔ simple_passing.cy.js XX:XX 1 1 - - - │ - └────────────────────────────────────────────────────────────────────────────────────────────────┘ - ✔ All specs passed! XX:XX 1 1 - - - - - -` diff --git a/system-tests/test/cdp_deprecated_firefox_spec.ts b/system-tests/test/cdp_deprecated_firefox_spec.ts deleted file mode 100644 index b9eb8706e4f..00000000000 --- a/system-tests/test/cdp_deprecated_firefox_spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import systemTests from '../lib/system-tests' - -describe('CDP deprecated in Firefox', () => { - systemTests.setup() - - systemTests.it('logs a warning to the user that CDP is deprecated and will be removed in Cypress 15', { - browser: 'firefox', - processEnv: { - FORCE_FIREFOX_CDP: '1', - }, - expectedExitCode: 0, - snapshot: true, - spec: 'simple_passing.cy.js', - onStdout: (stdout) => { - expect(stdout).to.include('Since Firefox 129, Chrome DevTools Protocol (CDP) has been deprecated in Firefox. In Firefox 135 and above, Cypress defaults to automating the Firefox browser with WebDriver BiDi. Cypress will no longer support CDP within Firefox in the future and is planned for removal in Cypress 15.') - }, - }) -}) diff --git a/tooling/v8-snapshot/cache/win32/snapshot-meta.json b/tooling/v8-snapshot/cache/win32/snapshot-meta.json index 985b27017d4..658ac8ba3e6 100644 --- a/tooling/v8-snapshot/cache/win32/snapshot-meta.json +++ b/tooling/v8-snapshot/cache/win32/snapshot-meta.json @@ -4232,5 +4232,5 @@ "./tooling/v8-snapshot/cache/win32/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "c99978c5469ebdc6acd39c011a582e126351fe2778b3bb06b895792393b4f344" + "deferredHash": "8314db54df0460f6d49850716cab3431b20fcf5dc94a69f6f5a6d87e071d0865" } \ No newline at end of file