subcli
Version:
helpers for creating command-line interfaces that support subcommands
186 lines (164 loc) • 7.36 kB
JavaScript
/* eslint-env mocha */
/* global expect, testContext */
/* eslint-disable prefer-arrow-callback, no-unused-expressions */
import usage, {usageInfo, exampleList, commandList, optionList, usageMessage} from '../usage'
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
}
describe(testContext(__filename), function () {
before(function () {
this.commandDescriptor = {
name: 'cmd',
description: 'test command',
usage: 'cmd [options] <argument>',
examples: [{
example: 'cmd --second',
description: 'run command with second option'
}, {
example: 'cmd foo bar',
description: 'run command with arguments'
}],
commands: [{
name: 'cmd1',
description: 'description for the first command',
usage: 'cmd1 <arg>',
examples: [{
example: 'cmd cmd1',
description: 'run command 1'
}],
}, {
name: 'cmd2',
description: 'description for the second command',
examples: [{
example: 'cmd cmd2 foo bar',
description: 'run command 2 with arguments'
}],
}],
options: [{
name: 'first',
abbr: 'f',
boolean: true,
help: 'first option',
}, {
name: 'second',
abbr: 's',
help: 'second option',
}],
}
})
describe('usageInfo', function () {
it('returns empty string if there is no usage', function () {
expect(usageInfo(undefined)).to.equal('')
expect(usageInfo(null)).to.equal('')
})
it('concatenates the parent command if it exists', function () {
const subcommandDescriptor = this.commandDescriptor.commands[1]
const lines = usageInfo(subcommandDescriptor.name, this.commandDescriptor.name).split('\n').filter(line => line.length > 0)
expect(lines[0]).to.match(/Usage:/)
expect(lines[1]).to.match(new RegExp(`\\s+${escapeRegExp(this.commandDescriptor.name)} ${escapeRegExp(subcommandDescriptor.name)}`))
})
it('returns the usage info', function () {
const lines = usageInfo(this.commandDescriptor.usage).split('\n').filter(line => line.length > 0)
expect(lines[0]).to.match(/Usage:/)
expect(lines[1]).to.match(new RegExp(`\\s+${escapeRegExp(this.commandDescriptor.usage)}`))
})
})
describe('commandList', function () {
it('returns empty string if there are no commands', function () {
expect(commandList(undefined)).to.equal('')
expect(commandList(null)).to.equal('')
expect(commandList([])).to.equal('')
})
it('returns commands with their descriptions', function () {
const lines = commandList(this.commandDescriptor.commands).split('\n').filter(line => line.length > 0)
expect(lines[0]).to.match(/Commands:/)
expect(lines[1]).to.match(/cmd1.+description for the first command/)
expect(lines[2]).to.match(/cmd2.+description for the second command/)
})
})
describe('optionList', function () {
it('returns empty string if there are no options', function () {
expect(optionList(undefined)).to.equal('')
expect(optionList(null)).to.equal('')
expect(optionList([])).to.equal('')
})
it('returns options with their descriptions', function () {
const lines = optionList(this.commandDescriptor.options).split('\n').filter(line => line.length > 0)
expect(lines[0]).to.match(/Options:/)
expect(lines[1]).to.match(/--first.+-f.+first option/)
expect(lines[2]).to.match(/--second.+-s.+second option/)
})
})
describe('exampleList', function () {
it('returns empty string if there are no examples', function () {
expect(exampleList(undefined)).to.equal('')
expect(exampleList(null)).to.equal('')
expect(exampleList([])).to.equal('')
})
it('returns examples with their descriptions', function () {
const lines = exampleList(this.commandDescriptor.examples).split('\n').filter(line => line.length > 0)
expect(lines[0]).to.match(/Examples:/)
expect(lines[1]).to.match(/#.+run command with second option/)
expect(lines[2]).to.match(/cmd --second/)
expect(lines[3]).to.match(/#.+run command with arguments/)
expect(lines[4]).to.match(/cmd foo bar/)
})
})
describe('usageMessage', function () {
it('returns the full usage for the command', function () {
const lines = usageMessage(this.commandDescriptor).split('\n').filter(line => line.length > 0)
expect(lines[0]).to.equal(`${this.commandDescriptor.name} - ${this.commandDescriptor.description}`)
expect(lines.length).to.equal(
1 + // name + description
2 + // Usage: and usage line
this.commandDescriptor.commands.length + 1 + // Commands: and command list
this.commandDescriptor.options.length + 1 + // Options: and option list
(this.commandDescriptor.examples.length * 2) + 1 // Examples: and example list
)
})
it('uses the command name if no usage info is provided', function () {
const subcommandDescriptor = this.commandDescriptor.commands[1]
const lines = usageMessage(subcommandDescriptor).split('\n').filter(line => line.length > 0)
expect(lines[2]).to.match(new RegExp(`\\s+${escapeRegExp(subcommandDescriptor.name)}`))
})
})
describe('usage', function () {
it('returns the parent command usage when called without parsed arguments', function () {
const usageString = usage(this.commandDescriptor)
expect(usageString).to.match(/^cmd -/)
})
})
describe('usage {includeHelp: true}', function () {
it('returns nothing if no help was requested', function () {
const args = {_: [], help: false}
const usageString = usage(this.commandDescriptor, args, {includeHelp: true})
expect(usageString).to.not.be.ok
})
it('returns the parent command usage when "--help" was requested on parent command', function () {
const args = {_: [], help: true}
const usageString = usage(this.commandDescriptor, args, {includeHelp: true})
expect(usageString).to.match(/^cmd -/)
})
it('returns the subcommand usage when "--help" was reqested on subcommand', function () {
const args = {_: ['cmd1'], $: {cmd1: {_: [], help: true}}}
const usageString = usage(this.commandDescriptor, args, {includeHelp: true})
expect(usageString).to.match(/^cmd cmd1 -/)
})
})
describe('usage (with commandPrefix option)', function () {
it('prefixes the command properly', function () {
const usageString = usage(this.commandDescriptor, null, {commandPrefix: '/'})
expect(usageString).to.match(/^\/cmd -[\s\S]+\/cmd/)
expect(usageString).to.match(/\/cmd foo bar/)
expect(usageString).to.match(/\/cmd --second/)
})
})
describe('usage (with maxWidth option)', function () {
it('wraps lines longer than the maximum width', function () {
const usageString = usage(this.commandDescriptor, null, {maxWidth: 20})
expect(usageString).to.match(/description\n\s+for the/)
expect(usageString).to.match(/first\n\s+option/)
expect(usageString).to.match(/# run command\n\s+# with arguments/)
})
})
})