• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const t = require('tap')
2const fs = require('fs')
3const path = require('path')
4const { load: loadMockNpm } = require('../../fixtures/mock-npm')
5
6const completionScript = fs
7  .readFileSync(path.resolve(__dirname, '../../../lib/utils/completion.sh'), { encoding: 'utf8' })
8  .replace(/^#!.*?\n/, '')
9
10const loadMockCompletion = async (t, o = {}) => {
11  const { globals = {}, windows, ...options } = o
12  const res = await loadMockNpm(t, {
13    command: 'completion',
14    ...options,
15    globals: (dirs) => ({
16      'process.platform': windows ? 'win32' : 'posix',
17      'process.env.term': 'notcygwin',
18      'process.env.msystem': 'nogmingw',
19      ...(typeof globals === 'function' ? globals(dirs) : globals),
20    }),
21  })
22  return {
23    resetGlobals: res.mockedGlobals.reset,
24    ...res,
25  }
26}
27
28const loadMockCompletionComp = async (t, word, line) =>
29  loadMockCompletion(t, {
30    globals: {
31      'process.env.COMP_CWORD': word,
32      'process.env.COMP_LINE': line,
33      'process.env.COMP_POINT': line.length,
34    },
35  })
36
37t.test('completion', async t => {
38  t.test('completion completion', async t => {
39    const { outputs, completion } = await loadMockCompletion(t, {
40      prefixDir: {
41        '.bashrc': 'aaa',
42        '.zshrc': 'aaa',
43      },
44      globals: ({ prefix }) => ({
45        'process.env.HOME': prefix,
46      }),
47    })
48
49    await completion.completion({ w: 2 })
50    t.matchSnapshot(outputs, 'both shells')
51  })
52
53  t.test('completion completion no known shells', async t => {
54    const { outputs, completion } = await loadMockCompletion(t, {
55      globals: ({ prefix }) => ({
56        'process.env.HOME': prefix,
57      }),
58    })
59
60    await completion.completion({ w: 2 })
61    t.matchSnapshot(outputs, 'no responses')
62  })
63
64  t.test('completion completion wrong word count', async t => {
65    const { outputs, completion } = await loadMockCompletion(t)
66
67    await completion.completion({ w: 3 })
68    t.matchSnapshot(outputs, 'no responses')
69  })
70
71  t.test('dump script when completion is not being attempted', async t => {
72    let errorHandler, data
73    const { completion, resetGlobals } = await loadMockCompletion(t, {
74      globals: {
75        'process.stdout.on': (event, handler) => {
76          errorHandler = handler
77          resetGlobals['process.stdout.on']()
78        },
79        'process.stdout.write': (chunk, callback) => {
80          data = chunk
81          process.nextTick(() => {
82            callback()
83            errorHandler({ errno: 'EPIPE' })
84          })
85          resetGlobals['process.stdout.write']()
86        },
87      },
88    })
89
90    await completion.exec()
91    t.equal(data, completionScript, 'wrote the completion script')
92  })
93
94  t.test('dump script exits correctly when EPIPE is emitted on stdout', async t => {
95    let errorHandler, data
96    const { completion, resetGlobals } = await loadMockCompletion(t, {
97      globals: {
98        'process.stdout.on': (event, handler) => {
99          if (event === 'error') {
100            errorHandler = handler
101          }
102          resetGlobals['process.stdout.on']()
103        },
104        'process.stdout.write': (chunk, callback) => {
105          data = chunk
106          process.nextTick(() => {
107            errorHandler({ errno: 'EPIPE' })
108            callback()
109          })
110          resetGlobals['process.stdout.write']()
111        },
112      },
113    })
114
115    await completion.exec()
116    t.equal(data, completionScript, 'wrote the completion script')
117  })
118
119  t.test('single command name', async t => {
120    const { outputs, completion } = await loadMockCompletionComp(t, 1, 'npm conf')
121
122    await completion.exec(['npm', 'conf'])
123    t.matchSnapshot(outputs, 'single command name')
124  })
125
126  t.test('multiple command names', async t => {
127    const { outputs, completion } = await loadMockCompletionComp(t, 1, 'npm a')
128
129    await completion.exec(['npm', 'a'])
130    t.matchSnapshot(outputs, 'multiple command names')
131  })
132
133  t.test('completion of invalid command name does nothing', async t => {
134    const { outputs, completion } = await loadMockCompletionComp(t, 1, 'npm compute')
135
136    await completion.exec(['npm', 'compute'])
137    t.matchSnapshot(outputs, 'no results')
138  })
139
140  t.test('subcommand completion', async t => {
141    const { outputs, completion } = await loadMockCompletionComp(t, 2, 'npm access ')
142
143    await completion.exec(['npm', 'access', ''])
144    t.matchSnapshot(outputs, 'subcommands')
145  })
146
147  t.test('filtered subcommands', async t => {
148    const { outputs, completion } = await loadMockCompletionComp(t, 2, 'npm access p')
149
150    await completion.exec(['npm', 'access', 'p'])
151    t.matchSnapshot(outputs, 'filtered subcommands')
152  })
153
154  t.test('commands with no completion', async t => {
155    const { outputs, completion } = await loadMockCompletionComp(t, 2, 'npm adduser ')
156
157    // quotes around adduser are to ensure coverage when unescaping commands
158    await completion.exec(['npm', "'adduser'", ''])
159    t.matchSnapshot(outputs, 'no results')
160  })
161
162  t.test('flags', async t => {
163    const { outputs, completion } = await loadMockCompletionComp(t, 2, 'npm install --v')
164
165    await completion.exec(['npm', 'install', '--v'])
166    t.matchSnapshot(outputs, 'flags')
167  })
168
169  t.test('--no- flags', async t => {
170    const { outputs, completion } = await loadMockCompletionComp(t, 2, 'npm install --no-v')
171
172    await completion.exec(['npm', 'install', '--no-v'])
173    t.matchSnapshot(outputs, 'flags')
174  })
175
176  t.test('double dashes escape from flag completion', async t => {
177    const { outputs, completion } = await loadMockCompletionComp(t, 2, 'npm -- install --')
178
179    await completion.exec(['npm', '--', 'install', '--'])
180    t.matchSnapshot(outputs, 'full command list')
181  })
182
183  t.test('completion cannot complete options that take a value in mid-command', async t => {
184    const { outputs, completion } = await loadMockCompletionComp(t, 2, 'npm --registry install')
185
186    await completion.exec(['npm', '--registry', 'install'])
187    t.matchSnapshot(outputs, 'does not try to complete option arguments in the middle of a command')
188  })
189})
190
191t.test('windows without bash', async t => {
192  const { outputs, completion } = await loadMockCompletion(t, { windows: true })
193  await t.rejects(
194    completion.exec(),
195    { code: 'ENOTSUP', message: /completion supported only in MINGW/ },
196    'returns the correct error'
197  )
198  t.matchSnapshot(outputs, 'no output')
199})
200