11cb0ef41Sopenharmony_ciconst t = require('tap') 21cb0ef41Sopenharmony_ciconst EventEmitter = require('events') 31cb0ef41Sopenharmony_ciconst tmock = require('../../fixtures/tmock') 41cb0ef41Sopenharmony_ciconst mockNpm = require('../../fixtures/mock-npm') 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ciconst mockOpenUrlPrompt = async (t, { 71cb0ef41Sopenharmony_ci questionShouldResolve = true, 81cb0ef41Sopenharmony_ci openUrlPromptInterrupted = false, 91cb0ef41Sopenharmony_ci openerResult = null, 101cb0ef41Sopenharmony_ci isTTY = true, 111cb0ef41Sopenharmony_ci emitter = null, 121cb0ef41Sopenharmony_ci url: openUrl = 'https://www.npmjs.com', 131cb0ef41Sopenharmony_ci ...config 141cb0ef41Sopenharmony_ci}) => { 151cb0ef41Sopenharmony_ci const mock = await mockNpm(t, { 161cb0ef41Sopenharmony_ci globals: { 171cb0ef41Sopenharmony_ci 'process.stdin.isTTY': isTTY, 181cb0ef41Sopenharmony_ci 'process.stdout.isTTY': isTTY, 191cb0ef41Sopenharmony_ci }, 201cb0ef41Sopenharmony_ci config, 211cb0ef41Sopenharmony_ci }) 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ci let openerUrl = null 241cb0ef41Sopenharmony_ci let openerOpts = null 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_ci const openUrlPrompt = tmock(t, '{LIB}/utils/open-url-prompt.js', { 271cb0ef41Sopenharmony_ci '@npmcli/promise-spawn': { 281cb0ef41Sopenharmony_ci open: async (url, options) => { 291cb0ef41Sopenharmony_ci openerUrl = url 301cb0ef41Sopenharmony_ci openerOpts = options 311cb0ef41Sopenharmony_ci if (openerResult) { 321cb0ef41Sopenharmony_ci throw openerResult 331cb0ef41Sopenharmony_ci } 341cb0ef41Sopenharmony_ci }, 351cb0ef41Sopenharmony_ci }, 361cb0ef41Sopenharmony_ci readline: { 371cb0ef41Sopenharmony_ci createInterface: () => ({ 381cb0ef41Sopenharmony_ci question: (_q, cb) => { 391cb0ef41Sopenharmony_ci if (questionShouldResolve === true) { 401cb0ef41Sopenharmony_ci cb() 411cb0ef41Sopenharmony_ci } 421cb0ef41Sopenharmony_ci }, 431cb0ef41Sopenharmony_ci close: () => {}, 441cb0ef41Sopenharmony_ci on: (_signal, cb) => { 451cb0ef41Sopenharmony_ci if (openUrlPromptInterrupted && _signal === 'SIGINT') { 461cb0ef41Sopenharmony_ci cb() 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci }, 491cb0ef41Sopenharmony_ci }), 501cb0ef41Sopenharmony_ci }, 511cb0ef41Sopenharmony_ci }) 521cb0ef41Sopenharmony_ci 531cb0ef41Sopenharmony_ci let error 541cb0ef41Sopenharmony_ci const args = [mock.npm, openUrl, 'npm home', 'prompt'] 551cb0ef41Sopenharmony_ci if (emitter) { 561cb0ef41Sopenharmony_ci mock.open = openUrlPrompt(...args, emitter) 571cb0ef41Sopenharmony_ci } else { 581cb0ef41Sopenharmony_ci await openUrlPrompt(...args).catch((er) => error = er) 591cb0ef41Sopenharmony_ci } 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci return { 621cb0ef41Sopenharmony_ci ...mock, 631cb0ef41Sopenharmony_ci openerUrl, 641cb0ef41Sopenharmony_ci openerOpts, 651cb0ef41Sopenharmony_ci OUTPUT: mock.joinedOutput(), 661cb0ef41Sopenharmony_ci emitter, 671cb0ef41Sopenharmony_ci error, 681cb0ef41Sopenharmony_ci } 691cb0ef41Sopenharmony_ci} 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_cit.test('does not open a url in non-interactive environments', async t => { 721cb0ef41Sopenharmony_ci const { openerUrl, openerOpts } = await mockOpenUrlPrompt(t, { isTTY: false }) 731cb0ef41Sopenharmony_ci 741cb0ef41Sopenharmony_ci t.equal(openerUrl, null, 'did not open') 751cb0ef41Sopenharmony_ci t.same(openerOpts, null, 'did not open') 761cb0ef41Sopenharmony_ci}) 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_cit.test('opens a url', async t => { 791cb0ef41Sopenharmony_ci const { OUTPUT, openerUrl, openerOpts } = await mockOpenUrlPrompt(t, { browser: true }) 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci t.equal(openerUrl, 'https://www.npmjs.com', 'opened the given url') 821cb0ef41Sopenharmony_ci t.same(openerOpts, { command: null }, 'passed command as null (the default)') 831cb0ef41Sopenharmony_ci t.matchSnapshot(OUTPUT) 841cb0ef41Sopenharmony_ci}) 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_cit.test('opens a url with browser string', async t => { 871cb0ef41Sopenharmony_ci const { openerUrl, openerOpts } = await mockOpenUrlPrompt(t, { browser: 'firefox' }) 881cb0ef41Sopenharmony_ci 891cb0ef41Sopenharmony_ci t.equal(openerUrl, 'https://www.npmjs.com', 'opened the given url') 901cb0ef41Sopenharmony_ci // FIXME: browser string is parsed as a boolean in config layer 911cb0ef41Sopenharmony_ci // this is a bug that should be fixed or the config should not allow it 921cb0ef41Sopenharmony_ci t.same(openerOpts, { command: null }, 'passed command as null (the default)') 931cb0ef41Sopenharmony_ci}) 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_cit.test('prints json output', async t => { 961cb0ef41Sopenharmony_ci const { OUTPUT } = await mockOpenUrlPrompt(t, { json: true }) 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci t.matchSnapshot(OUTPUT) 991cb0ef41Sopenharmony_ci}) 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_cit.test('returns error for non-https url', async t => { 1021cb0ef41Sopenharmony_ci const { error, OUTPUT, openerUrl, openerOpts } = await mockOpenUrlPrompt(t, { 1031cb0ef41Sopenharmony_ci url: 'ftp://www.npmjs.com', 1041cb0ef41Sopenharmony_ci }) 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci t.match(error, /Invalid URL/, 'got the correct error') 1071cb0ef41Sopenharmony_ci t.equal(openerUrl, null, 'did not open') 1081cb0ef41Sopenharmony_ci t.same(openerOpts, null, 'did not open') 1091cb0ef41Sopenharmony_ci t.same(OUTPUT, '', 'printed no output') 1101cb0ef41Sopenharmony_ci}) 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_cit.test('does not open url if canceled', async t => { 1131cb0ef41Sopenharmony_ci const emitter = new EventEmitter() 1141cb0ef41Sopenharmony_ci const { openerUrl, openerOpts, open } = await mockOpenUrlPrompt(t, { 1151cb0ef41Sopenharmony_ci questionShouldResolve: false, 1161cb0ef41Sopenharmony_ci emitter, 1171cb0ef41Sopenharmony_ci }) 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci emitter.emit('abort') 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_ci await open 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci t.equal(openerUrl, null, 'did not open') 1241cb0ef41Sopenharmony_ci t.same(openerOpts, null, 'did not open') 1251cb0ef41Sopenharmony_ci}) 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_cit.test('returns error when opener errors', async t => { 1281cb0ef41Sopenharmony_ci const { error, openerUrl } = await mockOpenUrlPrompt(t, { 1291cb0ef41Sopenharmony_ci openerResult: Object.assign(new Error('Opener failed'), { code: 1 }), 1301cb0ef41Sopenharmony_ci }) 1311cb0ef41Sopenharmony_ci 1321cb0ef41Sopenharmony_ci t.match(error, /Opener failed/, 'got the correct error') 1331cb0ef41Sopenharmony_ci t.equal(openerUrl, 'https://www.npmjs.com', 'did not open') 1341cb0ef41Sopenharmony_ci}) 1351cb0ef41Sopenharmony_ci 1361cb0ef41Sopenharmony_cit.test('does not error when opener can not find command', async t => { 1371cb0ef41Sopenharmony_ci const { OUTPUT, error, openerUrl } = await mockOpenUrlPrompt(t, { 1381cb0ef41Sopenharmony_ci // openerResult: new Error('Opener failed'), 1391cb0ef41Sopenharmony_ci openerResult: Object.assign(new Error('Opener failed'), { code: 127 }), 1401cb0ef41Sopenharmony_ci }) 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci t.notOk(error, 'Did not error') 1431cb0ef41Sopenharmony_ci t.equal(openerUrl, 'https://www.npmjs.com', 'did not open') 1441cb0ef41Sopenharmony_ci t.matchSnapshot(OUTPUT, 'Outputs extra Browser unavailable message and url') 1451cb0ef41Sopenharmony_ci}) 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_cit.test('throws "canceled" error on SIGINT', async t => { 1481cb0ef41Sopenharmony_ci const emitter = new EventEmitter() 1491cb0ef41Sopenharmony_ci const { open } = await mockOpenUrlPrompt(t, { 1501cb0ef41Sopenharmony_ci questionShouldResolve: false, 1511cb0ef41Sopenharmony_ci openUrlPromptInterrupted: true, 1521cb0ef41Sopenharmony_ci emitter, 1531cb0ef41Sopenharmony_ci }) 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci await t.rejects(open, /canceled/, 'message is canceled') 1561cb0ef41Sopenharmony_ci}) 157