mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-14 11:29:16 -05:00
91fe08e57e
* chore: rework vitest mocks * chore: turn on ANSI for snapshots in the CLI * chore: remove check-more-types from the CLI and just use lodash/regex * chore: remove lazy-ass in favor of the native assertion library in node
761 lines
19 KiB
TypeScript
761 lines
19 KiB
TypeScript
import { vi, describe, it, beforeEach, expect } from 'vitest'
|
||
import cp from 'child_process'
|
||
import os from 'os'
|
||
import tty from 'tty'
|
||
import path from 'path'
|
||
import treeKill from 'tree-kill'
|
||
import si, { Systeminformation } from 'systeminformation'
|
||
import { EventEmitter as EE } from 'events'
|
||
import readline from 'readline'
|
||
import createDebug from 'debug'
|
||
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 { needsSandbox } from '../../../lib/tasks/verify'
|
||
import util from '../../../lib/util'
|
||
|
||
const flushPromises = () => {
|
||
return new Promise<void>((resolve) => {
|
||
setTimeout(resolve, 100)
|
||
})
|
||
}
|
||
|
||
vi.mock('systeminformation', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
osInfo: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('os', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
platform: vi.fn(),
|
||
arch: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('readline', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
createInterface: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('process', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
stdin: {
|
||
// @ts-expect-error
|
||
...actual.stdin,
|
||
pipe: vi.fn(),
|
||
on: vi.fn(),
|
||
emit: vi.fn(),
|
||
},
|
||
stdout: vi.fn(),
|
||
stderr: {
|
||
// @ts-expect-error
|
||
...actual.stderr,
|
||
write: vi.fn(),
|
||
},
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
stdin: {
|
||
// @ts-expect-error
|
||
...actual.default.stdin,
|
||
pipe: vi.fn(),
|
||
on: vi.fn(),
|
||
},
|
||
stdout: vi.fn(),
|
||
stderr: {
|
||
// @ts-expect-error
|
||
...actual.default.stderr,
|
||
write: vi.fn(),
|
||
},
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('child_process', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
spawn: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('tty', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
isatty: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('tree-kill', () => {
|
||
return {
|
||
default: vi.fn(),
|
||
}
|
||
})
|
||
|
||
vi.mock('../../../lib/exec/xvfb', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
start: vi.fn(),
|
||
stop: vi.fn(),
|
||
isNeeded: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('../../../lib/tasks/state', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
getBinaryDir: vi.fn(),
|
||
getPathToExecutable: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
vi.mock('../../../lib/tasks/verify', async () => {
|
||
return {
|
||
needsSandbox: vi.fn(),
|
||
}
|
||
})
|
||
|
||
vi.mock('../../../lib/util', async (importActual) => {
|
||
const actual = await importActual()
|
||
|
||
return {
|
||
default: {
|
||
// @ts-expect-error
|
||
...actual.default,
|
||
supportsColor: vi.fn(),
|
||
},
|
||
}
|
||
})
|
||
|
||
const debug = createDebug('test')
|
||
|
||
const cwd = process.cwd()
|
||
const execPath = process.execPath
|
||
const nodeVersion = process.versions.node
|
||
const defaultBinaryDir = '/default/binary/dir'
|
||
|
||
describe('lib/exec/spawn', function () {
|
||
let spawnedProcess: any
|
||
let mockReadlineEE: any
|
||
|
||
beforeEach(function () {
|
||
vi.resetAllMocks()
|
||
vi.unstubAllEnvs()
|
||
vi.stubEnv('DISPLAY', undefined)
|
||
|
||
vi.mocked(os.platform).mockReturnValue('darwin')
|
||
vi.mocked(os.arch).mockReturnValue('x64')
|
||
vi.mocked(si.osInfo).mockResolvedValue({
|
||
distro: 'Foo',
|
||
release: 'OsVersion',
|
||
} as Systeminformation.OsData)
|
||
|
||
spawnedProcess = new EE()
|
||
spawnedProcess.unref = vi.fn().mockReturnValue(undefined)
|
||
spawnedProcess.stdin = {
|
||
on: vi.fn().mockReturnValue(undefined),
|
||
pipe: vi.fn().mockReturnValue(undefined),
|
||
}
|
||
|
||
spawnedProcess.stdout = {
|
||
on: vi.fn().mockReturnValue(undefined),
|
||
pipe: vi.fn().mockReturnValue(undefined),
|
||
}
|
||
|
||
spawnedProcess.stderr = {
|
||
pipe: vi.fn().mockReturnValue(undefined),
|
||
on: vi.fn().mockReturnValue(undefined),
|
||
}
|
||
|
||
spawnedProcess.kill = vi.fn()
|
||
|
||
mockReadlineEE = new EE()
|
||
|
||
vi.mocked(readline.createInterface).mockReturnValue(mockReadlineEE)
|
||
vi.mocked(cp.spawn).mockReturnValue(spawnedProcess)
|
||
vi.mocked(xvfb.start).mockResolvedValue(undefined)
|
||
vi.mocked(xvfb.stop).mockResolvedValue(undefined)
|
||
vi.mocked(xvfb.isNeeded).mockReturnValue(false)
|
||
vi.mocked(state.getBinaryDir).mockReturnValue(defaultBinaryDir)
|
||
vi.mocked(state.getPathToExecutable).mockImplementation((args) => {
|
||
if (args === '/default/binary/dir') {
|
||
return '/path/to/cypress'
|
||
}
|
||
})
|
||
})
|
||
|
||
describe('.start', function () {
|
||
// ️️⚠️ NOTE ⚠️
|
||
// when asserting the calls made to spawn the child Cypress process
|
||
// we have to be _very_ careful. Spawn uses process.env object, if an assertion
|
||
// fails, it will print the entire process.env object to the logs, which
|
||
// might contain sensitive environment variables. Think about what the
|
||
// failed assertion might print to the public CI logs and limit
|
||
// the environment variables when running tests on CI.
|
||
|
||
it('passes args + options to spawn', async () => {
|
||
vi.mocked(needsSandbox).mockReturnValue(false)
|
||
|
||
// start the process
|
||
const startPromise = spawn.start('--foo', { foo: 'bar' })
|
||
|
||
// simulate the process closing successfully
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
// await the process to complete and return
|
||
await startPromise
|
||
|
||
expect(cp.spawn).toHaveBeenCalledWith('/path/to/cypress', [
|
||
'--',
|
||
'--foo',
|
||
'--cwd',
|
||
cwd,
|
||
'--userNodePath',
|
||
execPath,
|
||
'--userNodeVersion',
|
||
nodeVersion,
|
||
], expect.objectContaining({
|
||
detached: false,
|
||
stdio: ['inherit', 'inherit', 'pipe'],
|
||
}))
|
||
})
|
||
|
||
it('uses --no-sandbox when needed', async function () {
|
||
vi.mocked(needsSandbox).mockReturnValue(true)
|
||
|
||
const startPromise = spawn.start('--foo', { foo: 'bar' })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// skip the options argument: we do not need anything about it
|
||
// and also less risk that a failed assertion would dump the
|
||
// entire ENV object with possible sensitive variables
|
||
// @ts-expect-error - vitest mock
|
||
const args = cp.spawn.mock.calls[0].slice(0, 2)
|
||
|
||
// it is important for "--no-sandbox" to appear before "--" separator
|
||
const expectedCliArgs = [
|
||
'--no-sandbox',
|
||
'--',
|
||
'--foo',
|
||
'--cwd',
|
||
cwd,
|
||
'--userNodePath',
|
||
execPath,
|
||
'--userNodeVersion',
|
||
nodeVersion,
|
||
]
|
||
|
||
expect(args).toEqual(['/path/to/cypress', expectedCliArgs])
|
||
})
|
||
|
||
it('uses npm command when running in dev mode', async () => {
|
||
vi.mocked(needsSandbox).mockReturnValue(false)
|
||
|
||
const startPromise = spawn.start('--foo', { dev: true, foo: 'bar' })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
const p = path.resolve('..', 'scripts', 'start.js')
|
||
|
||
expect(cp.spawn).toHaveBeenCalledWith('node', [
|
||
p,
|
||
'--',
|
||
'--foo',
|
||
'--cwd',
|
||
cwd,
|
||
'--userNodePath',
|
||
execPath,
|
||
'--userNodeVersion',
|
||
nodeVersion,
|
||
], expect.objectContaining({
|
||
detached: false,
|
||
stdio: ['inherit', 'inherit', 'pipe'],
|
||
}))
|
||
})
|
||
|
||
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' })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
const p = path.resolve('..', 'scripts', 'start.js')
|
||
|
||
expect(cp.spawn).toHaveBeenCalledWith('node', [
|
||
p,
|
||
'--',
|
||
'--foo',
|
||
'--cwd',
|
||
cwd,
|
||
'--userNodePath',
|
||
execPath,
|
||
'--userNodeVersion',
|
||
nodeVersion,
|
||
], expect.objectContaining({
|
||
detached: false,
|
||
stdio: ['inherit', 'inherit', 'pipe'],
|
||
}))
|
||
})
|
||
|
||
it('starts xvfb when needed', async () => {
|
||
vi.mocked(xvfb.isNeeded).mockReturnValue(true)
|
||
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
await flushPromises()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
expect(xvfb.start).toBeCalled()
|
||
})
|
||
|
||
describe('closes', function () {
|
||
['close', 'exit'].forEach((event) => {
|
||
it(`if '${event}' event fired`, async () => {
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
spawnedProcess.emit(event, 0)
|
||
|
||
const code = await startPromise
|
||
|
||
expect(code).toEqual(0)
|
||
})
|
||
})
|
||
|
||
it('if exit event fired and close event fired', async () => {
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
spawnedProcess.emit('exit', 0)
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
const code = await startPromise
|
||
|
||
expect(code).toEqual(0)
|
||
})
|
||
})
|
||
|
||
describe('detects kill signal', async () => {
|
||
it('exits with error on SIGKILL', async () => {
|
||
try {
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
spawnedProcess.emit('exit', null, 'SIGKILL')
|
||
|
||
await startPromise
|
||
|
||
throw new Error('should have hit error handler but did not')
|
||
} catch (e) {
|
||
expect(e.message).toMatch(/SIGKILL/)
|
||
expect(e.message).toMatchSnapshot()
|
||
}
|
||
})
|
||
})
|
||
|
||
it('does not start xvfb when its not needed', async () => {
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
await flushPromises()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
expect(xvfb.start).not.toBeCalled()
|
||
})
|
||
|
||
it('stops xvfb when spawn closes', async () => {
|
||
vi.mocked(xvfb.isNeeded).mockReturnValue(true)
|
||
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
await flushPromises()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
expect(xvfb.stop).toBeCalled()
|
||
})
|
||
|
||
it('resolves with spawned close code in the message', async () => {
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
spawnedProcess.emit('close', 10)
|
||
|
||
const code = await startPromise
|
||
|
||
expect(code).to.equal(10)
|
||
})
|
||
|
||
describe('Linux display', () => {
|
||
beforeEach(() => {
|
||
vi.stubEnv('DISPLAY', 'test-display')
|
||
})
|
||
|
||
it('retries with xvfb if fails with display exit code', async () => {
|
||
// mock display missing
|
||
spawnedProcess.stderr.on.mockImplementation((event, callback) => {
|
||
if (event === 'data') {
|
||
callback('[some noise here] Gtk: cannot open display: 987')
|
||
}
|
||
})
|
||
|
||
vi.mocked(os.platform).mockReturnValue('linux')
|
||
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
// mock display error due to missing display
|
||
spawnedProcess.emit('close', 1)
|
||
|
||
// mock the process actually starting up after xfvb is started
|
||
await flushPromises()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
const code = await startPromise
|
||
|
||
expect(xvfb.start).toHaveBeenCalledOnce()
|
||
expect(xvfb.stop).toHaveBeenCalledOnce()
|
||
expect(cp.spawn).toHaveBeenCalledTimes(2)
|
||
// second code should be 0 after successfully running with Xvfb
|
||
expect(code).toEqual(0)
|
||
})
|
||
})
|
||
|
||
it('rejects with error from spawn', async () => {
|
||
const msg = 'the error message'
|
||
|
||
const startPromise = spawn.start('--foo')
|
||
|
||
spawnedProcess.emit('error', new Error(msg))
|
||
|
||
try {
|
||
await startPromise
|
||
|
||
throw new Error('should have hit error handler but did not')
|
||
} catch (e) {
|
||
debug('error message', e.message)
|
||
expect(e.message).toMatch(msg)
|
||
}
|
||
})
|
||
|
||
it('unrefs if options.detached is true', async () => {
|
||
const startPromise = spawn.start(null, { detached: true })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
expect(spawnedProcess.unref).toHaveBeenCalledOnce()
|
||
})
|
||
|
||
it('does not unref by default', async () => {
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
const startPromise = spawn.start()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
expect(spawnedProcess.unref).not.toHaveBeenCalled()
|
||
})
|
||
|
||
it('sets process.env to options.env', async () => {
|
||
vi.stubEnv('FOO', 'bar')
|
||
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
const startPromise = spawn.start()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.env.FOO).toEqual('bar')
|
||
})
|
||
|
||
it('forces colors and streams when supported', async () => {
|
||
vi.mocked(util.supportsColor).mockReturnValue(true)
|
||
vi.mocked(tty.isatty).mockReturnValue(true)
|
||
|
||
const startPromise = spawn.start([], { env: {} })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.env).toMatchSnapshot()
|
||
})
|
||
|
||
it('sets windowsHide:false property in windows', async () => {
|
||
vi.mocked(os.platform).mockReturnValue('win32')
|
||
|
||
const startPromise = spawn.start([], { env: {} })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.windowsHide).toEqual(false)
|
||
})
|
||
|
||
it('propagates treeKill if SIGINT is detected in windows console', async () => {
|
||
spawnedProcess.pid = 7
|
||
vi.mocked(os.platform).mockReturnValue('win32')
|
||
|
||
const startPromise = spawn.start([], { env: {} })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
mockReadlineEE.emit('SIGINT')
|
||
// since the import of tree-kill is async inside spawn, we need to wait for it to be imported and called
|
||
await flushPromises()
|
||
|
||
expect(treeKill).toHaveBeenCalledWith(7, 'SIGINT')
|
||
})
|
||
|
||
it('does not set windowsHide property when in darwin', async () => {
|
||
const startPromise = spawn.start([], { env: {} })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.windowsHide).toBeUndefined()
|
||
})
|
||
|
||
it('does not force colors and streams when not supported', async () => {
|
||
vi.mocked(util.supportsColor).mockReturnValue(false)
|
||
vi.mocked(tty.isatty).mockReturnValue(false)
|
||
|
||
const startPromise = spawn.start([], { env: {} })
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.env).toMatchSnapshot()
|
||
})
|
||
|
||
it('pipes when on win32', async () => {
|
||
vi.mocked(os.platform).mockReturnValue('win32')
|
||
|
||
vi.mocked(xvfb.isNeeded).mockReturnValue(false)
|
||
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
const startPromise = spawn.start()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.stdio).toEqual('pipe')
|
||
|
||
expect(stdin.pipe).toHaveBeenCalledOnce()
|
||
expect(stdin.pipe).toHaveBeenCalledWith(spawnedProcess.stdin)
|
||
})
|
||
|
||
it('inherits when on linux and xvfb isn\'t needed', async () => {
|
||
vi.mocked(os.platform).mockReturnValue('linux')
|
||
vi.mocked(xvfb.isNeeded).mockReturnValue(false)
|
||
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
const startPromise = spawn.start()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.stdio).toEqual('inherit')
|
||
})
|
||
|
||
it('uses [inherit, inherit, pipe] when linux and xvfb is needed', async () => {
|
||
vi.mocked(os.platform).mockReturnValue('linux')
|
||
vi.mocked(xvfb.isNeeded).mockReturnValue(true)
|
||
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
const startPromise = spawn.start()
|
||
|
||
await flushPromises()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.stdio).toEqual(['inherit', 'inherit', 'pipe'])
|
||
})
|
||
|
||
it('uses [inherit, inherit, pipe] on darwin', async () => {
|
||
vi.mocked(os.platform).mockReturnValue('darwin')
|
||
vi.mocked(xvfb.isNeeded).mockReturnValue(false)
|
||
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
const startPromise = spawn.start()
|
||
|
||
await flushPromises()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// @ts-expect-error - mock argument
|
||
const thirdArg = cp.spawn.mock.calls[0][2]
|
||
|
||
expect(thirdArg.stdio).to.deep.eq([
|
||
'inherit', 'inherit', 'pipe',
|
||
])
|
||
})
|
||
|
||
it('writes everything on win32', async () => {
|
||
vi.mocked(os.platform).mockReturnValue('win32')
|
||
|
||
const buf1 = Buffer.from('asdf')
|
||
|
||
// mock display missing
|
||
spawnedProcess.stderr.on.mockImplementation((event, callback) => {
|
||
if (event === 'data') {
|
||
callback(buf1)
|
||
}
|
||
})
|
||
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
const startPromise = spawn.start()
|
||
|
||
spawnedProcess.emit('close', 0)
|
||
|
||
await startPromise
|
||
|
||
// validates the child process stderr event handler was called
|
||
expect(stderr.write).toHaveBeenCalledWith(buf1)
|
||
expect(stdin.pipe).toHaveBeenCalledExactlyOnceWith(spawnedProcess.stdin)
|
||
expect(spawnedProcess.stdout.pipe).toHaveBeenCalledExactlyOnceWith(stdout)
|
||
})
|
||
|
||
// https://github.com/cypress-io/cypress/issues/1841
|
||
// https://github.com/cypress-io/cypress/issues/5241
|
||
const errCodes = ['EPIPE', 'ENOTCONN']
|
||
|
||
errCodes.forEach((errCode) => {
|
||
beforeEach(() => {
|
||
// create an EventEmitter and bind it to process.stdin
|
||
const stdinEE = new EE()
|
||
|
||
vi.mocked(stdin.emit).mockImplementation((event, ...args) => {
|
||
return stdinEE.emit(event, ...args)
|
||
})
|
||
|
||
// @ts-expect-error - mock arguments
|
||
vi.mocked(stdin.on).mockImplementation((event, callback) => {
|
||
return stdinEE.on(event, callback)
|
||
})
|
||
})
|
||
|
||
it(`catches process.stdin errors and returns when code=${errCode}`, async () => {
|
||
expect(() => {
|
||
// kick off the mock process
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
spawn.start()
|
||
|
||
const err: any = new Error()
|
||
|
||
err.code = errCode
|
||
|
||
return stdin.emit('error', err)
|
||
}).not.toThrow()
|
||
})
|
||
})
|
||
|
||
it('throws process.stdin errors code!=EPIPE', function () {
|
||
expect(() => {
|
||
// kick off the mock process
|
||
// @ts-expect-error - invalid number of arguments for given type
|
||
spawn.start()
|
||
|
||
const err: any = new Error('wattttt')
|
||
|
||
err.code = 'FAILWHALE'
|
||
|
||
return stdin.emit('error', err)
|
||
}).toThrow(/wattttt/)
|
||
})
|
||
})
|
||
})
|