Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGar <gar+gh@danger.computer>2021-11-10 19:30:39 +0300
committerGar <gar+gh@danger.computer>2021-11-11 01:35:44 +0300
commit72ca4a4e39a1d4de03d6423480aa2ee82b021060 (patch)
tree81c0f265a4eceb14f4bef0c2adbf690d2bb52e3a /test
parentac4f9e4c55f9f97d99e2a11f0ef5dd9b900b0ef0 (diff)
fix: command completion
The fake npm object in the tests wasn't returning an async function Fixes: https://github.com/npm/cli/issues/4020 PR-URL: https://github.com/npm/cli/pull/4032 Credit: @wraithgar Close: #4032 Reviewed-by: @lukekarrys
Diffstat (limited to 'test')
-rw-r--r--test/lib/commands/completion.js737
1 files changed, 208 insertions, 529 deletions
diff --git a/test/lib/commands/completion.js b/test/lib/commands/completion.js
index 7a7e0a759..51212f06d 100644
--- a/test/lib/commands/completion.js
+++ b/test/lib/commands/completion.js
@@ -6,590 +6,269 @@ const completionScript = fs
.readFileSync(path.resolve(__dirname, '../../../lib/utils/completion.sh'), { encoding: 'utf8' })
.replace(/^#!.*?\n/, '')
-const output = []
-const npmConfig = {}
-let accessCompletionError = false
-
-const npm = {
- config: {
- set: (key, value) => {
- npmConfig[key] = value
- },
- clear: () => {
- for (const key in npmConfig) {
- delete npmConfig[key]
- }
- },
- },
- cmd: cmd => {
- return {
- completion: {
- completion: () => [['>>', '~/.bashrc']],
- },
- adduser: {},
- access: {
- completion: () => {
- if (accessCompletionError) {
- throw new Error('access completion failed')
- }
-
- return ['public', 'restricted']
- },
- },
- promise: {
- completion: () => Promise.resolve(['resolved_completion_promise']),
- },
- donothing: {
- completion: () => {
- return null
- },
- },
- driveaboat: {
- completion: () => {
- return ' fast'
- },
- },
- }[cmd]
- },
- output: line => {
- output.push(line)
- },
-}
-
-const cmdList = {
- aliases: {
- login: 'adduser',
- },
- cmdList: ['access', 'adduser', 'completion'],
- plumbing: [],
-}
-
-// only include a subset so that the snapshots aren't huge and
-// don't change when we add/remove config definitions.
-const definitions = require('../../../lib/utils/config/definitions.js')
-const config = {
- definitions: {
- global: definitions.global,
- browser: definitions.browser,
- registry: definitions.registry,
- },
- shorthands: {
- reg: ['--registry'],
- },
-}
-
-const deref = cmd => {
- return cmd
-}
-
-const Completion = t.mock('../../../lib/commands/completion.js', {
- '../../../lib/utils/cmd-list.js': cmdList,
- '../../../lib/utils/config/index.js': config,
- '../../../lib/utils/deref-command.js': deref,
- '../../../lib/utils/is-windows-shell.js': false,
-})
-const completion = new Completion(npm)
-
-t.test('completion completion', async t => {
- const home = process.env.HOME
- t.teardown(() => {
- process.env.HOME = home
- })
-
- process.env.HOME = t.testdir({
- '.bashrc': '',
- '.zshrc': '',
- })
+const { real: mockNpm } = require('../../fixtures/mock-npm')
- const res = await completion.completion({ w: 2 })
- t.strictSame(
- res,
- [
- ['>>', '~/.zshrc'],
- ['>>', '~/.bashrc'],
- ],
- 'identifies both shells'
- )
- t.end()
+const { Npm, outputs } = mockNpm(t, {
+ '../../lib/utils/is-windows-shell.js': false,
})
+const npm = new Npm()
+
+t.test('completion', async t => {
+ const completion = await npm.cmd('completion')
+ t.test('completion completion', async t => {
+ const home = process.env.HOME
+ t.teardown(() => {
+ process.env.HOME = home
+ })
-t.test('completion completion no known shells', async t => {
- const home = process.env.HOME
- t.teardown(() => {
- process.env.HOME = home
- })
+ process.env.HOME = t.testdir({
+ '.bashrc': '',
+ '.zshrc': '',
+ })
- process.env.HOME = t.testdir()
+ await completion.completion({ w: 2 })
+ t.matchSnapshot(outputs, 'both shells')
+ })
- const res = await completion.completion({ w: 2 })
- t.strictSame(res, [], 'no responses')
- t.end()
-})
+ t.test('completion completion no known shells', async t => {
+ const home = process.env.HOME
+ t.teardown(() => {
+ process.env.HOME = home
+ })
-t.test('completion completion wrong word count', async t => {
- const res = await completion.completion({ w: 3 })
- t.strictSame(res, undefined, 'no responses')
- t.end()
-})
+ process.env.HOME = t.testdir()
-t.test('completion errors in windows without bash', async t => {
- const Compl = t.mock('../../../lib/commands/completion.js', {
- '../../../lib/utils/is-windows-shell.js': true,
+ await completion.completion({ w: 2 })
+ t.matchSnapshot(outputs, 'no responses')
})
- const compl = new Compl()
-
- await t.rejects(
- compl.exec({}),
- { code: 'ENOTSUP', message: /completion supported only in MINGW/ },
- 'returns the correct error'
- )
-})
-
-t.test('dump script when completion is not being attempted', async t => {
- const _write = process.stdout.write
- const _on = process.stdout.on
- t.teardown(() => {
- process.stdout.write = _write
- process.stdout.on = _on
+ t.test('completion completion wrong word count', async t => {
+ await completion.completion({ w: 3 })
+ t.matchSnapshot(outputs, 'no responses')
})
- let errorHandler
- process.stdout.on = (event, handler) => {
- errorHandler = handler
- process.stdout.on = _on
- }
-
- let data
- process.stdout.write = (chunk, callback) => {
- data = chunk
- process.stdout.write = _write
- process.nextTick(() => {
- callback()
- errorHandler({ errno: 'EPIPE' })
+ t.test('dump script when completion is not being attempted', async t => {
+ const _write = process.stdout.write
+ const _on = process.stdout.on
+ t.teardown(() => {
+ process.stdout.write = _write
+ process.stdout.on = _on
})
- }
- await completion.exec({})
+ let errorHandler
+ process.stdout.on = (event, handler) => {
+ errorHandler = handler
+ process.stdout.on = _on
+ }
+
+ let data
+ process.stdout.write = (chunk, callback) => {
+ data = chunk
+ process.stdout.write = _write
+ process.nextTick(() => {
+ callback()
+ errorHandler({ errno: 'EPIPE' })
+ })
+ }
+
+ await completion.exec({})
+
+ t.equal(data, completionScript, 'wrote the completion script')
+ })
- t.equal(data, completionScript, 'wrote the completion script')
-})
+ t.test('dump script exits correctly when EPIPE is emitted on stdout', async t => {
+ const _write = process.stdout.write
+ const _on = process.stdout.on
+ t.teardown(() => {
+ process.stdout.write = _write
+ process.stdout.on = _on
+ })
-t.test('dump script exits correctly when EPIPE is emitted on stdout', async t => {
- const _write = process.stdout.write
- const _on = process.stdout.on
- t.teardown(() => {
- process.stdout.write = _write
- process.stdout.on = _on
+ let errorHandler
+ process.stdout.on = (event, handler) => {
+ errorHandler = handler
+ process.stdout.on = _on
+ }
+
+ let data
+ process.stdout.write = (chunk, callback) => {
+ data = chunk
+ process.stdout.write = _write
+ process.nextTick(() => {
+ errorHandler({ errno: 'EPIPE' })
+ callback()
+ })
+ }
+
+ await completion.exec({})
+ t.equal(data, completionScript, 'wrote the completion script')
})
- let errorHandler
- process.stdout.on = (event, handler) => {
- errorHandler = handler
- process.stdout.on = _on
- }
-
- let data
- process.stdout.write = (chunk, callback) => {
- data = chunk
- process.stdout.write = _write
- process.nextTick(() => {
- errorHandler({ errno: 'EPIPE' })
- callback()
- })
- }
+ t.test('single command name', async t => {
+ process.env.COMP_CWORD = 1
+ process.env.COMP_LINE = 'npm conf'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
- await completion.exec({})
- t.equal(data, completionScript, 'wrote the completion script')
-})
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
-// This test was only working by coincidence before, when we switch to full
-// async/await the race condition now makes it impossible to test. The root of
-// the problem is that if we override stdout.write then other things interfere
-// during testing.
-// t.test('non EPIPE errors cause failures', async t => {
-// const _write = process.stdout.write
-// const _on = process.stdout.on
-// t.teardown(() => {
-// process.stdout.write = _write
-// process.stdout.on = _on
-// })
-
-// let errorHandler
-// process.stdout.on = (event, handler) => {
-// errorHandler = handler
-// process.stdout.on = _on
-// }
-
-// let data
-// process.stdout.write = (chunk, callback) => {
-// data = chunk
-// process.stdout.write = _write
-// process.nextTick(() => {
-// errorHandler({ errno: 'ESOMETHINGELSE' })
-// callback()
-// })
-// }
-
-// await t.rejects(
-// completion.exec([]),
-// { errno: 'ESOMETHINGELSE' },
-// 'propagated error'
-// )
-// t.equal(data, completionScript, 'wrote the completion script')
-// })
-
-t.test('completion completes single command name', async t => {
- process.env.COMP_CWORD = 1
- process.env.COMP_LINE = 'npm c'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ await completion.exec(['npm', 'conf'])
+ t.matchSnapshot(outputs, 'single command name')
})
- await completion.exec(['npm', 'c'])
- t.strictSame(output, ['completion'], 'correctly completed a command name')
-})
+ t.test('multiple command names', async t => {
+ process.env.COMP_CWORD = 1
+ process.env.COMP_LINE = 'npm a'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
+
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
-t.test('completion completes command names', async t => {
- process.env.COMP_CWORD = 1
- process.env.COMP_LINE = 'npm a'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ await completion.exec(['npm', 'a'])
+ t.matchSnapshot(outputs, 'multiple command names')
})
- await completion.exec(['npm', 'a'])
- t.strictSame(output, [['access', 'adduser'].join('\n')], 'correctly completed a command name')
-})
+ t.test('completion of invalid command name does nothing', async t => {
+ process.env.COMP_CWORD = 1
+ process.env.COMP_LINE = 'npm compute'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
+
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
-t.test('completion of invalid command name does nothing', async t => {
- process.env.COMP_CWORD = 1
- process.env.COMP_LINE = 'npm compute'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ await completion.exec(['npm', 'compute'])
+ t.matchSnapshot(outputs, 'no results')
})
- await completion.exec(['npm', 'compute'])
- t.strictSame(output, [], 'returns no results')
-})
+ t.test('subcommand completion', async t => {
+ process.env.COMP_CWORD = 2
+ process.env.COMP_LINE = 'npm access '
+ process.env.COMP_POINT = process.env.COMP_LINE.length
-t.test('handles async completion function', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm promise'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
+
+ await completion.exec(['npm', 'access', ''])
+ t.matchSnapshot(outputs, 'subcommands')
})
- await completion.exec(['npm', 'promise', ''])
-
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'promise'],
- cooked: ['npm', 'promise'],
- original: ['npm', 'promise'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(output, ['resolved_completion_promise'], 'resolves async completion results')
-})
+ t.test('filtered subcommands', async t => {
+ process.env.COMP_CWORD = 2
+ process.env.COMP_LINE = 'npm access p'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
-t.test('completion triggers command completions', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm access '
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
+
+ await completion.exec(['npm', 'access', 'p'])
+ t.matchSnapshot(outputs, 'filtered subcommands')
})
- await completion.exec(['npm', 'access', ''])
-
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'access'],
- cooked: ['npm', 'access'],
- original: ['npm', 'access'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(
- output,
- [['public', 'restricted'].join('\n')],
- 'correctly completed a subcommand name'
- )
-})
+ t.test('commands with no completion', async t => {
+ process.env.COMP_CWORD = 2
+ process.env.COMP_LINE = 'npm adduser '
+ process.env.COMP_POINT = process.env.COMP_LINE.length
+
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
-t.test('completion triggers filtered command completions', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm access p'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ // quotes around adduser are to ensure coverage when unescaping commands
+ await completion.exec(['npm', "'adduser'", ''])
+ t.matchSnapshot(outputs, 'no results')
})
- await completion.exec(['npm', 'access', 'p'])
-
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'access'],
- cooked: ['npm', 'access'],
- original: ['npm', 'access'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(output, ['public'], 'correctly completed a subcommand name')
-})
+ t.test('flags', async t => {
+ process.env.COMP_CWORD = 2
+ process.env.COMP_LINE = 'npm install --v'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
-t.test('completions for commands that return nested arrays are joined', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm completion '
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
- })
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
- await completion.exec(['npm', 'completion', ''])
-
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'completion'],
- cooked: ['npm', 'completion'],
- original: ['npm', 'completion'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(output, ['>> ~/.bashrc'], 'joins nested arrays')
-})
+ await completion.exec(['npm', 'install', '--v'])
-t.test('completions for commands that return nothing work correctly', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm donothing '
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ t.matchSnapshot(outputs, 'flags')
})
- await completion.exec(['npm', 'donothing', ''])
-
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'donothing'],
- cooked: ['npm', 'donothing'],
- original: ['npm', 'donothing'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(output, [], 'returns nothing')
-})
+ t.test('--no- flags', async t => {
+ process.env.COMP_CWORD = 2
+ process.env.COMP_LINE = 'npm install --no-v'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
-t.test('completions for commands that return a single item work correctly', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm driveaboat '
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
- })
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
- await completion.exec(['npm', 'driveaboat', ''])
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'driveaboat'],
- cooked: ['npm', 'driveaboat'],
- original: ['npm', 'driveaboat'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(output, ["' fast'"], 'returns the correctly escaped string')
-})
+ await completion.exec(['npm', 'install', '--no-v'])
-t.test('command completion for commands with no completion return no results', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm adduser '
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ t.matchSnapshot(outputs, 'flags')
})
- // quotes around adduser are to ensure coverage when unescaping commands
- await completion.exec(['npm', "'adduser'", ''])
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'adduser'],
- cooked: ['npm', 'adduser'],
- original: ['npm', 'adduser'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(output, [], 'correctly completed a subcommand name')
-})
+ t.test('double dashes escape from flag completion', async t => {
+ process.env.COMP_CWORD = 2
+ process.env.COMP_LINE = 'npm -- install --'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
-t.test('command completion errors propagate', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm access '
- process.env.COMP_POINT = process.env.COMP_LINE.length
- accessCompletionError = true
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
- accessCompletionError = false
- })
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
- await t.rejects(
- completion.exec(['npm', 'access', '']),
- /access completion failed/,
- 'catches the appropriate error'
- )
- t.strictSame(
- npmConfig,
- {
- argv: {
- remain: ['npm', 'access'],
- cooked: ['npm', 'access'],
- original: ['npm', 'access'],
- },
- },
- 'applies command config appropriately'
- )
- t.strictSame(output, [], 'returns no results')
-})
+ await completion.exec(['npm', '--', 'install', '--'])
-t.test('completion can complete flags', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm install --'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ t.matchSnapshot(outputs, 'full command list')
})
- await completion.exec(['npm', 'install', '--'])
+ t.test('completion cannot complete options that take a value in mid-command', async t => {
+ process.env.COMP_CWORD = 2
+ process.env.COMP_LINE = 'npm --registry install'
+ process.env.COMP_POINT = process.env.COMP_LINE.length
- t.strictSame(npmConfig, {}, 'does not apply command config')
- t.strictSame(
- output,
- [['--global', '--browser', '--registry', '--reg', '--no-global', '--no-browser'].join('\n')],
- 'correctly completes flag names'
- )
-})
+ t.teardown(() => {
+ delete process.env.COMP_CWORD
+ delete process.env.COMP_LINE
+ delete process.env.COMP_POINT
+ })
-t.test('double dashes escape from flag completion', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm -- install --'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+ await completion.exec(['npm', '--registry', 'install'])
+ t.matchSnapshot(outputs, 'does not try to complete option arguments in the middle of a command')
})
-
- await completion.exec(['npm', '--', 'install', '--'])
-
- t.strictSame(npmConfig, {}, 'does not apply command config')
- t.strictSame(
- output,
- [['access', 'adduser', 'completion', 'login'].join('\n')],
- 'correctly completes flag names'
- )
})
-t.test('completion cannot complete options that take a value in mid-command', async t => {
- process.env.COMP_CWORD = 2
- process.env.COMP_LINE = 'npm --registry install'
- process.env.COMP_POINT = process.env.COMP_LINE.length
-
- t.teardown(() => {
- delete process.env.COMP_CWORD
- delete process.env.COMP_LINE
- delete process.env.COMP_POINT
- npm.config.clear()
- output.length = 0
+t.test('windows without bash', async t => {
+ const { Npm, outputs } = mockNpm(t, {
+ '../../lib/utils/is-windows-shell.js': true,
})
-
- await completion.exec(['npm', '--registry', 'install'])
- t.strictSame(npmConfig, {}, 'does not apply command config')
- t.strictSame(output, [], 'does not try to complete option arguments in the middle of a command')
+ const npm = new Npm()
+ const completion = await npm.cmd('completion')
+ await t.rejects(
+ completion.exec({}),
+ { code: 'ENOTSUP', message: /completion supported only in MINGW/ },
+ 'returns the correct error'
+ )
+ t.matchSnapshot(outputs, 'no output')
})