diff --git a/lib/utils/open-url-prompt.js b/lib/utils/open-url-prompt.js index 3eb3ac288c035..290040e5d14aa 100644 --- a/lib/utils/open-url-prompt.js +++ b/lib/utils/open-url-prompt.js @@ -34,6 +34,11 @@ const promptOpen = async (npm, url, title, prompt, emitter) => { }) const tryOpen = await new Promise(resolve => { + rl.on('SIGINT', () => { + rl.close() + resolve('SIGINT') + }) + rl.question(prompt, () => { resolve(true) }) @@ -50,6 +55,10 @@ const promptOpen = async (npm, url, title, prompt, emitter) => { } }) + if (tryOpen === 'SIGINT') { + throw new Error('canceled') + } + if (!tryOpen) { return } diff --git a/test/lib/utils/open-url-prompt.js b/test/lib/utils/open-url-prompt.js index 6908e36b7c81e..03f6b596d7643 100644 --- a/test/lib/utils/open-url-prompt.js +++ b/test/lib/utils/open-url-prompt.js @@ -28,6 +28,8 @@ const opener = (url, opts, cb) => { } let questionShouldResolve = true +let openUrlPromptInterrupted = false + const readline = { createInterface: () => ({ question: (_q, cb) => { @@ -36,6 +38,11 @@ const readline = { } }, close: () => {}, + on: (_signal, cb) => { + if (openUrlPromptInterrupted && _signal === 'SIGINT') { + cb() + } + }, }), } @@ -148,3 +155,25 @@ t.test('returns error when opener errors', async t => { ) t.equal(openerUrl, 'https://www.npmjs.com', 'did not open') }) + +t.test('throws "canceled" error on SIGINT', async t => { + t.teardown(() => { + openerUrl = null + openerOpts = null + OUTPUT.length = 0 + questionShouldResolve = true + openUrlPromptInterrupted = false + }) + + questionShouldResolve = false + openUrlPromptInterrupted = true + const emitter = new EventEmitter() + + const open = openUrlPrompt(npm, 'https://www.npmjs.com', 'npm home', 'prompt', emitter) + + try { + await open + } catch (err) { + t.equal(err.message, 'canceled') + } +})