426 lines
11 KiB
TypeScript
426 lines
11 KiB
TypeScript
import { arg, argparse } from './argparse'
|
|
|
|
describe('argparse', () => {
|
|
|
|
const CMD = 'command'
|
|
const exit = jest.fn()
|
|
const log = jest.fn()
|
|
|
|
beforeEach(() => {
|
|
exit.mockClear()
|
|
log.mockClear()
|
|
})
|
|
|
|
it('parses args', () => {
|
|
const args = argparse({
|
|
one: arg('string', {required: true}),
|
|
two: arg('number', {default: 100}),
|
|
four: {
|
|
type: 'boolean',
|
|
},
|
|
}).parse([CMD, '--one', '1', '--two', '2', '--four'])
|
|
|
|
const one: string = args.one
|
|
const two: number = args.two
|
|
const four: boolean = args.four
|
|
|
|
expect(one).toBe('1')
|
|
expect(two).toBe(2)
|
|
expect(four).toBe(true)
|
|
})
|
|
|
|
describe('boolean', () => {
|
|
it('is not required, and is false by default', () => {
|
|
const result = argparse({
|
|
bool: {
|
|
type: 'boolean',
|
|
},
|
|
}).parse([CMD])
|
|
const value: boolean = result.bool
|
|
expect(value).toBe(false)
|
|
})
|
|
it('can be made required', () => {
|
|
expect(() => argparse({
|
|
bool: {
|
|
type: 'boolean',
|
|
required: true,
|
|
},
|
|
}).parse([CMD])).toThrowError(/Missing required args: bool/)
|
|
})
|
|
it('optionally accepts a true/false value', () => {
|
|
const {parse} = argparse({
|
|
bool: {
|
|
type: 'boolean',
|
|
alias: 'b',
|
|
},
|
|
other: {
|
|
type: 'string',
|
|
},
|
|
})
|
|
expect(parse([CMD, '--bool']).bool).toBe(true)
|
|
expect(parse([CMD, '--bool', 'false']).bool).toBe(false)
|
|
expect(parse([CMD, '--bool', 'true']).bool).toBe(true)
|
|
expect(parse([CMD, '--bool', '--other', 'value'])).toEqual({
|
|
bool: true,
|
|
other: 'value',
|
|
})
|
|
})
|
|
it('can be grouped by shorthand (single dash) notation', () => {
|
|
const {parse} = argparse({
|
|
a1: {
|
|
type: 'boolean',
|
|
alias: 'a',
|
|
},
|
|
b: {
|
|
type: 'boolean',
|
|
alias: 'c',
|
|
},
|
|
other: {
|
|
type: 'string',
|
|
alias: 'o',
|
|
},
|
|
})
|
|
expect(parse([CMD])).toEqual({
|
|
a1: false,
|
|
b: false,
|
|
other: '',
|
|
})
|
|
expect(parse([CMD, '-ab'])).toEqual({
|
|
a1: true,
|
|
b: true,
|
|
other: '',
|
|
})
|
|
expect(parse([CMD, '-ca'])).toEqual({
|
|
a1: true,
|
|
b: true,
|
|
other: '',
|
|
})
|
|
expect(parse([CMD, '-abo', 'test'])).toEqual({
|
|
a1: true,
|
|
b: true,
|
|
other: 'test',
|
|
})
|
|
expect(() => parse([CMD, '-abo'])).toThrowError(/must be a string: -abo/)
|
|
})
|
|
})
|
|
|
|
describe('number', () => {
|
|
it('sets to NaN by default', () => {
|
|
const {parse} = argparse({
|
|
a: {
|
|
type: 'number',
|
|
},
|
|
})
|
|
expect(parse([CMD])).toEqual({
|
|
a: NaN,
|
|
})
|
|
expect(() => parse([CMD, '-a'])).toThrowError(/must be a number: -a/)
|
|
expect(() => parse([CMD, '-a', 'no-number']))
|
|
.toThrowError(/must be a number: -a/)
|
|
expect(() => parse([CMD, '--a', 'no-number']))
|
|
.toThrowError(/must be a number: --a/)
|
|
expect(parse([CMD, '-a', '10'])).toEqual({
|
|
a: 10,
|
|
})
|
|
expect(parse([CMD, '--a', '11'])).toEqual({
|
|
a: 11,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('choices', () => {
|
|
it('can enforce typed choices', () => {
|
|
const {parse} = argparse({
|
|
choice: arg('string', {
|
|
choices: ['a', 'b'],
|
|
}),
|
|
num: arg('number', {
|
|
choices: [1, 2],
|
|
}),
|
|
})
|
|
expect(() => parse([CMD, '--choice', 'c'])).toThrowError(/one of: a, b$/)
|
|
expect(() => parse([CMD, '--num', '3']))
|
|
.toThrowError(/must be one of: 1, 2$/)
|
|
expect(parse([CMD, '--choice', 'a', '--num', '1'])).toEqual({
|
|
choice: 'a',
|
|
num: 1,
|
|
})
|
|
expect(parse([CMD, '--choice', 'b', '--num', '2'])).toEqual({
|
|
choice: 'b',
|
|
num: 2,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('string[] and n', () => {
|
|
it('has a value of n = 1 by default', () => {
|
|
const {parse} = argparse({
|
|
value: {
|
|
type: 'string[]',
|
|
},
|
|
help: arg('boolean'),
|
|
}, '', exit, log)
|
|
expect(parse([CMD]).value).toEqual([])
|
|
expect(parse([CMD, '--value', 'one']).value).toEqual(['one'])
|
|
parse([CMD, '--help'])
|
|
expect(log.mock.calls[0][0]).toEqual([
|
|
`${CMD} [OPTIONS]`,
|
|
'',
|
|
'Options:',
|
|
' --value [VALUE]',
|
|
' --help boolean',
|
|
].join('\n'))
|
|
})
|
|
it('can be used to extract finite number of values', () => {
|
|
const {parse} = argparse({
|
|
value: {
|
|
type: 'string[]',
|
|
n: 3,
|
|
},
|
|
other: {
|
|
type: 'number',
|
|
alias: 'o',
|
|
},
|
|
help: arg('boolean'),
|
|
}, '', exit, log)
|
|
expect(parse([CMD]).value).toEqual([])
|
|
expect(parse([CMD, '--value', 'a', 'b', '--other', '-o', '3'])).toEqual({
|
|
value: ['a', 'b', '--other'],
|
|
other: 3,
|
|
help: false,
|
|
})
|
|
parse([CMD, '--help'])
|
|
expect(log.mock.calls[0][0]).toEqual([
|
|
`${CMD} [OPTIONS]`,
|
|
'',
|
|
'Options:',
|
|
' --value [VALUE1 VALUE2 VALUE3]',
|
|
' -o, --other number',
|
|
' --help boolean',
|
|
].join('\n'))
|
|
})
|
|
it('can be used to collect any remaining arguments when n = "+"', () => {
|
|
const {parse} = argparse({
|
|
value: arg('string[]', {n: '+', required: true}),
|
|
other: arg('number'),
|
|
help: arg('boolean'),
|
|
}, '', exit, log)
|
|
expect(() => parse([CMD])).toThrowError(/Missing required args: value/)
|
|
expect(parse([CMD, '--value', 'a', '--other', '3'])).toEqual({
|
|
value: ['a', '--other', '3'],
|
|
other: NaN,
|
|
help: false,
|
|
})
|
|
expect(parse([CMD, '--other', '2', '--value', 'a', '--other', '3']))
|
|
.toEqual({
|
|
value: ['a', '--other', '3'],
|
|
other: 2,
|
|
help: false,
|
|
})
|
|
parse([CMD, '--help'])
|
|
expect(log.mock.calls[0][0]).toEqual([
|
|
`${CMD} [OPTIONS]`,
|
|
'',
|
|
'Options:',
|
|
' --value VALUE... (required)',
|
|
' --other number',
|
|
' --help boolean',
|
|
].join('\n'))
|
|
})
|
|
it('can collect remaining positional arguments when n = "*"', () => {
|
|
const {parse} = argparse({
|
|
value: arg('string[]', {n: '*', required: true, positional: true}),
|
|
other: arg('number'),
|
|
help: arg('boolean'),
|
|
}, '', exit, log)
|
|
expect(parse([CMD, 'a', 'b']).value).toEqual(['a', 'b'])
|
|
expect(() => parse([CMD, '--other', '3']).value)
|
|
.toThrowError(/Missing.*: value/)
|
|
expect(parse([CMD, '--other', '2', '--', '--other', '3'])).toEqual({
|
|
value: ['--other', '3'],
|
|
other: 2,
|
|
help: false,
|
|
})
|
|
expect(parse([CMD, '--', '--other', '3'])).toEqual({
|
|
value: ['--other', '3'],
|
|
other: NaN,
|
|
help: false,
|
|
})
|
|
expect(parse([CMD, '--other', '3', 'a', 'b', 'c'])).toEqual({
|
|
value: ['a', 'b', 'c'],
|
|
other: 3,
|
|
help: false,
|
|
})
|
|
parse([CMD, '--help'])
|
|
expect(log.mock.calls[0][0]).toEqual([
|
|
`${CMD} [OPTIONS] [VALUE...]`,
|
|
'',
|
|
'Positional arguments:',
|
|
' VALUE string[] (required)',
|
|
'',
|
|
'Options:',
|
|
' --other number',
|
|
' --help boolean',
|
|
].join('\n'))
|
|
})
|
|
})
|
|
|
|
describe('positional', () => {
|
|
it('can be defined', () => {
|
|
const {parse} = argparse({
|
|
a: {
|
|
type: 'number',
|
|
positional: true,
|
|
},
|
|
}, '', exit, log)
|
|
expect(parse([CMD]).a).toBe(NaN)
|
|
expect(parse([CMD, '12']).a).toBe(12)
|
|
parse([CMD, '--help'])
|
|
expect(log.mock.calls[0][0]).toEqual(`${CMD} [A]
|
|
|
|
Positional arguments:
|
|
A number`)
|
|
})
|
|
it('works with booleans', () => {
|
|
const {parse} = argparse({
|
|
a: {
|
|
type: 'boolean',
|
|
positional: true,
|
|
},
|
|
})
|
|
expect(parse([CMD]).a).toBe(false)
|
|
expect(parse([CMD, 'true']).a).toBe(true)
|
|
expect(parse([CMD, 'false']).a).toBe(false)
|
|
expect(() => parse([CMD, 'invalid'])).toThrowError(/true or false/)
|
|
})
|
|
it('works with strings', () => {
|
|
const {parse} = argparse({
|
|
a: {
|
|
type: 'string',
|
|
positional: true,
|
|
},
|
|
})
|
|
expect(parse([CMD]).a).toBe('')
|
|
expect(parse([CMD, 'a']).a).toBe('a')
|
|
})
|
|
it('works with multiple positionals', () => {
|
|
const {parse} = argparse({
|
|
a: {
|
|
type: 'string',
|
|
positional: true,
|
|
},
|
|
b: {
|
|
type: 'string',
|
|
positional: true,
|
|
},
|
|
})
|
|
expect(parse([CMD, 'aaa', 'bbb'])).toEqual({
|
|
a: 'aaa',
|
|
b: 'bbb',
|
|
})
|
|
})
|
|
it('works amongst regular arguments', () => {
|
|
const {parse} = argparse({
|
|
arg1: {
|
|
type: 'string',
|
|
},
|
|
arg2: {
|
|
type: 'number',
|
|
positional: true,
|
|
},
|
|
arg3: {
|
|
type: 'string',
|
|
},
|
|
})
|
|
expect(parse([CMD, '--arg1', 'one', '--arg3', 'three', '2'])).toEqual({
|
|
arg1: 'one',
|
|
arg2: 2,
|
|
arg3: 'three',
|
|
})
|
|
expect(parse([CMD, '2'])).toEqual({
|
|
arg1: '',
|
|
arg2: 2,
|
|
arg3: '',
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('help', () => {
|
|
it('prints help string and exits', () => {
|
|
const {parse} = argparse({
|
|
one: arg('string'),
|
|
two: arg('number'),
|
|
three: arg('boolean'),
|
|
help: arg('boolean'),
|
|
}, 'This command does something', exit, log)
|
|
expect(exit.mock.calls.length).toBe(0)
|
|
parse([CMD, '--help'])
|
|
expect(exit.mock.calls.length).toBe(1)
|
|
expect(log.mock.calls[0][0]).toEqual([
|
|
`${CMD} [OPTIONS]`,
|
|
'',
|
|
'This command does something',
|
|
'',
|
|
'Options:',
|
|
' --one string',
|
|
' --two number',
|
|
' --three boolean',
|
|
' --help boolean',
|
|
].join('\n'))
|
|
})
|
|
it('returns help string with alias, description, and samples', () => {
|
|
const {parse} = argparse({
|
|
one: arg('string', {
|
|
description: 'first argument',
|
|
required: true,
|
|
choices: ['choice-1', 'choice-2'],
|
|
default: 'choice-1',
|
|
alias: 'o',
|
|
}),
|
|
two: arg('number', {
|
|
positional: true,
|
|
required: true,
|
|
}),
|
|
three: arg('number', {
|
|
positional: true,
|
|
}),
|
|
help: arg('boolean'),
|
|
}, '', exit, log)
|
|
expect(exit.mock.calls.length).toBe(0)
|
|
parse([CMD, '--help'])
|
|
expect(exit.mock.calls.length).toBe(1)
|
|
expect(log.mock.calls[0][0]).toEqual([
|
|
`${CMD} [OPTIONS] TWO [THREE]`,
|
|
'',
|
|
'Positional arguments:',
|
|
' TWO number (required)',
|
|
' THREE number',
|
|
'',
|
|
'Options:',
|
|
' -o, --one string first argument ' +
|
|
'(required, default: choice-1, choices: choice-1,choice-2)',
|
|
' --help boolean',
|
|
].join('\n'))
|
|
})
|
|
|
|
})
|
|
|
|
it('throws when required args missing', () => {
|
|
expect(() => argparse({
|
|
one: {
|
|
type: 'string',
|
|
required: true,
|
|
},
|
|
}).parse([CMD])).toThrowError(/missing required/i)
|
|
})
|
|
|
|
it('throws when arg type is unknown', () => {
|
|
expect(() => argparse({
|
|
a: {
|
|
type: 'test',
|
|
} as any, // eslint-disable-line
|
|
}).parse([CMD, '-a'])).toThrowError(/Unknown type: test/)
|
|
})
|
|
|
|
})
|