1const t = require('tap')
2const fs = require('fs')
3const { resolve } = require('path')
4const _mockNpm = require('../../fixtures/mock-npm')
5
6const mockNpm = async (t, opts = {}) => {
7  const res = await _mockNpm(t, {
8    ...opts,
9    mocks: {
10      ...opts.mocks,
11      '{LIB}/utils/reify-finish.js': async () => {},
12    },
13  })
14
15  return {
16    ...res,
17    uninstall: (args) => res.npm.exec('uninstall', args),
18  }
19}
20
21t.test('remove single installed lib', async t => {
22  const { uninstall, prefix } = await mockNpm(t, {
23    prefixDir: {
24      'package.json': JSON.stringify({
25        name: 'test-rm-single-lib',
26        version: '1.0.0',
27        dependencies: {
28          a: '*',
29          b: '*',
30        },
31      }),
32      node_modules: {
33        a: {
34          'package.json': JSON.stringify({
35            name: 'a',
36            version: '1.0.0',
37          }),
38        },
39        b: {
40          'package.json': JSON.stringify({
41            name: 'b',
42            version: '1.0.0',
43          }),
44        },
45      },
46      'package-lock.json': JSON.stringify({
47        name: 'test-rm-single-lib',
48        version: '1.0.0',
49        lockfileVersion: 2,
50        requires: true,
51        packages: {
52          '': {
53            name: 'test-rm-single-lib',
54            version: '1.0.0',
55            dependencies: {
56              a: '*',
57            },
58          },
59          'node_modules/a': {
60            version: '1.0.0',
61          },
62          'node_modules/b': {
63            version: '1.0.0',
64          },
65        },
66        dependencies: {
67          a: {
68            version: '1.0.0',
69          },
70          b: {
71            version: '1.0.0',
72          },
73        },
74      }),
75    },
76  })
77
78  const b = resolve(prefix, 'node_modules/b')
79  t.ok(fs.statSync(b))
80
81  await uninstall(['b'])
82
83  t.throws(() => fs.statSync(b), 'should have removed package from npm')
84})
85
86t.test('remove multiple installed libs', async t => {
87  const { uninstall, prefix } = await mockNpm(t, {
88    prefixDir: {
89      node_modules: {
90        a: {
91          'package.json': JSON.stringify({
92            name: 'a',
93            version: '1.0.0',
94          }),
95        },
96        b: {
97          'package.json': JSON.stringify({
98            name: 'b',
99            version: '1.0.0',
100          }),
101        },
102      },
103      'package-lock.json': JSON.stringify({
104        name: 'test-rm-single-lib',
105        version: '1.0.0',
106        lockfileVersion: 2,
107        requires: true,
108        packages: {
109          '': {
110            name: 'test-rm-single-lib',
111            version: '1.0.0',
112            dependencies: {
113              a: '*',
114            },
115          },
116          'node_modules/a': {
117            version: '1.0.0',
118          },
119          'node_modules/b': {
120            version: '1.0.0',
121          },
122        },
123        dependencies: {
124          a: {
125            version: '1.0.0',
126          },
127          b: {
128            version: '1.0.0',
129          },
130        },
131      }),
132    },
133  })
134
135  const a = resolve(prefix, 'node_modules/a')
136  const b = resolve(prefix, 'node_modules/b')
137  t.ok(fs.statSync(a))
138  t.ok(fs.statSync(b))
139
140  await uninstall(['b'])
141
142  t.throws(() => fs.statSync(a), 'should have removed a package from nm')
143  t.throws(() => fs.statSync(b), 'should have removed b package from nm')
144})
145
146t.test('no args local', async t => {
147  const { uninstall } = await mockNpm(t)
148
149  await t.rejects(
150    uninstall([]),
151    /Must provide a package name to remove/,
152    'should throw package name required error'
153  )
154})
155
156t.test('no args global', async t => {
157  const { uninstall, npm } = await mockNpm(t, {
158    prefixDir: {
159      'package.json': JSON.stringify({
160        name: 'a',
161        version: '1.0.0',
162      }),
163    },
164    globalPrefixDir: {
165      node_modules: {
166        a: t.fixture('symlink', '../../prefix'),
167      },
168    },
169    config: { global: true },
170  })
171
172  const a = resolve(npm.globalDir, 'a')
173  t.ok(fs.statSync(a))
174
175  await uninstall([])
176
177  t.throws(() => fs.statSync(a), 'should have removed global nm symlink')
178})
179
180t.test('no args global but no package.json', async t => {
181  const { uninstall } = await mockNpm(t, {
182    config: { global: true },
183  })
184
185  await t.rejects(
186    uninstall([]),
187    /npm uninstall/
188  )
189})
190
191t.test('non ENOENT error reading from localPrefix package.json', async t => {
192  const { uninstall } = await mockNpm(t, {
193    config: { global: true },
194    prefixDir: { 'package.json': 'not[json]' },
195  })
196
197  await t.rejects(
198    uninstall([]),
199    { code: 'EJSONPARSE' },
200    'should throw non ENOENT error'
201  )
202})
203