mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-05 23:00:03 -06:00
refactor: move more of video capture into browser automations (#23587)
This commit is contained in:
@@ -33,7 +33,5 @@ export const getPathToIndex = (pkg: RunnerPkg) => {
|
||||
}
|
||||
|
||||
export const getPathToDesktopIndex = (graphqlPort: number) => {
|
||||
// For now, if we see that there's a CYPRESS_INTERNAL_VITE_DEV
|
||||
// we assume we're running Cypress targeting that (dev server)
|
||||
return `http://localhost:${graphqlPort}/__launchpad/index.html`
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { URL } from 'url'
|
||||
|
||||
import type { Automation } from '../automation'
|
||||
import type { ResourceType, BrowserPreRequest, BrowserResponseReceived } from '@packages/proxy'
|
||||
import type { WriteVideoFrame } from '@packages/types'
|
||||
|
||||
export type CdpCommand = keyof ProtocolMapping.Commands
|
||||
|
||||
@@ -168,9 +169,9 @@ export const normalizeResourceType = (resourceType: string | undefined): Resourc
|
||||
return ffToStandardResourceTypeMap[resourceType] || 'other'
|
||||
}
|
||||
|
||||
type SendDebuggerCommand = (message: CdpCommand, data?: any) => Promise<any>
|
||||
type SendDebuggerCommand = <T extends CdpCommand>(message: T, data?: any) => Promise<ProtocolMapping.Commands[T]['returnType']>
|
||||
type SendCloseCommand = (shouldKeepTabOpen: boolean) => Promise<any> | void
|
||||
type OnFn = (eventName: CdpEvent, cb: Function) => void
|
||||
type OnFn = <T extends CdpEvent>(eventName: T, cb: (data: ProtocolMapping.Events[T][0]) => void) => 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
|
||||
@@ -188,6 +189,15 @@ export class CdpAutomation {
|
||||
onFn('Network.responseReceived', this.onResponseReceived)
|
||||
}
|
||||
|
||||
async startVideoRecording (writeVideoFrame: WriteVideoFrame, screencastOpts?) {
|
||||
this.onFn('Page.screencastFrame', async (e) => {
|
||||
writeVideoFrame(Buffer.from(e.data, 'base64'))
|
||||
await this.sendDebuggerCommandFn('Page.screencastFrameAck', { sessionId: e.sessionId })
|
||||
})
|
||||
|
||||
await this.sendDebuggerCommandFn('Page.startScreencast', screencastOpts)
|
||||
}
|
||||
|
||||
static async create (sendDebuggerCommandFn: SendDebuggerCommand, onFn: OnFn, sendCloseCommandFn: SendCloseCommand, automation: Automation, experimentalSessionAndOrigin: boolean): Promise<CdpAutomation> {
|
||||
const cdpAutomation = new CdpAutomation(sendDebuggerCommandFn, onFn, sendCloseCommandFn, automation, experimentalSessionAndOrigin)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { BrowserCriClient } from './browser-cri-client'
|
||||
import type { LaunchedBrowser } from '@packages/launcher/lib/browsers'
|
||||
import type { CriClient } from './cri-client'
|
||||
import type { Automation } from '../automation'
|
||||
import type { BrowserLaunchOpts, BrowserNewTabOpts } from '@packages/types'
|
||||
import type { BrowserLaunchOpts, BrowserNewTabOpts, WriteVideoFrame } from '@packages/types'
|
||||
|
||||
const debug = debugModule('cypress:server:browsers:chrome')
|
||||
|
||||
@@ -249,22 +249,10 @@ const _disableRestorePagesPrompt = function (userDir) {
|
||||
.catch(() => { })
|
||||
}
|
||||
|
||||
const _maybeRecordVideo = async function (client, options, browserMajorVersion) {
|
||||
if (!options.onScreencastFrame) {
|
||||
debug('options.onScreencastFrame is false')
|
||||
async function _recordVideo (cdpAutomation: CdpAutomation, writeVideoFrame: WriteVideoFrame, browserMajorVersion: number) {
|
||||
const opts = browserMajorVersion >= CHROME_VERSION_WITH_FPS_INCREASE ? screencastOpts() : screencastOpts(1)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
debug('starting screencast')
|
||||
client.on('Page.screencastFrame', (meta) => {
|
||||
options.onScreencastFrame(meta)
|
||||
client.send('Page.screencastFrameAck', { sessionId: meta.sessionId })
|
||||
})
|
||||
|
||||
await client.send('Page.startScreencast', browserMajorVersion >= CHROME_VERSION_WITH_FPS_INCREASE ? screencastOpts() : screencastOpts(1))
|
||||
|
||||
return client
|
||||
await cdpAutomation.startVideoRecording(writeVideoFrame, opts)
|
||||
}
|
||||
|
||||
// a utility function that navigates to the given URL
|
||||
@@ -434,7 +422,9 @@ const _handlePausedRequests = async (client) => {
|
||||
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)
|
||||
automation.use(cdpAutomation)
|
||||
|
||||
return cdpAutomation
|
||||
}
|
||||
|
||||
export = {
|
||||
@@ -448,7 +438,7 @@ export = {
|
||||
|
||||
_removeRootExtension,
|
||||
|
||||
_maybeRecordVideo,
|
||||
_recordVideo,
|
||||
|
||||
_navigateUsingCRI,
|
||||
|
||||
@@ -468,7 +458,7 @@ export = {
|
||||
return browserCriClient
|
||||
},
|
||||
|
||||
async _writeExtension (browser: Browser, options) {
|
||||
async _writeExtension (browser: Browser, options: BrowserLaunchOpts) {
|
||||
if (browser.isHeadless) {
|
||||
debug('chrome is running headlessly, not installing extension')
|
||||
|
||||
@@ -565,7 +555,7 @@ export = {
|
||||
await this.attachListeners(browser, options.url, pageCriClient, automation, options)
|
||||
},
|
||||
|
||||
async connectToExisting (browser: Browser, options: BrowserLaunchOpts, automation) {
|
||||
async connectToExisting (browser: Browser, options: BrowserLaunchOpts, automation: Automation) {
|
||||
const port = await protocol.getRemoteDebuggingPort()
|
||||
|
||||
debug('connecting to existing chrome instance with url and debugging port', { url: options.url, port })
|
||||
@@ -580,17 +570,17 @@ export = {
|
||||
await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
|
||||
},
|
||||
|
||||
async attachListeners (browser: Browser, url: string, pageCriClient, automation: Automation, options: BrowserLaunchOpts & { onInitializeNewBrowserTab?: () => void }) {
|
||||
async attachListeners (browser: Browser, url: string, pageCriClient: CriClient, automation: Automation, options: BrowserLaunchOpts | BrowserNewTabOpts) {
|
||||
if (!browserCriClient) throw new Error('Missing browserCriClient in attachListeners')
|
||||
|
||||
await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
|
||||
const cdpAutomation = await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
|
||||
|
||||
await pageCriClient.send('Page.enable')
|
||||
|
||||
await options.onInitializeNewBrowserTab?.()
|
||||
await options['onInitializeNewBrowserTab']?.()
|
||||
|
||||
await Promise.all([
|
||||
this._maybeRecordVideo(pageCriClient, options, browser.majorVersion),
|
||||
options.writeVideoFrame && this._recordVideo(cdpAutomation, options.writeVideoFrame, browser.majorVersion),
|
||||
this._handleDownloads(pageCriClient, options.downloadsFolder, automation),
|
||||
])
|
||||
|
||||
|
||||
@@ -8,9 +8,13 @@ import { CdpAutomation, screencastOpts, CdpCommand, CdpEvent } from './cdp_autom
|
||||
import * as savedState from '../saved_state'
|
||||
import utils from './utils'
|
||||
import * as errors from '../errors'
|
||||
import type { BrowserInstance } from './types'
|
||||
import type { Browser, BrowserInstance } from './types'
|
||||
import type { BrowserWindow, WebContents } from 'electron'
|
||||
import type { Automation } from '../automation'
|
||||
import type { BrowserLaunchOpts, Preferences } from '@packages/types'
|
||||
|
||||
// TODO: unmix these two types
|
||||
type ElectronOpts = Windows.WindowOptions & BrowserLaunchOpts
|
||||
|
||||
const debug = Debug('cypress:server:browsers:electron')
|
||||
const debugVerbose = Debug('cypress-verbose:server:browsers:electron')
|
||||
@@ -68,7 +72,7 @@ const _getAutomation = async function (win, options, parent) {
|
||||
// after upgrading to Electron 8, CDP screenshots can hang if a screencast is not also running
|
||||
// workaround: start and stop screencasts between screenshots
|
||||
// @see https://github.com/cypress-io/cypress/pull/6555#issuecomment-596747134
|
||||
if (!options.onScreencastFrame) {
|
||||
if (!options.writeVideoFrame) {
|
||||
await sendCommand('Page.startScreencast', screencastOpts())
|
||||
const ret = await fn(message, data)
|
||||
|
||||
@@ -105,37 +109,18 @@ function _installExtensions (win: BrowserWindow, extensionPaths: string[], optio
|
||||
}))
|
||||
}
|
||||
|
||||
const _maybeRecordVideo = async function (webContents, options) {
|
||||
const { onScreencastFrame } = options
|
||||
|
||||
debug('maybe recording video %o', { onScreencastFrame })
|
||||
|
||||
if (!onScreencastFrame) {
|
||||
return
|
||||
}
|
||||
|
||||
webContents.debugger.on('message', (event, method, params) => {
|
||||
if (method === 'Page.screencastFrame') {
|
||||
onScreencastFrame(params)
|
||||
webContents.debugger.sendCommand('Page.screencastFrameAck', { sessionId: params.sessionId })
|
||||
}
|
||||
})
|
||||
|
||||
await webContents.debugger.sendCommand('Page.startScreencast', screencastOpts())
|
||||
}
|
||||
|
||||
export = {
|
||||
_defaultOptions (projectRoot, state, options, automation) {
|
||||
_defaultOptions (projectRoot: string | undefined, state: Preferences, options: BrowserLaunchOpts, automation: Automation): ElectronOpts {
|
||||
const _this = this
|
||||
|
||||
const defaults = {
|
||||
x: state.browserX,
|
||||
y: state.browserY,
|
||||
const defaults: Windows.WindowOptions = {
|
||||
x: state.browserX || undefined,
|
||||
y: state.browserY || undefined,
|
||||
width: state.browserWidth || 1280,
|
||||
height: state.browserHeight || 720,
|
||||
devTools: state.isBrowserDevToolsOpen,
|
||||
minWidth: 100,
|
||||
minHeight: 100,
|
||||
devTools: state.isBrowserDevToolsOpen || undefined,
|
||||
contextMenu: true,
|
||||
partition: this._getPartition(options),
|
||||
trackState: {
|
||||
@@ -148,8 +133,21 @@ export = {
|
||||
webPreferences: {
|
||||
sandbox: true,
|
||||
},
|
||||
show: !options.browser.isHeadless,
|
||||
// prevents a tiny 1px padding around the window
|
||||
// causing screenshots/videos to be off by 1px
|
||||
resizable: !options.browser.isHeadless,
|
||||
onCrashed () {
|
||||
const err = errors.get('RENDERER_CRASHED')
|
||||
|
||||
errors.log(err)
|
||||
|
||||
if (!options.onError) throw new Error('Missing onError in onCrashed')
|
||||
|
||||
options.onError(err)
|
||||
},
|
||||
onFocus () {
|
||||
if (options.show) {
|
||||
if (!options.browser.isHeadless) {
|
||||
return menu.set({ withInternalDevTools: true })
|
||||
}
|
||||
},
|
||||
@@ -176,18 +174,12 @@ export = {
|
||||
},
|
||||
}
|
||||
|
||||
if (options.browser.isHeadless) {
|
||||
// prevents a tiny 1px padding around the window
|
||||
// causing screenshots/videos to be off by 1px
|
||||
options.resizable = false
|
||||
}
|
||||
|
||||
return _.defaultsDeep({}, options, defaults)
|
||||
},
|
||||
|
||||
_getAutomation,
|
||||
|
||||
async _render (url: string, automation: Automation, preferences, options: { projectRoot: string, isTextTerminal: boolean }) {
|
||||
async _render (url: string, automation: Automation, preferences, options: { projectRoot?: string, isTextTerminal: boolean }) {
|
||||
const win = Windows.create(options.projectRoot, preferences)
|
||||
|
||||
if (preferences.browser.isHeadless) {
|
||||
@@ -212,21 +204,25 @@ export = {
|
||||
|
||||
const [parentX, parentY] = parent.getPosition()
|
||||
|
||||
options = this._defaultOptions(projectRoot, state, options, automation)
|
||||
const electronOptions = this._defaultOptions(projectRoot, state, options, automation)
|
||||
|
||||
_.extend(options, {
|
||||
_.extend(electronOptions, {
|
||||
x: parentX + 100,
|
||||
y: parentY + 100,
|
||||
trackState: false,
|
||||
// in run mode, force new windows to automatically open with show: false
|
||||
// this prevents window.open inside of javascript client code to cause a new BrowserWindow instance to open
|
||||
// https://github.com/cypress-io/cypress/issues/123
|
||||
show: !options.isTextTerminal,
|
||||
})
|
||||
|
||||
const win = Windows.create(projectRoot, options)
|
||||
const win = Windows.create(projectRoot, electronOptions)
|
||||
|
||||
// needed by electron since we prevented default and are creating
|
||||
// our own BrowserWindow (https://electron.atom.io/docs/api/web-contents/#event-new-window)
|
||||
e.newGuest = win
|
||||
|
||||
return this._launch(win, url, automation, options)
|
||||
return this._launch(win, url, automation, electronOptions)
|
||||
},
|
||||
|
||||
async _launch (win: BrowserWindow, url: string, automation: Automation, options) {
|
||||
@@ -278,7 +274,7 @@ export = {
|
||||
|
||||
automation.use(cdpAutomation)
|
||||
await Promise.all([
|
||||
_maybeRecordVideo(win.webContents, options),
|
||||
options.writeVideoFrame && cdpAutomation.startVideoRecording(options.writeVideoFrame),
|
||||
this._handleDownloads(win, options.downloadsFolder, automation),
|
||||
])
|
||||
|
||||
@@ -459,30 +455,26 @@ export = {
|
||||
throw new Error('Attempting to connect to existing browser for Cypress in Cypress which is not yet implemented for electron')
|
||||
},
|
||||
|
||||
async open (browser, url, options, automation) {
|
||||
const { projectRoot, isTextTerminal } = options
|
||||
|
||||
async open (browser: Browser, url: string, options: BrowserLaunchOpts, automation: Automation) {
|
||||
debug('open %o', { browser, url })
|
||||
|
||||
const State = await savedState.create(projectRoot, isTextTerminal)
|
||||
const State = await savedState.create(options.projectRoot, options.isTextTerminal)
|
||||
const state = await State.get()
|
||||
|
||||
debug('received saved state %o', state)
|
||||
|
||||
// get our electron default options
|
||||
// TODO: this is bad, don't mutate the options object
|
||||
options = this._defaultOptions(projectRoot, state, options, automation)
|
||||
const electronOptions: ElectronOpts = Windows.defaults(
|
||||
this._defaultOptions(options.projectRoot, state, options, automation),
|
||||
)
|
||||
|
||||
// get the GUI window defaults now
|
||||
options = Windows.defaults(options)
|
||||
|
||||
debug('browser window options %o', _.omitBy(options, _.isFunction))
|
||||
debug('browser window options %o', _.omitBy(electronOptions, _.isFunction))
|
||||
|
||||
const defaultLaunchOptions = utils.getDefaultLaunchOptions({
|
||||
preferences: options,
|
||||
preferences: electronOptions,
|
||||
})
|
||||
|
||||
const launchOptions = await utils.executeBeforeBrowserLaunch(browser, defaultLaunchOptions, options)
|
||||
const launchOptions = await utils.executeBeforeBrowserLaunch(browser, defaultLaunchOptions, electronOptions)
|
||||
|
||||
const { preferences } = launchOptions
|
||||
|
||||
@@ -493,7 +485,7 @@ export = {
|
||||
isTextTerminal: options.isTextTerminal,
|
||||
})
|
||||
|
||||
await _installExtensions(win, launchOptions.extensions, options)
|
||||
await _installExtensions(win, launchOptions.extensions, electronOptions)
|
||||
|
||||
// cause the webview to receive focus so that
|
||||
// native browser focus + blur events fire correctly
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import _ from 'lodash'
|
||||
import Bluebird from 'bluebird'
|
||||
import fs from 'fs-extra'
|
||||
import Debug from 'debug'
|
||||
import getPort from 'get-port'
|
||||
@@ -21,6 +20,7 @@ import type { BrowserCriClient } from './browser-cri-client'
|
||||
import type { Automation } from '../automation'
|
||||
import { getCtx } from '@packages/data-context'
|
||||
import { getError } from '@packages/errors'
|
||||
import type { BrowserLaunchOpts, BrowserNewTabOpts } from '@packages/types'
|
||||
|
||||
const debug = Debug('cypress:server:browsers:firefox')
|
||||
|
||||
@@ -371,7 +371,7 @@ export function _createDetachedInstance (browserInstance: BrowserInstance, brows
|
||||
return detachedInstance
|
||||
}
|
||||
|
||||
export async function connectToNewSpec (browser: Browser, options: any = {}, automation: Automation) {
|
||||
export async function connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation) {
|
||||
await firefoxUtil.connectToNewSpec(options, automation, browserCriClient)
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ export function connectToExisting () {
|
||||
getCtx().onWarning(getError('UNEXPECTED_INTERNAL_ERROR', new Error('Attempting to connect to existing browser for Cypress in Cypress which is not yet implemented for firefox')))
|
||||
}
|
||||
|
||||
export async function open (browser: Browser, url, options: any = {}, automation): Promise<BrowserInstance> {
|
||||
export async function open (browser: Browser, url: string, options: BrowserLaunchOpts, automation: Automation): Promise<BrowserInstance> {
|
||||
// see revision comment here https://wiki.mozilla.org/index.php?title=WebDriver/RemoteProtocol&oldid=1234946
|
||||
const hasCdp = browser.majorVersion >= 86
|
||||
const defaultLaunchOptions = utils.getDefaultLaunchOptions({
|
||||
@@ -441,7 +441,7 @@ export async function open (browser: Browser, url, options: any = {}, automation
|
||||
const [
|
||||
foxdriverPort,
|
||||
marionettePort,
|
||||
] = await Bluebird.all([getPort(), getPort()])
|
||||
] = await Promise.all([getPort(), getPort()])
|
||||
|
||||
defaultLaunchOptions.preferences['devtools.debugger.remote-port'] = foxdriverPort
|
||||
defaultLaunchOptions.preferences['marionette.port'] = marionettePort
|
||||
@@ -452,7 +452,7 @@ export async function open (browser: Browser, url, options: any = {}, automation
|
||||
cacheDir,
|
||||
extensionDest,
|
||||
launchOptions,
|
||||
] = await Bluebird.all([
|
||||
] = await Promise.all([
|
||||
utils.ensureCleanCache(browser, options.isTextTerminal),
|
||||
utils.writeExtension(browser, options.isTextTerminal, options.proxyUrl, options.socketIoRoute),
|
||||
utils.executeBeforeBrowserLaunch(browser, defaultLaunchOptions, options),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-redeclare */
|
||||
import Bluebird from 'bluebird'
|
||||
import _ from 'lodash'
|
||||
import type { FoundBrowser } from '@packages/types'
|
||||
import type { BrowserLaunchOpts, FoundBrowser } from '@packages/types'
|
||||
import * as errors from '../errors'
|
||||
import * as plugins from '../plugins'
|
||||
import { getError } from '@packages/errors'
|
||||
@@ -132,13 +132,14 @@ async function executeBeforeBrowserLaunch (browser, launchOptions: typeof defaul
|
||||
return launchOptions
|
||||
}
|
||||
|
||||
function extendLaunchOptionsFromPlugins (launchOptions, pluginConfigResult, options) {
|
||||
function extendLaunchOptionsFromPlugins (launchOptions, pluginConfigResult, options: BrowserLaunchOpts) {
|
||||
// if we returned an array from the plugin
|
||||
// then we know the user is using the deprecated
|
||||
// interface and we need to warn them
|
||||
// TODO: remove this logic in >= v5.0.0
|
||||
if (pluginConfigResult[0]) {
|
||||
options.onWarning(getError(
|
||||
// eslint-disable-next-line no-console
|
||||
(options.onWarning || console.warn)(getError(
|
||||
'DEPRECATED_BEFORE_BROWSER_LAUNCH_ARGS',
|
||||
))
|
||||
|
||||
|
||||
@@ -3,34 +3,36 @@ import Bluebird from 'bluebird'
|
||||
import { BrowserWindow } from 'electron'
|
||||
import Debug from 'debug'
|
||||
import * as savedState from '../saved_state'
|
||||
import { getPathToDesktopIndex } from '@packages/resolve-dist'
|
||||
|
||||
const debug = Debug('cypress:server:windows')
|
||||
|
||||
export type WindowOptions = Electron.BrowserWindowConstructorOptions & {
|
||||
type?: 'INDEX'
|
||||
url?: string
|
||||
devTools?: boolean
|
||||
graphqlPort?: number
|
||||
contextMenu?: boolean
|
||||
partition?: string
|
||||
/**
|
||||
* Synchronizes properties of browserwindow with local state
|
||||
*/
|
||||
trackState?: TrackStateMap
|
||||
onFocus?: () => void
|
||||
onNewWindow?: (e, url, frameName, disposition, options) => Promise<void>
|
||||
onCrashed?: () => void
|
||||
}
|
||||
|
||||
export type WindowOpenOptions = WindowOptions & { url: string }
|
||||
|
||||
type TrackStateMap = Record<'width' | 'height' | 'x' | 'y' | 'devTools', string>
|
||||
|
||||
let windows = {}
|
||||
let recentlyCreatedWindow = false
|
||||
|
||||
const getUrl = function (type, port: number) {
|
||||
switch (type) {
|
||||
case 'INDEX':
|
||||
return getPathToDesktopIndex(port)
|
||||
|
||||
default:
|
||||
throw new Error(`No acceptable window type found for: '${type}'`)
|
||||
}
|
||||
}
|
||||
const getByType = (type) => {
|
||||
const getByType = (type: string) => {
|
||||
return windows[type]
|
||||
}
|
||||
|
||||
const setWindowProxy = function (win) {
|
||||
const setWindowProxy = function (win: BrowserWindow) {
|
||||
if (!process.env.HTTP_PROXY) {
|
||||
return
|
||||
}
|
||||
@@ -41,7 +43,7 @@ const setWindowProxy = function (win) {
|
||||
})
|
||||
}
|
||||
|
||||
export function installExtension (win: BrowserWindow, path) {
|
||||
export function installExtension (win: BrowserWindow, path: string) {
|
||||
return win.webContents.session.loadExtension(path)
|
||||
.then((data) => {
|
||||
debug('electron extension installed %o', { data, path })
|
||||
@@ -70,7 +72,7 @@ export function reset () {
|
||||
windows = {}
|
||||
}
|
||||
|
||||
export function destroy (type) {
|
||||
export function destroy (type: string) {
|
||||
let win
|
||||
|
||||
if (type && (win = getByType(type))) {
|
||||
@@ -78,7 +80,7 @@ export function destroy (type) {
|
||||
}
|
||||
}
|
||||
|
||||
export function get (type) {
|
||||
export function get (type: string) {
|
||||
return getByType(type) || (() => {
|
||||
throw new Error(`No window exists for: '${type}'`)
|
||||
})()
|
||||
@@ -143,7 +145,7 @@ export function defaults (options = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
export function create (projectRoot, _options: WindowOptions = {}, newBrowserWindow = _newBrowserWindow) {
|
||||
export function create (projectRoot, _options: WindowOptions, newBrowserWindow = _newBrowserWindow) {
|
||||
const options = defaults(_options)
|
||||
|
||||
if (options.show === false) {
|
||||
@@ -213,15 +215,15 @@ export function create (projectRoot, _options: WindowOptions = {}, newBrowserWin
|
||||
}
|
||||
|
||||
// open launchpad BrowserWindow
|
||||
export function open (projectRoot, launchpadPort: number, options: WindowOptions = {}, newBrowserWindow = _newBrowserWindow): Bluebird<BrowserWindow> {
|
||||
export async function open (projectRoot: string, options: WindowOpenOptions, newBrowserWindow = _newBrowserWindow): Promise<BrowserWindow> {
|
||||
// if we already have a window open based
|
||||
// on that type then just show + focus it!
|
||||
let win = getByType(options.type)
|
||||
const knownWin = options.type && getByType(options.type)
|
||||
|
||||
if (win) {
|
||||
win.show()
|
||||
if (knownWin) {
|
||||
knownWin.show()
|
||||
|
||||
return Bluebird.resolve(win)
|
||||
return Bluebird.resolve(knownWin)
|
||||
}
|
||||
|
||||
recentlyCreatedWindow = true
|
||||
@@ -235,11 +237,7 @@ export function open (projectRoot, launchpadPort: number, options: WindowOptions
|
||||
},
|
||||
})
|
||||
|
||||
if (!options.url) {
|
||||
options.url = getUrl(options.type, launchpadPort)
|
||||
}
|
||||
|
||||
win = create(projectRoot, options, newBrowserWindow)
|
||||
const win = create(projectRoot, options, newBrowserWindow)
|
||||
|
||||
debug('creating electron window with options %o', options)
|
||||
|
||||
@@ -251,21 +249,15 @@ export function open (projectRoot, launchpadPort: number, options: WindowOptions
|
||||
})
|
||||
}
|
||||
|
||||
// enable our url to be a promise
|
||||
// and wait for this to be resolved
|
||||
return Bluebird.join(
|
||||
options.url,
|
||||
setWindowProxy(win),
|
||||
)
|
||||
.spread((url) => {
|
||||
// navigate the window here!
|
||||
win.loadURL(url)
|
||||
await setWindowProxy(win)
|
||||
await win.loadURL(options.url)
|
||||
|
||||
recentlyCreatedWindow = false
|
||||
}).thenReturn(win)
|
||||
recentlyCreatedWindow = false
|
||||
|
||||
return win
|
||||
}
|
||||
|
||||
export function trackState (projectRoot, isTextTerminal, win, keys) {
|
||||
export function trackState (projectRoot, isTextTerminal, win, keys: TrackStateMap) {
|
||||
const isDestroyed = () => {
|
||||
return win.isDestroyed()
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ import { globalPubSub, getCtx, clearCtx } from '@packages/data-context'
|
||||
|
||||
// eslint-disable-next-line no-duplicate-imports
|
||||
import type { WebContents } from 'electron'
|
||||
import type { LaunchArgs } from '@packages/types'
|
||||
import type { LaunchArgs, Preferences } from '@packages/types'
|
||||
|
||||
import debugLib from 'debug'
|
||||
import { getPathToDesktopIndex } from '@packages/resolve-dist'
|
||||
|
||||
const debug = debugLib('cypress:server:interactive')
|
||||
|
||||
@@ -26,7 +27,7 @@ export = {
|
||||
return os.platform() === 'darwin'
|
||||
},
|
||||
|
||||
getWindowArgs (state) {
|
||||
getWindowArgs (url: string, state: Preferences) {
|
||||
// Electron Window's arguments
|
||||
// These options are passed to Electron's BrowserWindow
|
||||
const minWidth = Math.round(/* 13" MacBook Air */ 1792 / 3) // Thirds
|
||||
@@ -46,6 +47,7 @@ export = {
|
||||
}
|
||||
|
||||
const common = {
|
||||
url,
|
||||
// The backgroundColor should match the value we will show in the
|
||||
// launchpad frontend.
|
||||
|
||||
@@ -129,16 +131,10 @@ export = {
|
||||
return args[os.platform()]
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {import('@packages/types').LaunchArgs} options
|
||||
* @returns
|
||||
*/
|
||||
ready (options: {projectRoot?: string} = {}, port: number) {
|
||||
async ready (options: LaunchArgs, launchpadPort: number) {
|
||||
const { projectRoot } = options
|
||||
const ctx = getCtx()
|
||||
|
||||
// TODO: potentially just pass an event emitter
|
||||
// instance here instead of callback functions
|
||||
menu.set({
|
||||
withInternalDevTools: isDev(),
|
||||
onLogOutClicked () {
|
||||
@@ -149,15 +145,14 @@ export = {
|
||||
},
|
||||
})
|
||||
|
||||
return savedState.create(projectRoot, false).then((state) => state.get())
|
||||
.then((state) => {
|
||||
return Windows.open(projectRoot, port, this.getWindowArgs(state))
|
||||
.then((win) => {
|
||||
ctx?.actions.electron.setBrowserWindow(win)
|
||||
const State = await savedState.create(projectRoot, false)
|
||||
const state = await State.get()
|
||||
const url = getPathToDesktopIndex(launchpadPort)
|
||||
const win = await Windows.open(projectRoot, this.getWindowArgs(url, state))
|
||||
|
||||
return win
|
||||
})
|
||||
})
|
||||
ctx?.actions.electron.setBrowserWindow(win)
|
||||
|
||||
return win
|
||||
},
|
||||
|
||||
async run (options: LaunchArgs, _loading: Promise<void>) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable no-console, @cypress/dev/arrow-body-multiline-braces */
|
||||
import _ from 'lodash'
|
||||
import la from 'lazy-ass'
|
||||
import pkg from '@packages/root'
|
||||
import path from 'path'
|
||||
import chalk from 'chalk'
|
||||
@@ -22,7 +21,7 @@ import random from '../util/random'
|
||||
import system from '../util/system'
|
||||
import chromePolicyCheck from '../util/chrome_policy_check'
|
||||
import * as objUtils from '../util/obj_utils'
|
||||
import type { SpecWithRelativeRoot, SpecFile, TestingType, OpenProjectLaunchOpts, FoundBrowser } from '@packages/types'
|
||||
import type { SpecWithRelativeRoot, SpecFile, TestingType, OpenProjectLaunchOpts, FoundBrowser, WriteVideoFrame } from '@packages/types'
|
||||
import type { Cfg } from '../project-base'
|
||||
import type { Browser } from '../browsers/types'
|
||||
import * as printResults from '../util/print-run'
|
||||
@@ -41,7 +40,7 @@ let exitEarly = (err) => {
|
||||
earlyExitErr = err
|
||||
}
|
||||
let earlyExitErr: Error
|
||||
let currentWriteVideoFrameCallback: videoCapture.WriteVideoFrame
|
||||
let currentWriteVideoFrameCallback: WriteVideoFrame
|
||||
let currentSetScreenshotMetadata: SetScreenshotMetadata
|
||||
|
||||
const debug = Debug('cypress:server:run')
|
||||
@@ -121,70 +120,6 @@ async function getProjectId (project, id) {
|
||||
}
|
||||
}
|
||||
|
||||
const getDefaultBrowserOptsByFamily = (browser, project, writeVideoFrame, onError) => {
|
||||
la(browserUtils.isBrowserFamily(browser.family), 'invalid browser family in', browser)
|
||||
|
||||
if (browser.name === 'electron') {
|
||||
return getElectronProps(browser.isHeaded, writeVideoFrame, onError)
|
||||
}
|
||||
|
||||
if (browser.family === 'chromium') {
|
||||
return getCdpVideoProp(writeVideoFrame)
|
||||
}
|
||||
|
||||
if (browser.family === 'firefox') {
|
||||
return getFirefoxProps(project, writeVideoFrame)
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
const getFirefoxProps = (project, writeVideoFrame) => {
|
||||
if (writeVideoFrame) {
|
||||
project.on('capture:video:frames', writeVideoFrame)
|
||||
|
||||
return { onScreencastFrame: true }
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
const getCdpVideoProp = (writeVideoFrame) => {
|
||||
if (!writeVideoFrame) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
onScreencastFrame: (e) => {
|
||||
// https://chromedevtools.github.io/devtools-protocol/tot/Page#event-screencastFrame
|
||||
writeVideoFrame(Buffer.from(e.data, 'base64'))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const getElectronProps = (isHeaded, writeVideoFrame, onError) => {
|
||||
return {
|
||||
...getCdpVideoProp(writeVideoFrame),
|
||||
width: 1280,
|
||||
height: 720,
|
||||
show: isHeaded,
|
||||
onCrashed () {
|
||||
const err = errors.get('RENDERER_CRASHED')
|
||||
|
||||
errors.log(err)
|
||||
|
||||
onError(err)
|
||||
},
|
||||
onNewWindow (e, url, frameName, disposition, options) {
|
||||
// force new windows to automatically open with show: false
|
||||
// this prevents window.open inside of javascript client code
|
||||
// to cause a new BrowserWindow instance to open
|
||||
// https://github.com/cypress-io/cypress/issues/123
|
||||
options.show = false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const sumByProp = (runs, prop) => {
|
||||
return _.sumBy(runs, prop) || 0
|
||||
}
|
||||
@@ -380,15 +315,20 @@ async function postProcessRecording (name, cname, videoCompression, shouldUpload
|
||||
return continueProcessing(onProgress)
|
||||
}
|
||||
|
||||
function launchBrowser (options: { browser: Browser, spec: SpecWithRelativeRoot, writeVideoFrame?: videoCapture.WriteVideoFrame, setScreenshotMetadata: SetScreenshotMetadata, project: Project, screenshots: ScreenshotMetadata[], projectRoot: string, shouldLaunchNewTab: boolean, onError: (err: Error) => void }) {
|
||||
const { browser, spec, writeVideoFrame, setScreenshotMetadata, project, screenshots, projectRoot, shouldLaunchNewTab, onError } = options
|
||||
function launchBrowser (options: { browser: Browser, spec: SpecWithRelativeRoot, writeVideoFrame?: WriteVideoFrame, setScreenshotMetadata: SetScreenshotMetadata, project: Project, screenshots: ScreenshotMetadata[], projectRoot: string, shouldLaunchNewTab: boolean, onError: (err: Error) => void }) {
|
||||
const { browser, spec, setScreenshotMetadata, project, screenshots, projectRoot, shouldLaunchNewTab, onError } = options
|
||||
|
||||
const warnings = {}
|
||||
|
||||
if (options.writeVideoFrame && browser.family === 'firefox') {
|
||||
project.on('capture:video:frames', options.writeVideoFrame)
|
||||
}
|
||||
|
||||
const browserOpts: OpenProjectLaunchOpts = {
|
||||
...getDefaultBrowserOptsByFamily(browser, project, writeVideoFrame, onError),
|
||||
projectRoot,
|
||||
shouldLaunchNewTab,
|
||||
onError,
|
||||
writeVideoFrame: options.writeVideoFrame,
|
||||
automationMiddleware: {
|
||||
onBeforeRequest (message, data) {
|
||||
if (message === 'take:screenshot') {
|
||||
@@ -491,7 +431,7 @@ function writeVideoFrameCallback (data: Buffer) {
|
||||
return currentWriteVideoFrameCallback(data)
|
||||
}
|
||||
|
||||
function waitForBrowserToConnect (options: { project: Project, socketId: string, onError: (err: Error) => void, writeVideoFrame?: videoCapture.WriteVideoFrame, spec: SpecWithRelativeRoot, isFirstSpec: boolean, testingType: string, experimentalSingleTabRunMode: boolean, browser: Browser, screenshots: ScreenshotMetadata[], projectRoot: string, shouldLaunchNewTab: boolean, webSecurity: boolean }) {
|
||||
function waitForBrowserToConnect (options: { project: Project, socketId: string, onError: (err: Error) => void, writeVideoFrame?: WriteVideoFrame, spec: SpecWithRelativeRoot, isFirstSpec: boolean, testingType: string, experimentalSingleTabRunMode: boolean, browser: Browser, screenshots: ScreenshotMetadata[], projectRoot: string, shouldLaunchNewTab: boolean, webSecurity: boolean }) {
|
||||
if (globalThis.CY_TEST_MOCK?.waitForBrowserToConnect) return Promise.resolve()
|
||||
|
||||
const { project, socketId, onError, writeVideoFrame, spec } = options
|
||||
|
||||
@@ -84,7 +84,7 @@ export class OpenProject {
|
||||
proxyServer: cfg.proxyServer,
|
||||
socketIoRoute: cfg.socketIoRoute,
|
||||
chromeWebSecurity: cfg.chromeWebSecurity,
|
||||
isTextTerminal: cfg.isTextTerminal,
|
||||
isTextTerminal: !!cfg.isTextTerminal,
|
||||
downloadsFolder: cfg.downloadsFolder,
|
||||
experimentalSessionAndOrigin: cfg.experimentalSessionAndOrigin,
|
||||
experimentalModifyObstructiveThirdPartyCode: cfg.experimentalModifyObstructiveThirdPartyCode,
|
||||
|
||||
@@ -13,8 +13,6 @@ const debug = Debug('cypress:server:saved_state')
|
||||
|
||||
const stateFiles: Record<string, typeof FileUtil> = {}
|
||||
|
||||
// TODO: remove `showedOnBoardingModal` from this list - it is only included so that misleading `allowed` are not thrown
|
||||
// now that it has been removed from use
|
||||
export const formStatePath = (projectRoot?: string) => {
|
||||
return Bluebird.try(() => {
|
||||
debug('making saved state from %s', cwd())
|
||||
|
||||
@@ -7,8 +7,7 @@ import Bluebird from 'bluebird'
|
||||
import { path as ffmpegPath } from '@ffmpeg-installer/ffmpeg'
|
||||
import BlackHoleStream from 'black-hole-stream'
|
||||
import { fs } from './util/fs'
|
||||
|
||||
export type WriteVideoFrame = (data: Buffer) => void
|
||||
import type { WriteVideoFrame } from '@packages/types'
|
||||
|
||||
const debug = Debug('cypress:server:video')
|
||||
const debugVerbose = Debug('cypress-verbose:server:video')
|
||||
|
||||
@@ -510,7 +510,9 @@ describe('lib/cypress', () => {
|
||||
.then(() => {
|
||||
expect(browsers.open).to.be.calledWithMatch(ELECTRON_BROWSER, {
|
||||
proxyServer: 'http://localhost:8888',
|
||||
show: true,
|
||||
browser: {
|
||||
isHeadless: false,
|
||||
},
|
||||
})
|
||||
|
||||
this.expectExitWith(0)
|
||||
@@ -1022,7 +1024,7 @@ describe('lib/cypress', () => {
|
||||
browser: 'electron',
|
||||
foo: 'bar',
|
||||
onNewWindow: sinon.match.func,
|
||||
onScreencastFrame: sinon.match.func,
|
||||
writeVideoFrame: sinon.match.func,
|
||||
})
|
||||
|
||||
this.expectExitWith(0)
|
||||
|
||||
@@ -325,14 +325,14 @@ describe('lib/browsers/chrome', () => {
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/9265
|
||||
it('respond ACK after receiving new screenshot frame', function () {
|
||||
const frameMeta = { data: Buffer.from(''), sessionId: '1' }
|
||||
const frameMeta = { data: Buffer.from('foo'), sessionId: '1' }
|
||||
const write = sinon.stub()
|
||||
const options = { onScreencastFrame: write }
|
||||
const options = { writeVideoFrame: write }
|
||||
|
||||
return this.onCriEvent('Page.screencastFrame', frameMeta, options)
|
||||
.then(() => {
|
||||
expect(this.pageCriClient.send).to.have.been.calledWith('Page.startScreencast')
|
||||
expect(write).to.have.been.calledWith(frameMeta)
|
||||
expect(write).to.have.been.calledWithMatch((arg) => Buffer.isBuffer(arg) && arg.length > 0)
|
||||
expect(this.pageCriClient.send).to.have.been.calledWith('Page.screencastFrameAck', { sessionId: frameMeta.sessionId })
|
||||
})
|
||||
})
|
||||
@@ -516,12 +516,18 @@ describe('lib/browsers/chrome', () => {
|
||||
}
|
||||
|
||||
let onInitializeNewBrowserTabCalled = false
|
||||
const options = { ...openOpts, url: 'https://www.google.com', downloadsFolder: '/tmp/folder', onInitializeNewBrowserTab: () => {
|
||||
onInitializeNewBrowserTabCalled = true
|
||||
} }
|
||||
const options = {
|
||||
...openOpts,
|
||||
url: 'https://www.google.com',
|
||||
downloadsFolder: '/tmp/folder',
|
||||
writeVideoFrame: () => {},
|
||||
onInitializeNewBrowserTab: () => {
|
||||
onInitializeNewBrowserTabCalled = true
|
||||
},
|
||||
}
|
||||
|
||||
sinon.stub(chrome, '_getBrowserCriClient').returns(browserCriClient)
|
||||
sinon.stub(chrome, '_maybeRecordVideo').withArgs(pageCriClient, options, 354).resolves()
|
||||
sinon.stub(chrome, '_recordVideo').withArgs(sinon.match.object, options.writeVideoFrame, 354).resolves()
|
||||
sinon.stub(chrome, '_navigateUsingCRI').withArgs(pageCriClient, options.url, 354).resolves()
|
||||
sinon.stub(chrome, '_handleDownloads').withArgs(pageCriClient, options.downloadFolder, automation).resolves()
|
||||
|
||||
@@ -529,7 +535,7 @@ describe('lib/browsers/chrome', () => {
|
||||
|
||||
expect(automation.use).to.be.called
|
||||
expect(chrome._getBrowserCriClient).to.be.called
|
||||
expect(chrome._maybeRecordVideo).to.be.called
|
||||
expect(chrome._recordVideo).to.be.called
|
||||
expect(chrome._navigateUsingCRI).to.be.called
|
||||
expect(chrome._handleDownloads).to.be.called
|
||||
expect(onInitializeNewBrowserTabCalled).to.be.true
|
||||
|
||||
@@ -690,15 +690,16 @@ describe('lib/browsers/electron', () => {
|
||||
})
|
||||
|
||||
it('.onFocus', function () {
|
||||
let opts = electron._defaultOptions('/foo', this.state, { show: true, browser: {} })
|
||||
const headlessOpts = electron._defaultOptions('/foo', this.state, { browser: { isHeadless: false } })
|
||||
|
||||
opts.onFocus()
|
||||
headlessOpts.onFocus()
|
||||
expect(menu.set).to.be.calledWith({ withInternalDevTools: true })
|
||||
|
||||
menu.set.reset()
|
||||
|
||||
opts = electron._defaultOptions('/foo', this.state, { show: false, browser: {} })
|
||||
opts.onFocus()
|
||||
const headedOpts = electron._defaultOptions('/foo', this.state, { browser: { isHeadless: true } })
|
||||
|
||||
headedOpts.onFocus()
|
||||
|
||||
expect(menu.set).not.to.be.called
|
||||
})
|
||||
|
||||
@@ -9,7 +9,6 @@ import { EventEmitter } from 'events'
|
||||
import { BrowserWindow } from 'electron'
|
||||
import * as Windows from '../../../lib/gui/windows'
|
||||
import * as savedState from '../../../lib/saved_state'
|
||||
import { getPathToDesktopIndex } from '@packages/resolve-dist'
|
||||
|
||||
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Cypress/0.0.0 Chrome/59.0.3071.115 Electron/1.8.2 Safari/537.36'
|
||||
|
||||
@@ -42,23 +41,21 @@ describe('lib/gui/windows', () => {
|
||||
|
||||
context('.open', () => {
|
||||
it('sets default options', function () {
|
||||
const options: Windows.WindowOptions = {
|
||||
const options: Windows.WindowOpenOptions = {
|
||||
type: 'INDEX',
|
||||
url: 'foo',
|
||||
}
|
||||
|
||||
return Windows.open('/path/to/project', 1234, options, () => this.win)
|
||||
return Windows.open('/path/to/project', options, () => this.win)
|
||||
.then((win) => {
|
||||
expect(options).to.include({
|
||||
height: 500,
|
||||
width: 600,
|
||||
type: 'INDEX',
|
||||
show: true,
|
||||
url: getPathToDesktopIndex(1234),
|
||||
})
|
||||
|
||||
expect(win.loadURL).to.be.calledWith(getPathToDesktopIndex(
|
||||
1234,
|
||||
))
|
||||
expect(win.loadURL).to.be.calledWith('foo')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,13 +27,13 @@ describe('gui/interactive', () => {
|
||||
context('.getWindowArgs', () => {
|
||||
it('quits app when onClose is called', () => {
|
||||
electron.app.quit = sinon.stub()
|
||||
interactiveMode.getWindowArgs({}).onClose()
|
||||
interactiveMode.getWindowArgs('http://app', {}).onClose()
|
||||
|
||||
expect(electron.app.quit).to.be.called
|
||||
})
|
||||
|
||||
it('tracks state properties', () => {
|
||||
const { trackState } = interactiveMode.getWindowArgs({})
|
||||
const { trackState } = interactiveMode.getWindowArgs('http://app', {})
|
||||
|
||||
const args = _.pick(trackState, 'width', 'height', 'x', 'y', 'devTools')
|
||||
|
||||
@@ -51,49 +51,49 @@ describe('gui/interactive', () => {
|
||||
// Use the saved value if it's valid
|
||||
describe('when no dimension', () => {
|
||||
it('renders with preferred width if no width saved', () => {
|
||||
expect(interactiveMode.getWindowArgs({}).width).to.equal(1200)
|
||||
expect(interactiveMode.getWindowArgs('http://app', {}).width).to.equal(1200)
|
||||
})
|
||||
|
||||
it('renders with preferred height if no height saved', () => {
|
||||
expect(interactiveMode.getWindowArgs({}).height).to.equal(800)
|
||||
expect(interactiveMode.getWindowArgs('http://app', {}).height).to.equal(800)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when saved dimension is too small', () => {
|
||||
it('uses the preferred width', () => {
|
||||
expect(interactiveMode.getWindowArgs({ appWidth: 1 }).width).to.equal(1200)
|
||||
expect(interactiveMode.getWindowArgs('http://app', { appWidth: 1 }).width).to.equal(1200)
|
||||
})
|
||||
|
||||
it('uses the preferred height', () => {
|
||||
expect(interactiveMode.getWindowArgs({ appHeight: 1 }).height).to.equal(800)
|
||||
expect(interactiveMode.getWindowArgs('http://app', { appHeight: 1 }).height).to.equal(800)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when saved dimension is within min/max dimension', () => {
|
||||
it('uses the saved width', () => {
|
||||
expect(interactiveMode.getWindowArgs({ appWidth: 1500 }).width).to.equal(1500)
|
||||
expect(interactiveMode.getWindowArgs('http://app', { appWidth: 1500 }).width).to.equal(1500)
|
||||
})
|
||||
|
||||
it('uses the saved height', () => {
|
||||
expect(interactiveMode.getWindowArgs({ appHeight: 1500 }).height).to.equal(1500)
|
||||
expect(interactiveMode.getWindowArgs('http://app', { appHeight: 1500 }).height).to.equal(1500)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('renders with saved x if it exists', () => {
|
||||
expect(interactiveMode.getWindowArgs({ appX: 3 }).x).to.equal(3)
|
||||
expect(interactiveMode.getWindowArgs('http://app', { appX: 3 }).x).to.equal(3)
|
||||
})
|
||||
|
||||
it('renders with no x if no x saved', () => {
|
||||
expect(interactiveMode.getWindowArgs({}).x).to.be.undefined
|
||||
expect(interactiveMode.getWindowArgs('http://app', {}).x).to.be.undefined
|
||||
})
|
||||
|
||||
it('renders with saved y if it exists', () => {
|
||||
expect(interactiveMode.getWindowArgs({ appY: 4 }).y).to.equal(4)
|
||||
expect(interactiveMode.getWindowArgs('http://app', { appY: 4 }).y).to.equal(4)
|
||||
})
|
||||
|
||||
it('renders with no y if no y saved', () => {
|
||||
expect(interactiveMode.getWindowArgs({}).y).to.be.undefined
|
||||
expect(interactiveMode.getWindowArgs('http://app', {}).y).to.be.undefined
|
||||
})
|
||||
|
||||
describe('on window focus', () => {
|
||||
@@ -105,7 +105,7 @@ describe('gui/interactive', () => {
|
||||
const env = process.env['CYPRESS_INTERNAL_ENV']
|
||||
|
||||
process.env['CYPRESS_INTERNAL_ENV'] = 'development'
|
||||
interactiveMode.getWindowArgs({}).onFocus()
|
||||
interactiveMode.getWindowArgs('http://app', {}).onFocus()
|
||||
expect(menu.set.lastCall.args[0].withInternalDevTools).to.be.true
|
||||
process.env['CYPRESS_INTERNAL_ENV'] = env
|
||||
})
|
||||
@@ -114,7 +114,7 @@ describe('gui/interactive', () => {
|
||||
const env = process.env['CYPRESS_INTERNAL_ENV']
|
||||
|
||||
process.env['CYPRESS_INTERNAL_ENV'] = 'production'
|
||||
interactiveMode.getWindowArgs({}).onFocus()
|
||||
interactiveMode.getWindowArgs('http://app', {}).onFocus()
|
||||
expect(menu.set.lastCall.args[0].withInternalDevTools).to.be.false
|
||||
process.env['CYPRESS_INTERNAL_ENV'] = env
|
||||
})
|
||||
|
||||
@@ -2,23 +2,27 @@ import type { FoundBrowser } from './browser'
|
||||
import type { ReceivedCypressOptions } from './config'
|
||||
import type { PlatformName } from './platform'
|
||||
|
||||
export type WriteVideoFrame = (data: Buffer) => void
|
||||
|
||||
export type OpenProjectLaunchOpts = {
|
||||
projectRoot: string
|
||||
shouldLaunchNewTab: boolean
|
||||
automationMiddleware: AutomationMiddleware
|
||||
writeVideoFrame?: WriteVideoFrame
|
||||
onWarning: (err: Error) => void
|
||||
onError: (err: Error) => void
|
||||
}
|
||||
|
||||
export type BrowserLaunchOpts = {
|
||||
browsers: FoundBrowser[]
|
||||
browser: FoundBrowser
|
||||
browser: FoundBrowser & { isHeadless: boolean }
|
||||
url: string | undefined
|
||||
proxyServer: string
|
||||
isTextTerminal: boolean
|
||||
onBrowserClose?: (...args: unknown[]) => void
|
||||
onBrowserOpen?: (...args: unknown[]) => void
|
||||
onError?: (err: Error) => void
|
||||
} & Partial<OpenProjectLaunchOpts> // TODO: remove the `Partial` here by making it impossible for openProject.launch to be called w/o OpenProjectLaunchOpts
|
||||
& Pick<ReceivedCypressOptions, 'userAgent' | 'proxyUrl' | 'socketIoRoute' | 'chromeWebSecurity' | 'isTextTerminal' | 'downloadsFolder' | 'experimentalSessionAndOrigin' | 'experimentalModifyObstructiveThirdPartyCode'>
|
||||
& Pick<ReceivedCypressOptions, 'userAgent' | 'proxyUrl' | 'socketIoRoute' | 'chromeWebSecurity' | 'downloadsFolder' | 'experimentalSessionAndOrigin' | 'experimentalModifyObstructiveThirdPartyCode'>
|
||||
|
||||
export type BrowserNewTabOpts = { onInitializeNewBrowserTab: () => void } & BrowserLaunchOpts
|
||||
|
||||
|
||||
Reference in New Issue
Block a user