1const t = require('tap') 2const fs = require('fs') 3const path = require('path') 4const ini = require('ini') 5 6const { load: loadMockNpm } = require('../../fixtures/mock-npm.js') 7const mockGlobals = require('@npmcli/mock-globals') 8const MockRegistry = require('@npmcli/mock-registry') 9const stream = require('stream') 10 11const mockLogin = async (t, { stdin: stdinLines, registry: registryUrl, ...options } = {}) => { 12 let stdin 13 if (stdinLines) { 14 stdin = new stream.PassThrough() 15 for (const l of stdinLines) { 16 stdin.write(l + '\n') 17 } 18 mockGlobals(t, { 19 'process.stdin': stdin, 20 'process.stdout': new stream.PassThrough(), // to quiet readline 21 }, { replace: true }) 22 } 23 const mock = await loadMockNpm(t, { 24 ...options, 25 command: 'login', 26 }) 27 const registry = new MockRegistry({ 28 tap: t, 29 registry: registryUrl ?? mock.npm.config.get('registry'), 30 }) 31 return { 32 registry, 33 stdin, 34 rc: () => ini.parse(fs.readFileSync(path.join(mock.home, '.npmrc'), 'utf8')), 35 ...mock, 36 } 37} 38 39t.test('usage', async t => { 40 const { login } = await loadMockNpm(t, { command: 'login' }) 41 t.match(login.usage, 'login', 'usage has command name in it') 42}) 43 44t.test('legacy', t => { 45 t.test('basic login', async t => { 46 const { npm, registry, login, rc } = await mockLogin(t, { 47 stdin: ['test-user', 'test-password'], 48 config: { 'auth-type': 'legacy' }, 49 homeDir: { 50 '.npmrc': [ 51 '//registry.npmjs.org/:_authToken=user', 52 '//registry.npmjs.org/:always-auth=user', 53 '//registry.npmjs.org/:email=test-email-old@npmjs.org', 54 ].join('\n'), 55 }, 56 }) 57 registry.couchlogin({ 58 username: 'test-user', 59 password: 'test-password', 60 token: 'npm_test-token', 61 }) 62 await login.exec([]) 63 t.same(npm.config.get('//registry.npmjs.org/:_authToken'), 'npm_test-token') 64 t.same(rc(), { 65 '//registry.npmjs.org/:_authToken': 'npm_test-token', 66 email: 'test-email-old@npmjs.org', 67 }, 'should only have token and un-nerfed old email') 68 }) 69 70 t.test('scoped login default registry', async t => { 71 const { npm, registry, login, rc } = await mockLogin(t, { 72 stdin: ['test-user', 'test-password'], 73 config: { 74 'auth-type': 'legacy', 75 scope: '@npmcli', 76 }, 77 }) 78 registry.couchlogin({ 79 username: 'test-user', 80 password: 'test-password', 81 token: 'npm_test-token', 82 }) 83 await login.exec([]) 84 t.same(npm.config.get('//registry.npmjs.org/:_authToken'), 'npm_test-token') 85 t.same(npm.config.get('@npmcli:registry'), 'https://registry.npmjs.org/') 86 t.same(rc(), { 87 '//registry.npmjs.org/:_authToken': 'npm_test-token', 88 '@npmcli:registry': 'https://registry.npmjs.org/', 89 }, 'should only have token and scope:registry') 90 }) 91 92 t.test('scoped login scoped registry', async t => { 93 const { npm, registry, login, rc } = await mockLogin(t, { 94 stdin: ['test-user', 'test-password'], 95 registry: 'https://diff-registry.npmjs.org', 96 config: { 97 'auth-type': 'legacy', 98 scope: '@npmcli', 99 }, 100 homeDir: { 101 '.npmrc': '@npmcli:registry=https://diff-registry.npmjs.org', 102 }, 103 }) 104 registry.couchlogin({ 105 username: 'test-user', 106 password: 'test-password', 107 token: 'npm_test-token', 108 }) 109 await login.exec([]) 110 t.same(npm.config.get('//diff-registry.npmjs.org/:_authToken'), 'npm_test-token') 111 t.same(npm.config.get('@npmcli:registry'), 'https://diff-registry.npmjs.org') 112 t.same(rc(), { 113 '@npmcli:registry': 'https://diff-registry.npmjs.org', 114 '//diff-registry.npmjs.org/:_authToken': 'npm_test-token', 115 }, 'should only have token and scope:registry') 116 }) 117 t.end() 118}) 119 120t.test('web', t => { 121 t.test('basic login', async t => { 122 const { npm, registry, login, rc } = await mockLogin(t, { 123 config: { 'auth-type': 'web' }, 124 }) 125 registry.weblogin({ token: 'npm_test-token' }) 126 await login.exec([]) 127 t.same(npm.config.get('//registry.npmjs.org/:_authToken'), 'npm_test-token') 128 t.same(rc(), { 129 '//registry.npmjs.org/:_authToken': 'npm_test-token', 130 }) 131 }) 132 t.test('server error', async t => { 133 const { registry, login } = await mockLogin(t, { 134 config: { 'auth-type': 'web' }, 135 }) 136 registry.nock.post(registry.fullPath('/-/v1/login')) 137 .reply(503, {}) 138 await t.rejects( 139 login.exec([]), 140 { message: /503/ } 141 ) 142 }) 143 t.test('fallback', async t => { 144 const { npm, registry, login } = await mockLogin(t, { 145 stdin: ['test-user', 'test-password'], 146 config: { 'auth-type': 'web' }, 147 }) 148 registry.nock.post(registry.fullPath('/-/v1/login')) 149 .reply(404, {}) 150 registry.couchlogin({ 151 username: 'test-user', 152 password: 'test-password', 153 token: 'npm_test-token', 154 }) 155 await login.exec([]) 156 t.same(npm.config.get('//registry.npmjs.org/:_authToken'), 'npm_test-token') 157 }) 158 t.end() 159}) 160