1'use strict' 2 3const BB = require('bluebird') 4 5const common = require('../common-tap.js') 6const fs = require('graceful-fs') 7const mockTar = require('../util/mock-tarball.js') 8const mr = common.fakeRegistry.compat 9const path = require('path') 10const rimraf = BB.promisify(require('rimraf')) 11const Tacks = require('tacks') 12const { test } = require('tap') 13 14const { Dir, File } = Tacks 15const readdirAsync = BB.promisify(fs.readdir) 16const readFileAsync = BB.promisify(fs.readFile) 17 18const testDir = common.pkg 19 20let server 21test('setup', t => { 22 mr({}, (err, s) => { 23 t.ifError(err, 'registry mocked successfully') 24 server = s 25 t.end() 26 }) 27}) 28 29test('installs an npm: protocol alias package', t => { 30 const fixture = new Tacks(Dir({ 31 'package.json': File({}) 32 })) 33 fixture.create(testDir) 34 const packument = { 35 name: 'foo', 36 'dist-tags': { latest: '1.2.4' }, 37 versions: { 38 '1.2.3': { 39 name: 'foo', 40 version: '1.2.3', 41 dist: { 42 tarball: `${server.registry}/foo/-/foo-1.2.3.tgz` 43 } 44 }, 45 '1.2.4': { 46 name: 'foo', 47 version: '1.2.4', 48 dist: { 49 tarball: `${server.registry}/foo/-/foo-1.2.4.tgz` 50 } 51 } 52 } 53 } 54 server.get('/foo').reply(200, packument) 55 return mockTar({ 56 'package.json': JSON.stringify({ 57 name: 'foo', 58 version: '1.2.3' 59 }) 60 }).then(tarball => { 61 server.get('/foo/-/foo-1.2.3.tgz').reply(200, tarball) 62 server.get('/foo/-/foo-1.2.4.tgz').reply(200, tarball) 63 return common.npm([ 64 'install', 'foo@1.2.3', 65 '--cache', path.join(testDir, 'npmcache'), 66 '--registry', server.registry 67 ], { cwd: testDir }) 68 }).then(([code, stdout, stderr]) => { 69 t.equal(code, 0) 70 t.comment(stdout) 71 t.comment(stderr) 72 return common.npm([ 73 'install', 'bar@npm:foo@1.2.3', 74 '--cache', path.join(testDir, 'npmcache'), 75 '--registry', server.registry 76 ], { cwd: testDir }) 77 }).then(([code, stdout, stderr]) => { 78 t.equal(code, 0) 79 t.comment(stdout) 80 t.comment(stderr) 81 t.match(stdout, /\+ foo@1\.2\.3 \(as bar\)/, 'useful message') 82 return readFileAsync( 83 path.join(testDir, 'node_modules', 'bar', 'package.json'), 84 'utf8' 85 ) 86 }).then(JSON.parse).then(pkg => { 87 t.similar(pkg, { 88 name: 'foo', 89 version: '1.2.3' 90 }, 'successfully installed foo as bar in node_modules') 91 return common.npm(['ls', '--json'], { cwd: testDir }) 92 }).then(([code, stdout, stderr]) => { 93 t.comment(stdout) 94 t.comment(stderr) 95 t.equal(code, 0, 'ls is clean') 96 t.deepEqual(JSON.parse(stdout), { 97 dependencies: { 98 bar: { 99 version: '1.2.3', 100 from: 'bar@npm:foo@1.2.3', 101 resolved: 'http://localhost:' + common.port + '/foo/-/foo-1.2.3.tgz' 102 }, 103 foo: { 104 version: '1.2.3', 105 from: 'foo@1.2.3', 106 resolved: 'http://localhost:' + common.port + '/foo/-/foo-1.2.3.tgz' 107 } 108 } 109 }, 'both dependencies listed correctly') 110 return common.npm([ 111 'outdated', '--json', 112 '--registry', server.registry 113 ], { cwd: testDir }) 114 }).then(([code, stdout, stderr]) => { 115 t.equal(code, 1, 'non-zero because some packages outdated') 116 t.comment(stdout) 117 t.comment(stderr) 118 const parsed = JSON.parse(stdout) 119 t.match(parsed, { 120 foo: { 121 current: '1.2.3', 122 wanted: '1.2.4', 123 latest: '1.2.4', 124 location: /node_modules[/\\]foo/ 125 }, 126 bar: { 127 current: 'npm:foo@1.2.3', 128 wanted: 'npm:foo@1.2.4', 129 latest: 'npm:foo@1.2.4', 130 location: /node_modules[/\\]bar/ 131 } 132 }, 'both regular and aliased dependency reported') 133 return common.npm([ 134 'update', 135 '--registry', server.registry 136 ], { cwd: testDir }) 137 }).then(([code, stdout, stderr]) => { 138 t.equal(code, 0, 'update succeeded') 139 t.comment(stdout) 140 t.comment(stderr) 141 return common.npm(['ls', '--json'], { cwd: testDir }) 142 }).then(([code, stdout, stderr]) => { 143 t.equal(code, 0, 'ls succeeded') 144 t.comment(stdout) 145 t.comment(stderr) 146 const parsed = JSON.parse(stdout) 147 t.deepEqual(parsed, { 148 dependencies: { 149 bar: { 150 version: '1.2.4', 151 from: 'bar@npm:foo@1.2.4', 152 resolved: 'http://localhost:' + common.port + '/foo/-/foo-1.2.4.tgz' 153 }, 154 foo: { 155 version: '1.2.4', 156 from: 'foo@1.2.4', 157 resolved: 'http://localhost:' + common.port + '/foo/-/foo-1.2.4.tgz' 158 } 159 } 160 }, 'ls shows updated packages') 161 return common.npm([ 162 'rm', 'bar', 163 '--registry', server.registry 164 ], { cwd: testDir }) 165 }).then(([code, stdout, stderr]) => { 166 t.equal(code, 0, 'rm succeeded') 167 t.comment(stdout) 168 t.comment(stderr) 169 t.match(stdout, 'removed 1 package', 'notified of removed package') 170 return readdirAsync(path.join(testDir, 'node_modules')) 171 }).then(dir => { 172 t.deepEqual(dir, ['foo'], 'regular foo left in place') 173 }).then(() => rimraf(testDir)) 174}) 175 176test('installs a tarball dep as a different name than package.json', t => { 177 return mockTar({ 178 'package.json': JSON.stringify({ 179 name: 'foo', 180 version: '1.2.3' 181 }) 182 }).then(tarball => { 183 const fixture = new Tacks(Dir({ 184 'package.json': File({}), 185 'foo.tgz': File(tarball) 186 })) 187 fixture.create(testDir) 188 return common.npm([ 189 'install', 'file:foo.tgz', 190 '--cache', path.join(testDir, 'npmcache'), 191 '--registry', server.registry 192 ], { cwd: testDir }) 193 }).then(([code, stdout, stderr]) => { 194 return common.npm([ 195 'install', 'bar@file:foo.tgz', 196 '--cache', path.join(testDir, 'npmcache'), 197 '--registry', server.registry 198 ], { cwd: testDir }) 199 }).then(([code, stdout, stderr]) => { 200 t.comment(stdout) 201 t.comment(stderr) 202 t.match(stdout, /^\+ foo@1\.2\.3 \(as bar\)/, 'useful message') 203 return readFileAsync( 204 path.join(testDir, 'node_modules', 'bar', 'package.json'), 205 'utf8' 206 ) 207 }).then(JSON.parse).then(pkg => { 208 t.similar(pkg, { 209 name: 'foo', 210 version: '1.2.3' 211 }, 'successfully installed foo as bar in node_modules') 212 return common.npm(['ls', '--json'], { cwd: testDir }) 213 }).then(([code, stdout, stderr]) => { 214 t.comment(stdout) 215 t.comment(stderr) 216 t.similar(JSON.parse(stdout), { 217 dependencies: { 218 bar: { 219 version: '1.2.3', 220 from: 'file:foo.tgz' 221 }, 222 foo: { 223 version: '1.2.3', 224 from: 'file:foo.tgz' 225 } 226 } 227 }, 'both dependencies present') 228 }).then(() => rimraf(testDir)) 229}) 230 231test('installs a symlink dep as a different name than package.json', t => { 232 const fixture = new Tacks(Dir({ 233 'package.json': File({}), 234 'foo': Dir({ 235 'package.json': File({ 236 name: 'foo', 237 version: '1.2.3' 238 }) 239 }) 240 })) 241 fixture.create(testDir) 242 return common.npm([ 243 'install', 'bar@file:foo', 244 '--cache', path.join(testDir, 'npmcache'), 245 '--registry', server.registry 246 ], { cwd: testDir }).then(([code, stdout, stderr]) => { 247 t.comment(stdout) 248 t.comment(stderr) 249 t.match(stdout, /^\+ foo@1\.2\.3 \(as bar\)/, 'useful message') 250 return readFileAsync( 251 path.join(testDir, 'node_modules', 'bar', 'package.json'), 252 'utf8' 253 ) 254 }).then(JSON.parse).then(pkg => { 255 t.similar(pkg, { 256 name: 'foo', 257 version: '1.2.3' 258 }, 'successfully installed foo as bar in node_modules') 259 }).then(() => rimraf(testDir)) 260}) 261 262test('cleanup', t => { 263 server.close() 264 return rimraf(testDir) 265}) 266 267test('npm audit supports aliases') 268test('npm audit fix supports aliases') 269