• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const t = require('tap')
2const fs = require('fs')
3const { resolve } = require('path')
4const { load: loadMockNpm } = require('../../fixtures/mock-npm')
5
6// Attempt to parse json values in snapshots before
7// stringifying to remove escaped values like \\"
8// This also doesn't reorder the keys of the object
9// like tap does by default which is nice in this case
10t.formatSnapshot = obj =>
11  JSON.stringify(
12    obj,
13    (k, v) => {
14      try {
15        return JSON.parse(v)
16      } catch {
17        // leave invalid JSON as a string
18      }
19      return v
20    },
21    2
22  )
23
24// Run shrinkwrap against a specified prefixDir with config items
25// and make some assertions that should always be true. Sets
26// the results on t.context for use in child tests
27const shrinkwrap = async (t, prefixDir = {}, config = {}, mocks = {}) => {
28  const { npm, logs } = await loadMockNpm(t, {
29    mocks,
30    config,
31    prefixDir,
32  })
33
34  await npm.exec('shrinkwrap', [])
35
36  const newFile = resolve(npm.prefix, 'npm-shrinkwrap.json')
37  const oldFile = resolve(npm.prefix, 'package-lock.json')
38
39  t.notOk(fs.existsSync(oldFile), 'package-lock is always deleted')
40  t.same(logs.warn, [], 'no warnings')
41  t.teardown(() => delete t.context)
42  t.context = {
43    localPrefix: prefixDir,
44    config,
45    shrinkwrap: JSON.parse(fs.readFileSync(newFile)),
46    logs: logs.notice.map(([, m]) => m),
47  }
48}
49
50// Run shrinkwrap against all combinations of existing and config
51// lockfile versions
52const shrinkwrapMatrix = async (t, file, assertions) => {
53  const ancient = JSON.stringify({ lockfileVersion: 1 })
54  const existing = JSON.stringify({ lockfileVersion: 2 })
55  const upgrade = { 'lockfile-version': 3 }
56  const downgrade = { 'lockfile-version': 1 }
57
58  let ancientDir = {}
59  let existingDir = null
60  if (file === 'package-lock') {
61    ancientDir = { 'package-lock.json': ancient }
62    existingDir = { 'package-lock.json': existing }
63  } else if (file === 'npm-shrinkwrap') {
64    ancientDir = { 'npm-shrinkwrap.json': ancient }
65    existingDir = { 'npm-shrinkwrap.json': existing }
66  } else if (file === 'hidden-lockfile') {
67    ancientDir = { node_modules: { '.package-lock.json': ancient } }
68    existingDir = { node_modules: { '.package-lock.json': existing } }
69  }
70
71  await t.test('ancient', async t => {
72    await shrinkwrap(t, ancientDir)
73    t.match(t.context, assertions.ancient)
74    t.matchSnapshot(t.context)
75  })
76  await t.test('ancient upgrade', async t => {
77    await shrinkwrap(t, ancientDir, upgrade)
78    t.match(t.context, assertions.ancientUpgrade)
79    t.matchSnapshot(t.context)
80  })
81
82  if (existingDir) {
83    await t.test('existing', async t => {
84      await shrinkwrap(t, existingDir)
85      t.match(t.context, assertions.existing)
86      t.matchSnapshot(t.context)
87    })
88    await t.test('existing upgrade', async t => {
89      await shrinkwrap(t, existingDir, upgrade)
90      t.match(t.context, assertions.existingUpgrade)
91      t.matchSnapshot(t.context)
92    })
93    await t.test('existing downgrade', async t => {
94      await shrinkwrap(t, existingDir, downgrade)
95      t.match(t.context, assertions.existingDowngrade)
96      t.matchSnapshot(t.context)
97    })
98  }
99}
100
101const NOTICES = {
102  CREATED: (v = '') => [`created a lockfile as npm-shrinkwrap.json${v && ` with version ${v}`}`],
103  RENAMED: (v = '') => [
104    `package-lock.json has been renamed to npm-shrinkwrap.json${
105      v && ` and updated to version ${v}`
106    }`,
107  ],
108  UPDATED: (v = '') => [`npm-shrinkwrap.json updated to version ${v}`],
109  SAME: () => [`npm-shrinkwrap.json up to date`],
110}
111
112t.test('with nothing', t =>
113  shrinkwrapMatrix(t, null, {
114    ancient: {
115      shrinkwrap: { lockfileVersion: 3 },
116      logs: NOTICES.CREATED(3),
117    },
118    ancientUpgrade: {
119      shrinkwrap: { lockfileVersion: 3 },
120      logs: NOTICES.CREATED(3),
121    },
122  })
123)
124
125t.test('with package-lock.json', t =>
126  shrinkwrapMatrix(t, 'package-lock', {
127    ancient: {
128      shrinkwrap: { lockfileVersion: 3 },
129      logs: NOTICES.RENAMED(3),
130    },
131    ancientUpgrade: {
132      shrinkwrap: { lockfileVersion: 3 },
133      logs: NOTICES.RENAMED(3),
134    },
135    existing: {
136      shrinkwrap: { lockfileVersion: 2 },
137      logs: NOTICES.RENAMED(),
138    },
139    existingUpgrade: {
140      shrinkwrap: { lockfileVersion: 3 },
141      logs: NOTICES.RENAMED(3),
142    },
143    existingDowngrade: {
144      shrinkwrap: { lockfileVersion: 1 },
145      logs: NOTICES.RENAMED(1),
146    },
147  })
148)
149
150t.test('with npm-shrinkwrap.json', t =>
151  shrinkwrapMatrix(t, 'npm-shrinkwrap', {
152    ancient: {
153      shrinkwrap: { lockfileVersion: 3 },
154      logs: NOTICES.UPDATED(3),
155    },
156    ancientUpgrade: {
157      shrinkwrap: { lockfileVersion: 3 },
158      logs: NOTICES.UPDATED(3),
159    },
160    existing: {
161      shrinkwrap: { lockfileVersion: 2 },
162      logs: NOTICES.SAME(),
163    },
164    existingUpgrade: {
165      shrinkwrap: { lockfileVersion: 3 },
166      logs: NOTICES.UPDATED(3),
167    },
168    existingDowngrade: {
169      shrinkwrap: { lockfileVersion: 1 },
170      logs: NOTICES.UPDATED(1),
171    },
172  })
173)
174
175t.test('with hidden lockfile', t =>
176  shrinkwrapMatrix(t, 'hidden-lockfile', {
177    ancient: {
178      shrinkwrap: { lockfileVersion: 1 },
179      logs: NOTICES.CREATED(),
180    },
181    ancientUpgrade: {
182      shrinkwrap: { lockfileVersion: 3 },
183      logs: NOTICES.CREATED(),
184    },
185    existing: {
186      shrinkwrap: { lockfileVersion: 2 },
187      logs: NOTICES.CREATED(),
188    },
189    existingUpgrade: {
190      shrinkwrap: { lockfileVersion: 3 },
191      logs: NOTICES.CREATED(3),
192    },
193    existingDowngrade: {
194      shrinkwrap: { lockfileVersion: 1 },
195      logs: NOTICES.CREATED(1),
196    },
197  })
198)
199
200t.test('throws in global mode', async t => {
201  t.rejects(
202    shrinkwrap(
203      t,
204      {},
205      {
206        global: true,
207      }
208    ),
209    {
210      message: '`npm shrinkwrap` does not work for global packages',
211      code: 'ESHRINKWRAPGLOBAL',
212    }
213  )
214})
215