Skip to main content
Module

x/yargs/test/command.cjs

yargs the modern, pirate-themed successor to optimist.
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169
/* global describe, it, beforeEach *//* eslint-disable no-unused-vars */'use strict';const assert = require('assert');const yargs = require('../index.cjs');const expect = require('chai').expect;const checkOutput = require('./helpers/utils.cjs').checkOutput;
require('chai').should();const noop = () => {};async function wait() { return new Promise(resolve => { setTimeout(resolve, 10); });}
describe('Command', () => { beforeEach(() => { yargs.getInternalMethods().reset(); });
describe('positional arguments', () => { it('parses command string and populates optional and required positional arguments', () => { const y = yargs([]).command( 'foo <bar> [awesome]', 'my awesome command', yargs => yargs ); const command = y.getInternalMethods().getCommandInstance(); const handlers = command.getCommandHandlers(); handlers.foo.demanded.should.deep.include({ cmd: ['bar'], variadic: false, }); handlers.foo.optional.should.deep.include({ cmd: ['awesome'], variadic: false, }); });
it('populates inner argv with positional arguments', done => { yargs('foo hello world') .command('foo <bar> [awesome]', 'my awesome command', noop, argv => { argv._.should.include('foo'); argv.bar.should.equal('hello'); argv.awesome.should.equal('world'); return done(); }) .parse(); });
it('populates outer argv with positional arguments when unknown-options-as-args is not set', () => { const argv = yargs('foo hello world') .command('foo <bar> [awesome]') .parse();
argv._.should.include('foo'); argv.should.have.property('bar', 'hello'); argv.should.have.property('awesome', 'world'); });
it('populates outer argv with positional arguments when unknown-options-as-args is set', () => { const argv = yargs('foo hello world') .command('foo <bar> [awesome]') .parserConfiguration({'unknown-options-as-args': true}) .parse();
argv._.should.include('foo'); argv.should.have.property('bar', 'hello'); argv.should.have.property('awesome', 'world'); });
it('populates argv with camel-case variants of arguments when possible', () => { const argv = yargs('foo hello world') .command('foo <foo-bar> [baz-qux]') .parse();
argv._.should.include('foo'); argv['foo-bar'].should.equal('hello'); argv.fooBar.should.equal('hello'); argv.bazQux.should.equal('world'); argv['baz-qux'].should.equal('world'); });
it('populates argv with camel-case variants of variadic args when possible', () => { const argv = yargs('foo hello world !') .command('foo <foo-bar> [baz-qux..]') .parse();
argv._.should.include('foo'); argv['foo-bar'].should.equal('hello'); argv.fooBar.should.equal('hello'); argv.bazQux.should.deep.equal(['world', '!']); argv['baz-qux'].should.deep.equal(['world', '!']); });
it("populates subcommand's inner argv with positional arguments", () => { yargs('foo bar hello world') .command('foo', 'my awesome command', yargs => yargs.command( 'bar <greeting> [recipient]', 'subcommands are cool', noop, argv => { argv._.should.deep.equal(['foo', 'bar']); argv.greeting.should.equal('hello'); argv.recipient.should.equal('world'); } ) ) .parse(); });
it('ignores positional args for aliases', () => { const y = yargs([]).command( ['foo [awesome]', 'wat <yo>'], 'my awesome command' ); const command = y.getInternalMethods().getCommandInstance(); const handlers = command.getCommandHandlers(); handlers.foo.optional.should.deep.include({ cmd: ['awesome'], variadic: false, }); handlers.foo.demanded.should.deep.equal([]); expect(handlers.wat).to.equal(undefined); command.getCommands().should.deep.equal(['foo', 'wat']); });
it('does not overwrite existing values in argv for keys that are not positional', () => { const argv = yargs('foo foo.js --reporter=html') .command('foo <file>') .default('reporter', 'text') .parse(); argv.file.should.equal('foo.js'); argv.reporter.should.equal('html'); });
// bug reported by @boneskull during mocha migration. it('does not load config twice when command executed', () => { let parseCount = 0; yargs('cmd --config=.foo.json') .command( '$0 [foo..]', 'does a thing', yargs => yargs .option('config', { default: '.foo.json', }) .positional('foo', { description: 'bar', }) .config('config', filepath => { parseCount++; return {}; }), argv => {} ) .parse(); parseCount.should.equal(1); });
// see: https://github.com/yargs/yargs/issues/1457 it('handles -- in conjunction with positional arguments', () => { let called = false; const argv = yargs('foo hello world series -- apple banana') .command( 'foo <bar> [awesome...]', 'my awesome command', noop, argv2 => { argv2.bar.should.eql('hello'); argv2.awesome.should.eql(['world', 'series']); argv2._.should.eql(['foo', 'apple', 'banana']); called = true; } ) .parse(); argv.bar.should.eql('hello'); argv.awesome.should.eql(['world', 'series']); argv._.should.eql(['foo', 'apple', 'banana']); called.should.equal(true); });
// see: https://github.com/yargs/yargs/issues/1457 it('continues to support populate-- for commands, post #1457', () => { let called = false; const argv = yargs('foo hello world series -- apple banana') .command( 'foo <bar> [awesome...]', 'my awesome command', noop, argv2 => { argv2.bar.should.eql('hello'); argv2.awesome.should.eql(['world', 'series']); argv2._.should.eql(['foo']); argv2['--'].should.eql(['apple', 'banana']); called = true; } ) .parserConfiguration({ 'populate--': true, }) .parse(); argv.bar.should.eql('hello'); argv.awesome.should.eql(['world', 'series']); argv._.should.eql(['foo']); argv['--'].should.eql(['apple', 'banana']); called.should.equal(true); }); });
describe('variadic', () => { it('allows required arguments to be variadic', () => { const argv = yargs('foo /root file1 file2 file3') .command('foo <root> <files..>') .parse();
argv.root.should.equal('/root'); argv.files.should.deep.equal(['file1', 'file2', 'file3']); });
it('allows optional arguments to be variadic', () => { const argv = yargs('foo /root file1 file2 file3') .command('foo <root> [files..]') .parse();
argv.root.should.equal('/root'); argv.files.should.deep.equal(['file1', 'file2', 'file3']); });
it('fails if required arguments are missing', done => { yargs('foo /root') .command('foo <root> <files..>') .fail(err => { err.should.match(/Not enough non-option arguments/); return done(); }) .parse(); });
it('does not fail if zero optional arguments are provided', () => { const argv = yargs('foo /root').command('foo <root> [files...]').parse();
argv.root.should.equal('/root'); argv.files.should.deep.equal([]); });
it('only allows the last argument to be variadic', () => { const argv = yargs('foo /root file1 file2') .command('foo <root..> <file>') .parse();
argv.root.should.equal('/root'); argv.file.should.equal('file1'); argv._.should.include('file2'); });
// addresses: https://github.com/yargs/yargs/issues/1246 it('allows camel-case, variadic arguments, and strict mode to be combined', () => { const argv = yargs('ls one two three') .command('ls [expandMe...]') .strict() .parse();
argv.expandMe.should.deep.equal(['one', 'two', 'three']); argv['expand-me'].should.deep.equal(['one', 'two', 'three']); }); });
describe('missing positional arguments', () => { it('fails if a required argument is missing', done => { const argv = yargs('foo hello') .command('foo <bar> <awesome>') .fail(err => { err.should.match(/got 1, need at least 2/); return done(); }) .parse();
argv.bar.should.equal('hello'); });
it('does not fail if optional argument is missing', () => { const argv = yargs('foo hello').command('foo <bar> [awesome]').parse();
expect(argv.awesome).to.equal(undefined); argv.bar.should.equal('hello'); }); });
describe('API', () => { it('accepts string, string as first 2 arguments', () => { const cmd = 'foo'; const desc = "i'm not feeling very creative at the moment"; const isDefault = false; const aliases = []; const deprecated = false;
const y = yargs([]).command(cmd, desc); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands[0].should.deep.equal([ cmd, desc, isDefault, aliases, deprecated, ]); });
it('accepts array, string as first 2 arguments', () => { const aliases = ['bar', 'baz']; const cmd = 'foo <qux>'; const desc = "i'm not feeling very creative at the moment"; const isDefault = false; const deprecated = false;
const y = yargs([]).command([cmd].concat(aliases), desc); const usageCommands = y .getInternalMethods() .getUsageInstance() .getCommands(); usageCommands[0].should.deep.equal([ cmd, desc, isDefault, aliases, deprecated, ]); const cmdCommands = y .getInternalMethods() .getCommandInstance() .getCommands(); cmdCommands.should.deep.equal(['foo', 'bar', 'baz']); });
it('accepts string, boolean as first 2 arguments', () => { const cmd = 'foo'; const desc = false;
const y = yargs([]).command(cmd, desc); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands.should.deep.equal([]); });
it('accepts array, boolean as first 2 arguments', () => { const aliases = ['bar', 'baz']; const cmd = 'foo <qux>'; const desc = false;
const y = yargs([]).command([cmd].concat(aliases), desc); const usageCommands = y .getInternalMethods() .getUsageInstance() .getCommands(); usageCommands.should.deep.equal([]); const cmdCommands = y .getInternalMethods() .getCommandInstance() .getCommands(); cmdCommands.should.deep.equal(['foo', 'bar', 'baz']); });
it('accepts function as 3rd argument', () => { const cmd = 'foo'; const desc = "i'm not feeling very creative at the moment"; const builder = yargs => yargs;
const y = yargs([]).command(cmd, desc, builder); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(cmd); handlers.foo.builder.should.equal(builder); });
it('accepts options object as 3rd argument', () => { const cmd = 'foo'; const desc = "i'm not feeling very creative at the moment"; const builder = { hello: {default: 'world'}, };
const y = yargs([]).command(cmd, desc, builder); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(cmd); handlers.foo.builder.should.equal(builder); });
it('accepts module (with builder function and handler function) as 3rd argument', () => { const cmd = 'foo'; const desc = "i'm not feeling very creative at the moment"; const module = { builder(yargs) { return yargs; }, handler(argv) {}, };
const y = yargs([]).command(cmd, desc, module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(cmd); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); });
it('accepts module (with builder object and handler function) as 3rd argument', () => { const cmd = 'foo'; const desc = "i'm not feeling very creative at the moment"; const module = { builder: { hello: {default: 'world'}, }, handler(argv) {}, };
const y = yargs([]).command(cmd, desc, module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(cmd); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); });
it('accepts module (describe key, builder function) as 1st argument', () => { const module = { command: 'foo', describe: "i'm not feeling very creative at the moment", builder(yargs) { return yargs; }, handler(argv) {}, }; const isDefault = false; const aliases = []; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands[0].should.deep.equal([ module.command, module.describe, isDefault, aliases, deprecated, ]); });
it('accepts module (description key, builder function) as 1st argument', () => { const module = { command: 'foo', description: "i'm not feeling very creative at the moment", builder(yargs) { return yargs; }, handler(argv) {}, }; const isDefault = false; const aliases = []; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands[0].should.deep.equal([ module.command, module.description, isDefault, aliases, deprecated, ]); });
it('accepts module (desc key, builder function) as 1st argument', () => { const module = { command: 'foo', desc: "i'm not feeling very creative at the moment", builder(yargs) { return yargs; }, handler(argv) {}, }; const isDefault = false; const aliases = []; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands[0].should.deep.equal([ module.command, module.desc, isDefault, aliases, deprecated, ]); });
it('accepts module (false describe, builder function) as 1st argument', () => { const module = { command: 'foo', describe: false, builder(yargs) { return yargs; }, handler(argv) {}, };
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands.should.deep.equal([]); });
it('accepts module (missing describe, builder function) as 1st argument', () => { const module = { command: 'foo', builder(yargs) { return yargs; }, handler(argv) {}, };
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands.should.deep.equal([]); });
it('accepts module (describe key, builder object) as 1st argument', () => { const module = { command: 'foo', describe: "i'm not feeling very creative at the moment", builder: { hello: { default: 'world', }, }, handler(argv) {}, }; const isDefault = false; const aliases = []; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands[0].should.deep.equal([ module.command, module.describe, isDefault, aliases, deprecated, ]); });
it('accepts module (missing handler function) as 1st argument', () => { const module = { command: 'foo', describe: "i'm not feeling very creative at the moment", builder: { hello: { default: 'world', }, }, }; const isDefault = false; const aliases = []; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); expect(typeof handlers.foo.handler).to.equal('function'); const commands = y.getInternalMethods().getUsageInstance().getCommands(); commands[0].should.deep.equal([ module.command, module.describe, isDefault, aliases, deprecated, ]); });
it('accepts module (with command array) as 1st argument', () => { const module = { command: ['foo <qux>', 'bar', 'baz'], describe: "i'm not feeling very creative at the moment", builder(yargs) { return yargs; }, handler(argv) {}, }; const isDefault = false; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command[0]); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const usageCommands = y .getInternalMethods() .getUsageInstance() .getCommands(); usageCommands[0].should.deep.equal([ module.command[0], module.describe, isDefault, ['bar', 'baz'], deprecated, ]); const cmdCommands = y .getInternalMethods() .getCommandInstance() .getCommands(); cmdCommands.should.deep.equal(['foo', 'bar', 'baz']); });
it('accepts module (with command string and aliases array) as 1st argument', () => { const module = { command: 'foo <qux>', aliases: ['bar', 'baz'], describe: "i'm not feeling very creative at the moment", builder(yargs) { return yargs; }, handler(argv) {}, }; const isDefault = false; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const usageCommands = y .getInternalMethods() .getUsageInstance() .getCommands(); usageCommands[0].should.deep.equal([ module.command, module.describe, isDefault, module.aliases, deprecated, ]); const cmdCommands = y .getInternalMethods() .getCommandInstance() .getCommands(); cmdCommands.should.deep.equal(['foo', 'bar', 'baz']); });
it('accepts module (with command array and aliases array) as 1st argument', () => { const module = { command: ['foo <qux>', 'bar'], aliases: ['baz', 'nat'], describe: "i'm not feeling very creative at the moment", builder(yargs) { return yargs; }, handler(argv) {}, }; const isDefault = false; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command[0]); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const usageCommands = y .getInternalMethods() .getUsageInstance() .getCommands(); usageCommands[0].should.deep.equal([ module.command[0], module.describe, isDefault, ['bar', 'baz', 'nat'], deprecated, ]); const cmdCommands = y .getInternalMethods() .getCommandInstance() .getCommands(); cmdCommands.should.deep.equal(['foo', 'bar', 'baz', 'nat']); });
it('accepts module (with command string and aliases string) as 1st argument', () => { const module = { command: 'foo <qux>', aliases: 'bar', describe: "i'm not feeling very creative at the moment", builder(yargs) { return yargs; }, handler(argv) {}, }; const isDefault = false; const deprecated = false;
const y = yargs([]).command(module); const handlers = y .getInternalMethods() .getCommandInstance() .getCommandHandlers(); handlers.foo.original.should.equal(module.command); handlers.foo.builder.should.equal(module.builder); handlers.foo.handler.should.equal(module.handler); const usageCommands = y .getInternalMethods() .getUsageInstance() .getCommands(); usageCommands[0].should.deep.equal([ module.command, module.describe, isDefault, ['bar'], deprecated, ]); const cmdCommands = y .getInternalMethods() .getCommandInstance() .getCommands(); cmdCommands.should.deep.equal(['foo', 'bar']); });
it('accepts deprecated as 5th argument', () => { const command = 'command'; const description = 'description'; const isDefault = false; const aliases = []; const deprecated = false; const y = yargs([]).command( command, description, {}, () => {}, [], deprecated ); const usageCommands = y .getInternalMethods() .getUsageInstance() .getCommands(); usageCommands[0].should.deep.equal([ command, description, isDefault, aliases, deprecated, ]); }); });
describe('commandDir', () => { it('supports relative dirs', () => { const r = checkOutput(() => yargs('--help').wrap(null).commandDir('fixtures/cmddir').parse() ); r.exit.should.equal(true); r.exitCode.should.equal(0); r.errors.length.should.equal(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'usage [command]', 'Commands:', ' usage dream [command] [opts] Go to sleep and dream', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); });
it('supports nested subcommands', () => { const r = checkOutput( () => yargs('dream --help') .wrap(null) .commandDir('fixtures/cmddir') .parse(), ['./command'] ); r.exit.should.equal(true); r.errors.length.should.equal(0); r.logs[0] .split(/\n+/) .should.deep.equal([ 'command dream [command] [opts]', 'Go to sleep and dream', 'Commands:', ' command dream of-memory <memory> Dream about a specific memory', ' command dream within-a-dream [command] [opts] Dream within a dream', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --shared Is the dream shared with others? [boolean]', ' --extract Attempt extraction? [boolean]', ]); });
it('supports a "recurse" boolean option', () => { const r = checkOutput(() => yargs('--help') .wrap(null) .commandDir('fixtures/cmddir', {recurse: true}) .parse() ); r.exit.should.equal(true); r.errors.length.should.equal(0); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'usage [command]', 'Commands:', ' usage limbo [opts] Get lost in pure subconscious', ' usage inception [command] [opts] Enter another dream, where inception is possible', ' usage within-a-dream [command] [opts] Dream within a dream', ' usage dream [command] [opts] Go to sleep and dream', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); });
it('supports a "visit" function option', () => { let commandObject; let pathToFile; let filename; const r = checkOutput(() => yargs('--help') .wrap(null) .commandDir('fixtures/cmddir', { visit(_commandObject, _pathToFile, _filename) { commandObject = _commandObject; pathToFile = _pathToFile; filename = _filename; return false; // exclude command }, }) .parse() ); commandObject.should.have .property('command') .and.equal('dream [command] [opts]'); commandObject.should.have .property('desc') .and.equal('Go to sleep and dream'); commandObject.should.have.property('builder'); commandObject.should.have.property('handler'); pathToFile.should.contain( require('path').join('test', 'fixtures', 'cmddir', 'dream.js') ); filename.should.equal('dream.js'); r.exit.should.equal(true); r.errors.length.should.equal(0); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); });
it('detects and ignores cyclic dir references', () => { const r = checkOutput( () => yargs('cyclic --help') .wrap(null) .commandDir('fixtures/cmddir_cyclic') .parse(), ['./command'] ); r.exit.should.equal(true); r.errors.length.should.equal(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'command cyclic', 'Attempts to (re)apply its own dir', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); });
it("derives 'command' string from filename when not exported", () => { const r = checkOutput(() => yargs('--help').wrap(null).commandDir('fixtures/cmddir_noname').parse() ); r.exit.should.equal(true); r.errors.length.should.equal(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'usage [command]', 'Commands:', ' usage nameless Command name derived from module filename', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); });
describe('help command', () => { it('displays command help appropriately', () => { const sub = { command: 'sub', desc: 'Run the subcommand', builder: {}, handler(argv) {}, };
const cmd = { command: 'cmd <sub>', desc: 'Try a command', builder(yargs) { return yargs.command(sub); }, handler(argv) {}, };
const helpCmd = checkOutput( () => yargs('help cmd').wrap(null).command(cmd).parse(), ['./command'] );
const cmdHelp = checkOutput( () => yargs('cmd help').wrap(null).command(cmd).parse(), ['./command'] );
const helpCmdSub = checkOutput( () => yargs('help cmd sub').wrap(null).command(cmd).parse(), ['./command'] );
const cmdHelpSub = checkOutput( () => yargs('cmd help sub').wrap(null).command(cmd).parse(), ['./command'] );
const cmdSubHelp = checkOutput( () => yargs('cmd sub help').wrap(null).command(cmd).parse(), ['./command'] );
const expectedCmd = [ 'command cmd <sub>', 'Try a command', 'Commands:', ' command cmd sub Run the subcommand', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ];
const expectedSub = [ 'command cmd sub', 'Run the subcommand', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ];
// no help is output if help isn't last // positional argument. helpCmd.logs.should.eql([]); helpCmdSub.logs.should.eql([]); cmdHelpSub.logs.should.eql([]);
// shows help if it is the last positional argument. cmdHelp.logs.join('\n').split(/\n+/).should.deep.equal(expectedCmd); cmdSubHelp.logs.join('\n').split(/\n+/).should.deep.equal(expectedSub); }); });
// see: https://github.com/yargs/yargs/pull/553 it('preserves top-level envPrefix', () => { process.env.FUN_DIP_STICK = 'yummy'; process.env.FUN_DIP_POWDER = 'true'; yargs('eat') .env('FUN_DIP') .global('stick') // this does not actually need to be global .command( 'eat', 'Adult supervision recommended', yargs => yargs.boolean('powder').exitProcess(false), argv => { argv.powder.should.equal(true); argv.stick.should.equal('yummy'); } ) .exitProcess(false) .parse(); });
// addresses https://github.com/yargs/yargs/issues/514. it('respects order of positional arguments when matching commands', () => { const output = []; yargs('bar foo') .command('foo', 'foo command', yargs => { output.push('foo'); }) .command('bar', 'bar command', yargs => { output.push('bar'); }) .parse();
output.should.include('bar'); output.should.not.include('foo'); });
// addresses https://github.com/yargs/yargs/issues/558 it('handles positional arguments if command is invoked using .parse()', () => { const y = yargs([]).command('foo <second>', 'the foo command', {}); const argv = y.parse(['foo', 'bar']); argv.second.should.equal('bar'); });
// addresses https://github.com/yargs/yargs/issues/710 it('invokes command handler repeatedly if parse() is called multiple times', () => { let counter = 0; const y = yargs([]).command('foo', 'the foo command', {}, argv => { counter++; }); y.parse(['foo']); y.parse(['foo']); counter.should.equal(2); });
// addresses: https://github.com/yargs/yargs/issues/776 it('allows command handler to be invoked repeatedly when help is enabled', done => { let counter = 0; const y = yargs([]).command('foo', 'the foo command', {}, argv => { counter++; }); y.parse(['foo'], noop); y.parse(['foo'], () => { counter.should.equal(2); return done(); }); });
// addresses https://github.com/yargs/yargs/issues/522 it('does not require builder function to return', () => { const argv = yargs('yo') .command( 'yo [someone]', 'Send someone a yo', yargs => { yargs.default('someone', 'Pat'); }, argv => { argv.should.have.property('someone').and.equal('Pat'); } ) .parse(); argv.should.have.property('someone').and.equal('Pat'); });
it('allows builder function to parse argv without returning', () => { const argv = yargs('yo Jude') .command( 'yo <someone>', 'Send someone a yo', yargs => { yargs.parse(); }, argv => { argv.should.have.property('someone').and.equal('Jude'); } ) .parse(); argv.should.have.property('someone').and.equal('Jude'); });
it('allows builder function to return parsed argv', () => { const argv = yargs('yo Leslie') .command( 'yo <someone>', 'Send someone a yo', yargs => yargs.parse(), argv => { argv.should.have.property('someone').and.equal('Leslie'); } ) .parse(); argv.should.have.property('someone').and.equal('Leslie'); });
// addresses https://github.com/yargs/yargs/issues/540 it('ignores extra spaces in command string', () => { const y = yargs([]).command( 'foo [awesome]', 'my awesome command', yargs => yargs ); const command = y.getInternalMethods().getCommandInstance(); const handlers = command.getCommandHandlers(); handlers.foo.demanded.should.not.include({ cmd: '', variadic: false, }); handlers.foo.demanded.should.have.lengthOf(0); });
it('executes a command via alias', () => { let commandCalled = false; const argv = yargs('hi world') .command(['hello <someone>', 'hi'], 'Say hello', {}, argv => { commandCalled = true; argv.should.have.property('someone').and.equal('world'); }) .parse(); argv.should.have.property('someone').and.equal('world'); commandCalled.should.equal(true); });
describe('positional aliases', () => { it('allows an alias to be defined for a required positional argument', () => { const argv = yargs('yo bcoe 113993') .command('yo <user | email> [ssn]', 'Send someone a yo') .parse(); argv.user.should.equal('bcoe'); argv.email.should.equal('bcoe'); argv.ssn.should.equal(113993); });
it('allows an alias to be defined for an optional positional argument', () => { let argv; yargs('yo 113993') .command('yo [ssn|sin]', 'Send someone a yo', {}, _argv => { argv = _argv; }) .parse(); argv.ssn.should.equal(113993); argv.sin.should.equal(113993); });
it('allows several aliases to be defined for a required positional argument', () => { const argv = yargs('yo bcoe 113993') .command('yo <user | email | id> [ssn]', 'Send someone a yo', yargs => yargs.alias('user', 'somethingElse') ) .parse(); argv.user.should.equal('bcoe'); argv.email.should.equal('bcoe'); argv.id.should.equal('bcoe'); argv.somethingElse.should.equal('bcoe'); argv.ssn.should.equal(113993); });
it('allows several aliases to be defined for an optional positional argument', () => { let argv; yargs('yo 113993') .command( 'yo [ssn|sin|id]', 'Send someone a yo', yargs => yargs.alias('ssn', 'somethingElse'), _argv => { argv = _argv; } ) .parse(); argv.ssn.should.equal(113993); argv.sin.should.equal(113993); argv.id.should.equal(113993); argv.somethingElse.should.equal(113993); });
it('allows variadic and positional arguments to be combined', () => { const parser = yargs.command( 'yo <user|email> [ ssns | sins... ]', 'Send someone a yo' ); const argv = parser.parse('yo bcoe 113993 112888'); argv.user.should.equal('bcoe'); argv.email.should.equal('bcoe'); argv.ssns.should.deep.equal([113993, 112888]); argv.sins.should.deep.equal([113993, 112888]); }); });
describe('global parsing hints', () => { describe('config', () => { it('does not load config for command if global is false', done => { yargs('command --foo ./package.json') .command('command', 'a command', {}, argv => { expect(argv.license).to.equal(undefined); return done(); }) .config('foo') .global('foo', false) .parse(); });
it('loads config for command by default', done => { yargs('command --foo ./package.json') .command('command', 'a command', {}, argv => { argv.license.should.equal('MIT'); return done(); }) .config('foo') .parse(); }); });
describe('validation', () => { it('resets implies logic for command if global is false', done => { yargs('command --foo 99') .command('command', 'a command', {}, argv => { argv.foo.should.equal(99); return done(); }) .implies('foo', 'bar') .global('foo', false) .parse(); });
it('applies conflicts logic for command by default', done => { yargs('command --foo --bar') .command('command', 'a command', {}, argv => {}) .fail(msg => { msg.should.match(/mutually exclusive/); return done(); }) .conflicts('foo', 'bar') .parse(); });
it('resets conflicts logic for command if global is false', done => { yargs('command --foo --bar') .command('command', 'a command', {}, argv => { argv.foo.should.equal(true); argv.bar.should.equal(true); return done(); }) .conflicts('foo', 'bar') .global('foo', false) .parse(); });
it('applies custom checks globally by default', done => { yargs('command blerg --foo') .command('command <snuh>', 'a command') .check(argv => { argv.snuh.should.equal('blerg'); argv.foo.should.equal(true); argv._.should.include('command'); done(); return true; }) .parse(); });
it('resets custom check if global is false', () => { let checkCalled = false; yargs('command blerg --foo') .command('command <snuh>', 'a command') .check(argv => { checkCalled = true; return true; }, false) .parse(); checkCalled.should.equal(false); });
it('allows each builder to specify own middleware', () => { let checkCalled1 = 0; let checkCalled2 = 0; const y = yargs() .command('command <snuh>', 'a command', () => { yargs.check(argv => { checkCalled1++; return true; }); }) .command('command2 <snuh>', 'a second command', yargs => { yargs.check(argv => { checkCalled2++; return true; }); }); y.parse('command blerg --foo'); y.parse('command2 blerg --foo'); y.parse('command blerg --foo'); checkCalled1.should.equal(2); checkCalled2.should.equal(1); });
it('applies demandOption globally', done => { yargs('command blerg --foo') .command('command <snuh>', 'a command') .fail(msg => { msg.should.match(/Missing required argument: bar/); return done(); }) .demandOption('bar') .parse(); }); });
describe('strict', () => { it('defaults to false when not called', () => { let commandCalled = false; yargs('hi').command('hi', 'The hi command', innerYargs => { commandCalled = true; innerYargs.getStrict().should.equal(false); }); yargs.getStrict().should.equal(false); yargs.parse(); // parse and run command commandCalled.should.equal(true); });
it('can be enabled just for a command', () => { let commandCalled = false; yargs('hi').command('hi', 'The hi command', innerYargs => { commandCalled = true; innerYargs.strict().getStrict().should.equal(true); }); yargs.getStrict().should.equal(false); yargs.parse(); // parse and run command commandCalled.should.equal(true); });
it('applies strict globally by default', () => { let commandCalled = false; yargs('hi') .strict() .command('hi', 'The hi command', innerYargs => { commandCalled = true; innerYargs.getStrict().should.equal(true); }); yargs.getStrict().should.equal(true); yargs.parse(); // parse and run command commandCalled.should.equal(true); });
// address regression introduced in #766, thanks @nexdrew! it('does not fail strict check due to postional command arguments', done => { yargs() .strict() .command('hi <name>', 'The hi command') .parse('hi ben', (err, argv, output) => { expect(err).to.equal(null); return done(); }); });
// address https://github.com/yargs/yargs/issues/795 it('does not fail strict check due to postional command arguments in nested commands', done => { yargs() .strict() .command('hi', 'The hi command', yargs => { yargs.command('ben <age>', 'ben command', noop, noop); }) .parse('hi ben 99', (err, argv, output) => { expect(err).to.equal(null); return done(); }); });
it('allows a command to override global`', () => { let commandCalled = false; yargs('hi') .strict() .command('hi', 'The hi command', innerYargs => { commandCalled = true; innerYargs.strict(false).getStrict().should.equal(false); }); yargs.getStrict().should.equal(true); yargs.parse(); // parse and run command commandCalled.should.equal(true); });
it('does not fire command if validation fails', done => { let commandRun = false; yargs() .strict() .command('hi <name>', 'The hi command', noop, argv => { commandRun = true; }) .parse('hi ben --hello=world', (err, argv, output) => { commandRun.should.equal(false); err.message.should.equal('Unknown argument: hello'); return done(); }); }); });
describe('types', () => { it('applies array type globally', () => { const argv = yargs('command --foo 1 2') .command('command', 'a command') .array('foo') .parse(); argv.foo.should.eql([1, 2]); });
it('allows global setting to be disabled for array type', () => { const argv = yargs('command --foo 1 2') .command('command', 'a command') .array('foo') .global('foo', false) .parse(); argv.foo.should.eql(1); });
it('applies choices type globally', done => { yargs('command --foo 99') .command('command', 'a command') .choices('foo', [33, 88]) .fail(msg => { msg.should.match(/Choices: 33, 88/); return done(); }) .parse(); }); });
describe('aliases', () => { it('defaults to applying aliases globally', done => { yargs('command blerg --foo 22') .command('command <snuh>', 'a command', {}, argv => { argv.foo.should.equal(22); argv.bar.should.equal(22); argv.snuh.should.equal('blerg'); return done(); }) .alias('foo', 'bar') .parse(); });
it('allows global application of alias to be disabled', done => { yargs('command blerg --foo 22') .command('command <snuh>', 'a command', {}, argv => { argv.foo.should.equal(22); expect(argv.bar).to.equal(undefined); argv.snuh.should.equal('blerg'); return done(); }) .option('foo', { alias: 'bar', global: false, }) .parse(); }); });
describe('coerce', () => { it('defaults to applying coerce rules globally', done => { yargs('command blerg --foo 22') .command('command <snuh>', 'a command', {}, argv => { argv.foo.should.equal(44); argv.snuh.should.equal('blerg'); return done(); }) .coerce('foo', arg => arg * 2) .parse(); });
// addresses https://github.com/yargs/yargs/issues/794 it('should bubble errors thrown by coerce function inside commands', done => { yargs .command('foo', 'the foo command', yargs => { yargs.coerce('x', arg => { throw Error('yikes an error'); }); }) .parse('foo -x 99', err => { err.message.should.match(/yikes an error/); return done(); }); }); });
describe('defaults', () => { it('applies defaults globally', done => { yargs('command --foo 22') .command('command [snuh]', 'a command', {}, argv => { argv.foo.should.equal(22); argv.snuh.should.equal(55); return done(); }) .default('snuh', 55) .parse(); }); });
describe('describe', () => { it('flags an option as global if a description is set', done => { yargs() .command('command [snuh]', 'a command') .describe('foo', 'an awesome argument') .parse('command --help', (err, argv, output) => { if (err) return done(err); output.should.not.match(/Commands:/); output.should.match(/an awesome argument/); return done(); }); }); });
describe('help', () => { it('applies help globally', done => { yargs() .command('command [snuh]', 'a command') .describe('foo', 'an awesome argument') .help('hellllllp') .parse('command --hellllllp', (err, argv, output) => { if (err) return done(err); output.should.match(/--hellllllp {2}Show help/); return done(); }); }); });
describe('version', () => { it('applies version globally', done => { yargs() .command('command [snuh]', 'a command') .describe('foo', 'an awesome argument') .version('ver', 'show version', '9.9.9') .parse('command --ver', (err, argv, output) => { if (err) return done(err); output.should.equal('9.9.9'); return done(); }); }); });
describe('groups', () => { it('should apply custom option groups globally', done => { yargs() .command('command [snuh]', 'a command') .group('foo', 'Bad Variable Names:') .group('snuh', 'Bad Variable Names:') .describe('foo', 'foo option') .describe('snuh', 'snuh positional') .parse('command --help', (err, argv, output) => { if (err) return done(err); output.should.match(/Bad Variable Names:\W*--foo/); return done(); }); }); }); });
describe('default commands', () => { it('executes default command if no positional arguments given', done => { yargs('--foo bar') .command('*', 'default command', noop, argv => { argv.foo.should.equal('bar'); return done(); }) .parse(); });
it('executes default command if undefined positional arguments and only command', done => { yargs('baz --foo bar') .command('*', 'default command', noop, argv => { argv.foo.should.equal('bar'); argv._.should.contain('baz'); return done(); }) .parse(); });
it('executes default command if defined positional arguments and only command', done => { yargs('baz --foo bar') .command('* <target>', 'default command', noop, argv => { argv.foo.should.equal('bar'); argv.target.should.equal('baz'); return done(); }) .parse(); });
it('allows $0 as an alias for a default command', done => { yargs('9999') .command('$0 [port]', 'default command', noop, argv => { argv.port.should.equal(9999); return done(); }) .parse(); });
it('does not execute default command if another command is provided', done => { yargs('run bcoe --foo bar') .command('*', 'default command', noop, argv => {}) .command('run <name>', 'run command', noop, argv => { argv.name.should.equal('bcoe'); argv.foo.should.equal('bar'); return done(); }) .parse(); });
it('allows default command to be set as alias', done => { yargs('bcoe --foo bar') .command(['start <name>', '*'], 'start command', noop, argv => { argv._.should.eql([]); argv.name.should.equal('bcoe'); argv.foo.should.equal('bar'); return done(); }) .parse(); });
it('allows command to be run when alias is default command', done => { yargs('start bcoe --foo bar') .command(['start <name>', '*'], 'start command', noop, argv => { argv._.should.eql(['start']); argv.name.should.equal('bcoe'); argv.foo.should.equal('bar'); return done(); }) .parse(); });
it('the last default command set should take precedence', done => { yargs('bcoe --foo bar') .command(['first', '*'], 'override me', noop, noop) .command(['second <name>', '*'], 'start command', noop, argv => { argv._.should.eql([]); argv.name.should.equal('bcoe'); argv.foo.should.equal('bar'); return done(); }) .parse(); });
describe('strict', () => { it('executes default command when strict mode is enabled', done => { yargs('--foo bar') .command('*', 'default command', noop, argv => { argv.foo.should.equal('bar'); return done(); }) .option('foo', { describe: 'a foo command', }) .strict() .parse(); });
it('allows default command aliases, when strict mode is enabled', done => { yargs('bcoe --foo bar') .command(['start <name>', '*'], 'start command', noop, argv => { argv._.should.eql([]); argv.name.should.equal('bcoe'); argv.foo.should.equal('bar'); return done(); }) .strict() .option('foo', { describe: 'a foo command', }) .parse(); }); }); });
describe('deprecated command', () => { describe('using arg', () => { it('shows deprecated notice with boolean', () => { const command = 'command'; const description = 'description'; const deprecated = true; const r = checkOutput(() => { yargs('--help') .command(command, description, {}, () => {}, [], deprecated) .parse(); }); r.logs.should.match(/\[deprecated\]/); }); it('shows deprecated notice with string', () => { const command = 'command'; const description = 'description'; const deprecated = 'deprecated'; const r = checkOutput(() => { yargs('--help') .command(command, description, {}, () => {}, [], deprecated) .parse(); }); r.logs.should.match(/\[deprecated: deprecated\]/); }); }); describe('using module', () => { it('shows deprecated notice with boolean', () => { const command = 'command'; const description = 'description'; const deprecated = true; const r = checkOutput(() => { yargs('--help').command({command, description, deprecated}).parse(); }); r.logs.should.match(/\[deprecated\]/); }); it('shows deprecated notice with string', () => { const command = 'command'; const description = 'description'; const deprecated = 'deprecated'; const r = checkOutput(() => { yargs('--help').command({command, description, deprecated}).parse(); }); r.logs.should.match(/\[deprecated: deprecated\]/); }); }); });
// addresses: https://github.com/yargs/yargs/issues/819 it('should kick along [demand] configuration to commands', () => { let called = false; const r = checkOutput(() => { yargs('foo') .command('foo', 'foo command', noop, argv => { called = true; }) .option('bar', { describe: 'a foo command', demand: true, }) .parse(); }); called.should.equal(false); r.exitCode.should.equal(1); r.errors.should.match(/Missing required argument/); });
it('should support numeric commands', () => { const output = []; yargs('1') .command('1', 'numeric command', yargs => { output.push('1'); }) .parse(); output.should.include('1'); });
// see: https://github.com/yargs/yargs/issues/853 it('should not execute command if it is proceeded by another positional argument', () => { let commandCalled = false; yargs() .command('foo', 'foo command', noop, () => { commandCalled = true; }) .parse('bar foo', (err, argv) => { expect(err).to.equal(null); commandCalled.should.equal(false); argv._.should.eql(['bar', 'foo']); }); });
// see: https://github.com/yargs/yargs/issues/861 phew! that's an edge-case. it('should allow positional arguments for inner commands in strict mode, when no handler is provided', () => { yargs() .command('foo', 'outer command', yargs => { yargs.command('bar [optional]', 'inner command'); }) .strict() .parse('foo bar 33', (err, argv) => { expect(err).to.equal(null); argv.optional.should.equal(33); argv._.should.eql(['foo', 'bar']); }); });
describe('usage', () => { it('allows you to configure a default command', () => { yargs() .usage('$0 <port>', 'default command', yargs => { yargs.positional('port', { type: 'string', }); }) .parse('33', (err, argv) => { expect(err).to.equal(null); argv.port.should.equal('33'); }); });
it('throws exception if default command does not have leading $0', () => { expect(() => { yargs().usage('<port>', 'default command', yargs => { yargs.positional('port', { type: 'string', }); }); }).to.throw(/.*\.usage\(\) description must start with \$0.*/); }); });
describe('async', () => { // addresses https://github.com/yargs/yargs/issues/510 it('fails when the promise returned by the command handler rejects', done => { const error = new Error(); yargs('foo') .command('foo', 'foo command', noop, yargs => Promise.reject(error)) .fail((msg, err) => { expect(msg).to.equal(null); expect(err).to.equal(error); done(); }) .parse(); });
it('returns promise that resolves arguments once handler succeeds', async () => { let complete = false; const handler = new Promise((resolve, reject) => { setTimeout(() => { complete = true; return resolve(); }, 10); }); const parsedPromise = yargs('foo hello') .command( 'foo <pos>', 'foo command', () => {}, () => handler ) .parse(); complete.should.equal(false); const parsed = await parsedPromise; complete.should.equal(true); parsed.pos.should.equal('hello'); });
it('returns promise that can be caught, when fail(false)', async () => { let complete = false; const handler = new Promise((resolve, reject) => { setTimeout(() => { complete = true; return reject(Error('error from handler')); }, 10); }); const parsedPromise = yargs('foo hello') .command( 'foo <pos>', 'foo command', () => {}, () => handler ) .fail(false) .parse(); try { complete.should.equal(false); await parsedPromise; throw Error('unreachable'); } catch (err) { err.message.should.match(/error from handler/); complete.should.equal(true); } });
// see: https://github.com/yargs/yargs/issues/1144 it('displays error and appropriate help message when handler fails', async () => { // the bug reported in #1144 only happens when // usage.help() is called, this does not occur when // console output is suppressed. tldr; we capture // the log output: const r = await checkOutput(async () => { return yargs('foo') .command( 'foo', 'foo command', yargs => { yargs.option('bar', { describe: 'bar option', }); }, argv => { return Promise.reject(Error('foo error')); } ) .exitProcess(false) .parse(); }); const errorLog = r.errors.join('\n'); // ensure the appropriate help is displayed: errorLog.should.include('bar option'); // ensure error was displayed: errorLog.should.include('foo error'); }); });
// see: https://github.com/yargs/yargs/issues/1099 it('does not coerce number from positional with leading "+"', () => { const argv = yargs.command('$0 <phone>', '', yargs => {}).parse('+5550100'); argv.phone.should.equal('+5550100'); });
it('allows an array of commands to be provided', () => { const innerCommands = [ { command: 'c <x> <y>', describe: 'add x to y', builder: () => {}, handler: argv => { argv.output.value = argv.x + argv.y; }, }, ]; const cmds = [ { command: 'a', describe: 'numeric comamand', builder: yargs => { yargs.command(innerCommands); }, handler: () => {}, }, ]; const context = { output: {}, }; yargs().command(cmds).parse('a c 10 5', context); context.output.value.should.equal(15); });
it('allows async exception in handler to be caught', async () => { await assert.rejects( yargs(['mw']) .fail(false) .command( 'mw', 'adds func to middleware', () => {}, async () => { throw Error('not cool'); } ) .parse(), /not cool/ ); });
it('reports error if error occurs parsing positional', () => { try { yargs(['cmd', 'neat']) .fail(false) .command('cmd <foo>', 'run a foo command', yargs => { yargs.option('foo', { nargs: 3, }); }) .parse(); throw Error('unreachable'); } catch (err) { err.message.should.match(/Not enough arguments/); } });
describe('async builder', async () => { it('allows positionals to be configured asynchronously', async () => { const argvPromise = yargs(['cmd', '999']) .command('cmd <foo>', 'a test command', async yargs => { await wait(); yargs.positional('foo', { type: 'string', }); }) .parse(); (typeof argvPromise.then).should.equal('function'); const argv = await argvPromise; (typeof argv.foo).should.equal('string'); }); describe('helpOrVersionSet', () => { it('--help', async () => { let set = false; await yargs() .command('cmd <foo>', 'a test command', (yargs, helpOrVersionSet) => { set = helpOrVersionSet; if (!helpOrVersionSet) { return wait(); } }) .parse('cmd --help', () => {}); assert.strictEqual(set, true); }); }); // Refs: https://github.com/yargs/yargs/issues/1894 it('does not print to stdout when parse callback is provided', async () => { await yargs() .command('cmd <foo>', 'a test command', async () => { await wait(); }) .parse('cmd --help', (_err, argv, output) => { output.should.include('a test command'); argv.help.should.equal(true); }); }); // Refs: https://github.com/yargs/yargs/issues/1917 it('allows command to be defined in async builder', async () => { let invoked = false; await yargs('alpha beta') .strict() .command({ command: 'alpha', describe: 'A', builder: async yargs => { await wait(); yargs .command({ command: 'beta', describe: 'B', handler: () => { invoked = true; }, }) .demandCommand(1); }, }) .demandCommand(1) .parse(); assert.strictEqual(invoked, true); }); it('allows deeply nested command to be defined in async builder', async () => { let invoked = false; await yargs('alpha beta gamma') .strict() .command('alpha', 'A', async yargs => { await wait(); yargs .command({ command: 'beta', describe: 'B', builder: async yargs => { await wait(); return yargs.command( 'gamma', 'C', async () => { await wait(); }, async () => { await wait(); invoked = true; } ); }, }) .demandCommand(1); }) .demandCommand(1) .parse(); assert.strictEqual(invoked, true); }); });
describe('builder', () => { // Refs: https://github.com/yargs/yargs/issues/1042 describe('helpOrVersionSet', () => { it('--version', () => { let set = false; yargs() .command('cmd <foo>', 'a test command', (yargs, helpOrVersionSet) => { set = helpOrVersionSet; }) .parse('cmd --version', () => {}); assert.strictEqual(set, true); }); it('--help', () => { let set = false; yargs() .command('cmd <foo>', 'a test command', (yargs, helpOrVersionSet) => { set = helpOrVersionSet; }) .parse('cmd --help', () => {}); assert.strictEqual(set, true); }); it('help', () => { let set = false; yargs() .command('cmd <foo>', 'a test command', (yargs, helpOrVersionSet) => { set = helpOrVersionSet; }) .parse('cmd help', () => {}); assert.strictEqual(set, true); }); it('cmd', () => { let set = false; const argv = yargs() .command('cmd <foo>', 'a test command', (yargs, helpOrVersionSet) => { set = helpOrVersionSet; }) .parse('cmd bar', () => {}); assert.strictEqual(set, false); assert.strictEqual(argv.foo, 'bar'); }); }); });});