UNPKG

capitano

Version:

Powerful, non opitionated command line parser for serious applications

509 lines (413 loc) 13.9 kB
_ = require('lodash') chai = require('chai') expect = chai.expect parse = require('../lib/parse') state = require('../lib/state') Option = require('../lib/option') Signature = require('../lib/signature') settings = require('../lib/settings') describe 'Parse:', -> describe '#normalizeInput()', -> it 'should handle strings', -> result = parse.normalizeInput('-x 3 -y 4') expect(result).to.deep.equal([ '-x', '3', '-y', '4' ]) it 'should handle arrays', -> result = parse.normalizeInput([ '-x', '3', '-y', '4' ]) expect(result).to.deep.equal([ '-x', '3', '-y', '4' ]) it 'should discard first arguments if process.argv', -> result = parse.normalizeInput(process.argv) expect(result).to.deep.equal(process.argv.slice(2)) it 'should throw an error if invalid input', -> expect -> parse.normalizeInput({ hello: 'world' }) .to.throw(Error) describe '#parse()', -> describe 'options', -> beforeEach -> state.globalOptions = [] it 'should be able to parse options', -> argv = parse.split('-x 3 -y 4') result = parse.parse(argv) expect(result).to.deep.equal global: {} options: x: 3 y: 4 it 'should be able to parse boolean options', -> argv = parse.split('-x --foo') result = parse.parse(argv) expect(result).to.deep.equal global: {} options: x: true foo: true it 'should be able to compile global options', -> state.globalOptions.push new Option signature: new Signature('quiet') boolean: true argv = parse.split('foo --quiet') result = parse.parse(argv) expect(result).to.deep.equal global: quiet: true options: quiet: true command: 'foo' it 'should be able to compile global options with aliases', -> state.globalOptions.push new Option signature: new Signature('quiet') boolean: true alias: 'q' argv = parse.split('foo -q') result = parse.parse(argv) expect(result).to.deep.equal global: quiet: true options: q: true command: 'foo' describe 'commands', -> it 'should be able to parse commands', -> argv = parse.split('auth login') result = parse.parse(argv) expect(result).to.deep.equal command: 'auth login' options: {} global: {} it 'should be able to parse commands with suffix options', -> argv = parse.split('auth login -f -n 10') result = parse.parse(argv) expect(result).to.deep.equal command: 'auth login' global: {} options: f: true n: 10 it 'should be able to parse commands with prefix options', -> argv = parse.split('-f -n 10 auth login') result = parse.parse(argv) expect(result).to.deep.equal command: 'auth login' global: {} options: f: true n: 10 it 'should be able to parse commands with infix options', -> argv = parse.split('auth -f -n 10 login') result = parse.parse(argv) expect(result).to.deep.equal command: 'auth login' global: {} options: f: true n: 10 it 'should be able to parse commands with arguments', -> argv = parse.split('auth login <credentials>') result = parse.parse(argv) expect(result).to.deep.equal command: 'auth login <credentials>' global: {} options: {} it 'should be able to parse commands with multiple arguments', -> argv = parse.split('auth login <credentials> <foo>') result = parse.parse(argv) expect(result).to.deep.equal command: 'auth login <credentials> <foo>' global: {} options: {} it 'should be able to parse commands with optional arguments', -> argv = parse.split('auth login [foo]') result = parse.parse(argv) expect(result).to.deep.equal command: 'auth login [foo]' global: {} options: {} it 'should be able to parse commands with multiple arguments', -> argv = parse.split('transfer <from name> <to name>') result = parse.parse(argv) expect(result).to.deep.equal command: 'transfer <from name> <to name>' global: {} options: {} it 'should be able to parse commands with optional arguments', -> argv = parse.split('greet [their name]') result = parse.parse(argv) expect(result).to.deep.equal command: 'greet [their name]' global: {} options: {} it 'should not discard single quotes when parsing the command', -> argv = parse.split('hello \'John Doe\'') result = parse.parse(argv) expect(result).to.deep.equal command: 'hello "John Doe"' global: {} options: {} it 'should not discard double quotes when parsing the command', -> argv = parse.split('hello "John Doe"') result = parse.parse(argv) expect(result).to.deep.equal command: 'hello "John Doe"' global: {} options: {} it 'should not parse numbers in scientific notation automatically', -> argv = parse.split('hello -x 43e8273') result = parse.parse(argv) expect(result).to.deep.equal command: 'hello' global: {} options: x: '43e8273' it 'should parse float numbers automatically', -> argv = parse.split('hello -x 1.5 -y .9') result = parse.parse(argv) expect(result).to.deep.equal command: 'hello' global: {} options: x: 1.5 y: 0.9 it 'should not parse \'.\' as float number', -> argv = parse.split('hello -x .') result = parse.parse(argv) expect(result).to.deep.equal command: 'hello' global: {} options: x: '.' describe '#split()', -> it 'should return an empty array if no signature', -> signature = undefined result = [] expect(parse.split(signature)).to.deep.equal(result) it 'should split a wildcard signature correctly', -> signature = settings.signatures.wildcard result = [ settings.signatures.wildcard ] expect(parse.split(signature)).to.deep.equal(result) it 'should split signatures correctly', -> signature = 'foo <bar>' result = [ 'foo', '<bar>' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split multi word required parameters', -> signature = '<hello world> <foo bar baz>' result = [ '<hello world>', '<foo bar baz>' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split multi word optional parameters', -> signature = '[hello world] [foo bar baz]' result = [ '[hello world]', '[foo bar baz]' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split multi word variadic required parameters', -> signature = '<hello world...> <foo bar baz...>' result = [ '<hello world...>', '<foo bar baz...>' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split multi word optional parameters', -> signature = '[hello world...] [foo bar baz...]' result = [ '[hello world...]', '[foo bar baz...]' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split absolute paths parameters correctly', -> signature = 'foo /Users/me/foo/bar' result = [ 'foo', '/Users/me/foo/bar' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split absolute paths (win32) parameters correctly', -> signature = 'foo C:\\Users\\me\\foo\\bar' result = [ 'foo', 'C:\\Users\\me\\foo\\bar' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split relative paths parameters correctly', -> signature = 'foo ../hello/world' result = [ 'foo', '../hello/world' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split home relative paths parameters correctly', -> signature = 'foo ~/.ssh/id_rsa.pub' result = [ 'foo', '~/.ssh/id_rsa.pub' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split words surrounded by quotes correctly', -> signature = 'foo \'hello world\'' result = [ 'foo', 'hello world' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split words surrounded by double quotes correctly', -> signature = 'foo "hello world"' result = [ 'foo', 'hello world' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split words with escaped double quotes and surrounded by double quotes correctly', -> signature = 'foo "hello \\\"world\\\""' result = [ 'foo', 'hello \\"world\\"' ] expect(parse.split(signature)).to.deep.equal(result) it 'should split words with escaped single quotes and surrounded by single quotes correctly', -> signature = 'foo \'hello \\\'world\\\'\'' result = [ 'foo', 'hello \\\'world\\\'' ] expect(parse.split(signature)).to.deep.equal(result) describe '#parseOptions()', -> it 'should not throw if options is undefined', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') boolean: true expect -> parse.parseOptions(definedOptions, undefined) .to.not.throw(Error) it 'should return an empty object if defined options is empty', -> options = hello: 'world' quiet: true expect(parse.parseOptions([], options)).to.deep.equal({}) it 'should return an empty object if options is empty', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' expect(parse.parseOptions(definedOptions, [])).to.deep.equal({}) it 'should parse simple options (without aliases)', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' definedOptions.push new Option signature: new Signature('quiet') boolean: true options = foo: 'baz' quiet: true result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 'baz' quiet: true it 'should parse options starting with a number correctly', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' options = foo: '10foobar' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: '10foobar' it 'should parse options containing integers as numbers', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' options = foo: '10' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 10 it 'should discard non matched options', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' definedOptions.push new Option signature: new Signature('hello') parameter: 'world' definedOptions.push new Option signature: new Signature('quiet') boolean: true options = foo: 'baz' quiet: 'hello' hello: true result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 'baz' it 'shoud omit extra defined options', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' definedOptions.push new Option signature: new Signature('quiet') boolean: true options = foo: 'baz' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 'baz' it 'should handle string aliases', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' alias: 'f' options = f: 'baz' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 'baz' it 'should handle array aliases', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' alias: [ 'a', 'b', 'c' ] options = b: 'baz' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 'baz' it 'should handle multiletter aliases', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' alias: 'hello' options = hello: 'world' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 'world' it 'should give precedence to long names', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' alias: [ 'a', 'b', 'c' ] options = foo: 'bar' b: 'baz' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 'bar' it 'should parse numbers automatically', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' options = foo: '25' result = parse.parseOptions(definedOptions, options) expect(result).to.deep.equal foo: 25 describe 'given an option required key', -> it 'should throw a generic error if true', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' required: true options = hello: 'world' expect -> parse.parseOptions(definedOptions, options) .to.throw('Option foo is required') it 'should not throw if false', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' required: false options = hello: 'world' expect -> parse.parseOptions(definedOptions, options) .to.not.throw('Option foo is required') it 'should throw a custom error if required is a string', -> definedOptions = [] definedOptions.push new Option signature: new Signature('foo') parameter: 'bar' required: 'Custom error!' options = hello: 'world' expect -> parse.parseOptions(definedOptions, options) .to.throw('Custom error!')