1const t = require('tap') 2const { load: loadMockNpm } = require('../../fixtures/mock-npm') 3 4t.test('exec commands', async t => { 5 await t.test('with args, dev=true', async t => { 6 const SCRIPTS = [] 7 let ARB_ARGS = null 8 let REIFY_CALLED = false 9 let ARB_OBJ = null 10 11 const { npm } = await loadMockNpm(t, { 12 mocks: { 13 '@npmcli/run-script': ({ event }) => { 14 SCRIPTS.push(event) 15 }, 16 '@npmcli/arborist': function (args) { 17 ARB_ARGS = args 18 ARB_OBJ = this 19 this.reify = () => { 20 REIFY_CALLED = true 21 } 22 }, 23 '{LIB}/utils/reify-finish.js': (_, arb) => { 24 if (arb !== ARB_OBJ) { 25 throw new Error('got wrong object passed to reify-finish') 26 } 27 }, 28 }, 29 config: { 30 // This is here because CI calls tests with `--ignore-scripts`, which config 31 // picks up from argv 32 'ignore-scripts': false, 33 'audit-level': 'low', 34 dev: true, 35 }, 36 }) 37 38 await npm.exec('install', ['fizzbuzz']) 39 40 t.match( 41 ARB_ARGS, 42 { global: false, path: npm.prefix, auditLevel: null }, 43 'Arborist gets correct args and ignores auditLevel' 44 ) 45 t.equal(REIFY_CALLED, true, 'called reify') 46 t.strictSame(SCRIPTS, [], 'no scripts when adding dep') 47 }) 48 49 await t.test('without args', async t => { 50 const SCRIPTS = [] 51 let ARB_ARGS = null 52 let REIFY_CALLED = false 53 let ARB_OBJ = null 54 55 const { npm } = await loadMockNpm(t, { 56 mocks: { 57 '@npmcli/run-script': ({ event }) => { 58 SCRIPTS.push(event) 59 }, 60 '@npmcli/arborist': function (args) { 61 ARB_ARGS = args 62 ARB_OBJ = this 63 this.reify = () => { 64 REIFY_CALLED = true 65 } 66 }, 67 '{LIB}/utils/reify-finish.js': (_, arb) => { 68 if (arb !== ARB_OBJ) { 69 throw new Error('got wrong object passed to reify-finish') 70 } 71 }, 72 }, 73 config: { 74 75 }, 76 }) 77 78 await npm.exec('install', []) 79 t.match(ARB_ARGS, { global: false, path: npm.prefix }) 80 t.equal(REIFY_CALLED, true, 'called reify') 81 t.strictSame(SCRIPTS, [ 82 'preinstall', 83 'install', 84 'postinstall', 85 'prepublish', 86 'preprepare', 87 'prepare', 88 'postprepare', 89 ], 'exec scripts when doing local build') 90 }) 91 92 await t.test('should ignore scripts with --ignore-scripts', async t => { 93 const SCRIPTS = [] 94 let REIFY_CALLED = false 95 const { npm } = await loadMockNpm(t, { 96 mocks: { 97 '{LIB}/utils/reify-finish.js': async () => {}, 98 '@npmcli/run-script': ({ event }) => { 99 SCRIPTS.push(event) 100 }, 101 '@npmcli/arborist': function () { 102 this.reify = () => { 103 REIFY_CALLED = true 104 } 105 }, 106 }, 107 config: { 108 'ignore-scripts': true, 109 }, 110 }) 111 112 await npm.exec('install', []) 113 t.equal(REIFY_CALLED, true, 'called reify') 114 t.strictSame(SCRIPTS, [], 'no scripts when adding dep') 115 }) 116 117 await t.test('should install globally using Arborist', async t => { 118 const SCRIPTS = [] 119 let ARB_ARGS = null 120 let REIFY_CALLED 121 const { npm } = await loadMockNpm(t, { 122 mocks: { 123 '@npmcli/run-script': ({ event }) => { 124 SCRIPTS.push(event) 125 }, 126 '{LIB}/utils/reify-finish.js': async () => {}, 127 '@npmcli/arborist': function (args) { 128 ARB_ARGS = args 129 this.reify = () => { 130 REIFY_CALLED = true 131 } 132 }, 133 }, 134 config: { 135 global: true, 136 }, 137 }) 138 await npm.exec('install', []) 139 t.match( 140 ARB_ARGS, 141 { global: true, path: npm.globalPrefix } 142 ) 143 t.equal(REIFY_CALLED, true, 'called reify') 144 t.strictSame(SCRIPTS, [], 'no scripts when installing globally') 145 t.notOk(npm.config.get('audit', 'cli')) 146 }) 147 148 await t.test('should not install invalid global package name', async t => { 149 const { npm } = await loadMockNpm(t, { 150 mocks: { 151 '@npmcli/run-script': () => {}, 152 '{LIB}/utils/reify-finish.js': async () => {}, 153 '@npmcli/arborist': function (args) { 154 throw new Error('should not reify') 155 }, 156 }, 157 config: { 158 global: true, 159 }, 160 }) 161 await t.rejects( 162 npm.exec('install', ['']), 163 /Usage:/, 164 'should not install invalid package name' 165 ) 166 }) 167 168 await t.test('npm i -g npm engines check success', async t => { 169 const { npm } = await loadMockNpm(t, { 170 mocks: { 171 '{LIB}/utils/reify-finish.js': async () => {}, 172 '@npmcli/arborist': function () { 173 this.reify = () => {} 174 }, 175 pacote: { 176 manifest: () => { 177 return { 178 version: '100.100.100', 179 engines: { 180 node: '>1', 181 }, 182 } 183 }, 184 }, 185 }, 186 config: { 187 global: true, 188 }, 189 }) 190 await npm.exec('install', ['npm']) 191 t.ok('No exceptions happen') 192 }) 193 194 await t.test('npm i -g npm engines check failure', async t => { 195 const { npm } = await loadMockNpm(t, { 196 mocks: { 197 pacote: { 198 manifest: () => { 199 return { 200 _id: 'npm@1.2.3', 201 version: '100.100.100', 202 engines: { 203 node: '>1000', 204 }, 205 } 206 }, 207 }, 208 }, 209 config: { 210 global: true, 211 }, 212 }) 213 await t.rejects( 214 npm.exec('install', ['npm']), 215 { 216 message: 'Unsupported engine', 217 pkgid: 'npm@1.2.3', 218 current: { 219 node: process.version, 220 npm: '100.100.100', 221 }, 222 required: { 223 node: '>1000', 224 }, 225 code: 'EBADENGINE', 226 } 227 ) 228 }) 229 230 await t.test('npm i -g npm engines check failure forced override', async t => { 231 const { npm } = await loadMockNpm(t, { 232 mocks: { 233 '{LIB}/utils/reify-finish.js': async () => {}, 234 '@npmcli/arborist': function () { 235 this.reify = () => {} 236 }, 237 pacote: { 238 manifest: () => { 239 return { 240 _id: 'npm@1.2.3', 241 version: '100.100.100', 242 engines: { 243 node: '>1000', 244 }, 245 } 246 }, 247 }, 248 }, 249 config: { 250 global: true, 251 force: true, 252 }, 253 }) 254 await npm.exec('install', ['npm']) 255 t.ok('Does not throw') 256 }) 257 258 await t.test('npm i -g npm@version engines check failure', async t => { 259 const { npm } = await loadMockNpm(t, { 260 mocks: { 261 '{LIB}/utils/reify-finish.js': async () => {}, 262 '@npmcli/arborist': function () { 263 this.reify = () => {} 264 }, 265 pacote: { 266 manifest: () => { 267 return { 268 _id: 'npm@1.2.3', 269 version: '100.100.100', 270 engines: { 271 node: '>1000', 272 }, 273 } 274 }, 275 }, 276 }, 277 config: { 278 global: true, 279 }, 280 }) 281 await t.rejects( 282 npm.exec('install', ['npm@100']), 283 { 284 message: 'Unsupported engine', 285 pkgid: 'npm@1.2.3', 286 current: { 287 node: process.version, 288 npm: '100.100.100', 289 }, 290 required: { 291 node: '>1000', 292 }, 293 code: 'EBADENGINE', 294 } 295 ) 296 }) 297}) 298 299t.test('completion', async t => { 300 const mockComp = async (t, { noChdir } = {}) => loadMockNpm(t, { 301 command: 'install', 302 prefixDir: { 303 arborist: { 304 'package.json': '{}', 305 }, 306 'arborist.txt': 'just a file', 307 'other-dir': { a: 'a' }, 308 }, 309 ...(noChdir ? { chdir: false } : {}), 310 }) 311 312 await t.test('completion to folder - has a match', async t => { 313 const { install } = await mockComp(t) 314 const res = await install.completion({ partialWord: './ar' }) 315 t.strictSame(res, ['arborist'], 'package dir match') 316 }) 317 318 await t.test('completion to folder - invalid dir', async t => { 319 const { install } = await mockComp(t, { noChdir: true }) 320 const res = await install.completion({ partialWord: '/does/not/exist' }) 321 t.strictSame(res, [], 'invalid dir: no matching') 322 }) 323 324 await t.test('completion to folder - no matches', async t => { 325 const { install } = await mockComp(t) 326 const res = await install.completion({ partialWord: './pa' }) 327 t.strictSame(res, [], 'no name match') 328 }) 329 330 await t.test('completion to folder - match is not a package', async t => { 331 const { install } = await mockComp(t) 332 const res = await install.completion({ partialWord: './othe' }) 333 t.strictSame(res, [], 'no name match') 334 }) 335 336 await t.test('completion to url', async t => { 337 const { install } = await mockComp(t) 338 const res = await install.completion({ partialWord: 'http://path/to/url' }) 339 t.strictSame(res, []) 340 }) 341 342 await t.test('no /', async t => { 343 const { install } = await mockComp(t) 344 const res = await install.completion({ partialWord: 'toto' }) 345 t.notOk(res) 346 }) 347 348 await t.test('only /', async t => { 349 const { install } = await mockComp(t) 350 const res = await install.completion({ partialWord: '/' }) 351 t.strictSame(res, []) 352 }) 353}) 354 355t.test('location detection and audit', async (t) => { 356 await t.test('audit false without package.json', async t => { 357 const { npm } = await loadMockNpm(t, { 358 command: 'install', 359 prefixDir: { 360 // no package.json 361 'readme.txt': 'just a file', 362 'other-dir': { a: 'a' }, 363 }, 364 }) 365 t.equal(npm.config.get('location'), 'user') 366 t.equal(npm.config.get('audit'), false) 367 }) 368 369 await t.test('audit true with package.json', async t => { 370 const { npm } = await loadMockNpm(t, { 371 command: 'install', 372 prefixDir: { 373 'package.json': '{ "name": "testpkg", "version": "1.0.0" }', 374 'readme.txt': 'just a file', 375 }, 376 }) 377 t.equal(npm.config.get('location'), 'user') 378 t.equal(npm.config.get('audit'), true) 379 }) 380 381 await t.test('audit true without package.json when set', async t => { 382 const { npm } = await loadMockNpm(t, { 383 command: 'install', 384 prefixDir: { 385 // no package.json 386 'readme.txt': 'just a file', 387 'other-dir': { a: 'a' }, 388 }, 389 config: { 390 audit: true, 391 }, 392 }) 393 t.equal(npm.config.get('location'), 'user') 394 t.equal(npm.config.get('audit'), true) 395 }) 396 397 await t.test('audit true in root config without package.json', async t => { 398 const { npm } = await loadMockNpm(t, { 399 command: 'install', 400 prefixDir: { 401 // no package.json 402 'readme.txt': 'just a file', 403 'other-dir': { a: 'a' }, 404 }, 405 // change npmRoot to get it to use a builtin rc file 406 otherDirs: { npmrc: 'audit=true' }, 407 npm: ({ other }) => ({ npmRoot: other }), 408 }) 409 t.equal(npm.config.get('location'), 'user') 410 t.equal(npm.config.get('audit'), true) 411 }) 412 413 await t.test('test for warning when --global & --audit', async t => { 414 const { npm, logs } = await loadMockNpm(t, { 415 command: 'install', 416 prefixDir: { 417 // no package.json 418 'readme.txt': 'just a file', 419 'other-dir': { a: 'a' }, 420 }, 421 config: { 422 audit: true, 423 global: true, 424 }, 425 }) 426 t.equal(npm.config.get('location'), 'user') 427 t.equal(npm.config.get('audit'), true) 428 t.equal(logs.warn[0][0], 'config') 429 t.equal(logs.warn[0][1], 'includes both --global and --audit, which is currently unsupported.') 430 }) 431}) 432