Skip to content

Commit

Permalink
chore(server): convert remaining browsers code to typescript (#23556)
Browse files Browse the repository at this point in the history
  • Loading branch information
flotwig committed Aug 29, 2022
1 parent 4871ebc commit 3c2fea2
Show file tree
Hide file tree
Showing 26 changed files with 588 additions and 602 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe('Cypress In Cypress CT', { viewportWidth: 1500, defaultCommandTimeout:
expect(ctx.actions.browser.setActiveBrowserById).to.have.been.calledWith(browserId)
expect(genId).to.eql('firefox-firefox-stable')
expect(ctx.actions.project.launchProject).to.have.been.calledWith(
ctx.coreData.currentTestingType, {}, o.sinon.match(new RegExp('cypress\-in\-cypress\/src\/TestComponent\.spec\.jsx$')),
ctx.coreData.currentTestingType, undefined, o.sinon.match(new RegExp('cypress\-in\-cypress\/src\/TestComponent\.spec\.jsx$')),
)
})
})
Expand Down
2 changes: 1 addition & 1 deletion packages/app/cypress/e2e/top-nav.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe('App Top Nav Workflows', () => {
expect(ctx.actions.browser.setActiveBrowserById).to.have.been.calledWith(browserId)
expect(genId).to.eql('edge-chromium-stable')
expect(ctx.actions.project.launchProject).to.have.been.calledWith(
ctx.coreData.currentTestingType, {}, undefined,
ctx.coreData.currentTestingType, undefined, undefined,
)
})
})
Expand Down
8 changes: 4 additions & 4 deletions packages/data-context/src/actions/ProjectActions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CodeGenType, MutationSetProjectPreferencesInGlobalCacheArgs, NexusGenObjects, NexusGenUnions } from '@packages/graphql/src/gen/nxs.gen'
import type { InitializeProjectOptions, FoundBrowser, FoundSpec, LaunchOpts, OpenProjectLaunchOptions, Preferences, TestingType, ReceivedCypressOptions, AddProject, FullConfig, AllowedState, SpecWithRelativeRoot } from '@packages/types'
import type { InitializeProjectOptions, FoundBrowser, FoundSpec, OpenProjectLaunchOptions, Preferences, TestingType, ReceivedCypressOptions, AddProject, FullConfig, AllowedState, SpecWithRelativeRoot, OpenProjectLaunchOpts } from '@packages/types'
import type { EventEmitter } from 'events'
import execa from 'execa'
import path from 'path'
Expand All @@ -22,7 +22,7 @@ export interface ProjectApiShape {
* order for CT to startup
*/
openProjectCreate(args: InitializeProjectOptions, options: OpenProjectLaunchOptions): Promise<unknown>
launchProject(browser: FoundBrowser, spec: Cypress.Spec, options: LaunchOpts): Promise<void>
launchProject(browser: FoundBrowser, spec: Cypress.Spec, options?: OpenProjectLaunchOpts): Promise<void>
insertProjectToCache(projectRoot: string): Promise<void>
removeProjectFromCache(projectRoot: string): Promise<void>
getProjectRootsFromCache(): Promise<ProjectShape[]>
Expand Down Expand Up @@ -175,7 +175,7 @@ export class ProjectActions {
// When switching testing type, the project should be relaunched in the previously selected browser
if (this.ctx.coreData.app.relaunchBrowser) {
this.ctx.project.setRelaunchBrowser(false)
await this.ctx.actions.project.launchProject(this.ctx.coreData.currentTestingType, {})
await this.ctx.actions.project.launchProject(this.ctx.coreData.currentTestingType)
}
})
} catch (e) {
Expand Down Expand Up @@ -228,7 +228,7 @@ export class ProjectActions {
}
}

async launchProject (testingType: Cypress.TestingType | null, options: LaunchOpts, specPath?: string | null) {
async launchProject (testingType: Cypress.TestingType | null, options?: OpenProjectLaunchOpts, specPath?: string | null) {
if (!this.ctx.currentProject) {
return null
}
Expand Down
2 changes: 1 addition & 1 deletion packages/data-context/src/data/ProjectLifecycleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export class ProjectLifecycleManager {
if (this.ctx.coreData.activeBrowser) {
// if `cypress open` was launched with a `--project` and `--testingType`, go ahead and launch the `--browser`
if (this.ctx.modeOptions.project && this.ctx.modeOptions.testingType) {
await this.ctx.actions.project.launchProject(this.ctx.coreData.currentTestingType, {})
await this.ctx.actions.project.launchProject(this.ctx.coreData.currentTestingType)
}

return
Expand Down
1 change: 1 addition & 0 deletions packages/frontend-shared/cypress/e2e/support/e2eSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ function startAppServer (mode: 'component' | 'e2e' = 'e2e', options: { skipMocki
if (!ctx.lifecycleManager.browsers?.length) throw new Error('No browsers available in startAppServer')

await ctx.actions.browser.setActiveBrowser(ctx.lifecycleManager.browsers[0])
// @ts-expect-error this interface is strict about the options it expects
await ctx.actions.project.launchProject(o.mode, { url: o.url })

if (!o.skipMockingPrompts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ export const mutation = mutationType({
specPath: stringArg(),
},
resolve: async (_, args, ctx) => {
await ctx.actions.project.launchProject(ctx.coreData.currentTestingType, {}, args.specPath)
await ctx.actions.project.launchProject(ctx.coreData.currentTestingType, undefined, args.specPath)

return ctx.lifecycleManager
},
Expand Down
10 changes: 5 additions & 5 deletions packages/server/lib/browsers/browser-cri-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import CRI from 'chrome-remote-interface'
import Debug from 'debug'
import { _connectAsync, _getDelayMsForRetry } from './protocol'
import * as errors from '../errors'
import { create, CRIWrapper } from './cri-client'
import { create, CriClient } from './cri-client'

const HOST = '127.0.0.1'

Expand Down Expand Up @@ -67,8 +67,8 @@ const retryWithIncreasingDelay = async <T>(retryable: () => Promise<T>, browserN
}

export class BrowserCriClient {
currentlyAttachedTarget: CRIWrapper.Client | undefined
private constructor (private browserClient: CRIWrapper.Client, private versionInfo, private port: number, private browserName: string, private onAsynchronousError: Function) {}
currentlyAttachedTarget: CriClient | undefined
private constructor (private browserClient: CriClient, private versionInfo, private port: number, private browserName: string, private onAsynchronousError: Function) {}

/**
* Factory method for the browser cri client. Connects to the browser and then returns a chrome remote interface wrapper around the
Expand All @@ -79,7 +79,7 @@ export class BrowserCriClient {
* @param onAsynchronousError callback for any cdp fatal errors
* @returns a wrapper around the chrome remote interface that is connected to the browser target
*/
static async create (port: number, browserName: string, onAsynchronousError: Function, onReconnect?: (client: CRIWrapper.Client) => void): Promise<BrowserCriClient> {
static async create (port: number, browserName: string, onAsynchronousError: Function, onReconnect?: (client: CriClient) => void): Promise<BrowserCriClient> {
await ensureLiveBrowser(port, browserName)

return retryWithIncreasingDelay(async () => {
Expand Down Expand Up @@ -110,7 +110,7 @@ export class BrowserCriClient {
* @param url the url to attach to
* @returns the chrome remote interface wrapper for the target
*/
attachToTargetUrl = async (url: string): Promise<CRIWrapper.Client> => {
attachToTargetUrl = async (url: string): Promise<CriClient> => {
// Continue trying to re-attach until succcessful.
// If the browser opens slowly, this will fail until
// The browser and automation API is ready, so we try a few
Expand Down
11 changes: 8 additions & 3 deletions packages/server/lib/browsers/cdp_automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
import _ from 'lodash'
import Bluebird from 'bluebird'
import type { Protocol } from 'devtools-protocol'
import type ProtocolMapping from 'devtools-protocol/types/protocol-mapping'
import { cors, uri } from '@packages/network'
import debugModule from 'debug'
import { URL } from 'url'

import type { Automation } from '../automation'
import type { ResourceType, BrowserPreRequest, BrowserResponseReceived } from '@packages/proxy'

export type CdpCommand = keyof ProtocolMapping.Commands

export type CdpEvent = keyof ProtocolMapping.Events

const debugVerbose = debugModule('cypress-verbose:server:browsers:cdp_automation')

export type CyCookie = Pick<chrome.cookies.Cookie, 'name' | 'value' | 'expirationDate' | 'hostOnly' | 'domain' | 'path' | 'secure' | 'httpOnly'> & {
Expand Down Expand Up @@ -163,9 +168,9 @@ export const normalizeResourceType = (resourceType: string | undefined): Resourc
return ffToStandardResourceTypeMap[resourceType] || 'other'
}

type SendDebuggerCommand = (message: string, data?: any) => Promise<any>
type SendCloseCommand = (shouldKeepTabOpen: boolean) => Promise<any>
type OnFn = (eventName: string, cb: Function) => void
type SendDebuggerCommand = (message: CdpCommand, data?: any) => Promise<any>
type SendCloseCommand = (shouldKeepTabOpen: boolean) => Promise<any> | void
type OnFn = (eventName: CdpEvent, cb: Function) => void

// the intersection of what's valid in CDP and what's valid in FFCDP
// Firefox: https://searchfox.org/mozilla-central/rev/98a9257ca2847fad9a19631ac76199474516b31e/remote/cdp/domains/parent/Network.jsm#22
Expand Down
85 changes: 44 additions & 41 deletions packages/server/lib/browsers/chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ import utils from './utils'
import type { Browser } from './types'
import { BrowserCriClient } from './browser-cri-client'
import type { LaunchedBrowser } from '@packages/launcher/lib/browsers'
import type { CRIWrapper } from './cri-client'
import type { CriClient } from './cri-client'
import type { Automation } from '../automation'

// TODO: this is defined in `cypress-npm-api` but there is currently no way to get there
type CypressConfiguration = any
import type { BrowserLaunchOpts, BrowserNewTabOpts } from '@packages/types'

const debug = debugModule('cypress:server:browsers:chrome')

Expand Down Expand Up @@ -123,7 +121,7 @@ const DEFAULT_ARGS = [
'--disable-dev-shm-usage',
]

let browserCriClient
let browserCriClient: BrowserCriClient | undefined

/**
* Reads all known preference files (CHROME_PREFERENCE_PATHS) from disk and retur
Expand Down Expand Up @@ -320,15 +318,15 @@ const _handleDownloads = async function (client, dir, automation) {
let frameTree
let gettingFrameTree

const onReconnect = (client: CRIWrapper.Client) => {
const onReconnect = (client: CriClient) => {
// if the client disconnects (e.g. due to a computer sleeping), update
// the frame tree on reconnect in cases there were changes while
// the client was disconnected
return _updateFrameTree(client, 'onReconnect')()
}

// eslint-disable-next-line @cypress/dev/arrow-body-multiline-braces
const _updateFrameTree = (client: CRIWrapper.Client, eventName) => async () => {
const _updateFrameTree = (client: CriClient, eventName) => async () => {
debug(`update frame tree for ${eventName}`)

gettingFrameTree = new Promise<void>(async (resolve) => {
Expand Down Expand Up @@ -433,8 +431,8 @@ const _handlePausedRequests = async (client) => {
})
}

const _setAutomation = async (client: CRIWrapper.Client, automation: Automation, resetBrowserTargets: (shouldKeepTabOpen: boolean) => Promise<void>, options: CypressConfiguration = {}) => {
const cdpAutomation = await CdpAutomation.create(client.send, client.on, resetBrowserTargets, automation, options.experimentalSessionAndOrigin)
const _setAutomation = async (client: CriClient, automation: Automation, resetBrowserTargets: (shouldKeepTabOpen: boolean) => Promise<void>, options: BrowserLaunchOpts) => {
const cdpAutomation = await CdpAutomation.create(client.send, client.on, resetBrowserTargets, automation, !!options.experimentalSessionAndOrigin)

return automation.use(cdpAutomation)
}
Expand Down Expand Up @@ -490,7 +488,7 @@ export = {
return extensionDest
},

_getArgs (browser: Browser, options: CypressConfiguration, port: string) {
_getArgs (browser: Browser, options: BrowserLaunchOpts, port: string) {
const args = ([] as string[]).concat(DEFAULT_ARGS)

if (os.platform() === 'linux') {
Expand Down Expand Up @@ -551,43 +549,60 @@ export = {
return args
},

async connectToNewSpec (browser: Browser, options: CypressConfiguration = {}, automation: Automation) {
async connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation) {
debug('connecting to new chrome tab in existing instance with url and debugging port', { url: options.url })

const browserCriClient = this._getBrowserCriClient()

if (!browserCriClient) throw new Error('Missing browserCriClient in connectToNewSpec')

const pageCriClient = browserCriClient.currentlyAttachedTarget

if (!pageCriClient) throw new Error('Missing pageCriClient in connectToNewSpec')

if (!options.url) throw new Error('Missing url in connectToNewSpec')

await this.attachListeners(browser, options.url, pageCriClient, automation, options)
},

async connectToExisting (browser: Browser, options: BrowserLaunchOpts, automation) {
const port = await protocol.getRemoteDebuggingPort()

debug('connecting to existing chrome instance with url and debugging port', { url: options.url, port })
if (!options.onError) throw new Error('Missing onError in connectToExisting')

const browserCriClient = await BrowserCriClient.create(port, browser.displayName, options.onError, onReconnect)

if (!options.url) throw new Error('Missing url in connectToExisting')

const pageCriClient = await browserCriClient.attachToTargetUrl(options.url)

await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
},

async attachListeners (browser: Browser, url: string, pageCriClient, automation: Automation, options: BrowserLaunchOpts & { onInitializeNewBrowserTab?: () => void }) {
if (!browserCriClient) throw new Error('Missing browserCriClient in attachListeners')

await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)

// make sure page events are re enabled or else frame tree updates will NOT work as well as other items listening for page events
await pageCriClient.send('Page.enable')

await options.onInitializeNewBrowserTab()
await options.onInitializeNewBrowserTab?.()

await Promise.all([
this._maybeRecordVideo(pageCriClient, options, browser.majorVersion),
this._handleDownloads(pageCriClient, options.downloadsFolder, automation),
])

await this._navigateUsingCRI(pageCriClient, options.url)
await this._navigateUsingCRI(pageCriClient, url)

if (options.experimentalSessionAndOrigin) {
await this._handlePausedRequests(pageCriClient)
_listenForFrameTreeChanges(pageCriClient)
}
},

async connectToExisting (browser: Browser, options: CypressConfiguration = {}, automation) {
const port = await protocol.getRemoteDebuggingPort()

debug('connecting to existing chrome instance with url and debugging port', { url: options.url, port })
const browserCriClient = await BrowserCriClient.create(port, browser.displayName, options.onError, onReconnect)
const pageCriClient = await browserCriClient.attachToTargetUrl(options.url)

await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
},

async open (browser: Browser, url, options: CypressConfiguration = {}, automation: Automation): Promise<LaunchedBrowser> {
async open (browser: Browser, url, options: BrowserLaunchOpts, automation: Automation): Promise<LaunchedBrowser> {
const { isTextTerminal } = options

const userDir = utils.getProfileDir(browser, isTextTerminal)
Expand Down Expand Up @@ -646,6 +661,8 @@ export = {
// SECOND connect to the Chrome remote interface
// and when the connection is ready
// navigate to the actual url
if (!options.onError) throw new Error('Missing onError in chrome#open')

browserCriClient = await BrowserCriClient.create(port, browser.displayName, options.onError, onReconnect)

la(browserCriClient, 'expected Chrome remote interface reference', browserCriClient)
Expand All @@ -669,7 +686,7 @@ export = {
debug('closing remote interface client')

// Do nothing on failure here since we're shutting down anyway
browserCriClient.close().catch()
browserCriClient?.close().catch()
browserCriClient = undefined

debug('closing chrome')
Expand All @@ -679,21 +696,7 @@ export = {

const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')

await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)

await pageCriClient.send('Page.enable')

await Promise.all([
this._maybeRecordVideo(pageCriClient, options, browser.majorVersion),
this._handleDownloads(pageCriClient, options.downloadsFolder, automation),
])

await this._navigateUsingCRI(pageCriClient, url)

if (options.experimentalSessionAndOrigin) {
await this._handlePausedRequests(pageCriClient)
_listenForFrameTreeChanges(pageCriClient)
}
await this.attachListeners(browser, url, pageCriClient, automation, options)

// return the launched browser process
// with additional method to close the remote connection
Expand Down
Loading

5 comments on commit 3c2fea2

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3c2fea2 Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.7.0/linux-x64/develop-3c2fea216bc3ccb448ffa28a68d88e40ab7e1d82/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3c2fea2 Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.7.0/linux-arm64/develop-3c2fea216bc3ccb448ffa28a68d88e40ab7e1d82/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3c2fea2 Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.7.0/darwin-x64/develop-3c2fea216bc3ccb448ffa28a68d88e40ab7e1d82/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3c2fea2 Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.7.0/darwin-arm64/develop-3c2fea216bc3ccb448ffa28a68d88e40ab7e1d82/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3c2fea2 Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.7.0/win32-x64/develop-3c2fea216bc3ccb448ffa28a68d88e40ab7e1d82/cypress.tgz

Please sign in to comment.