diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 6e9465c8124..b8319c5e46a 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -7,6 +7,7 @@ _Released 11/18/2025 (PENDING)_ - Fixed an issue where [`cy.wrap()`](https://docs.cypress.io/api/commands/wrap) would cause infinite recursion and freeze the Cypress App when called with objects containing circular references. Fixes [#24715](https://github.com/cypress-io/cypress/issues/24715). Addressed in [#32917](https://github.com/cypress-io/cypress/pull/32917). - Fixed an issue where top changes on test retries could cause attempt numbers to show up more than one time in the reporter and cause attempts to be lost in Test Replay. Addressed in [#32888](https://github.com/cypress-io/cypress/pull/32888). +- Fixed an issue where a EPIPE error shows up after CTRL+C is done in terminal. Fixes [#30659](https://github.com/cypress-io/cypress/issues/30659). Addressed in [#32873](https://github.com/cypress-io/cypress/pull/32873). **Misc:** diff --git a/packages/data-context/src/data/ProjectConfigIpc.ts b/packages/data-context/src/data/ProjectConfigIpc.ts index 45bbf068385..e097dc08267 100644 --- a/packages/data-context/src/data/ProjectConfigIpc.ts +++ b/packages/data-context/src/data/ProjectConfigIpc.ts @@ -157,7 +157,16 @@ export class ProjectConfigIpc extends EventEmitter { let resolved = false - this._childProcess.on('error', (err) => { + this._childProcess.on('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EPIPE') { + debug('EPIPE error in loadConfig() of child process %s', err) + + // @ts-ignore + resolve() + + return + } + debug('unhandled error in child process %s', err) this.handleChildProcessError(err, this, resolved, reject) reject(err) @@ -229,7 +238,16 @@ export class ProjectConfigIpc extends EventEmitter { return new Promise((resolve, reject) => { let resolved = false - this._childProcess.on('error', (err) => { + this._childProcess.on('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EPIPE') { + debug('EPIPE error in registerSetupIpcHandlers() of child process %s', err) + + // @ts-ignore + resolve() + + return + } + this.handleChildProcessError(err, this, resolved, reject) reject(err) }) diff --git a/packages/data-context/test/unit/data/ProjectConfigIpc-real-child-process.spec.ts b/packages/data-context/test/unit/data/ProjectConfigIpc-real-child-process.spec.ts new file mode 100644 index 00000000000..43617b6de25 --- /dev/null +++ b/packages/data-context/test/unit/data/ProjectConfigIpc-real-child-process.spec.ts @@ -0,0 +1,74 @@ +import { describe, expect, it, beforeEach, afterEach, jest } from '@jest/globals' +import { scaffoldMigrationProject as scaffoldProject } from '../helper' +import { ProjectConfigIpc } from '../../../src/data/ProjectConfigIpc' + +jest.mock('debug', () => { + globalThis.debugMessages = [] + const originalDebugModule = jest.requireActual('debug') + + const debug = (namespace) => { + // @ts-expect-error - mock + const originalDebug = originalDebugModule(namespace) + + return ((message) => { + if (namespace === 'cypress:lifecycle:ProjectConfigIpc') { + globalThis.debugMessages.push(message) + } + + originalDebug(message) + }) + } + + debug.formatters = {} + + return debug +}) + +describe('ProjectConfigIpc', () => { + describe('real-child-process', () => { + let projectConfigIpc + + beforeEach(async () => { + const projectPath = await scaffoldProject('e2e') + + projectConfigIpc = new ProjectConfigIpc( + undefined, + undefined, + projectPath, + '', + false, + (error) => {}, + () => {}, + () => {}, + ) + }) + + afterEach(() => { + projectConfigIpc.cleanupIpc() + }) + + it('EPIPE error test', async () => { + const err: NodeJS.ErrnoException = new Error + + err.code = 'EPIPE' + + const OG_once = projectConfigIpc.once + + projectConfigIpc.once = function (evt, listener) { + if (evt === 'setupTestingType:reply') { + return listener() + } + + return OG_once.apply(this, [evt, listener]) + } + + await projectConfigIpc.loadConfig() + await projectConfigIpc.registerSetupIpcHandlers() + + projectConfigIpc._childProcess.emit('error', err) + + expect(globalThis.debugMessages.at(-2)).toEqual('EPIPE error in loadConfig() of child process %s') + expect(globalThis.debugMessages.at(-1)).toEqual('EPIPE error in registerSetupIpcHandlers() of child process %s') + }, 20_000) + }) +})