• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const { resolve } = require('path')
2const { readFileSync } = require('fs')
3const t = require('tap')
4const _mockNpm = require('../../fixtures/mock-npm')
5const { cleanCwd } = require('../../fixtures/clean-snapshot')
6
7t.cleanSnapshot = (str) => cleanCwd(str)
8
9const mockNpm = async (t, { ...opts } = {}) => {
10  const res = await _mockNpm(t, opts)
11
12  const readPackageJson = (dir = '') =>
13    JSON.parse(readFileSync(resolve(res.prefix, dir, 'package.json'), 'utf8'))
14
15  return {
16    ...res,
17    pkg: (...args) => res.npm.exec('pkg', args),
18    readPackageJson,
19    OUTPUT: () => res.joinedOutput(),
20  }
21}
22
23t.test('no args', async t => {
24  const { pkg } = await mockNpm(t)
25
26  await t.rejects(
27    pkg(),
28    { code: 'EUSAGE' },
29    'should throw usage error'
30  )
31})
32
33t.test('no global mode', async t => {
34  const { pkg } = await mockNpm(t, {
35    config: { global: true },
36  })
37
38  await t.rejects(
39    pkg('get', 'foo'),
40    { code: 'EPKGGLOBAL' },
41    'should throw no global mode error'
42  )
43})
44
45t.test('get no args', async t => {
46  const { pkg, OUTPUT } = await mockNpm(t, {
47    prefixDir: {
48      'package.json': JSON.stringify({
49        name: 'foo',
50        version: '1.1.1',
51      }),
52    },
53  })
54
55  await pkg('get')
56
57  t.strictSame(
58    JSON.parse(OUTPUT()),
59    {
60      name: 'foo',
61      version: '1.1.1',
62    },
63    'should print package.json content'
64  )
65})
66
67t.test('get single arg', async t => {
68  const { pkg, OUTPUT } = await mockNpm(t, {
69    prefixDir: {
70      'package.json': JSON.stringify({
71        name: 'foo',
72        version: '1.1.1',
73      }),
74    },
75  })
76
77  await pkg('get', 'version')
78
79  t.strictSame(
80    JSON.parse(OUTPUT()),
81    '1.1.1',
82    'should print retrieved package.json field'
83  )
84})
85
86t.test('get nested arg', async t => {
87  const { pkg, OUTPUT } = await mockNpm(t, {
88    prefixDir: {
89      'package.json': JSON.stringify({
90        name: 'foo',
91        version: '1.1.1',
92        scripts: {
93          test: 'node test.js',
94        },
95      }),
96    },
97  })
98
99  await pkg('get', 'scripts.test')
100
101  t.strictSame(
102    JSON.parse(OUTPUT()),
103    'node test.js',
104    'should print retrieved nested field'
105  )
106})
107
108t.test('get array field', async t => {
109  const files = [
110    'index.js',
111    'cli.js',
112  ]
113  const { pkg, OUTPUT } = await mockNpm(t, {
114    prefixDir: {
115      'package.json': JSON.stringify({
116        name: 'foo',
117        version: '1.1.1',
118        files,
119      }),
120    },
121  })
122
123  await pkg('get', 'files')
124
125  t.strictSame(
126    JSON.parse(OUTPUT()),
127    files,
128    'should print retrieved array field'
129  )
130})
131
132t.test('get array item', async t => {
133  const files = [
134    'index.js',
135    'cli.js',
136  ]
137  const { pkg, OUTPUT } = await mockNpm(t, {
138    prefixDir: {
139      'package.json': JSON.stringify({
140        name: 'foo',
141        version: '1.1.1',
142        files,
143      }),
144    },
145  })
146
147  await pkg('get', 'files[0]')
148
149  t.strictSame(
150    JSON.parse(OUTPUT()),
151    'index.js',
152    'should print retrieved array field'
153  )
154})
155
156t.test('get array nested items notation', async t => {
157  const contributors = [
158    {
159      name: 'Ruy',
160      url: 'http://example.com/ruy',
161    },
162    {
163      name: 'Gar',
164      url: 'http://example.com/gar',
165    },
166  ]
167  const { pkg, OUTPUT } = await mockNpm(t, {
168    prefixDir: {
169      'package.json': JSON.stringify({
170        name: 'foo',
171        version: '1.1.1',
172        contributors,
173      }),
174    },
175  })
176
177  await pkg('get', 'contributors.name')
178  t.strictSame(
179    JSON.parse(OUTPUT()),
180    {
181      'contributors[0].name': 'Ruy',
182      'contributors[1].name': 'Gar',
183    },
184    'should print json result containing matching results'
185  )
186})
187
188t.test('set no args', async t => {
189  const { pkg } = await mockNpm(t, {
190    prefixDir: {
191      'package.json': JSON.stringify({ name: 'foo' }),
192    },
193  })
194  await t.rejects(
195    pkg('set'),
196    { code: 'EUSAGE' },
197    'should throw an error if no args'
198  )
199})
200
201t.test('set missing value', async t => {
202  const { pkg } = await mockNpm(t, {
203    prefixDir: {
204      'package.json': JSON.stringify({ name: 'foo' }),
205    },
206  })
207  await t.rejects(
208    pkg('set', 'key='),
209    { code: 'EUSAGE' },
210    'should throw an error if missing value'
211  )
212})
213
214t.test('set missing key', async t => {
215  const { pkg } = await mockNpm(t, {
216    prefixDir: {
217      'package.json': JSON.stringify({ name: 'foo' }),
218    },
219  })
220  await t.rejects(
221    pkg('set', '=value'),
222    { code: 'EUSAGE' },
223    'should throw an error if missing key'
224  )
225})
226
227t.test('set single field', async t => {
228  const json = {
229    name: 'foo',
230    version: '1.1.1',
231  }
232  const { pkg, readPackageJson } = await mockNpm(t, {
233    prefixDir: {
234      'package.json': JSON.stringify(json),
235    },
236  })
237
238  await pkg('set', 'description=Awesome stuff')
239  t.strictSame(
240    readPackageJson(),
241    {
242      ...json,
243      description: 'Awesome stuff',
244    },
245    'should add single field to package.json'
246  )
247})
248
249t.test('push to array syntax', async t => {
250  const json = {
251    name: 'foo',
252    version: '1.1.1',
253    keywords: [
254      'foo',
255    ],
256  }
257  const { pkg, readPackageJson } = await mockNpm(t, {
258    prefixDir: {
259      'package.json': JSON.stringify(json),
260    },
261  })
262
263  await pkg('set', 'keywords[]=bar', 'keywords[]=baz')
264  t.strictSame(
265    readPackageJson(),
266    {
267      ...json,
268      keywords: [
269        'foo',
270        'bar',
271        'baz',
272      ],
273    },
274    'should append to arrays using empty bracket syntax'
275  )
276})
277
278t.test('set multiple fields', async t => {
279  const json = {
280    name: 'foo',
281    version: '1.1.1',
282  }
283  const { pkg, readPackageJson } = await mockNpm(t, {
284    prefixDir: {
285      'package.json': JSON.stringify(json),
286    },
287  })
288
289  await pkg('set', 'bin.foo=foo.js', 'scripts.test=node test.js')
290  t.strictSame(
291    readPackageJson(),
292    {
293      ...json,
294      bin: {
295        foo: 'foo.js',
296      },
297      scripts: {
298        test: 'node test.js',
299      },
300    },
301    'should add single field to package.json'
302  )
303})
304
305t.test('set = separate value', async t => {
306  const json = {
307    name: 'foo',
308    version: '1.1.1',
309  }
310  const { pkg, readPackageJson } = await mockNpm(t, {
311    prefixDir: {
312      'package.json': JSON.stringify(json),
313    },
314  })
315
316  await pkg('set', 'tap[test-env][0]=LC_ALL=sk')
317  t.strictSame(
318    readPackageJson(),
319    {
320      ...json,
321      tap: {
322        'test-env': [
323          'LC_ALL=sk',
324        ],
325      },
326    },
327    'should add single field to package.json'
328  )
329})
330
331t.test('set --json', async t => {
332  const { pkg, readPackageJson } = await mockNpm(t, {
333    prefixDir: {
334      'package.json': JSON.stringify({
335        name: 'foo',
336        version: '1.1.1',
337      }),
338    },
339    config: { json: true },
340  })
341
342  await pkg('set', 'private=true')
343  t.strictSame(
344    readPackageJson(),
345    {
346      name: 'foo',
347      version: '1.1.1',
348      private: true,
349    },
350    'should add boolean field to package.json'
351  )
352
353  await pkg('set', 'tap.timeout=60')
354  t.strictSame(
355    readPackageJson(),
356    {
357      name: 'foo',
358      version: '1.1.1',
359      private: true,
360      tap: {
361        timeout: 60,
362      },
363    },
364    'should add number field to package.json'
365  )
366
367  await pkg('set', 'foo={ "bar": { "baz": "BAZ" } }')
368  t.strictSame(
369    readPackageJson(),
370    {
371      name: 'foo',
372      version: '1.1.1',
373      private: true,
374      tap: {
375        timeout: 60,
376      },
377      foo: {
378        bar: {
379          baz: 'BAZ',
380        },
381      },
382    },
383    'should add object field to package.json'
384  )
385
386  await pkg('set', 'workspaces=["packages/*"]')
387  t.strictSame(
388    readPackageJson(),
389    {
390      name: 'foo',
391      version: '1.1.1',
392      private: true,
393      workspaces: [
394        'packages/*',
395      ],
396      tap: {
397        timeout: 60,
398      },
399      foo: {
400        bar: {
401          baz: 'BAZ',
402        },
403      },
404    },
405    'should add object field to package.json'
406  )
407
408  await pkg('set', 'description="awesome"')
409  t.strictSame(
410    readPackageJson(),
411    {
412      name: 'foo',
413      version: '1.1.1',
414      description: 'awesome',
415      private: true,
416      workspaces: [
417        'packages/*',
418      ],
419      tap: {
420        timeout: 60,
421      },
422      foo: {
423        bar: {
424          baz: 'BAZ',
425        },
426      },
427    },
428    'should add object field to package.json'
429  )
430})
431
432t.test('delete no args', async t => {
433  const { pkg } = await mockNpm(t, {
434    prefixDir: {
435      'package.json': JSON.stringify({ name: 'foo' }),
436    },
437  })
438  await t.rejects(
439    pkg('delete'),
440    { code: 'EUSAGE' },
441    'should throw an error if deleting no args'
442  )
443})
444
445t.test('delete invalid key', async t => {
446  const { pkg } = await mockNpm(t, {
447    prefixDir: {
448      'package.json': JSON.stringify({ name: 'foo' }),
449    },
450  })
451  await t.rejects(
452    pkg('delete', ''),
453    { code: 'EUSAGE' },
454    'should throw an error if deleting invalid args'
455  )
456})
457
458t.test('delete single field', async t => {
459  const { pkg, readPackageJson } = await mockNpm(t, {
460    prefixDir: {
461      'package.json': JSON.stringify({
462        name: 'foo',
463        version: '1.0.0',
464      }),
465    },
466  })
467  await pkg('delete', 'version')
468  t.strictSame(
469    readPackageJson(),
470    {
471      name: 'foo',
472    },
473    'should delete single field from package.json'
474  )
475})
476
477t.test('delete multiple field', async t => {
478  const { pkg, readPackageJson } = await mockNpm(t, {
479    prefixDir: {
480      'package.json': JSON.stringify({
481        name: 'foo',
482        version: '1.0.0',
483        description: 'awesome',
484      }),
485    },
486  })
487  await pkg('delete', 'version', 'description')
488  t.strictSame(
489    readPackageJson(),
490    {
491      name: 'foo',
492    },
493    'should delete multiple fields from package.json'
494  )
495})
496
497t.test('delete nested field', async t => {
498  const { pkg, readPackageJson } = await mockNpm(t, {
499    prefixDir: {
500      'package.json': JSON.stringify({
501        name: 'foo',
502        version: '1.0.0',
503        info: {
504          foo: {
505            bar: [
506              {
507                baz: 'deleteme',
508              },
509            ],
510          },
511        },
512      }),
513    },
514  })
515  await pkg('delete', 'info.foo.bar[0].baz')
516  t.strictSame(
517    readPackageJson(),
518    {
519      name: 'foo',
520      version: '1.0.0',
521      info: {
522        foo: {
523          bar: [
524            {},
525          ],
526        },
527      },
528    },
529    'should delete nested fields from package.json'
530  )
531})
532
533t.test('workspaces', async t => {
534  const { pkg, OUTPUT, readPackageJson } = await mockNpm(t, {
535    prefixDir: {
536      'package.json': JSON.stringify({
537        name: 'root',
538        version: '1.0.0',
539        workspaces: [
540          'packages/*',
541        ],
542      }),
543      packages: {
544        a: {
545          'package.json': JSON.stringify({
546            name: 'a',
547            version: '1.0.0',
548          }),
549        },
550        b: {
551          'package.json': JSON.stringify({
552            name: 'b',
553            version: '1.2.3',
554          }),
555        },
556      },
557    },
558    config: { workspaces: true },
559  })
560
561  await pkg('get', 'name', 'version')
562
563  t.strictSame(
564    JSON.parse(OUTPUT()),
565    {
566      a: {
567        name: 'a',
568        version: '1.0.0',
569      },
570      b: {
571        name: 'b',
572        version: '1.2.3',
573      },
574    },
575    'should return expected result for configured workspaces'
576  )
577
578  await pkg('set', 'funding=http://example.com')
579
580  t.strictSame(
581    readPackageJson('packages/a'),
582    {
583      name: 'a',
584      version: '1.0.0',
585      funding: 'http://example.com',
586    },
587    'should add field to workspace a'
588  )
589
590  t.strictSame(
591    readPackageJson('packages/b'),
592    {
593      name: 'b',
594      version: '1.2.3',
595      funding: 'http://example.com',
596    },
597    'should add field to workspace b'
598  )
599
600  await pkg('delete', 'version')
601
602  t.strictSame(
603    readPackageJson('packages/a'),
604    {
605      name: 'a',
606      funding: 'http://example.com',
607    },
608    'should delete version field from workspace a'
609  )
610
611  t.strictSame(
612    readPackageJson('packages/b'),
613    {
614      name: 'b',
615      funding: 'http://example.com',
616    },
617    'should delete version field from workspace b'
618  )
619})
620
621t.test('fix', async t => {
622  const { pkg, readPackageJson } = await mockNpm(t, {
623    prefixDir: {
624      'package.json': JSON.stringify({
625        name: 'foo ',
626        version: 'v1.1.1',
627      }),
628    },
629  })
630
631  await pkg('fix')
632  t.strictSame(
633    readPackageJson(),
634    { name: 'foo', version: '1.1.1' },
635    'fixes package.json issues'
636  )
637})
638