mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-06 06:29:45 -06:00
fix: strip dbus messaging from cli launcher stderr (#32547)
* fix: strip dbus messaging from cli launcher stderr * persist prerelease binaries * expand pattern * chore: fix changelog (#32552) * chore: release 15.3.0 (#32553) * chore: add branches on semantic-pull-request workflow (#32560) * chore: update validate changelog to pull target branch (#32561) * chore: Update Chrome (stable) to 140.0.7339.207 (#32563) * chore: Update Chrome (stable) to 140.0.7339.207 * empty commit --------- Co-authored-by: cypress-bot[bot] <41898282+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Jennifer Shehane <shehane.jennifer@gmail.com> * updates changelog * Update CHANGELOG.md --------- Co-authored-by: Jennifer Shehane <jennifer@cypress.io> Co-authored-by: Bill Glesias <bglesias@gmail.com> Co-authored-by: Matt Schile <mschile@cypress.io> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: cypress-bot[bot] <41898282+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Jennifer Shehane <shehane.jennifer@gmail.com>
This commit is contained in:
@@ -116,7 +116,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" != "chore/refactor_cli_to_ts" ]]; then
|
||||
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "fix/dbus-messages" ]]; 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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
|
||||
linux-x64:
|
||||
when: &full-workflow-filters
|
||||
or:
|
||||
- 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/dbus-messages', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
|
||||
@@ -868,7 +868,7 @@ commands:
|
||||
- run:
|
||||
command: |
|
||||
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
|
||||
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/refactor_cli_to_ts" ]]; then
|
||||
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "fix/dbus-messages" ]]; then
|
||||
export SHOULD_PERSIST_ARTIFACTS=true
|
||||
fi' >> "$BASH_ENV"
|
||||
name: Set environment variable to determine whether or not to persist artifacts
|
||||
@@ -3717,6 +3717,9 @@ workflows:
|
||||
- equal:
|
||||
- update-v8-snapshot-cache-on-develop
|
||||
- << pipeline.git.branch >>
|
||||
- equal:
|
||||
- fix/dbus-messages
|
||||
- << pipeline.git.branch >>
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -3771,6 +3774,9 @@ workflows:
|
||||
- equal:
|
||||
- update-v8-snapshot-cache-on-develop
|
||||
- << pipeline.git.branch >>
|
||||
- equal:
|
||||
- fix/dbus-messages
|
||||
- << pipeline.git.branch >>
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -3836,6 +3842,9 @@ workflows:
|
||||
- equal:
|
||||
- update-v8-snapshot-cache-on-develop
|
||||
- << pipeline.git.branch >>
|
||||
- equal:
|
||||
- fix/dbus-messages
|
||||
- << pipeline.git.branch >>
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -4321,6 +4330,9 @@ workflows:
|
||||
- equal:
|
||||
- update-v8-snapshot-cache-on-develop
|
||||
- << pipeline.git.branch >>
|
||||
- equal:
|
||||
- fix/dbus-messages
|
||||
- << pipeline.git.branch >>
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -5156,6 +5168,9 @@ workflows:
|
||||
- equal:
|
||||
- update-v8-snapshot-cache-on-develop
|
||||
- << pipeline.git.branch >>
|
||||
- equal:
|
||||
- fix/dbus-messages
|
||||
- << pipeline.git.branch >>
|
||||
- matches:
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
|
||||
## 15.3.1
|
||||
|
||||
_Released 10/07/2025 (PENDING)_
|
||||
|
||||
**Bugfixes:**
|
||||
|
||||
- Fixed a regression introduced in [`15.0.0`](https://docs.cypress.io/guides/references/changelog#15-0-0) where `dbus` connection error messages appear in docker containers when launching Cypress. Fixes [#32290](https://github.com/cypress-io/cypress/issues/32290).
|
||||
|
||||
## 15.3.0
|
||||
|
||||
_Released 9/23/2025_
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
import spawn from './spawn'
|
||||
import { start as spawnStart } from './spawn'
|
||||
import util from '../util'
|
||||
import state from '../tasks/state'
|
||||
import os from 'os'
|
||||
@@ -47,7 +47,7 @@ const formatCypressVariables = (): any => {
|
||||
methods.start = async (options: any = {}): Promise<void> => {
|
||||
const args = ['--mode=info']
|
||||
|
||||
await spawn.start(args, {
|
||||
await spawnStart(args, {
|
||||
dev: options.dev,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Debug from 'debug'
|
||||
import util from '../util'
|
||||
import spawn from './spawn'
|
||||
import { start as spawnStart } from './spawn'
|
||||
import { start as verifyStart } from '../tasks/verify'
|
||||
import { processTestingType, checkConfigFile } from './shared'
|
||||
import { exitWithError } from '../errors'
|
||||
@@ -79,7 +79,7 @@ export const start = async (options: any = {}): Promise<any> => {
|
||||
try {
|
||||
const args = processOpenOptions(options)
|
||||
|
||||
return spawn.start(args, {
|
||||
return spawnStart(args, {
|
||||
dev: options.dev,
|
||||
detached: Boolean(options.detached),
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash'
|
||||
import Debug from 'debug'
|
||||
import util from '../util'
|
||||
import spawn from './spawn'
|
||||
import { start as spawnStart } from './spawn'
|
||||
import { start } from '../tasks/verify'
|
||||
import { exitWithError, errors } from '../errors'
|
||||
import { processTestingType, throwInvalidOptionError, checkConfigFile } from './shared'
|
||||
@@ -179,7 +179,7 @@ const runModule = {
|
||||
|
||||
debug('run to spawn.start args %j', args)
|
||||
|
||||
return spawn.start(args, {
|
||||
return spawnStart(args, {
|
||||
dev: options.dev,
|
||||
})
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -14,6 +14,8 @@ import { stdin, stdout, stderr } from 'process'
|
||||
|
||||
const debug = Debug('cypress:cli')
|
||||
|
||||
const DBUS_ERROR_PATTERN = /ERROR:dbus\/(bus|object_proxy)\.cc/
|
||||
|
||||
function isPlatform (platform: string): boolean {
|
||||
return os.platform() === platform
|
||||
}
|
||||
@@ -32,7 +34,7 @@ function needsEverythingPipedDirectly (): boolean {
|
||||
return isPlatform('win32')
|
||||
}
|
||||
|
||||
function getStdio (needsXvfb: boolean): any {
|
||||
function getStdioStrategy (needsXvfb: boolean): string | string[] {
|
||||
if (needsEverythingPipedDirectly()) {
|
||||
return 'pipe'
|
||||
}
|
||||
@@ -50,260 +52,272 @@ function getStdio (needsXvfb: boolean): any {
|
||||
return 'inherit'
|
||||
}
|
||||
|
||||
const spawnModule = {
|
||||
async start (args: any, options: any = {}): Promise<any> {
|
||||
const needsXvfb = xvfb.isNeeded()
|
||||
let executable = state.getPathToExecutable(state.getBinaryDir())
|
||||
function createSpawnFunction (
|
||||
executable: string,
|
||||
args: string[],
|
||||
options: any,
|
||||
) {
|
||||
return (overrides: any = {}): any => {
|
||||
return new Bluebird((resolve: any, reject: any) => {
|
||||
_.defaults(overrides, {
|
||||
onStderrData: false,
|
||||
})
|
||||
|
||||
if (util.getEnv('CYPRESS_RUN_BINARY')) {
|
||||
executable = path.resolve(util.getEnv('CYPRESS_RUN_BINARY') as string)
|
||||
}
|
||||
const { onStderrData } = overrides
|
||||
const envOverrides = util.getEnvOverrides(options)
|
||||
const electronArgs: string[] = []
|
||||
const node11WindowsFix = isPlatform('win32')
|
||||
|
||||
debug('needs to start own Xvfb?', needsXvfb)
|
||||
let startScriptPath: string | undefined
|
||||
|
||||
// Always push cwd into the args
|
||||
// which additionally acts as a signal to the
|
||||
// binary that it was invoked through the NPM module
|
||||
args = args || []
|
||||
if (typeof args === 'string') {
|
||||
args = [args]
|
||||
}
|
||||
if (options.dev) {
|
||||
executable = 'node'
|
||||
// if we're in dev then reset
|
||||
// the launch cmd to be 'npm run dev'
|
||||
startScriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js')
|
||||
|
||||
args = [...args, '--cwd', process.cwd(), '--userNodePath', process.execPath, '--userNodeVersion', process.versions.node]
|
||||
debug('in dev mode the args became %o', args)
|
||||
}
|
||||
|
||||
_.defaults(options, {
|
||||
dev: false,
|
||||
env: process.env,
|
||||
detached: false,
|
||||
stdio: getStdio(needsXvfb),
|
||||
})
|
||||
if (!options.dev && needsSandbox()) {
|
||||
electronArgs.push('--no-sandbox')
|
||||
}
|
||||
|
||||
const spawn = (overrides: any = {}): any => {
|
||||
return new Bluebird((resolve: any, reject: any) => {
|
||||
_.defaults(overrides, {
|
||||
onStderrData: false,
|
||||
})
|
||||
|
||||
const { onStderrData } = overrides
|
||||
const envOverrides = util.getEnvOverrides(options)
|
||||
const electronArgs: string[] = []
|
||||
const node11WindowsFix = isPlatform('win32')
|
||||
|
||||
let startScriptPath: string | undefined
|
||||
|
||||
if (options.dev) {
|
||||
executable = 'node'
|
||||
// if we're in dev then reset
|
||||
// the launch cmd to be 'npm run dev'
|
||||
startScriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js')
|
||||
|
||||
debug('in dev mode the args became %o', args)
|
||||
}
|
||||
|
||||
if (!options.dev && needsSandbox()) {
|
||||
electronArgs.push('--no-sandbox')
|
||||
}
|
||||
|
||||
// strip dev out of child process options
|
||||
/**
|
||||
// strip dev out of child process options
|
||||
/**
|
||||
* @type {import('child_process').ForkOptions}
|
||||
*/
|
||||
let stdioOptions: any = _.pick(options, 'env', 'detached', 'stdio')
|
||||
let stdioOptions: any = _.pick(options, 'env', 'detached', 'stdio')
|
||||
|
||||
// figure out if we're going to be force enabling or disabling colors.
|
||||
// also figure out whether we should force stdout and stderr into thinking
|
||||
// it is a tty as opposed to a pipe.
|
||||
stdioOptions.env = _.extend({}, stdioOptions.env, envOverrides)
|
||||
// figure out if we're going to be force enabling or disabling colors.
|
||||
// also figure out whether we should force stdout and stderr into thinking
|
||||
// it is a tty as opposed to a pipe.
|
||||
stdioOptions.env = _.extend({}, stdioOptions.env, envOverrides)
|
||||
|
||||
if (node11WindowsFix) {
|
||||
stdioOptions = _.extend({}, stdioOptions, { windowsHide: false })
|
||||
}
|
||||
if (node11WindowsFix) {
|
||||
stdioOptions = _.extend({}, stdioOptions, { windowsHide: false })
|
||||
}
|
||||
|
||||
if (util.isPossibleLinuxWithIncorrectDisplay()) {
|
||||
// make sure we use the latest DISPLAY variable if any
|
||||
debug('passing DISPLAY', process.env.DISPLAY)
|
||||
stdioOptions.env.DISPLAY = process.env.DISPLAY
|
||||
}
|
||||
if (util.isPossibleLinuxWithIncorrectDisplay()) {
|
||||
// make sure we use the latest DISPLAY variable if any
|
||||
debug('passing DISPLAY', process.env.DISPLAY)
|
||||
stdioOptions.env.DISPLAY = process.env.DISPLAY
|
||||
}
|
||||
|
||||
if (stdioOptions.env.ELECTRON_RUN_AS_NODE) {
|
||||
// Since we are running electron as node, we need to add an entry point file.
|
||||
startScriptPath = path.join(state.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js')
|
||||
} else {
|
||||
// Start arguments with "--" so Electron knows these are OUR
|
||||
// arguments and does not try to sanitize them. Otherwise on Windows
|
||||
// an url in one of the arguments crashes it :(
|
||||
// https://github.com/cypress-io/cypress/issues/5466
|
||||
args = [...electronArgs, '--', ...args]
|
||||
}
|
||||
if (stdioOptions.env.ELECTRON_RUN_AS_NODE) {
|
||||
// Since we are running electron as node, we need to add an entry point file.
|
||||
startScriptPath = path.join(state.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js')
|
||||
} else {
|
||||
// Start arguments with "--" so Electron knows these are OUR
|
||||
// arguments and does not try to sanitize them. Otherwise on Windows
|
||||
// an url in one of the arguments crashes it :(
|
||||
// https://github.com/cypress-io/cypress/issues/5466
|
||||
args = [...electronArgs, '--', ...args]
|
||||
}
|
||||
|
||||
if (startScriptPath) {
|
||||
args.unshift(startScriptPath)
|
||||
}
|
||||
if (startScriptPath) {
|
||||
args.unshift(startScriptPath)
|
||||
}
|
||||
|
||||
if (process.env.CYPRESS_INTERNAL_DEV_DEBUG) {
|
||||
args.unshift(process.env.CYPRESS_INTERNAL_DEV_DEBUG)
|
||||
}
|
||||
if (process.env.CYPRESS_INTERNAL_DEV_DEBUG) {
|
||||
args.unshift(process.env.CYPRESS_INTERNAL_DEV_DEBUG)
|
||||
}
|
||||
|
||||
debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'))
|
||||
debug('spawning Cypress with executable: %s', executable)
|
||||
debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'))
|
||||
debug('spawning Cypress with executable: %s', executable)
|
||||
|
||||
const child = cp.spawn(executable, args, stdioOptions)
|
||||
const child = cp.spawn(executable, args, stdioOptions)
|
||||
|
||||
function resolveOn (event: any): any {
|
||||
return async function (code: any, signal: any): Promise<any> {
|
||||
debug('child event fired %o', { event, code, signal })
|
||||
function resolveOn (event: any): any {
|
||||
return async function (code: any, signal: any): Promise<any> {
|
||||
debug('child event fired %o', { event, code, signal })
|
||||
|
||||
if (code === null) {
|
||||
const errorObject = errors.childProcessKilled(event, signal)
|
||||
if (code === null) {
|
||||
const errorObject = errors.childProcessKilled(event, signal)
|
||||
|
||||
const err = await getError(errorObject)
|
||||
const err = await getError(errorObject)
|
||||
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
resolve(code)
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
resolve(code)
|
||||
}
|
||||
}
|
||||
|
||||
child.on('close', resolveOn('close'))
|
||||
child.on('exit', resolveOn('exit'))
|
||||
child.on('error', reject)
|
||||
child.on('close', resolveOn('close'))
|
||||
child.on('exit', resolveOn('exit'))
|
||||
child.on('error', reject)
|
||||
|
||||
if (isPlatform('win32')) {
|
||||
const rl = readline.createInterface({
|
||||
input: stdin,
|
||||
output: stdout,
|
||||
})
|
||||
if (isPlatform('win32')) {
|
||||
const rl = readline.createInterface({
|
||||
input: stdin,
|
||||
output: stdout,
|
||||
})
|
||||
|
||||
// on windows, SIGINT does not propagate to the child process when ctrl+c is pressed
|
||||
// this makes sure all nested processes are closed(ex: firefox inside the server)
|
||||
rl.on('SIGINT', async function () {
|
||||
const kill = (await import('tree-kill')).default
|
||||
// on windows, SIGINT does not propagate to the child process when ctrl+c is pressed
|
||||
// this makes sure all nested processes are closed(ex: firefox inside the server)
|
||||
rl.on('SIGINT', async function () {
|
||||
const kill = (await import('tree-kill')).default
|
||||
|
||||
kill(child.pid as number, 'SIGINT')
|
||||
})
|
||||
}
|
||||
kill(child.pid as number, 'SIGINT')
|
||||
})
|
||||
}
|
||||
|
||||
// if stdio options is set to 'pipe', then
|
||||
// we should set up pipes:
|
||||
// process STDIN (read stream) => child STDIN (writeable)
|
||||
// child STDOUT => process STDOUT
|
||||
// child STDERR => process STDERR with additional filtering
|
||||
if (child.stdin) {
|
||||
debug('piping process STDIN into child STDIN')
|
||||
stdin.pipe(child.stdin)
|
||||
}
|
||||
// if stdio options is set to 'pipe', then
|
||||
// we should set up pipes:
|
||||
// process STDIN (read stream) => child STDIN (writeable)
|
||||
// child STDOUT => process STDOUT
|
||||
// child STDERR => process STDERR with additional filtering
|
||||
if (child.stdin) {
|
||||
debug('piping process STDIN into child STDIN')
|
||||
stdin.pipe(child.stdin)
|
||||
}
|
||||
|
||||
if (child.stdout) {
|
||||
debug('piping child STDOUT to process STDOUT')
|
||||
child.stdout.pipe(stdout)
|
||||
}
|
||||
if (child.stdout) {
|
||||
debug('piping child STDOUT to process STDOUT')
|
||||
child.stdout.pipe(stdout)
|
||||
}
|
||||
|
||||
// if this is defined then we are manually piping for linux
|
||||
// to filter out the garbage
|
||||
if (child.stderr) {
|
||||
debug('piping child STDERR to process STDERR')
|
||||
child.stderr.on('data', (data: any) => {
|
||||
const str = data.toString()
|
||||
// if this is defined then we are manually piping for linux
|
||||
// to filter out the garbage
|
||||
if (child.stderr) {
|
||||
debug('piping child STDERR to process STDERR')
|
||||
child.stderr.on('data', (data: any) => {
|
||||
const str = data.toString()
|
||||
|
||||
// if we have a callback and this explicitly returns
|
||||
// false then bail
|
||||
if (onStderrData && onStderrData(str)) {
|
||||
return
|
||||
}
|
||||
|
||||
// else pass it along!
|
||||
stderr.write(data)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/1841
|
||||
// https://github.com/cypress-io/cypress/issues/5241
|
||||
// In some versions of node, it will throw on windows
|
||||
// when you close the parent process after piping
|
||||
// into the child process. unpiping does not seem
|
||||
// to have any effect. so we're just catching the
|
||||
// error here and not doing anything.
|
||||
stdin.on('error', (err: any) => {
|
||||
if (['EPIPE', 'ENOTCONN'].includes(err.code)) {
|
||||
// if we have a callback and this explicitly returns
|
||||
// false then bail
|
||||
if (onStderrData && onStderrData(str)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw err
|
||||
if (str.match(DBUS_ERROR_PATTERN)) {
|
||||
debug(str)
|
||||
} else {
|
||||
// else pass it along!
|
||||
stderr.write(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (stdioOptions.detached) {
|
||||
child.unref()
|
||||
// https://github.com/cypress-io/cypress/issues/1841
|
||||
// https://github.com/cypress-io/cypress/issues/5241
|
||||
// In some versions of node, it will throw on windows
|
||||
// when you close the parent process after piping
|
||||
// into the child process. unpiping does not seem
|
||||
// to have any effect. so we're just catching the
|
||||
// error here and not doing anything.
|
||||
stdin.on('error', (err: any) => {
|
||||
if (['EPIPE', 'ENOTCONN'].includes(err.code)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
const spawnInXvfb = async (): Promise<number> => {
|
||||
try {
|
||||
await xvfb.start()
|
||||
|
||||
const code = await userFriendlySpawn()
|
||||
|
||||
return code
|
||||
} finally {
|
||||
await xvfb.stop()
|
||||
if (stdioOptions.detached) {
|
||||
child.unref()
|
||||
}
|
||||
}
|
||||
|
||||
const userFriendlySpawn = async (linuxWithDisplayEnv?: any): Promise<any> => {
|
||||
debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv))
|
||||
|
||||
let brokenGtkDisplay: boolean = false
|
||||
|
||||
const overrides: any = {}
|
||||
|
||||
if (linuxWithDisplayEnv) {
|
||||
_.extend(overrides, {
|
||||
electronLogging: true,
|
||||
onStderrData (str: string): any {
|
||||
// if we receive a broken pipe anywhere
|
||||
// then we know that's why cypress exited early
|
||||
if (util.isBrokenGtkDisplay(str)) {
|
||||
brokenGtkDisplay = true
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
const code: number = await spawn(overrides)
|
||||
|
||||
if (code !== 0 && brokenGtkDisplay) {
|
||||
util.logBrokenGtkDisplayWarning()
|
||||
|
||||
return spawnInXvfb()
|
||||
}
|
||||
|
||||
return code
|
||||
} catch (error: any) {
|
||||
// we can format and handle an error message from the code above
|
||||
// prevent wrapping error again by using "known: undefined" filter
|
||||
if ((error as any).known === undefined) {
|
||||
const raiseErrorFn = throwFormErrorText(errors.unexpected)
|
||||
|
||||
await raiseErrorFn(error.message)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
if (needsXvfb) {
|
||||
return spawnInXvfb()
|
||||
}
|
||||
|
||||
// if we are on linux and there's already a DISPLAY
|
||||
// set, then we may need to rerun cypress after
|
||||
// spawning our own Xvfb server
|
||||
const linuxWithDisplayEnv = util.isPossibleLinuxWithIncorrectDisplay()
|
||||
|
||||
return userFriendlySpawn(linuxWithDisplayEnv)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default spawnModule
|
||||
async function spawnInXvfb (spawn: ReturnType<typeof createSpawnFunction>): Promise<number> {
|
||||
try {
|
||||
await xvfb.start()
|
||||
|
||||
const code = await userFriendlySpawn(spawn)
|
||||
|
||||
return code
|
||||
} finally {
|
||||
await xvfb.stop()
|
||||
}
|
||||
}
|
||||
|
||||
async function userFriendlySpawn (spawn: ReturnType<typeof createSpawnFunction>, linuxWithDisplayEnv?: any): Promise<any> {
|
||||
debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv))
|
||||
|
||||
let brokenGtkDisplay: boolean = false
|
||||
|
||||
const overrides: any = {}
|
||||
|
||||
if (linuxWithDisplayEnv) {
|
||||
_.extend(overrides, {
|
||||
electronLogging: true,
|
||||
onStderrData (str: string): any {
|
||||
// if we receive a broken pipe anywhere
|
||||
// then we know that's why cypress exited early
|
||||
if (util.isBrokenGtkDisplay(str)) {
|
||||
brokenGtkDisplay = true
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
const code: number = await spawn(overrides)
|
||||
|
||||
if (code !== 0 && brokenGtkDisplay) {
|
||||
util.logBrokenGtkDisplayWarning()
|
||||
|
||||
return spawnInXvfb(spawn)
|
||||
}
|
||||
|
||||
return code
|
||||
} catch (error: any) {
|
||||
// we can format and handle an error message from the code above
|
||||
// prevent wrapping error again by using "known: undefined" filter
|
||||
if ((error as any).known === undefined) {
|
||||
const raiseErrorFn = throwFormErrorText(errors.unexpected)
|
||||
|
||||
await raiseErrorFn(error.message)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
interface StartOptions {
|
||||
dev?: boolean
|
||||
env?: Record<string, string | undefined>
|
||||
detached?: boolean
|
||||
stdio?: string | string[]
|
||||
}
|
||||
|
||||
export async function start (args: string | string[], options: StartOptions = {}): Promise<any> {
|
||||
let executable = util.getEnv('CYPRESS_RUN_BINARY') ?
|
||||
path.resolve(util.getEnv('CYPRESS_RUN_BINARY') as string) :
|
||||
state.getPathToExecutable(state.getBinaryDir())
|
||||
|
||||
// Always push cwd into the args
|
||||
// which additionally acts as a signal to the
|
||||
// binary that it was invoked through the NPM module
|
||||
const baseArgs = args ? (typeof args === 'string' ? [args] : args) : []
|
||||
const decoratedArgs = baseArgs.concat([
|
||||
'--cwd', process.cwd(),
|
||||
'--userNodePath', process.execPath,
|
||||
'--userNodeVersion', process.versions.node,
|
||||
])
|
||||
|
||||
const needsXvfb = xvfb.isNeeded()
|
||||
|
||||
debug('needs to start own Xvfb?', needsXvfb)
|
||||
|
||||
const stdio = options.stdio ?? getStdioStrategy(needsXvfb)
|
||||
const dev = options.dev ?? false
|
||||
const detached = options.detached ?? false
|
||||
const env = options.env ?? process.env
|
||||
|
||||
const spawn = createSpawnFunction(executable, decoratedArgs, { stdio, dev, detached, env })
|
||||
|
||||
if (needsXvfb) {
|
||||
return spawnInXvfb(spawn)
|
||||
}
|
||||
|
||||
// if we are on linux and there's already a DISPLAY
|
||||
// set, then we may need to rerun cypress after
|
||||
// spawning our own Xvfb server
|
||||
const linuxWithDisplayEnv = util.isPossibleLinuxWithIncorrectDisplay()
|
||||
|
||||
return userFriendlySpawn(spawn, linuxWithDisplayEnv)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import si, { Systeminformation } from 'systeminformation'
|
||||
import util from '../../../lib/util'
|
||||
import state from '../../../lib/tasks/state'
|
||||
import info from '../../../lib/exec/info'
|
||||
import spawn from '../../../lib/exec/spawn'
|
||||
import { start as spawnStart } from '../../../lib/exec/spawn'
|
||||
|
||||
vi.mock('os', async (importActual) => {
|
||||
const actual = await importActual()
|
||||
@@ -34,15 +34,9 @@ vi.mock('systeminformation', async (importActual) => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../../../lib/exec/spawn', async (importActual) => {
|
||||
const actual = await importActual()
|
||||
|
||||
vi.mock('../../../lib/exec/spawn', async () => {
|
||||
return {
|
||||
default: {
|
||||
// @ts-expect-error
|
||||
...actual.default,
|
||||
start: vi.fn(),
|
||||
},
|
||||
start: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -105,7 +99,7 @@ describe('exec info', () => {
|
||||
vi.stubEnv('NO_PROXY', undefined)
|
||||
vi.stubEnv('CYPRESS_COMMERCIAL_RECOMMENDATIONS', undefined)
|
||||
// common stubs
|
||||
vi.mocked(spawn.start).mockResolvedValue(null)
|
||||
vi.mocked(spawnStart).mockResolvedValue(null)
|
||||
vi.mocked(os.platform).mockReturnValue('linux')
|
||||
vi.mocked(os.totalmem).mockReturnValue(1.2e+9)
|
||||
vi.mocked(os.freemem).mockReturnValue(4e+8)
|
||||
@@ -142,7 +136,7 @@ describe('exec info', () => {
|
||||
|
||||
expect(output()).toMatchSnapshot('cypress info without browsers or vars')
|
||||
|
||||
expect(spawn.start).toBeCalledWith(['--mode=info'], { dev: undefined })
|
||||
expect(spawnStart).toBeCalledWith(['--mode=info'], { dev: undefined })
|
||||
})
|
||||
|
||||
it('prints proxy and cypress env vars', async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { vi, describe, it, beforeEach, expect } from 'vitest'
|
||||
import util from '../../../lib/util'
|
||||
import { start as verifyStart } from '../../../lib/tasks/verify'
|
||||
import spawn from '../../../lib/exec/spawn'
|
||||
import { start as spawnStart } from '../../../lib/exec/spawn'
|
||||
import open from '../../../lib/exec/open'
|
||||
|
||||
vi.mock('../../../lib/util', async (importActual) => {
|
||||
@@ -16,15 +16,9 @@ vi.mock('../../../lib/util', async (importActual) => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../../../lib/exec/spawn', async (importActual) => {
|
||||
const actual = await importActual()
|
||||
|
||||
vi.mock('../../../lib/exec/spawn', async () => {
|
||||
return {
|
||||
default: {
|
||||
// @ts-expect-error
|
||||
...actual.default,
|
||||
start: vi.fn(),
|
||||
},
|
||||
start: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -42,7 +36,7 @@ describe('exec open', function () {
|
||||
|
||||
vi.mocked(util.isInstalledGlobally).mockReturnValue(true)
|
||||
vi.mocked(verifyStart).mockResolvedValue(undefined)
|
||||
vi.mocked(spawn.start).mockResolvedValue(undefined)
|
||||
vi.mocked(spawnStart).mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
it('verifies download', async () => {
|
||||
@@ -52,7 +46,7 @@ describe('exec open', function () {
|
||||
|
||||
it('calls spawn with correct options', async () => {
|
||||
await open.start({ dev: true })
|
||||
expect(spawn.start).toHaveBeenCalledWith([], {
|
||||
expect(spawnStart).toHaveBeenCalledWith([], {
|
||||
detached: false,
|
||||
dev: true,
|
||||
})
|
||||
@@ -60,12 +54,12 @@ describe('exec open', function () {
|
||||
|
||||
it('spawns with port', async () => {
|
||||
await open.start({ port: '1234' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--port', '1234'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--port', '1234'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --env', async () => {
|
||||
await open.start({ env: 'host=http://localhost:1337,name=brian' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(
|
||||
expect(spawnStart).toHaveBeenCalledWith(
|
||||
['--env', 'host=http://localhost:1337,name=brian'],
|
||||
expect.anything(),
|
||||
)
|
||||
@@ -73,7 +67,7 @@ describe('exec open', function () {
|
||||
|
||||
it('spawns with --config', async () => {
|
||||
await open.start({ config: 'watchForFileChanges=false,baseUrl=localhost' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(
|
||||
expect(spawnStart).toHaveBeenCalledWith(
|
||||
['--config', 'watchForFileChanges=false,baseUrl=localhost'],
|
||||
expect.anything(),
|
||||
)
|
||||
@@ -81,7 +75,7 @@ describe('exec open', function () {
|
||||
|
||||
it('spawns with --config-file set', async () => {
|
||||
await open.start({ configFile: 'special-cypress.config.js' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(
|
||||
expect(spawnStart).toHaveBeenCalledWith(
|
||||
['--config-file', 'special-cypress.config.js'],
|
||||
expect.anything(),
|
||||
)
|
||||
@@ -91,14 +85,14 @@ describe('exec open', function () {
|
||||
vi.mocked(util.isInstalledGlobally).mockReturnValue(false)
|
||||
|
||||
await open.start()
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--project', process.cwd()], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--project', process.cwd()], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns without --project if not installed globally and passing --global option', async () => {
|
||||
vi.mocked(util.isInstalledGlobally).mockReturnValue(false)
|
||||
|
||||
await open.start({ global: true })
|
||||
expect(spawn.start).not.toHaveBeenCalledWith(
|
||||
expect(spawnStart).not.toHaveBeenCalledWith(
|
||||
['--project', process.cwd()],
|
||||
)
|
||||
})
|
||||
@@ -107,7 +101,7 @@ describe('exec open', function () {
|
||||
vi.mocked(util.isInstalledGlobally).mockReturnValue(false)
|
||||
|
||||
await open.start({ project: '/path/to/project' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(
|
||||
expect(spawnStart).toHaveBeenCalledWith(
|
||||
['--project', '/path/to/project'],
|
||||
expect.anything(),
|
||||
)
|
||||
@@ -115,7 +109,7 @@ describe('exec open', function () {
|
||||
|
||||
it('spawns with --project if specified and installed globally', async () => {
|
||||
await open.start({ project: '/path/to/project' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(
|
||||
expect(spawnStart).toHaveBeenCalledWith(
|
||||
['--project', '/path/to/project'],
|
||||
expect.anything(),
|
||||
)
|
||||
@@ -123,22 +117,22 @@ describe('exec open', function () {
|
||||
|
||||
it('spawns without --project if not specified and installed globally', async () => {
|
||||
await open.start()
|
||||
expect(spawn.start).toHaveBeenCalledWith([], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith([], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns without --testing-type when not specified', async () => {
|
||||
await open.start()
|
||||
expect(spawn.start).toHaveBeenCalledWith([], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith([], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --testing-type e2e', async () => {
|
||||
await open.start({ testingType: 'e2e' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--testing-type', 'e2e'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--testing-type', 'e2e'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --testing-type component', async () => {
|
||||
await open.start({ testingType: 'component' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--testing-type', 'component'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--testing-type', 'component'], expect.anything())
|
||||
})
|
||||
|
||||
it('throws if --testing-type is invalid', () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { vi, describe, it, beforeEach, expect } from 'vitest'
|
||||
import os from 'os'
|
||||
import util from '../../../lib/util'
|
||||
import run from '../../../lib/exec/run'
|
||||
import spawn from '../../../lib/exec/spawn'
|
||||
import { start as spawnStart } from '../../../lib/exec/spawn'
|
||||
import { start as verifyStart } from '../../../lib/tasks/verify'
|
||||
|
||||
vi.mock('os', async (importActual) => {
|
||||
@@ -29,15 +29,9 @@ vi.mock('../../../lib/util', async (importActual) => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../../../lib/exec/spawn', async (importActual) => {
|
||||
const actual = await importActual()
|
||||
|
||||
vi.mock('../../../lib/exec/spawn', async () => {
|
||||
return {
|
||||
default: {
|
||||
// @ts-expect-error
|
||||
...actual.default,
|
||||
start: vi.fn(),
|
||||
},
|
||||
start: vi.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -152,7 +146,7 @@ describe('exec run', () => {
|
||||
|
||||
describe('.start', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(spawn.start).mockResolvedValue(undefined)
|
||||
vi.mocked(spawnStart).mockResolvedValue(undefined)
|
||||
vi.mocked(verifyStart).mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
@@ -163,77 +157,77 @@ describe('exec run', () => {
|
||||
|
||||
it('spawns with --key and xvfb', async () => {
|
||||
await run.start({ port: '1234' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--port', '1234'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--port', '1234'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --env', async () => {
|
||||
await run.start({ env: 'host=http://localhost:1337,name=brian' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--env', 'host=http://localhost:1337,name=brian'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--env', 'host=http://localhost:1337,name=brian'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --config', async () => {
|
||||
await run.start({ config: 'watchForFileChanges=false,baseUrl=localhost' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--config', 'watchForFileChanges=false,baseUrl=localhost'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--config', 'watchForFileChanges=false,baseUrl=localhost'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --config-file set', async () => {
|
||||
await run.start({ configFile: 'special-cypress.config.js' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--config-file', 'special-cypress.config.js'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--config-file', 'special-cypress.config.js'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --record false', async () => {
|
||||
await run.start({ record: false })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--record', false], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--record', false], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --headed true', async () => {
|
||||
await run.start({ headed: true })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--headed', true], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--headed', true], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --no-exit', async () => {
|
||||
await run.start({ exit: false })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--no-exit'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--no-exit'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --output-path', async () => {
|
||||
await run.start({ outputPath: '/path/to/output' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--output-path', '/path/to/output'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--output-path', '/path/to/output'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --testing-type e2e when given --e2e', async () => {
|
||||
await run.start({ e2e: true })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--testing-type', 'e2e'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--testing-type', 'e2e'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --testing-type component when given --component', async () => {
|
||||
await run.start({ component: true })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--testing-type', 'component'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--testing-type', 'component'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --tag value', async () => {
|
||||
await run.start({ tag: 'nightly' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--tag', 'nightly'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--tag', 'nightly'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with several --tag words unchanged', async () => {
|
||||
await run.start({ tag: 'nightly, sanity' })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--tag', 'nightly, sanity'], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--tag', 'nightly, sanity'], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --auto-cancel-after-failures value', async () => {
|
||||
await run.start({ autoCancelAfterFailures: 4 })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--auto-cancel-after-failures', 4], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--auto-cancel-after-failures', 4], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --auto-cancel-after-failures value false', async () => {
|
||||
await run.start({ autoCancelAfterFailures: false })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--auto-cancel-after-failures', false], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--auto-cancel-after-failures', false], expect.anything())
|
||||
})
|
||||
|
||||
it('spawns with --runner-ui', async () => {
|
||||
await run.start({ runnerUi: true })
|
||||
expect(spawn.start).toHaveBeenCalledWith(['--run-project', process.cwd(), '--runner-ui', true], expect.anything())
|
||||
expect(spawnStart).toHaveBeenCalledWith(['--run-project', process.cwd(), '--runner-ui', true], expect.anything())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ import { stdin, stdout, stderr } from 'process'
|
||||
|
||||
import state from '../../../lib/tasks/state'
|
||||
import xvfb from '../../../lib/exec/xvfb'
|
||||
import spawn from '../../../lib/exec/spawn'
|
||||
import { start } from '../../../lib/exec/spawn'
|
||||
import { needsSandbox } from '../../../lib/tasks/verify'
|
||||
import util from '../../../lib/util'
|
||||
|
||||
@@ -240,7 +240,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.mocked(needsSandbox).mockReturnValue(false)
|
||||
|
||||
// start the process
|
||||
const startPromise = spawn.start('--foo', { foo: 'bar' })
|
||||
const startPromise = start('--foo', { foo: 'bar' })
|
||||
|
||||
// simulate the process closing successfully
|
||||
spawnedProcess.emit('close', 0)
|
||||
@@ -266,7 +266,7 @@ describe('lib/exec/spawn', function () {
|
||||
it('uses --no-sandbox when needed', async function () {
|
||||
vi.mocked(needsSandbox).mockReturnValue(true)
|
||||
|
||||
const startPromise = spawn.start('--foo', { foo: 'bar' })
|
||||
const startPromise = start('--foo', { foo: 'bar' })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -297,7 +297,7 @@ describe('lib/exec/spawn', function () {
|
||||
it('uses npm command when running in dev mode', async () => {
|
||||
vi.mocked(needsSandbox).mockReturnValue(false)
|
||||
|
||||
const startPromise = spawn.start('--foo', { dev: true, foo: 'bar' })
|
||||
const startPromise = start('--foo', { dev: true, foo: 'bar' })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -324,7 +324,7 @@ describe('lib/exec/spawn', function () {
|
||||
it('does not pass --no-sandbox when running in dev mode', async function () {
|
||||
vi.mocked(needsSandbox).mockReturnValue(true)
|
||||
|
||||
const startPromise = spawn.start('--foo', { dev: true, foo: 'bar' })
|
||||
const startPromise = start('--foo', { dev: true, foo: 'bar' })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -351,7 +351,7 @@ describe('lib/exec/spawn', function () {
|
||||
it('starts xvfb when needed', async () => {
|
||||
vi.mocked(xvfb.isNeeded).mockReturnValue(true)
|
||||
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
await flushPromises()
|
||||
|
||||
@@ -365,7 +365,7 @@ describe('lib/exec/spawn', function () {
|
||||
describe('closes', function () {
|
||||
['close', 'exit'].forEach((event) => {
|
||||
it(`if '${event}' event fired`, async () => {
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
spawnedProcess.emit(event, 0)
|
||||
|
||||
@@ -376,7 +376,7 @@ describe('lib/exec/spawn', function () {
|
||||
})
|
||||
|
||||
it('if exit event fired and close event fired', async () => {
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
spawnedProcess.emit('exit', 0)
|
||||
spawnedProcess.emit('close', 0)
|
||||
@@ -390,7 +390,7 @@ describe('lib/exec/spawn', function () {
|
||||
describe('detects kill signal', async () => {
|
||||
it('exits with error on SIGKILL', async () => {
|
||||
try {
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
spawnedProcess.emit('exit', null, 'SIGKILL')
|
||||
|
||||
@@ -405,7 +405,7 @@ describe('lib/exec/spawn', function () {
|
||||
})
|
||||
|
||||
it('does not start xvfb when its not needed', async () => {
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
await flushPromises()
|
||||
|
||||
@@ -419,7 +419,7 @@ describe('lib/exec/spawn', function () {
|
||||
it('stops xvfb when spawn closes', async () => {
|
||||
vi.mocked(xvfb.isNeeded).mockReturnValue(true)
|
||||
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
await flushPromises()
|
||||
|
||||
@@ -431,7 +431,7 @@ describe('lib/exec/spawn', function () {
|
||||
})
|
||||
|
||||
it('resolves with spawned close code in the message', async () => {
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
spawnedProcess.emit('close', 10)
|
||||
|
||||
@@ -455,7 +455,7 @@ describe('lib/exec/spawn', function () {
|
||||
|
||||
vi.mocked(os.platform).mockReturnValue('linux')
|
||||
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
// mock display error due to missing display
|
||||
spawnedProcess.emit('close', 1)
|
||||
@@ -478,7 +478,7 @@ describe('lib/exec/spawn', function () {
|
||||
it('rejects with error from spawn', async () => {
|
||||
const msg = 'the error message'
|
||||
|
||||
const startPromise = spawn.start('--foo')
|
||||
const startPromise = start('--foo')
|
||||
|
||||
spawnedProcess.emit('error', new Error(msg))
|
||||
|
||||
@@ -493,7 +493,7 @@ describe('lib/exec/spawn', function () {
|
||||
})
|
||||
|
||||
it('unrefs if options.detached is true', async () => {
|
||||
const startPromise = spawn.start(null, { detached: true })
|
||||
const startPromise = start(null, { detached: true })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -504,7 +504,7 @@ describe('lib/exec/spawn', function () {
|
||||
|
||||
it('does not unref by default', async () => {
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = spawn.start()
|
||||
const startPromise = start()
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -517,7 +517,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.stubEnv('FOO', 'bar')
|
||||
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = spawn.start()
|
||||
const startPromise = start()
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -533,7 +533,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.mocked(util.supportsColor).mockReturnValue(true)
|
||||
vi.mocked(tty.isatty).mockReturnValue(true)
|
||||
|
||||
const startPromise = spawn.start([], { env: {} })
|
||||
const startPromise = start([], { env: {} })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -548,7 +548,7 @@ describe('lib/exec/spawn', function () {
|
||||
it('sets windowsHide:false property in windows', async () => {
|
||||
vi.mocked(os.platform).mockReturnValue('win32')
|
||||
|
||||
const startPromise = spawn.start([], { env: {} })
|
||||
const startPromise = start([], { env: {} })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -564,7 +564,7 @@ describe('lib/exec/spawn', function () {
|
||||
spawnedProcess.pid = 7
|
||||
vi.mocked(os.platform).mockReturnValue('win32')
|
||||
|
||||
const startPromise = spawn.start([], { env: {} })
|
||||
const startPromise = start([], { env: {} })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -578,7 +578,7 @@ describe('lib/exec/spawn', function () {
|
||||
})
|
||||
|
||||
it('does not set windowsHide property when in darwin', async () => {
|
||||
const startPromise = spawn.start([], { env: {} })
|
||||
const startPromise = start([], { env: {} })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -594,7 +594,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.mocked(util.supportsColor).mockReturnValue(false)
|
||||
vi.mocked(tty.isatty).mockReturnValue(false)
|
||||
|
||||
const startPromise = spawn.start([], { env: {} })
|
||||
const startPromise = start([], { env: {} })
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -612,7 +612,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.mocked(xvfb.isNeeded).mockReturnValue(false)
|
||||
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = spawn.start()
|
||||
const startPromise = start()
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -632,7 +632,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.mocked(xvfb.isNeeded).mockReturnValue(false)
|
||||
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = spawn.start()
|
||||
const startPromise = start()
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -649,7 +649,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.mocked(xvfb.isNeeded).mockReturnValue(true)
|
||||
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = spawn.start()
|
||||
const startPromise = start()
|
||||
|
||||
await flushPromises()
|
||||
|
||||
@@ -668,7 +668,7 @@ describe('lib/exec/spawn', function () {
|
||||
vi.mocked(xvfb.isNeeded).mockReturnValue(false)
|
||||
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = spawn.start()
|
||||
const startPromise = start()
|
||||
|
||||
await flushPromises()
|
||||
|
||||
@@ -697,7 +697,7 @@ describe('lib/exec/spawn', function () {
|
||||
})
|
||||
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = spawn.start()
|
||||
const startPromise = start()
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
@@ -709,6 +709,44 @@ describe('lib/exec/spawn', function () {
|
||||
expect(spawnedProcess.stdout.pipe).toHaveBeenCalledExactlyOnceWith(stdout)
|
||||
})
|
||||
|
||||
it('filters out dbus errors on linux', async () => {
|
||||
vi.mocked(os.platform).mockReturnValue('linux')
|
||||
|
||||
const dbusErrors = [
|
||||
Buffer.from('ERROR:dbus/bus.cc:123: Failed to connect to session bus'),
|
||||
Buffer.from('[246:0820/083339.099956:ERROR:dbus/object_proxy.cc:590] Failed to call method: org.freedesktop.DBus.NameHasOwner: object_path= /org/freedesktop/DBus: unknown error type:'),
|
||||
]
|
||||
|
||||
const normalError = Buffer.from('Some other error message')
|
||||
|
||||
let dataCallback: (data: Buffer) => void
|
||||
|
||||
// mock stderr data handler
|
||||
spawnedProcess.stderr.on.mockImplementation((event, callback) => {
|
||||
if (event === 'data') {
|
||||
dataCallback = callback
|
||||
}
|
||||
})
|
||||
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
const startPromise = start()
|
||||
|
||||
// Emit dbus error - should be filtered out (not written to stderr)
|
||||
dbusErrors.forEach((err) => {
|
||||
dataCallback!(err)
|
||||
expect(stderr.write).not.toHaveBeenCalledWith(err)
|
||||
})
|
||||
|
||||
// Emit normal error - should be written to stderr
|
||||
dataCallback!(normalError)
|
||||
|
||||
expect(stderr.write).toHaveBeenCalledWith(normalError)
|
||||
|
||||
spawnedProcess.emit('close', 0)
|
||||
|
||||
await startPromise
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/1841
|
||||
// https://github.com/cypress-io/cypress/issues/5241
|
||||
const errCodes = ['EPIPE', 'ENOTCONN']
|
||||
@@ -732,7 +770,7 @@ describe('lib/exec/spawn', function () {
|
||||
expect(() => {
|
||||
// kick off the mock process
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
spawn.start()
|
||||
start()
|
||||
|
||||
const err: any = new Error()
|
||||
|
||||
@@ -747,7 +785,7 @@ describe('lib/exec/spawn', function () {
|
||||
expect(() => {
|
||||
// kick off the mock process
|
||||
// @ts-expect-error - invalid number of arguments for given type
|
||||
spawn.start()
|
||||
start()
|
||||
|
||||
const err: any = new Error('wattttt')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user