diff --git a/packages/core/core.test.mts b/packages/core/core.test.mts index c8f72cdf4..ba1161d3d 100644 --- a/packages/core/core.test.mts +++ b/packages/core/core.test.mts @@ -1,4 +1,5 @@ import { AsyncResource } from 'node:async_hooks'; +import { Stream } from 'node:stream'; import { describe, it, expect, vi } from 'vitest'; import { render } from '@inquirer/testing'; import stripAnsi from 'strip-ansi'; @@ -509,6 +510,40 @@ describe('createPrompt()', () => { await expect(answer).resolves.toBe('closed'); expect(exitSpy).toHaveBeenCalledTimes(1); }); + + it('release listeners when done', async () => { + class WritableStream extends Stream.Writable { + override _write() {} + } + + const Prompt = (config: { message: string }, done: (value: string) => void) => { + useKeypress((key: KeypressEvent) => { + if (isEnterKey(key)) { + done('done'); + } + }); + + return config.message; + }; + const prompt = createPrompt(Prompt); + + const warningSpy = vi.fn(); + process.on('warning', warningSpy); + + // We need to reuse the same stream to ensure it gets cleaned up properly. + const output = new WritableStream(); + for (let i = 0; i < 15; i++) { + const { answer, events } = await render( + prompt, + { message: `Question ${i}` }, + { output }, + ); + events.keypress('enter'); + await expect(answer).resolves.toEqual('done'); + } + + expect(warningSpy).not.toHaveBeenCalled(); + }); }); it('allow cancelling the prompt multiple times', async () => { diff --git a/packages/core/src/lib/create-prompt.mts b/packages/core/src/lib/create-prompt.mts index dc5a234de..7b757dffa 100644 --- a/packages/core/src/lib/create-prompt.mts +++ b/packages/core/src/lib/create-prompt.mts @@ -59,7 +59,7 @@ export function createPrompt(view: ViewFunction) { removeExitListener(); rl.input.removeListener('keypress', checkCursorPos); rl.removeListener('close', hooksCleanup); - output.destroy(); + output.end(); } cancel = () => {