• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const t = require('tap')
2const tmock = require('../../fixtures/tmock')
3const mockNpm = require('../../fixtures/mock-npm')
4
5const mockOpenUrl = async (t, args, { openerResult, ...config } = {}) => {
6  let openerUrl = null
7  let openerOpts = null
8
9  const open = async (url, options) => {
10    openerUrl = url
11    openerOpts = options
12    if (openerResult) {
13      throw openerResult
14    }
15  }
16
17  const mock = await mockNpm(t, { config })
18
19  const openUrl = tmock(t, '{LIB}/utils/open-url.js', {
20    '@npmcli/promise-spawn': { open },
21  })
22
23  const openWithNpm = (...a) => openUrl(mock.npm, ...a)
24
25  if (args) {
26    await openWithNpm(...args)
27  }
28
29  return {
30    ...mock,
31    openUrl: openWithNpm,
32    openerUrl: () => openerUrl,
33    openerOpts: () => openerOpts,
34  }
35}
36
37t.test('opens a url', async t => {
38  const { openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t,
39    ['https://www.npmjs.com', 'npm home'])
40  t.equal(openerUrl(), 'https://www.npmjs.com', 'opened the given url')
41  t.same(openerOpts(), { command: null }, 'passed command as null (the default)')
42  t.same(joinedOutput(), '', 'printed no output')
43})
44
45t.test('returns error for non-https url', async t => {
46  const { openUrl, openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t)
47  await t.rejects(
48    openUrl('ftp://www.npmjs.com', 'npm home'),
49    /Invalid URL/,
50    'got the correct error'
51  )
52  t.equal(openerUrl(), null, 'did not open')
53  t.same(openerOpts(), null, 'did not open')
54  t.same(joinedOutput(), '', 'printed no output')
55})
56
57t.test('returns error for file url', async t => {
58  const { openUrl, openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t)
59  await t.rejects(
60    openUrl('file:///usr/local/bin/ls', 'npm home'),
61    /Invalid URL/,
62    'got the correct error'
63  )
64  t.equal(openerUrl(), null, 'did not open')
65  t.same(openerOpts(), null, 'did not open')
66  t.same(joinedOutput(), '', 'printed no output')
67})
68
69t.test('file url allowed if explicitly asked for', async t => {
70  const { openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t,
71    ['file:///man/page/npm-install', 'npm home', true])
72  t.equal(openerUrl(), 'file:///man/page/npm-install', 'opened the given url')
73  t.same(openerOpts(), { command: null }, 'passed command as null (the default)')
74  t.same(joinedOutput(), '', 'printed no output')
75})
76
77t.test('returns error for non-parseable url', async t => {
78  const { openUrl, openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t)
79  await t.rejects(
80    openUrl('git+ssh://user@host:repo.git', 'npm home'),
81    /Invalid URL/,
82    'got the correct error'
83  )
84  t.equal(openerUrl(), null, 'did not open')
85  t.same(openerOpts(), null, 'did not open')
86  t.same(joinedOutput(), '', 'printed no output')
87})
88
89t.test('encodes non-URL-safe characters in url provided', async t => {
90  const { openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t,
91    ['https://www.npmjs.com/|cat', 'npm home'])
92  t.equal(openerUrl(), 'https://www.npmjs.com/%7Ccat', 'opened the encoded url')
93  t.same(openerOpts(), { command: null }, 'passed command as null (the default)')
94  t.same(joinedOutput(), '', 'printed no output')
95})
96
97t.test('opens a url with the given browser', async t => {
98  const { openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t,
99    ['https://www.npmjs.com', 'npm home'], { browser: 'chrome' })
100  t.equal(openerUrl(), 'https://www.npmjs.com', 'opened the given url')
101  // FIXME: browser string is parsed as a boolean in config layer
102  // this is a bug that should be fixed or the config should not allow it
103  t.same(openerOpts(), { command: null }, 'passed the given browser as command')
104  t.same(joinedOutput(), '', 'printed no output')
105})
106
107t.test('prints where to go when browser is disabled', async t => {
108  const { openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t,
109    ['https://www.npmjs.com', 'npm home'], { browser: false })
110  t.equal(openerUrl(), null, 'did not open')
111  t.same(openerOpts(), null, 'did not open')
112  t.matchSnapshot(joinedOutput(), 'printed expected message')
113})
114
115t.test('prints where to go when browser is disabled and json is enabled', async t => {
116  const { openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t,
117    ['https://www.npmjs.com', 'npm home'], { browser: false, json: true })
118  t.equal(openerUrl(), null, 'did not open')
119  t.same(openerOpts(), null, 'did not open')
120  t.matchSnapshot(joinedOutput(), 'printed expected message')
121})
122
123t.test('prints where to go when given browser does not exist', async t => {
124  const { openerUrl, openerOpts, joinedOutput } = await mockOpenUrl(t,
125    ['https://www.npmjs.com', 'npm home'],
126    {
127      openerResult: Object.assign(new Error('failed'), { code: 127 }),
128    }
129  )
130
131  t.equal(openerUrl(), 'https://www.npmjs.com', 'tried to open the correct url')
132  t.same(openerOpts(), { command: null }, 'tried to use the correct browser')
133  t.matchSnapshot(joinedOutput(), 'printed expected message')
134})
135
136t.test('handles unknown opener error', async t => {
137  const { openUrl } = await mockOpenUrl(t, null, {
138    browser: 'firefox',
139    openerResult: Object.assign(new Error('failed'), { code: 'ENOBRIAN' }),
140  })
141
142  await t.rejects(openUrl('https://www.npmjs.com', 'npm home'), 'failed', 'got the correct error')
143})
144