Skip to content

Commit

Permalink
misc: replace marionette-client with geckodriver as b2g marionette cl…
Browse files Browse the repository at this point in the history
…ient is no longer supported [run ci]
  • Loading branch information
AtofStryker committed Sep 17, 2024
1 parent c17d1ff commit 161ebbf
Show file tree
Hide file tree
Showing 18 changed files with 1,573 additions and 336 deletions.
2 changes: 1 addition & 1 deletion .circleci/cache-version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Bump this version to force CI to re-create the cache from scratch.

09-12-24
09-17-24
9 changes: 5 additions & 4 deletions .circleci/workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
- 'MikeMcC399:issue-30124-electron-packager-to-18.3.4'
- 'misc/remove_marionette_for_geckodriver'
- 'publish-binary'

# usually we don't build Mac app - it takes a long time
Expand All @@ -42,6 +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: [ 'misc/remove_marionette_for_geckodriver', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand All @@ -52,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: [ 'fix/revert_ozone_hint', << pipeline.git.branch >> ]
- equal: [ 'misc/remove_marionette_for_geckodriver', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand All @@ -75,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: [ 'MikeMcC399:issue-30124-electron-packager-to-18.3.4', << pipeline.git.branch >> ]
- equal: [ 'misc/remove_marionette_for_geckodriver', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand Down Expand Up @@ -151,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" != "fix/revert_ozone_hint" ]]; then
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "misc/remove_marionette_for_geckodriver" ]]; 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
Expand Down
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ _Released 9/24/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). Addressed in [#30250](https://github.com/cypress-io/cypress/pull/30250).
- Pass along the related log to the `createSnapshot` function for protocol usage. Addressed in [#30244](https://github.com/cypress-io/cypress/pull/30244).

**Dependency Updates:**
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/errors/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1192,11 +1192,11 @@ export const AllCypressErrors = {
The error was: ${fmt.highlightSecondary(errMsg)}`
},
FIREFOX_MARIONETTE_FAILURE: (origin: string, err: Error) => {
FIREFOX_GECKODRIVER_FAILURE: (origin: string, err: Error) => {
return errTemplate`\
Cypress could not connect to Firefox.
An unexpected error was received from Marionette: ${fmt.highlightSecondary(origin)}
An unexpected error was received from GeckoDriver: ${fmt.highlightSecondary(origin)}
To avoid this error, ensure that there are no other instances of Firefox launched by Cypress running.
Expand Down
2 changes: 1 addition & 1 deletion packages/errors/test/unit/visualSnapshotErrors_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,7 @@ describe('visual error templates', () => {
default: ['spec', '1', 'spec must be a string or comma-separated list'],
}
},
FIREFOX_MARIONETTE_FAILURE: () => {
FIREFOX_GECKODRIVER_FAILURE: () => {
const err = makeErr()

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/graphql/schemas/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,7 @@ enum ErrorTypeEnum {
EXTENSION_NOT_LOADED
FIREFOX_COULD_NOT_CONNECT
FIREFOX_GC_INTERVAL_REMOVED
FIREFOX_MARIONETTE_FAILURE
FIREFOX_GECKODRIVER_FAILURE
FIXTURE_NOT_FOUND
FOLDER_NOT_WRITABLE
FREE_PLAN_EXCEEDS_MONTHLY_PRIVATE_TESTS
Expand Down
2 changes: 1 addition & 1 deletion packages/server/lib/browsers/browser-cri-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export class BrowserCriClient {
*
* @param {BrowserCriClientCreateOptions} options the options for creating the browser cri client
* @param options.browserName the display name of the browser being launched
* @param options.fullyManageTabs whether or not to fully manage tabs. This is useful for firefox where some work is done with marionette and some with CDP. We don't want to handle disconnections in this class in those scenarios
* @param options.fullyManageTabs whether or not to fully manage tabs. This is useful for firefox where some work is done with GeckoDriver and some with CDP. We don't want to handle disconnections in this class in those scenarios
* @param options.hosts the hosts to which to attempt to connect
* @param options.onAsynchronousError callback for any cdp fatal errors
* @param options.onReconnect callback for when the browser cri client reconnects to the browser
Expand Down
188 changes: 89 additions & 99 deletions packages/server/lib/browsers/firefox-util.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import Bluebird from 'bluebird'
import Debug from 'debug'
import _ from 'lodash'
import Marionette from 'marionette-client'
import { Command } from 'marionette-client/lib/marionette/message.js'
import util from 'util'
import Foxdriver from '@benmalka/foxdriver'
import * as protocol from './protocol'
import { CdpAutomation } from './cdp_automation'
import { BrowserCriClient } from './browser-cri-client'
import type { Automation } from '../automation'

const errors = require('../errors')
import { GeckoDriver } from './geckodriver'
import type { CypressError } from '@packages/errors'

const debug = Debug('cypress:server:browsers:firefox-util')

Expand All @@ -22,11 +20,7 @@ let timings = {
collections: [] as any[],
}

let driver

const sendMarionette = (data) => {
return driver.send(new Command(data))
}
let geckoDriver: GeckoDriver

const getTabId = (tab) => {
return _.get(tab, 'browsingContextID')
Expand Down Expand Up @@ -104,55 +98,49 @@ const attachToTabMemory = Bluebird.method((tab) => {
})
})

async function connectMarionetteToNewTab () {
async function connectToNewTabClassic () {
// Firefox keeps a blank tab open in versions of Firefox 123 and lower when the last tab is closed.
// For versions 124 and above, a new tab is not created, so @packages/extension creates one for us.
// Since the tab is always available on our behalf,
// we can connect to it here and navigate it to about:blank to set it up for CDP connection
const handles = await sendMarionette({
name: 'WebDriver:GetWindowHandles',
})
const handles = await geckoDriver.getWindowHandlesWebDriverClassic()

await sendMarionette({
name: 'WebDriver:SwitchToWindow',
parameters: { handle: handles[0] },
})
await geckoDriver.switchToWindowWebDriverClassic(handles[0])

await navigateToUrl('about:blank')
await geckoDriver.navigateWebdriverClassic('about:blank')
}

async function connectToNewSpec (options, automation: Automation, browserCriClient: BrowserCriClient) {
debug('firefox: reconnecting to blank tab')

await connectMarionetteToNewTab()
await connectToNewTabClassic()

debug('firefox: reconnecting CDP')

await browserCriClient.currentlyAttachedTarget?.close().catch(() => {})
const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')
if (browserCriClient) {
await browserCriClient.currentlyAttachedTarget?.close().catch(() => {})
const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')

await CdpAutomation.create(pageCriClient.send, pageCriClient.on, pageCriClient.off, browserCriClient.resetBrowserTargets, automation)
await CdpAutomation.create(pageCriClient.send, pageCriClient.on, pageCriClient.off, browserCriClient.resetBrowserTargets, automation)
}

await options.onInitializeNewBrowserTab()

debug(`firefox: navigating to ${options.url}`)
await navigateToUrl(options.url)
await navigateToUrlClassic(options.url)
}

async function setupRemote (remotePort, automation, onError): Promise<BrowserCriClient> {
const browserCriClient = await BrowserCriClient.create({ hosts: ['127.0.0.1', '::1'], port: remotePort, browserName: 'Firefox', onAsynchronousError: onError, onServiceWorkerClientEvent: automation.onServiceWorkerClientEvent })
async function setupCDP (remotePort: number, automation: Automation, onError?: (err: Error) => void): Promise<BrowserCriClient> {
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
}

async function navigateToUrl (url) {
await sendMarionette({
name: 'WebDriver:Navigate',
parameters: { url },
})
async function navigateToUrlClassic (url: string) {
await geckoDriver.navigateWebdriverClassic(url)
}

const logGcDetails = () => {
Expand Down Expand Up @@ -219,28 +207,89 @@ export default {
return forceGcCc()
},

setup ({
async setup ({
automation,
extensions,
onError,
url,
marionettePort,
foxdriverPort,
marionettePort,
geckoDriverPort,
remotePort,
}): Bluebird<BrowserCriClient> {
return Bluebird.all([
browserName,
profilePath,
binaryPath,
isHeadless,
isTextTerminal,
}: {
automation: Automation
extensions: string[]
onError?: (err: Error) => void
url: string
foxdriverPort: number
marionettePort: number
geckoDriverPort: number
remotePort: number
browserName: string
profilePath: string
binaryPath: string
isHeadless: boolean
isTextTerminal: boolean
}): Promise<BrowserCriClient> {
const [,, browserCriClient] = await Promise.all([
this.setupFoxdriver(foxdriverPort),
this.setupMarionette(extensions, url, marionettePort),
remotePort && setupRemote(remotePort, automation, onError),
]).then(([,, browserCriClient]) => navigateToUrl(url).then(() => browserCriClient))
this.setupGeckoDriver({
port: geckoDriverPort,
marionettePort,
remotePort,
browserName,
extensions,
profilePath,
binaryPath,
isHeadless,
isTextTerminal,
}),
setupCDP(remotePort, automation, onError),
])

await navigateToUrlClassic(url)

return browserCriClient
},

connectToNewSpec,

navigateToUrl,

setupRemote,
navigateToUrlClassic,

setupCDP,

async setupGeckoDriver (opts: {
port: number
marionettePort: number
remotePort: number
browserName: string
extensions: string[]
profilePath: string
binaryPath: string
isHeadless: boolean
isTextTerminal: boolean
}) {
geckoDriver = await GeckoDriver.create({
host: '127.0.0.1',
port: opts.port,
marionetteHost: '127.0.0.1',
marionettePort: opts.marionettePort,
remotePort: opts.remotePort,
browserName: opts.browserName,
extensions: opts.extensions,
profilePath: opts.profilePath,
binaryPath: opts.binaryPath,
isHeadless: opts.isHeadless,
isTextTerminal: opts.isTextTerminal,
})
},

// NOTE: this is going to be removed in Cypress 14. @see https://github.com/cypress-io/cypress/issues/30222
async setupFoxdriver (port) {
await protocol._connectAsync({
host: '127.0.0.1',
Expand Down Expand Up @@ -305,63 +354,4 @@ export default {
})
}
},

async setupMarionette (extensions, url, port) {
await protocol._connectAsync({
host: '127.0.0.1',
port,
getDelayMsForRetry,
})

driver = new Marionette.Drivers.Promises({
port,
tries: 1, // marionette-client has its own retry logic which we want to avoid
})

debug('firefox: navigating page with webdriver')

const onError = (from, reject?) => {
if (!reject) {
reject = (err) => {
throw err
}
}

return (err) => {
debug('error in marionette %o', { from, err })
reject(errors.get('FIREFOX_MARIONETTE_FAILURE', from, err))
}
}

await driver.connect()
.catch(onError('connection'))

await new Bluebird((resolve, reject) => {
const _onError = (from) => {
return onError(from, reject)
}

const { tcp } = driver

tcp.socket.on('error', _onError('Socket'))
tcp.client.on('error', _onError('CommandStream'))

sendMarionette({
name: 'WebDriver:NewSession',
parameters: { acceptInsecureCerts: true },
}).then(() => {
return Bluebird.all(_.map(extensions, (path) => {
return sendMarionette({
name: 'Addon:Install',
parameters: { path, temporary: true },
})
}))
})
.then(resolve)
.catch(_onError('commands'))
})

// even though Marionette is not used past this point, we have to keep the session open
// or else `acceptInsecureCerts` will cease to apply and SSL validation prompts will appear.
},
}
Loading

0 comments on commit 161ebbf

Please sign in to comment.