xref: /third_party/node/deps/npm/test/lib/commands/repo.js (revision 1cb0ef41)
1const t = require('tap')
2const mockNpm = require('../../fixtures/mock-npm.js')
3const { sep } = require('path')
4
5const fixture = {
6  'package.json': JSON.stringify({
7    name: 'thispkg',
8    version: '1.2.3',
9    repository: 'https://example.com/thispkg.git',
10  }),
11  norepo: {
12    'package.json': JSON.stringify({
13      name: 'norepo',
14      version: '1.2.3',
15    }),
16  },
17  'repoobbj-nourl': {
18    'package.json': JSON.stringify({
19      name: 'repoobj-nourl',
20      repository: { no: 'url' },
21    }),
22  },
23  hostedgit: {
24    'package.json': JSON.stringify({
25      repository: 'git://github.com/foo/hostedgit',
26    }),
27  },
28  hostedgitat: {
29    'package.json': JSON.stringify({
30      repository: 'git@github.com:foo/hostedgitat',
31    }),
32  },
33  hostedssh: {
34    'package.json': JSON.stringify({
35      repository: 'ssh://git@github.com/foo/hostedssh',
36    }),
37  },
38  hostedgitssh: {
39    'package.json': JSON.stringify({
40      repository: 'git+ssh://git@github.com/foo/hostedgitssh',
41    }),
42  },
43  hostedgithttp: {
44    'package.json': JSON.stringify({
45      repository: 'git+http://github.com/foo/hostedgithttp',
46    }),
47  },
48  hostedgithttps: {
49    'package.json': JSON.stringify({
50      repository: 'git+https://github.com/foo/hostedgithttps',
51    }),
52  },
53  hostedgitobj: {
54    'package.json': JSON.stringify({
55      repository: { url: 'git://github.com/foo/hostedgitobj' },
56    }),
57  },
58  hostedgitatobj: {
59    'package.json': JSON.stringify({
60      repository: { url: 'git@github.com:foo/hostedgitatobj' },
61    }),
62  },
63  hostedsshobj: {
64    'package.json': JSON.stringify({
65      repository: { url: 'ssh://git@github.com/foo/hostedsshobj' },
66    }),
67  },
68  hostedgitsshobj: {
69    'package.json': JSON.stringify({
70      repository: { url: 'git+ssh://git@github.com/foo/hostedgitsshobj' },
71    }),
72  },
73  hostedgithttpobj: {
74    'package.json': JSON.stringify({
75      repository: { url: 'git+http://github.com/foo/hostedgithttpobj' },
76    }),
77  },
78  hostedgithttpsobj: {
79    'package.json': JSON.stringify({
80      repository: { url: 'git+https://github.com/foo/hostedgithttpsobj' },
81    }),
82  },
83  unhostedgit: {
84    'package.json': JSON.stringify({
85      repository: 'git://gothib.com/foo/unhostedgit',
86    }),
87  },
88  unhostedgitat: {
89    'package.json': JSON.stringify({
90      repository: 'git@gothib.com:foo/unhostedgitat',
91    }),
92  },
93  unhostedssh: {
94    'package.json': JSON.stringify({
95      repository: 'ssh://git@gothib.com/foo/unhostedssh',
96    }),
97  },
98  unhostedgitssh: {
99    'package.json': JSON.stringify({
100      repository: 'git+ssh://git@gothib.com/foo/unhostedgitssh',
101    }),
102  },
103  unhostedgithttp: {
104    'package.json': JSON.stringify({
105      repository: 'git+http://gothib.com/foo/unhostedgithttp',
106    }),
107  },
108  unhostedgithttps: {
109    'package.json': JSON.stringify({
110      repository: 'git+https://gothib.com/foo/unhostedgithttps',
111    }),
112  },
113  unhostedgitobj: {
114    'package.json': JSON.stringify({
115      repository: { url: 'git://gothib.com/foo/unhostedgitobj' },
116    }),
117  },
118  unhostedgitatobj: {
119    'package.json': JSON.stringify({
120      repository: { url: 'git@gothib.com:foo/unhostedgitatobj' },
121    }),
122  },
123  unhostedsshobj: {
124    'package.json': JSON.stringify({
125      repository: { url: 'ssh://git@gothib.com/foo/unhostedsshobj' },
126    }),
127  },
128  unhostedgitsshobj: {
129    'package.json': JSON.stringify({
130      repository: { url: 'git+ssh://git@gothib.com/foo/unhostedgitsshobj' },
131    }),
132  },
133  unhostedgithttpobj: {
134    'package.json': JSON.stringify({
135      repository: { url: 'git+http://gothib.com/foo/unhostedgithttpobj' },
136    }),
137  },
138  unhostedgithttpsobj: {
139    'package.json': JSON.stringify({
140      repository: { url: 'git+https://gothib.com/foo/unhostedgithttpsobj' },
141    }),
142  },
143  directory: {
144    'package.json': JSON.stringify({
145      repository: {
146        type: 'git',
147        url: 'git+https://github.com/foo/test-repo-with-directory.git',
148        directory: 'some/directory',
149      },
150    }),
151  },
152}
153
154const workspaceFixture = {
155  'package.json': JSON.stringify({
156    name: 'workspaces-test',
157    version: '1.2.3-test',
158    workspaces: ['workspace-a', 'workspace-b', 'workspace-c'],
159    repository: 'https://github.com/npm/workspaces-test',
160  }),
161  'workspace-a': {
162    'package.json': JSON.stringify({
163      name: 'workspace-a',
164      version: '1.2.3-a',
165      repository: 'http://repo.workspace-a/',
166    }),
167  },
168  'workspace-b': {
169    'package.json': JSON.stringify({
170      name: 'workspace-b',
171      version: '1.2.3-n',
172      repository: 'https://github.com/npm/workspace-b',
173    }),
174  },
175  'workspace-c': JSON.stringify({
176    'package.json': {
177      name: 'workspace-n',
178      version: '1.2.3-n',
179    },
180  }),
181}
182
183const loadMockNpm = async (t, prefixDir, config = {}) => {
184  // keep a tally of which urls got opened
185  const opened = {}
186
187  const mock = await mockNpm(t, {
188    command: 'repo',
189    mocks: {
190      '{LIB}/utils/open-url.js': async (_, url) => {
191        opened[url] = opened[url] || 0
192        opened[url]++
193      },
194    },
195    config,
196    prefixDir,
197  })
198
199  return {
200    ...mock,
201    opened,
202  }
203}
204
205t.test('open repo urls', async t => {
206  const { repo, opened } = await loadMockNpm(t, fixture)
207  const expect = {
208    hostedgit: 'https://github.com/foo/hostedgit',
209    hostedgitat: 'https://github.com/foo/hostedgitat',
210    hostedssh: 'https://github.com/foo/hostedssh',
211    hostedgitssh: 'https://github.com/foo/hostedgitssh',
212    hostedgithttp: 'https://github.com/foo/hostedgithttp',
213    hostedgithttps: 'https://github.com/foo/hostedgithttps',
214    hostedgitobj: 'https://github.com/foo/hostedgitobj',
215    hostedgitatobj: 'https://github.com/foo/hostedgitatobj',
216    hostedsshobj: 'https://github.com/foo/hostedsshobj',
217    hostedgitsshobj: 'https://github.com/foo/hostedgitsshobj',
218    hostedgithttpobj: 'https://github.com/foo/hostedgithttpobj',
219    hostedgithttpsobj: 'https://github.com/foo/hostedgithttpsobj',
220    unhostedgit: 'https://gothib.com/foo/unhostedgit',
221    unhostedssh: 'https://gothib.com/foo/unhostedssh',
222    unhostedgitssh: 'https://gothib.com/foo/unhostedgitssh',
223    unhostedgithttp: 'http://gothib.com/foo/unhostedgithttp',
224    unhostedgithttps: 'https://gothib.com/foo/unhostedgithttps',
225    unhostedgitobj: 'https://gothib.com/foo/unhostedgitobj',
226    unhostedsshobj: 'https://gothib.com/foo/unhostedsshobj',
227    unhostedgitsshobj: 'https://gothib.com/foo/unhostedgitsshobj',
228    unhostedgithttpobj: 'http://gothib.com/foo/unhostedgithttpobj',
229    unhostedgithttpsobj: 'https://gothib.com/foo/unhostedgithttpsobj',
230    directory: 'https://github.com/foo/test-repo-with-directory/tree/HEAD/some/directory',
231    '.': 'https://example.com/thispkg',
232  }
233  for (const [pkg, url] of Object.entries(expect)) {
234    await repo.exec([['.', pkg].join(sep)])
235    t.equal(opened[url], 1, `opened ${url}`)
236  }
237})
238
239t.test('fail if cannot figure out repo url', async t => {
240  const { repo } = await loadMockNpm(t, fixture)
241
242  const cases = [
243    'norepo',
244    'repoobbj-nourl',
245    'unhostedgitat',
246    'unhostedgitatobj',
247  ]
248
249  for (const pkg of cases) {
250    await t.rejects(
251      repo.exec([['.', pkg].join(sep)]),
252      { pkgid: pkg }
253    )
254  }
255})
256
257t.test('open default package if none specified', async t => {
258  const { repo, opened } = await loadMockNpm(t, fixture)
259  await repo.exec([])
260  t.equal(opened['https://example.com/thispkg'], 1, 'opened expected url', { opened })
261})
262
263t.test('workspaces', async t => {
264  const mockWorkspaces = (t, config) => loadMockNpm(t, workspaceFixture, config)
265
266  t.test('include workspace root', async (t) => {
267    const { opened, repo } = await mockWorkspaces(t, {
268      workspaces: true,
269      'include-workspace-root': true,
270    })
271    await repo.exec([])
272    t.match({
273      'https://github.com/npm/workspaces-test': 1,
274      'https://repo.workspace-a/': 1, // Gets translated to https!
275      'https://github.com/npm/workspace-b': 1,
276    }, opened, 'opened two valid repo urls')
277  })
278
279  t.test('all workspaces', async (t) => {
280    const { opened, repo } = await mockWorkspaces(t, {
281      workspaces: true,
282    })
283    await repo.exec([])
284    t.match({
285      'https://repo.workspace-a/': 1, // Gets translated to https!
286      'https://github.com/npm/workspace-b': 1,
287    }, opened, 'opened two valid repo urls')
288  })
289
290  t.test('one workspace', async (t) => {
291    const { opened, repo } = await mockWorkspaces(t, {
292      workspace: ['workspace-a'],
293    })
294    await repo.exec([])
295    t.match({
296      'https://repo.workspace-a/': 1,
297    }, opened, 'opened one requested repo urls')
298  })
299
300  t.test('invalid workspace', async (t) => {
301    const { opened, repo } = await mockWorkspaces(t, {
302      workspace: ['workspace-x'],
303    })
304    await t.rejects(
305      repo.exec([]),
306      /workspace-x/
307    )
308    t.match({}, opened, 'opened no repo urls')
309  })
310
311  t.test('package arg and workspace', async (t) => {
312    const { opened, repo } = await mockWorkspaces(t, {
313      workspace: ['workspace-x'],
314    })
315    await repo.exec(['.'])
316    t.match({
317      'https://github.com/npm/workspaces-test': 1,
318    }, opened, 'opened url for package arg, not workspace')
319  })
320})
321