From d71db297829ebcc597df53cd620f65823def0874 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 16 Jul 2020 18:40:44 -0700 Subject: [PATCH] refactor and test for main cli script --- bin/npm-cli.js | 69 +------------------- lib/npm.js | 2 +- test/lib/cli.js | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 69 deletions(-) create mode 100644 test/lib/cli.js diff --git a/bin/npm-cli.js b/bin/npm-cli.js index 7507ef40a1afd..577abe03e7b8e 100755 --- a/bin/npm-cli.js +++ b/bin/npm-cli.js @@ -1,69 +1,2 @@ #!/usr/bin/env node - -process.title = 'npm' - -const { - checkForBrokenNode, - checkForUnsupportedNode -} = require('../lib/utils/unsupported.js') - -checkForBrokenNode() - -const log = require('npmlog') -// pause it here so it can unpause when we've loaded the configs -// and know what loglevel we should be printing. -log.pause() - -checkForUnsupportedNode() - -const npm = require('../lib/npm.js') -const { defs: { shorthands, types } } = require('../lib/config/core.js') -const errorHandler = require('../lib/utils/error-handler.js') -const nopt = require('nopt') - -// if npm is called as "npmg" or "npm_g", then -// run in global mode. -if (process.argv[1][process.argv[1].length - 1] === 'g') { - process.argv.splice(1, 1, 'npm', '-g') -} - -log.verbose('cli', process.argv) - -const conf = nopt(types, shorthands) -npm.argv = conf.argv.remain - -if (conf.version) { - console.log(npm.version) - return errorHandler.exit(0) -} - -if (conf.versions) { - npm.argv = ['version'] - conf.usage = false -} - -log.info('using', 'npm@%s', npm.version) -log.info('using', 'node@%s', process.version) - -process.on('uncaughtException', errorHandler) -process.on('unhandledRejection', errorHandler) - -// now actually fire up npm and run the command. -// this is how to use npm programmatically: -conf._exit = true -const updateNotifier = require('../lib/utils/update-notifier.js') -npm.load(conf, function (er) { - if (er) return errorHandler(er) - - updateNotifier(npm) - - const cmd = npm.argv.shift() - const impl = npm.commands[cmd] - if (impl) { - impl(npm.argv, errorHandler) - } else { - npm.config.set('usage', false) - npm.argv.unshift(cmd) - npm.commands.help(npm.argv, errorHandler) - } -}) +require('../lib/cli.js')(process) diff --git a/lib/npm.js b/lib/npm.js index a3f04bb091ee1..6354309799a4f 100644 --- a/lib/npm.js +++ b/lib/npm.js @@ -287,5 +287,5 @@ const cleanUpLogFiles = require('./utils/cleanup-log-files.js') const getProjectScope = require('./utils/get-project-scope.js') if (require.main === module) { - require('./npm-cli.js')(process) + require('./cli.js')(process) } diff --git a/test/lib/cli.js b/test/lib/cli.js new file mode 100644 index 0000000000000..daa8e56dee6b8 --- /dev/null +++ b/test/lib/cli.js @@ -0,0 +1,166 @@ +const t = require('tap') + +let LOAD_ERROR = null +const npmock = { + version: '99.99.99', + load: (config, cb) => cb(LOAD_ERROR), + argv: [], + config: { + settings: {}, + get: (k) => npmock.config.settings[k], + set: (k, v) => { npmock.config.settings[k] = v }, + }, + commands: {} +} + +const unsupportedMock = { + checkForBrokenNode: () => {}, + checkForUnsupportedNode: () => {} +} + +let errorHandlerCalled = null +const errorHandlerMock = (...args) => { + errorHandlerCalled = args +} +let errorHandlerExitCalled = null +errorHandlerMock.exit = code => { + errorHandlerExitCalled = code +} + +const logs = [] +const npmlogMock = { + pause: () => logs.push('pause'), + verbose: (...msg) => logs.push(['verbose', ...msg]), + info: (...msg) => logs.push(['info', ...msg]) +} + +const requireInject = require('require-inject') +const cli = requireInject.installGlobally('../../lib/cli.js', { + '../../lib/npm.js': npmock, + '../../lib/utils/unsupported.js': unsupportedMock, + '../../lib/utils/error-handler.js': errorHandlerMock, + npmlog: npmlogMock +}) + +t.test('print the version, and treat npm_g to npm -g', t => { + const { log } = console + const consoleLogs = [] + console.log = (...msg) => consoleLogs.push(msg) + const proc = { + argv: ['node', 'npm_g', '-v'], + on: () => {} + } + + cli(proc) + + t.strictSame(npmock.argv, []) + t.strictSame(proc.argv, [ 'node', 'npm', '-g', '-v' ]) + t.strictSame(logs, [ + 'pause', + [ 'verbose', 'cli', [ 'node', 'npm', '-g', '-v' ] ] + ]) + t.strictSame(consoleLogs, [ [ '99.99.99' ] ]) + t.strictSame(errorHandlerExitCalled, 0) + + console.log = log + npmock.argv = 0 + proc.argv.length = 0 + logs.length = 0 + consoleLogs.length = 0 + errorHandlerExitCalled = null + + t.end() +}) + +t.test('calling with --versions calls npm version with no args', t => { + const { log } = console + const consoleLogs = [] + console.log = (...msg) => consoleLogs.push(msg) + const proc = { + argv: ['node', 'npm', 'install', 'or', 'whatever', '--versions'], + on: () => {} + } + + t.teardown(() => { + console.log = log + npmock.argv = 0 + proc.argv.length = 0 + logs.length = 0 + consoleLogs.length = 0 + errorHandlerExitCalled = null + delete npmock.commands.version + }) + + npmock.commands.version = (args, cb) => { + t.equal(proc.title, 'npm') + t.strictSame(npmock.argv, []) + t.strictSame(proc.argv, [ 'node', 'npm', 'install', 'or', 'whatever', '--versions' ]) + t.strictSame(logs, [ + 'pause', + [ 'verbose', 'cli', [ 'node', 'npm', 'install', 'or', 'whatever', '--versions' ] ], + [ 'info', 'using', 'npm@%s', '99.99.99' ], + [ 'info', 'using', 'node@%s', undefined ] + ]) + + t.strictSame(consoleLogs, []) + t.strictSame(errorHandlerExitCalled, null) + + t.strictSame(args, []) + t.end() + } + + cli(proc) +}) + +t.test('print usage if -h provided', t => { + const { log } = console + const consoleLogs = [] + console.log = (...msg) => consoleLogs.push(msg) + const proc = { + argv: ['node', 'npm', 'asdf'], + on: () => {} + } + + t.teardown(() => { + console.log = log + npmock.argv = 0 + proc.argv.length = 0 + logs.length = 0 + consoleLogs.length = 0 + errorHandlerExitCalled = null + delete npmock.commands.help + }) + + npmock.commands.help = (args, cb) => { + delete npmock.commands.help + t.equal(proc.title, 'npm') + t.strictSame(args, ['asdf']) + t.strictSame(npmock.argv, ['asdf']) + t.strictSame(proc.argv, [ 'node', 'npm', 'asdf' ]) + t.strictSame(logs, [ + 'pause', + [ 'verbose', 'cli', [ 'node', 'npm', 'asdf' ] ], + [ 'info', 'using', 'npm@%s', '99.99.99' ], + [ 'info', 'using', 'node@%s', undefined ] + ]) + t.strictSame(consoleLogs, []) + t.strictSame(errorHandlerExitCalled, null) + t.end() + } + + cli(proc) +}) + +t.test('load error calls error handler', t => { + const er = new Error('poop') + LOAD_ERROR = er + const proc = { + argv: ['node', 'npm', 'asdf'], + on: () => {} + } + cli(proc) + t.strictSame(errorHandlerCalled, [er]) + LOAD_ERROR = null + t.end() +}) +