1const t = require('tap') 2const { load: loadMockNpm } = require('../fixtures/mock-npm.js') 3const tmock = require('../fixtures/tmock.js') 4const validateEngines = require('../../lib/es6/validate-engines.js') 5 6const cliMock = async (t, opts) => { 7 let exitHandlerArgs = null 8 let npm = null 9 const exitHandlerMock = (...args) => { 10 exitHandlerArgs = args 11 npm.unload() 12 } 13 exitHandlerMock.setNpm = _npm => npm = _npm 14 15 const { Npm, outputs, logMocks, logs } = await loadMockNpm(t, { ...opts, init: false }) 16 const cli = tmock(t, '{LIB}/cli-entry.js', { 17 '{LIB}/npm.js': Npm, 18 '{LIB}/utils/exit-handler.js': exitHandlerMock, 19 ...logMocks, 20 }) 21 22 return { 23 Npm, 24 cli: (p) => validateEngines(p, () => cli), 25 outputs, 26 exitHandlerCalled: () => exitHandlerArgs, 27 exitHandlerNpm: () => npm, 28 logs, 29 logsBy: (title) => logs.verbose.filter(([p]) => p === title).map(([p, ...rest]) => rest), 30 } 31} 32 33t.test('print the version, and treat npm_g as npm -g', async t => { 34 const { logsBy, logs, cli, Npm, outputs, exitHandlerCalled } = await cliMock(t, { 35 globals: { 'process.argv': ['node', 'npm_g', '-v'] }, 36 }) 37 await cli(process) 38 39 t.strictSame(process.argv, ['node', 'npm', '-g', '-v'], 'system process.argv was rewritten') 40 t.strictSame(logsBy('cli'), [['node npm']]) 41 t.strictSame(logsBy('title'), [['npm']]) 42 t.match(logsBy('argv'), [['"--global" "--version"']]) 43 t.strictSame(logs.info, [ 44 ['using', 'npm@%s', Npm.version], 45 ['using', 'node@%s', process.version], 46 ]) 47 t.equal(outputs.length, 1) 48 t.strictSame(outputs, [[Npm.version]]) 49 t.strictSame(exitHandlerCalled(), []) 50}) 51 52t.test('calling with --versions calls npm version with no args', async t => { 53 const { logsBy, cli, outputs, exitHandlerCalled } = await cliMock(t, { 54 globals: { 55 'process.argv': ['node', 'npm', 'install', 'or', 'whatever', '--versions'], 56 }, 57 }) 58 await cli(process) 59 60 t.equal(process.title, 'npm install or whatever') 61 t.strictSame(logsBy('cli'), [['node npm']]) 62 t.strictSame(logsBy('title'), [['npm install or whatever']]) 63 t.match(logsBy('argv'), [['"install" "or" "whatever" "--versions"']]) 64 t.equal(outputs.length, 1) 65 t.match(outputs[0][0], { npm: String, node: String, v8: String }) 66 t.strictSame(exitHandlerCalled(), []) 67}) 68 69t.test('logged argv is sanitized', async t => { 70 const { logsBy, cli } = await cliMock(t, { 71 globals: { 72 'process.argv': [ 73 'node', 74 'npm', 75 'version', 76 '--registry', 77 'https://u:password@npmjs.org/password', 78 ], 79 }, 80 }) 81 82 await cli(process) 83 t.equal(process.title, 'npm version') 84 t.strictSame(logsBy('cli'), [['node npm']]) 85 t.strictSame(logsBy('title'), [['npm version']]) 86 t.match(logsBy('argv'), [['"version" "--registry" "https://u:***@npmjs.org/password"']]) 87}) 88 89t.test('logged argv is sanitized with equals', async t => { 90 const { logsBy, cli } = await cliMock(t, { 91 globals: { 92 'process.argv': [ 93 'node', 94 'npm', 95 'version', 96 '--registry=https://u:password@npmjs.org', 97 ], 98 }, 99 }) 100 await cli(process) 101 102 t.match(logsBy('argv'), [['"version" "--registry" "https://u:***@npmjs.org/"']]) 103}) 104 105t.test('print usage if no params provided', async t => { 106 const { cli, outputs, exitHandlerCalled, exitHandlerNpm } = await cliMock(t, { 107 globals: { 108 'process.argv': ['node', 'npm'], 109 }, 110 }) 111 await cli(process) 112 113 t.match(outputs[0][0], 'Usage:', 'outputs npm usage') 114 t.match(exitHandlerCalled(), [], 'should call exitHandler with no args') 115 t.ok(exitHandlerNpm(), 'exitHandler npm is set') 116 t.match(process.exitCode, 1) 117}) 118 119t.test('print usage if non-command param provided', async t => { 120 const { cli, outputs, exitHandlerCalled, exitHandlerNpm } = await cliMock(t, { 121 globals: { 122 'process.argv': ['node', 'npm', 'tset'], 123 }, 124 }) 125 await cli(process) 126 127 t.match(outputs[0][0], 'Unknown command: "tset"') 128 t.match(outputs[0][0], 'Did you mean this?') 129 t.match(exitHandlerCalled(), [], 'should call exitHandler with no args') 130 t.ok(exitHandlerNpm(), 'exitHandler npm is set') 131 t.match(process.exitCode, 1) 132}) 133 134t.test('load error calls error handler', async t => { 135 const err = new Error('test load error') 136 const { cli, exitHandlerCalled } = await cliMock(t, { 137 mocks: { 138 '@npmcli/config': class BadConfig { 139 async load () { 140 throw err 141 } 142 }, 143 }, 144 globals: { 145 'process.argv': ['node', 'npm', 'asdf'], 146 }, 147 }) 148 await cli(process) 149 t.strictSame(exitHandlerCalled(), [err]) 150}) 151 152t.test('unsupported node version', async t => { 153 const { cli, logs } = await cliMock(t, { 154 globals: { 155 'process.version': '12.6.0', 156 }, 157 }) 158 await cli(process) 159 t.match( 160 logs.warn[0][1], 161 /npm v.* does not support Node\.js 12\.6\.0\./ 162 ) 163}) 164