1const t = require('tap') 2const { load: _loadMockNpm } = require('../../fixtures/mock-npm') 3const MockRegistry = require('@npmcli/mock-registry') 4 5const path = require('path') 6const fs = require('fs') 7 8// t.cleanSnapshot = str => str.replace(/ in [0-9ms]+/g, ' in {TIME}') 9 10const loadMockNpm = async (t, opts) => { 11 const mock = await _loadMockNpm(t, opts) 12 const registry = new MockRegistry({ 13 tap: t, 14 registry: mock.npm.config.get('registry'), 15 }) 16 return { registry, ...mock } 17} 18 19const packageJson = { 20 name: 'test-package', 21 version: '1.0.0', 22 dependencies: { 23 abbrev: '^1.0.0', 24 }, 25} 26const packageLock = { 27 name: 'test-package', 28 version: '1.0.0', 29 lockfileVersion: 2, 30 requires: true, 31 packages: { 32 '': { 33 name: 'test-package', 34 version: '1.0.0', 35 dependencies: { 36 abbrev: '^1.0.0', 37 }, 38 }, 39 'node_modules/abbrev': { 40 version: '1.0.0', 41 resolved: 'https://registry.npmjs.org/abbrev/-/abbrev-1.0.0.tgz', 42 // integrity changes w/ each test cause the path is different? 43 }, 44 }, 45 dependencies: { 46 abbrev: { 47 version: '1.0.0', 48 resolved: 'https://registry.npmjs.org/abbrev/-/abbrev-1.0.0.tgz', 49 // integrity changes w/ each test cause the path is different? 50 }, 51 }, 52} 53 54const abbrev = { 55 'package.json': '{"name": "abbrev", "version": "1.0.0"}', 56 test: 'test file', 57} 58 59t.test('reifies, audits, removes node_modules', async t => { 60 const { npm, joinedOutput, registry } = await loadMockNpm(t, { 61 prefixDir: { 62 abbrev: abbrev, 63 'package.json': JSON.stringify(packageJson), 64 'package-lock.json': JSON.stringify(packageLock), 65 node_modules: { test: 'test file that will be removed' }, 66 }, 67 }) 68 const manifest = registry.manifest({ name: 'abbrev' }) 69 await registry.tarball({ 70 manifest: manifest.versions['1.0.0'], 71 tarball: path.join(npm.prefix, 'abbrev'), 72 }) 73 registry.nock.post('/-/npm/v1/security/advisories/bulk').reply(200, {}) 74 await npm.exec('ci', []) 75 t.match(joinedOutput(), 'added 1 package, and audited 2 packages in') 76 const nmTest = path.join(npm.prefix, 'node_modules', 'test') 77 t.equal(fs.existsSync(nmTest), false, 'existing node_modules is removed') 78 const nmAbbrev = path.join(npm.prefix, 'node_modules', 'abbrev') 79 t.equal(fs.existsSync(nmAbbrev), true, 'installs abbrev') 80}) 81 82t.test('reifies, audits, removes node_modules on repeat run', async t => { 83 const { npm, joinedOutput, registry } = await loadMockNpm(t, { 84 prefixDir: { 85 abbrev: abbrev, 86 'package.json': JSON.stringify(packageJson), 87 'package-lock.json': JSON.stringify(packageLock), 88 node_modules: { test: 'test file that will be removed' }, 89 }, 90 }) 91 const manifest = registry.manifest({ name: 'abbrev' }) 92 await registry.tarball({ 93 manifest: manifest.versions['1.0.0'], 94 tarball: path.join(npm.prefix, 'abbrev'), 95 }) 96 registry.nock.post('/-/npm/v1/security/advisories/bulk').reply(200, {}) 97 await npm.exec('ci', []) 98 await npm.exec('ci', []) 99 t.match(joinedOutput(), 'added 1 package, and audited 2 packages in') 100 const nmTest = path.join(npm.prefix, 'node_modules', 'test') 101 t.equal(fs.existsSync(nmTest), false, 'existing node_modules is removed') 102 const nmAbbrev = path.join(npm.prefix, 'node_modules', 'abbrev') 103 t.equal(fs.existsSync(nmAbbrev), true, 'installs abbrev') 104}) 105 106t.test('--no-audit and --ignore-scripts', async t => { 107 const { npm, joinedOutput, registry } = await loadMockNpm(t, { 108 config: { 109 'ignore-scripts': true, 110 audit: false, 111 }, 112 prefixDir: { 113 abbrev: { 114 'package.json': '{"name": "abbrev", "version": "1.0.0"}', 115 test: 'test-file', 116 }, 117 'package.json': JSON.stringify({ 118 ...packageJson, 119 // Would make install fail 120 scripts: { install: 'echo "SHOULD NOT RUN" && exit 1' }, 121 }), 122 'package-lock.json': JSON.stringify(packageLock), 123 }, 124 }) 125 require('nock').emitter.on('no match', req => { 126 t.fail('Should not audit') 127 }) 128 const manifest = registry.manifest({ name: 'abbrev' }) 129 await registry.tarball({ 130 manifest: manifest.versions['1.0.0'], 131 tarball: path.join(npm.prefix, 'abbrev'), 132 }) 133 await npm.exec('ci', []) 134 t.match(joinedOutput(), 'added 1 package in', 'would fail if install script ran') 135}) 136 137t.test('lifecycle scripts', async t => { 138 const scripts = [] 139 const { npm, registry } = await loadMockNpm(t, { 140 prefixDir: { 141 abbrev: abbrev, 142 'package.json': JSON.stringify(packageJson), 143 'package-lock.json': JSON.stringify(packageLock), 144 }, 145 mocks: { 146 '@npmcli/run-script': (opts) => { 147 t.ok(opts.banner) 148 scripts.push(opts.event) 149 }, 150 }, 151 }) 152 const manifest = registry.manifest({ name: 'abbrev' }) 153 await registry.tarball({ 154 manifest: manifest.versions['1.0.0'], 155 tarball: path.join(npm.prefix, 'abbrev'), 156 }) 157 registry.nock.post('/-/npm/v1/security/advisories/bulk').reply(200, {}) 158 await npm.exec('ci', []) 159 t.same(scripts, [ 160 'preinstall', 161 'install', 162 'postinstall', 163 'prepublish', 164 'preprepare', 165 'prepare', 166 'postprepare', 167 ], 'runs appropriate scripts, in order') 168}) 169 170t.test('should throw if package-lock.json or npm-shrinkwrap missing', async t => { 171 const { npm } = await loadMockNpm(t, { 172 prefixDir: { 173 'package.json': JSON.stringify(packageJson), 174 node_modules: { 175 'test-file': 'should not be removed', 176 }, 177 }, 178 }) 179 await t.rejects(npm.exec('ci', []), { code: 'EUSAGE', message: /package-lock.json/ }) 180 const nmTestFile = path.join(npm.prefix, 'node_modules', 'test-file') 181 t.equal(fs.existsSync(nmTestFile), true, 'does not remove node_modules') 182}) 183 184t.test('should throw ECIGLOBAL', async t => { 185 const { npm } = await loadMockNpm(t, { 186 config: { global: true }, 187 }) 188 await t.rejects(npm.exec('ci', []), { code: 'ECIGLOBAL' }) 189}) 190 191t.test('should throw error when ideal inventory mismatches virtual', async t => { 192 const { npm, registry } = await loadMockNpm(t, { 193 prefixDir: { 194 abbrev: abbrev, 195 'package.json': JSON.stringify({ 196 ...packageJson, 197 dependencies: { notabbrev: '^1.0.0' }, 198 }), 199 'package-lock.json': JSON.stringify(packageLock), 200 node_modules: { 201 'test-file': 'should not be removed', 202 }, 203 }, 204 }) 205 const manifest = registry.manifest({ name: 'notabbrev' }) 206 await registry.package({ manifest }) 207 await t.rejects( 208 npm.exec('ci', []), 209 { code: 'EUSAGE', message: /in sync/ } 210 ) 211 const nmTestFile = path.join(npm.prefix, 'node_modules', 'test-file') 212 t.equal(fs.existsSync(nmTestFile), true, 'does not remove node_modules') 213}) 214