Skip to content

Commit fb1d075

Browse files
committed
test: add lib/uninstall.js tests
Fixes: npm/statusboard#179 PR-URL: #2269 Credit: @ruyadorno Close: #2269 Reviewed-by: @darcyclarke
1 parent 6b15751 commit fb1d075

File tree

2 files changed

+275
-15
lines changed

2 files changed

+275
-15
lines changed

lib/uninstall.js

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
// remove a package.
1+
'use strict'
22

3+
const { resolve } = require('path')
34
const Arborist = require('@npmcli/arborist')
4-
const npm = require('./npm.js')
55
const rpj = require('read-package-json-fast')
6-
const { resolve } = require('path')
6+
7+
const npm = require('./npm.js')
78
const usageUtil = require('./utils/usage.js')
89
const reifyFinish = require('./utils/reify-finish.js')
10+
const completion = require('./utils/completion/installed-shallow.js')
11+
12+
const usage = usageUtil(
13+
'uninstall',
14+
'npm uninstall [<@scope>/]<pkg>[@<version>]... [--save-prod|--save-dev|--save-optional] [--no-save]'
15+
)
916

1017
const cmd = (args, cb) => rm(args).then(() => cb()).catch(cb)
1118

@@ -16,12 +23,19 @@ const rm = async args => {
1623

1724
if (!args.length) {
1825
if (!global)
19-
throw new Error('must provide a package name to remove')
26+
throw new Error('Must provide a package name to remove')
2027
else {
21-
const pkg = await rpj(resolve(npm.localPrefix, 'package.json'))
22-
.catch(er => {
23-
throw er.code !== 'ENOENT' && er.code !== 'ENOTDIR' ? er : usage()
24-
})
28+
let pkg
29+
30+
try {
31+
pkg = await rpj(resolve(npm.localPrefix, 'package.json'))
32+
} catch (er) {
33+
if (er.code !== 'ENOENT' && er.code !== 'ENOTDIR')
34+
throw er
35+
else
36+
throw usage
37+
}
38+
2539
args.push(pkg.name)
2640
}
2741
}
@@ -35,11 +49,4 @@ const rm = async args => {
3549
await reifyFinish(arb)
3650
}
3751

38-
const usage = usageUtil(
39-
'uninstall',
40-
'npm uninstall [<@scope>/]<pkg>[@<version>]... [--save-prod|--save-dev|--save-optional] [--no-save]'
41-
)
42-
43-
const completion = require('./utils/completion/installed-shallow.js')
44-
4552
module.exports = Object.assign(cmd, { usage, completion })

test/lib/uninstall.js

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
const fs = require('fs')
2+
const { resolve } = require('path')
3+
const t = require('tap')
4+
const requireInject = require('require-inject')
5+
6+
const npm = {
7+
globalDir: '',
8+
flatOptions: {
9+
global: false,
10+
prefix: '',
11+
},
12+
localPrefix: '',
13+
}
14+
const mocks = {
15+
'../../lib/npm.js': npm,
16+
'../../lib/utils/reify-finish.js': () => Promise.resolve(),
17+
'../../lib/utils/usage.js': () => 'usage instructions',
18+
}
19+
20+
const uninstall = requireInject('../../lib/uninstall.js', mocks)
21+
22+
t.afterEach(cb => {
23+
npm.globalDir = ''
24+
npm.prefix = ''
25+
npm.flatOptions.global = false
26+
npm.flatOptions.prefix = ''
27+
cb()
28+
})
29+
30+
t.test('remove single installed lib', t => {
31+
const path = t.testdir({
32+
'package.json': JSON.stringify({
33+
name: 'test-rm-single-lib',
34+
version: '1.0.0',
35+
dependencies: {
36+
a: '*',
37+
b: '*',
38+
},
39+
}),
40+
node_modules: {
41+
a: {
42+
'package.json': JSON.stringify({
43+
name: 'a',
44+
version: '1.0.0',
45+
}),
46+
},
47+
b: {
48+
'package.json': JSON.stringify({
49+
name: 'b',
50+
version: '1.0.0',
51+
}),
52+
},
53+
},
54+
'package-lock.json': JSON.stringify({
55+
name: 'test-rm-single-lib',
56+
version: '1.0.0',
57+
lockfileVersion: 2,
58+
requires: true,
59+
packages: {
60+
'': {
61+
name: 'test-rm-single-lib',
62+
version: '1.0.0',
63+
dependencies: {
64+
a: '*',
65+
},
66+
},
67+
'node_modules/a': {
68+
version: '1.0.0',
69+
},
70+
'node_modules/b': {
71+
version: '1.0.0',
72+
},
73+
},
74+
dependencies: {
75+
a: {
76+
version: '1.0.0',
77+
},
78+
b: {
79+
version: '1.0.0',
80+
},
81+
},
82+
}),
83+
})
84+
85+
const b = resolve(path, 'node_modules/b')
86+
t.ok(() => fs.statSync(b))
87+
88+
npm.flatOptions.prefix = path
89+
90+
uninstall(['b'], err => {
91+
if (err)
92+
throw err
93+
94+
t.throws(() => fs.statSync(b), 'should have removed package from nm')
95+
t.end()
96+
})
97+
})
98+
99+
t.test('remove multiple installed libs', t => {
100+
const path = t.testdir({
101+
node_modules: {
102+
a: {
103+
'package.json': JSON.stringify({
104+
name: 'a',
105+
version: '1.0.0',
106+
}),
107+
},
108+
b: {
109+
'package.json': JSON.stringify({
110+
name: 'b',
111+
version: '1.0.0',
112+
}),
113+
},
114+
},
115+
'package-lock.json': JSON.stringify({
116+
name: 'test-rm-single-lib',
117+
version: '1.0.0',
118+
lockfileVersion: 2,
119+
requires: true,
120+
packages: {
121+
'': {
122+
name: 'test-rm-single-lib',
123+
version: '1.0.0',
124+
dependencies: {
125+
a: '*',
126+
},
127+
},
128+
'node_modules/a': {
129+
version: '1.0.0',
130+
},
131+
'node_modules/b': {
132+
version: '1.0.0',
133+
},
134+
},
135+
dependencies: {
136+
a: {
137+
version: '1.0.0',
138+
},
139+
b: {
140+
version: '1.0.0',
141+
},
142+
},
143+
}),
144+
})
145+
146+
const a = resolve(path, 'node_modules/a')
147+
const b = resolve(path, 'node_modules/b')
148+
t.ok(() => fs.statSync(a))
149+
t.ok(() => fs.statSync(b))
150+
151+
npm.flatOptions.prefix = path
152+
153+
uninstall(['b'], err => {
154+
if (err)
155+
throw err
156+
157+
t.throws(() => fs.statSync(a), 'should have removed a package from nm')
158+
t.throws(() => fs.statSync(b), 'should have removed b package from nm')
159+
t.end()
160+
})
161+
})
162+
163+
t.test('no args local', t => {
164+
const path = t.testdir()
165+
166+
npm.flatOptions.prefix = path
167+
168+
uninstall([], err => {
169+
t.match(
170+
err,
171+
/Must provide a package name to remove/,
172+
'should throw package name required error'
173+
)
174+
175+
t.end()
176+
})
177+
})
178+
179+
t.test('no args global', t => {
180+
const path = t.testdir({
181+
lib: {
182+
node_modules: {
183+
a: t.fixture('symlink', '../../projects/a'),
184+
},
185+
},
186+
projects: {
187+
a: {
188+
'package.json': JSON.stringify({
189+
name: 'a',
190+
version: '1.0.0',
191+
}),
192+
},
193+
},
194+
})
195+
196+
npm.localPrefix = resolve(path, 'projects', 'a')
197+
npm.globalDir = resolve(path, 'lib', 'node_modules')
198+
npm.flatOptions.global = true
199+
npm.flatOptions.prefix = path
200+
201+
const a = resolve(path, 'lib/node_modules/a')
202+
t.ok(() => fs.statSync(a))
203+
204+
uninstall([], err => {
205+
if (err)
206+
throw err
207+
208+
t.throws(() => fs.statSync(a), 'should have removed global nm symlink')
209+
210+
t.end()
211+
})
212+
})
213+
214+
t.test('no args global but no package.json', t => {
215+
const path = t.testdir({})
216+
217+
npm.prefix = path
218+
npm.localPrefix = path
219+
npm.flatOptions.global = true
220+
221+
uninstall([], err => {
222+
t.match(
223+
err,
224+
'usage instructions',
225+
'should throw usage instructions'
226+
)
227+
228+
t.end()
229+
})
230+
})
231+
232+
t.test('unknown error reading from localPrefix package.json', t => {
233+
const path = t.testdir({})
234+
235+
const uninstall = requireInject('../../lib/uninstall.js', {
236+
...mocks,
237+
'read-package-json-fast': () => Promise.reject(new Error('ERR')),
238+
})
239+
240+
npm.prefix = path
241+
npm.localPrefix = path
242+
npm.flatOptions.global = true
243+
244+
uninstall([], err => {
245+
t.match(
246+
err,
247+
/ERR/,
248+
'should throw unknown error'
249+
)
250+
251+
t.end()
252+
})
253+
})

0 commit comments

Comments
 (0)