Skip to content

Commit 88e4241

Browse files
ruyadornoisaacs
authored andcommitted
test: add lib/logout.js unit tests
- Fixed config.delete to remove from user config on logout - Tidy up lib/logout.js code - Added test/lib/logout.js PR-URL: #1698 Credit: @ruyadorno Close: #1698 Reviewed-by: @isaacs
1 parent fcf5fb0 commit 88e4241

2 files changed

Lines changed: 286 additions & 11 deletions

File tree

lib/logout.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
1+
'use strict'
2+
13
const eu = encodeURIComponent
2-
const getAuth = require('npm-registry-fetch/auth.js')
34
const log = require('npmlog')
4-
const npm = require('./npm.js')
5+
const getAuth = require('npm-registry-fetch/auth.js')
56
const npmFetch = require('npm-registry-fetch')
7+
const npm = require('./npm.js')
68
const usageUtil = require('./utils/usage.js')
7-
8-
const usage = usageUtil('logout', 'npm logout [--registry=<url>] [--scope=<@scope>]')
99
const completion = require('./utils/completion/none.js')
1010

11+
const usage = usageUtil(
12+
'logout',
13+
'npm logout [--registry=<url>] [--scope=<@scope>]'
14+
)
15+
1116
const cmd = (args, cb) => logout(args).then(() => cb()).catch(cb)
1217

13-
const logout = async args => {
14-
const reg = npmFetch.pickRegistry('foo', npm.flatOptions)
18+
const logout = async (args) => {
19+
const { registry, scope } = npm.flatOptions
20+
const regRef = `${scope}:registry`
21+
let reg = registry
22+
23+
if (scope) {
24+
const scopedRef = npm.flatOptions[regRef]
25+
reg = scopedRef || reg
26+
}
27+
1528
const auth = getAuth(reg, npm.flatOptions)
29+
1630
if (auth.token) {
1731
log.verbose('logout', `clearing token for ${reg}`)
1832
await npmFetch(`/-/user/token/${eu(auth.token)}`, {
@@ -23,16 +37,16 @@ const logout = async args => {
2337
} else if (auth.username || auth.password) {
2438
log.verbose('logout', `clearing user credentials for ${reg}`)
2539
} else {
26-
throw Object.assign(new Error(`not logged in to ${reg}, so can't log out!`), {
27-
code: 'ENEEDAUTH'
28-
})
40+
const msg = `not logged in to ${reg}, so can't log out!`
41+
throw Object.assign(new Error(msg), { code: 'ENEEDAUTH' })
2942
}
3043

31-
const scope = npm.config.get('scope')
3244
if (scope) {
33-
npm.config.del(`${scope}:registry`)
45+
npm.config.delete(regRef, 'user')
3446
}
47+
3548
npm.config.clearCredentialsByURI(reg)
49+
3650
await npm.config.save('user')
3751
}
3852

test/lib/logout.js

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
const requireInject = require('require-inject')
2+
const { test } = require('tap')
3+
4+
const _flatOptions = {
5+
registry: 'https://registry.npmjs.org/',
6+
scope: ''
7+
}
8+
9+
const config = {}
10+
const npmlog = {}
11+
12+
let result = null
13+
const npmFetch = (url, opts) => {
14+
result = { url, opts }
15+
}
16+
17+
const mocks = {
18+
npmlog,
19+
'npm-registry-fetch': npmFetch,
20+
'../../lib/npm.js': {
21+
flatOptions: _flatOptions,
22+
config
23+
}
24+
}
25+
26+
const logout = requireInject('../../lib/logout.js', mocks)
27+
28+
test('token logout', async (t) => {
29+
t.plan(6)
30+
31+
_flatOptions.token = '@foo/'
32+
33+
npmlog.verbose = (title, msg) => {
34+
t.equal(title, 'logout', 'should have correcct log prefix')
35+
t.equal(
36+
msg,
37+
'clearing token for https://registry.npmjs.org/',
38+
'should log message with correct registry'
39+
)
40+
}
41+
42+
config.clearCredentialsByURI = (registry) => {
43+
t.equal(
44+
registry,
45+
'https://registry.npmjs.org/',
46+
'should clear credentials from the expected registry'
47+
)
48+
}
49+
50+
config.save = (type) => {
51+
t.equal(type, 'user', 'should save to user config')
52+
}
53+
54+
await new Promise((res, rej) => {
55+
logout([], (err) => {
56+
t.ifError(err, 'should not error out')
57+
58+
t.deepEqual(
59+
result,
60+
{
61+
url: '/-/user/token/%40foo%2F',
62+
opts: {
63+
registry: 'https://registry.npmjs.org/',
64+
scope: '',
65+
token: '@foo/',
66+
method: 'DELETE',
67+
ignoreBody: true
68+
}
69+
},
70+
'should call npm-registry-fetch with expected values'
71+
)
72+
73+
delete _flatOptions.token
74+
result = null
75+
mocks['npm-registry-fetch'] = null
76+
config.clearCredentialsByURI = null
77+
config.delete = null
78+
config.save = null
79+
npmlog.verbose = null
80+
81+
res()
82+
})
83+
})
84+
})
85+
86+
test('token scoped logout', async (t) => {
87+
t.plan(8)
88+
89+
_flatOptions.token = '@foo/'
90+
_flatOptions.scope = '@myscope'
91+
_flatOptions['@myscope:registry'] = 'https://diff-registry.npmjs.com/'
92+
93+
npmlog.verbose = (title, msg) => {
94+
t.equal(title, 'logout', 'should have correcct log prefix')
95+
t.equal(
96+
msg,
97+
'clearing token for https://diff-registry.npmjs.com/',
98+
'should log message with correct registry'
99+
)
100+
}
101+
102+
config.clearCredentialsByURI = (registry) => {
103+
t.equal(
104+
registry,
105+
'https://diff-registry.npmjs.com/',
106+
'should clear credentials from the expected registry'
107+
)
108+
}
109+
110+
config.delete = (ref, type) => {
111+
t.equal(
112+
ref,
113+
'@myscope:registry',
114+
'should delete scoped registyr from config'
115+
)
116+
t.equal(type, 'user', 'should delete from user config')
117+
}
118+
119+
config.save = (type) => {
120+
t.equal(type, 'user', 'should save to user config')
121+
}
122+
123+
await new Promise((res, rej) => {
124+
logout([], (err) => {
125+
t.ifError(err, 'should not error out')
126+
127+
t.deepEqual(
128+
result,
129+
{
130+
url: '/-/user/token/%40foo%2F',
131+
opts: {
132+
registry: 'https://registry.npmjs.org/',
133+
'@myscope:registry': 'https://diff-registry.npmjs.com/',
134+
scope: '@myscope',
135+
token: '@foo/',
136+
method: 'DELETE',
137+
ignoreBody: true
138+
}
139+
},
140+
'should call npm-registry-fetch with expected values'
141+
)
142+
143+
_flatOptions.scope = ''
144+
delete _flatOptions['@myscope:registry']
145+
delete _flatOptions.token
146+
result = null
147+
mocks['npm-registry-fetch'] = null
148+
config.clearCredentialsByURI = null
149+
config.delete = null
150+
config.save = null
151+
npmlog.verbose = null
152+
153+
res()
154+
})
155+
})
156+
})
157+
158+
test('user/pass logout', async (t) => {
159+
t.plan(3)
160+
161+
_flatOptions.username = 'foo'
162+
_flatOptions.password = 'bar'
163+
164+
npmlog.verbose = (title, msg) => {
165+
t.equal(title, 'logout', 'should have correcct log prefix')
166+
t.equal(
167+
msg,
168+
'clearing user credentials for https://registry.npmjs.org/',
169+
'should log message with correct registry'
170+
)
171+
}
172+
173+
config.clearCredentialsByURI = () => null
174+
config.save = () => null
175+
176+
await new Promise((res, rej) => {
177+
logout([], (err) => {
178+
t.ifError(err, 'should not error out')
179+
180+
delete _flatOptions.username
181+
delete _flatOptions.password
182+
config.clearCredentialsByURI = null
183+
config.save = null
184+
npmlog.verbose = null
185+
186+
res()
187+
})
188+
})
189+
})
190+
191+
test('missing credentials', (t) => {
192+
logout([], (err) => {
193+
t.match(
194+
err.message,
195+
/not logged in to https:\/\/registry.npmjs.org\/, so can't log out!/,
196+
'should throw with expected message'
197+
)
198+
t.equal(err.code, 'ENEEDAUTH', 'should throw with expected error code')
199+
t.end()
200+
})
201+
})
202+
203+
test('ignore invalid scoped registry config', async (t) => {
204+
t.plan(5)
205+
206+
_flatOptions.token = '@foo/'
207+
_flatOptions.scope = '@myscope'
208+
_flatOptions['@myscope:registry'] = ''
209+
210+
npmlog.verbose = (title, msg) => {
211+
t.equal(title, 'logout', 'should have correcct log prefix')
212+
t.equal(
213+
msg,
214+
'clearing token for https://registry.npmjs.org/',
215+
'should log message with correct registry'
216+
)
217+
}
218+
219+
config.clearCredentialsByURI = (registry) => {
220+
t.equal(
221+
registry,
222+
'https://registry.npmjs.org/',
223+
'should clear credentials from the expected registry'
224+
)
225+
}
226+
227+
config.delete = () => null
228+
config.save = () => null
229+
230+
await new Promise((res, rej) => {
231+
logout([], (err) => {
232+
t.ifError(err, 'should not error out')
233+
234+
t.deepEqual(
235+
result,
236+
{
237+
url: '/-/user/token/%40foo%2F',
238+
opts: {
239+
registry: 'https://registry.npmjs.org/',
240+
scope: '@myscope',
241+
'@myscope:registry': '',
242+
token: '@foo/',
243+
method: 'DELETE',
244+
ignoreBody: true
245+
}
246+
},
247+
'should call npm-registry-fetch with expected values'
248+
)
249+
250+
delete _flatOptions.token
251+
result = null
252+
mocks['npm-registry-fetch'] = null
253+
config.clearCredentialsByURI = null
254+
config.delete = null
255+
config.save = null
256+
npmlog.verbose = null
257+
258+
res()
259+
})
260+
})
261+
})

0 commit comments

Comments
 (0)