mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-12 10:10:32 -06:00
164 lines
5.7 KiB
TypeScript
164 lines
5.7 KiB
TypeScript
import Debug from 'debug'
|
|
import { EventEmitter } from 'events'
|
|
import type playwright from 'playwright-webkit'
|
|
import type { Browser, BrowserInstance } from './types'
|
|
import type { Automation } from '../automation'
|
|
import { WebKitAutomation } from './webkit-automation'
|
|
import * as unhandledExceptions from '../unhandled_exceptions'
|
|
import type { BrowserLaunchOpts, BrowserNewTabOpts } from '@packages/types'
|
|
import utils from './utils'
|
|
|
|
const debug = Debug('cypress:server:browsers:webkit')
|
|
|
|
let wkAutomation: WebKitAutomation | undefined
|
|
|
|
export async function connectToNewSpec (browser: Browser, options: BrowserNewTabOpts, automation: Automation) {
|
|
if (!wkAutomation) throw new Error('connectToNewSpec called without wkAutomation')
|
|
|
|
automation.use(wkAutomation)
|
|
wkAutomation.automation = automation
|
|
await options.onInitializeNewBrowserTab()
|
|
await wkAutomation.reset({
|
|
newUrl: options.url,
|
|
downloadsFolder: options.downloadsFolder,
|
|
videoApi: options.videoApi,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Clear instance state for the webkit instance, this is normally called in on kill or on exit.
|
|
*/
|
|
export function clearInstanceState () {
|
|
wkAutomation = undefined
|
|
}
|
|
|
|
export function connectToExisting () {
|
|
throw new Error('Cypress-in-Cypress is not supported for WebKit.')
|
|
}
|
|
|
|
export function connectProtocolToBrowser (): Promise<void> {
|
|
throw new Error('Protocol is not yet supported in WebKit.')
|
|
}
|
|
|
|
/**
|
|
* Playwright adds an `exit` event listener to run a cleanup process. It tries to use the current binary to run a Node script by passing it as argv[1].
|
|
* However, the Electron binary does not support an entrypoint, leading Cypress to think it's being opened in global mode (no args) when this fn is called.
|
|
* Solution is to filter out the problematic function.
|
|
* TODO(webkit): do we want to run this cleanup script another way?
|
|
* @see https://github.com/microsoft/playwright/blob/7e2aec7454f596af452b51a2866e86370291ac8b/packages/playwright-core/src/utils/processLauncher.ts#L191-L203
|
|
*/
|
|
function removeBadExitListener () {
|
|
const killProcessAndCleanup = process.rawListeners('exit').find((fn) => fn.name === 'killProcessAndCleanup')
|
|
|
|
// @ts-expect-error Electron's Process types override those of @types/node, leading to `exit` not being recognized as an event
|
|
if (killProcessAndCleanup) process.removeListener('exit', killProcessAndCleanup)
|
|
else debug('did not find killProcessAndCleanup, which may cause interactive mode to unexpectedly open')
|
|
}
|
|
|
|
export async function open (browser: Browser, url: string, options: BrowserLaunchOpts, automation: Automation): Promise<BrowserInstance> {
|
|
if (!options.experimentalWebKitSupport) {
|
|
throw new Error('WebKit was launched, but the experimental feature was not enabled. Please add `experimentalWebKitSupport: true` to your config file to launch WebKit.')
|
|
}
|
|
|
|
// resolve pw from user's project path
|
|
const pwModulePath = require.resolve('playwright-webkit', { paths: [process.cwd()] })
|
|
|
|
let pw: typeof playwright
|
|
|
|
try {
|
|
pw = await import(pwModulePath)
|
|
} catch (err) {
|
|
err.message = `There was an error importing \`playwright-webkit\`, is it installed?\n\nError text: ${err.stack}`
|
|
throw err
|
|
}
|
|
|
|
const defaultLaunchOptions = {
|
|
preferences: {
|
|
proxy: {
|
|
server: options.proxyServer,
|
|
},
|
|
headless: browser.isHeadless,
|
|
},
|
|
extensions: [],
|
|
args: [],
|
|
env: {},
|
|
}
|
|
|
|
const launchOptions = await utils.executeBeforeBrowserLaunch(browser, defaultLaunchOptions, options)
|
|
|
|
if (launchOptions.extensions.length) options.onWarning?.(new Error('WebExtensions not supported in WebKit, but extensions were passed in before:browser:launch.'))
|
|
|
|
launchOptions.preferences.args = [...launchOptions.args, ...(launchOptions.preferences.args || [])]
|
|
|
|
let pwServer: playwright.BrowserServer
|
|
|
|
try {
|
|
pwServer = await pw.webkit.launchServer(launchOptions.preferences)
|
|
} catch (err) {
|
|
err.message = `There was an error launching \`playwright-webkit\`: \n\n\`\`\`${err.message}\n\`\`\``
|
|
throw err
|
|
}
|
|
|
|
removeBadExitListener()
|
|
|
|
const websocketUrl = pwServer.wsEndpoint()
|
|
const pwBrowser = await pw.webkit.connect(websocketUrl)
|
|
|
|
wkAutomation = await WebKitAutomation.create({
|
|
automation,
|
|
browser: pwBrowser,
|
|
initialUrl: url,
|
|
downloadsFolder: options.downloadsFolder,
|
|
videoApi: options.videoApi,
|
|
})
|
|
|
|
automation.use(wkAutomation)
|
|
|
|
class WkInstance extends EventEmitter implements BrowserInstance {
|
|
pid = pwServer.process().pid
|
|
|
|
constructor () {
|
|
super()
|
|
|
|
pwBrowser.on('disconnected', () => {
|
|
debug('pwBrowser disconnected')
|
|
this.emit('exit')
|
|
})
|
|
|
|
this.suppressUnhandledEconnreset()
|
|
}
|
|
|
|
async kill () {
|
|
debug('closing pwBrowser')
|
|
clearInstanceState()
|
|
await pwBrowser.close()
|
|
}
|
|
|
|
/**
|
|
* An unhandled `read ECONNRESET` in the depths of `playwright-webkit` is causing the process to crash when running kitchensink on Linux. Absent a
|
|
* way to attach to the `error` event, this replaces the global `unhandledException` handler with one that will not exit the process on ECONNRESET.
|
|
*/
|
|
private suppressUnhandledEconnreset () {
|
|
unhandledExceptions.handle((err: NodeJS.ErrnoException) => {
|
|
return err.code === 'ECONNRESET'
|
|
})
|
|
|
|
// restore normal exception handling behavior
|
|
this.once('exit', () => unhandledExceptions.handle())
|
|
}
|
|
}
|
|
|
|
await utils.executeAfterBrowserLaunch(browser, {
|
|
webSocketDebuggerUrl: websocketUrl,
|
|
})
|
|
|
|
return new WkInstance()
|
|
}
|
|
|
|
export async function closeExtraTargets () {
|
|
// we're currently holding off on implementing Webkit support in order
|
|
// to release Chromium support as soon as possible and may add Webkit
|
|
// support in the future
|
|
debug('Closing extra targets is not currently supported in Webkit')
|
|
}
|