deno.land / x / yargs@v17.6.0-deno / test / usage.cjs
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869'use strict';/* global describe, it, beforeEach *//* eslint-disable no-unused-vars */
const checkUsage = require('./helpers/utils.cjs').checkOutput;const chalk = require('chalk');const yargs = require('../index.cjs');const expect = require('chai').expect;const {YError} = require('../build/index.cjs');
const should = require('chai').should();
const noop = () => {};async function wait(n = 10) { return new Promise(resolve => { setTimeout(resolve, n); });}
describe('usage tests', () => { beforeEach(() => { yargs.getInternalMethods().reset(); }); describe('demand options', () => { describe('using .demand()', () => { it('should show an error along with the missing arguments on demand fail', () => { const r = checkUsage(() => yargs('-x 10 -z 20') .usage('Usage: $0 -x NUM -y NUM') .demand(['x', 'y']) .wrap(null) .parse() ); r.result.should.have.property('x', 10); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -x NUM -y NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -x [required]', ' -y [required]', 'Missing required argument: y', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); it('missing argument message given if one command, but an argument not on the list is provided', () => { const r = checkUsage(() => yargs('wombat -w 10 -y 10') .usage('Usage: $0 -w NUM -m NUM') .demand(1, ['w', 'm']) .strict() .wrap(null) .parse() ); r.result.should.have.property('w', 10); r.result.should.have.property('y', 10); r.result.should.have.property('_').with.length(1); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -w NUM -m NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -w [required]', ' -m [required]', 'Missing required argument: m', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); it('missing command message if all the required arguments exist, but not enough commands are provided', () => { const r = checkUsage(() => yargs('-w 10 -y 10') .usage('Usage: $0 -w NUM -m NUM') .demand(1, ['w', 'm']) .strict() .wrap(null) .parse() ); r.result.should.have.property('w', 10); r.result.should.have.property('y', 10); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -w NUM -m NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -w [required]', ' -m [required]', 'Not enough non-option arguments: got 0, need at least 1', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); it('no failure occurs if the required arguments and the required number of commands are provided', () => { const r = checkUsage(() => yargs('wombat -w 10 -m 10') .usage('Usage: $0 -w NUM -m NUM') .command('wombat', 'wombat handlers') .demand(1, ['w', 'm']) .wrap(null) .parse() ); r.result.should.have.property('w', 10); r.result.should.have.property('m', 10); r.result.should.have.property('_').with.length(1); r.should.have.property('errors').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit', false); }); describe('using .require()', () => { it('should show an error along with the missing arguments on demand fail', () => { const r = checkUsage(() => yargs('-x 10 -z 20') .usage('Usage: $0 -x NUM -y NUM') .require(['x', 'y']) .wrap(null) .parse() ); r.result.should.have.property('x', 10); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -x NUM -y NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -x [required]', ' -y [required]', 'Missing required argument: y', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); it('missing argument message given if one command and an argument not on the list are provided', () => { const r = checkUsage(() => yargs('wombat -w 10 -y 10') .usage('Usage: $0 -w NUM -m NUM') .required(1, ['w', 'm']) .strict() .wrap(null) .parse() ); r.result.should.have.property('w', 10); r.result.should.have.property('y', 10); r.result.should.have.property('_').with.length(1); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -w NUM -m NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -w [required]', ' -m [required]', 'Missing required argument: m', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); }); it('missing command message if all the required arguments exist, but not enough commands are provided', () => { const r = checkUsage(() => yargs('-w 10 -y 10') .usage('Usage: $0 -w NUM -m NUM') .require(1, ['w', 'm']) .strict() .wrap(null) .parse() ); r.result.should.have.property('w', 10); r.result.should.have.property('y', 10); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -w NUM -m NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -w [required]', ' -m [required]', 'Not enough non-option arguments: got 0, need at least 1', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); }); it('should show an error along with a custom message on demand fail', () => { const r = checkUsage(() => yargs('-z 20') .usage('Usage: $0 -x NUM -y NUM') .demand( ['x', 'y'], 'x and y are both required to multiply all the things' ) .wrap(null) .parse() ); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -x NUM -y NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -x [required]', ' -y [required]', 'Missing required arguments: x, y', 'x and y are both required to multiply all the things', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); it('should return valid values when demand passes', () => { const r = checkUsage(() => yargs('-x 10 -y 20') .usage('Usage: $0 -x NUM -y NUM') .demand(['x', 'y']) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('x', 10); r.result.should.have.property('y', 20); r.result.should.have.property('_').with.length(0); r.should.have.property('errors').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit', false); }); it('should not show a custom message if msg is null', () => { const r = checkUsage(() => yargs('').usage('Usage: foo').demand(1, null).wrap(null).parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: foo', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', '', ]); }); // see #169. describe('min/max demanded count', () => { it("does not output an error if '_' count is within the min/max range", () => { const r = checkUsage(() => yargs(['foo', 'bar', 'apple']) .usage('Usage: foo') .demand(2, 3) .wrap(null) .parse() ); r.errors.length.should.equal(0); }); it("outputs an error if '_' count is above max", () => { const r = checkUsage(() => yargs(['foo', 'bar', 'apple', 'banana']) .usage('Usage: foo') .demand(2, 3) .wrap(null) .parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: foo', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'Too many non-option arguments: got 4, maximum of 3', ]); }); it("outputs an error if '_' count is below min", () => { const r = checkUsage(() => yargs(['foo']).usage('Usage: foo').demand(2, 3).wrap(null).parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: foo', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'Not enough non-option arguments: got 1, need at least 2', ]); }); it('allows a customer error message to be provided', () => { const r = checkUsage(() => yargs(['foo']) .usage('Usage: foo') .demand(2, 3, 'pork chop sandwiches') .wrap(null) .parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: foo', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'pork chop sandwiches', ]); }); it("shouldn't interpret the second argument as a max when it is an array", () => { const r = checkUsage(() => yargs(['koala', 'wombat', '--1']) .usage('Usage: foo') .demand(1, ['1']) .wrap(null) .parse() ); r.errors.length.should.equal(0); }); }); }); describe('deprecate options', () => { describe('using .option(x, {deprecate: [boolean|string]})', () => { it('{deprecated: true} should show [deprecated]', () => { const r = checkUsage(() => yargs('--help').option('x', {deprecated: true}).wrap(null).parse() ); r.logs[0].should.include(' -x [deprecated]'); }); it('{deprecated: string} should show [deprecated: string]', () => { const r = checkUsage(() => yargs('--help').option('x', {deprecated: 'string'}).wrap(null).parse() ); r.logs[0].should.include(' -x [deprecated: string]'); }); it('{deprecated: boolean} in sub-command', () => { const r = checkUsage(() => yargs('command --help') .option('x', {deprecated: true}) .command('command', 'command', yargs => yargs.option('y', {deprecated: true}) ) .wrap(null) .parse() ); r.logs[0].should.include(' -x [deprecated]'); r.logs[0].should.include(' -y [deprecated]'); }); it('{deprecated: string} in sub-command', () => { const r = checkUsage(() => yargs('command --help') .option('x', {deprecated: 'string'}) .command('command', 'command', yargs => yargs.option('y', {deprecated: 'string'}) ) .wrap(null) .parse() ); r.logs[0].should.include(' -x [deprecated: string]'); r.logs[0].should.include(' -y [deprecated: string]'); }); }); describe('using .deprecateOption(x, [string])', () => { it('.deprecateOption(x) should show [deprecated]', () => { const r = checkUsage(() => yargs('--help').option('x').deprecateOption('x').wrap(null).parse() ); r.logs[0].should.include(' -x [deprecated]'); }); it('.deprecateOption(x, string) should show [deprecated: string]', () => { const r = checkUsage(() => yargs('--help') .option('x') .deprecateOption('x', 'string') .wrap(null) .parse() ); r.logs[0].should.include(' -x [deprecated: string]'); }); it('.deprecateOption(x) in a sub-command', () => { const r = checkUsage(() => yargs('command --help') .option('x') .deprecateOption('x') .command('command', 'command', yargs => yargs.option('y').deprecateOption('y') ) .wrap(null) .parse() ); r.logs[0].should.include(' -x [deprecated]'); r.logs[0].should.include(' -y [deprecated]'); }); it('.deprecateOption(x, string) in a sub-command', () => { const r = checkUsage(() => yargs('command --help') .option('x') .deprecateOption('x', 'string') .command('command', 'command', yargs => yargs.option('y').deprecateOption('y', 'string') ) .wrap(null) .parse() ); r.logs[0].should.include(' -x [deprecated: string]'); r.logs[0].should.include(' -y [deprecated: string]'); }); }); }); it('should return valid values when check passes', () => { const r = checkUsage(() => yargs('-x 10 -y 20') .usage('Usage: $0 -x NUM -y NUM') .check(argv => { if (!('x' in argv)) throw Error('You forgot about -x'); if (!('y' in argv)) throw Error('You forgot about -y'); else return true; }) .parse() ); r.should.have.property('result'); r.result.should.have.property('x', 10); r.result.should.have.property('y', 20); r.result.should.have.property('_').with.length(0); r.should.have.property('errors').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit', false); }); it('should display missing arguments when check fails with a thrown exception', () => { const r = checkUsage(() => yargs('-x 10 -z 20') .usage('Usage: $0 -x NUM -y NUM') .wrap(null) .check(argv => { if (!('x' in argv)) throw Error('You forgot about -x'); if (!('y' in argv)) throw Error('You forgot about -y'); }) .parse() ); r.should.have.property('result'); r.result.should.have.property('x', 10); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -x NUM -y NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'You forgot about -y', ]); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); }); it('should display missing arguments when check fails with a return value', () => { const r = checkUsage(() => yargs('-x 10 -z 20') .usage('Usage: $0 -x NUM -y NUM') .wrap(null) .check(argv => { if (!('x' in argv)) return 'You forgot about -x'; if (!('y' in argv)) return 'You forgot about -y'; }) .parse() ); r.should.have.property('result'); r.result.should.have.property('x', 10); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.should.have.property('errors'); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -x NUM -y NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'You forgot about -y', ]); }); it('should return a valid result when check condition passes', () => { function checker(argv) { return 'x' in argv && 'y' in argv; } const r = checkUsage(() => yargs('-x 10 -y 20') .usage('Usage: $0 -x NUM -y NUM') .check(checker) .parse() ); r.should.have.property('result'); r.result.should.have.property('x', 10); r.result.should.have.property('y', 20); r.result.should.have.property('_').with.length(0); r.should.have.property('errors').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit', false); }); it('should display a failed message when check condition fails', () => { function checker(argv) { return 'x' in argv && 'y' in argv; } const r = checkUsage(() => yargs('-x 10 -z 20') .usage('Usage: $0 -x NUM -y NUM') .check(checker) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('x', 10); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.should.have.property('errors'); r.errors .join('\n') .split(/\n+/) .join('\n') .should.equal( 'Usage: usage -x NUM -y NUM\n' + 'Options:\n' + ' --help Show help [boolean]\n' + ' --version Show version number [boolean]\n' + `Argument check failed: ${checker.toString()}` ); }); describe('when exitProcess is false', () => { describe('when check fails with a thrown exception', () => { it('should display missing arguments once', () => { const r = checkUsage(() => { try { return yargs('-x 10 -z 20') .usage('Usage: $0 -x NUM -y NUM') .exitProcess(false) .wrap(null) .check(argv => { if (!('x' in argv)) throw Error('You forgot about -x'); if (!('y' in argv)) throw Error('You forgot about -y'); }) .parse(); } catch (err) { // ignore the error, we only test the output here } }); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage -x NUM -y NUM', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'You forgot about -y', ]); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(false); }); }); describe('fail()', () => { it('is called with the original error message as the first parameter', () => { const r = checkUsage(() => { return yargs() .fail(message => { console.log(message); }) .wrap(null) .check(argv => { throw new Error('foo'); }) .parse(); }); r.logs.should.deep.equal(['foo']); r.should.have.property('exit').and.equal(false); }); it('is invoked with yargs instance as third argument', () => { const r = checkUsage(() => yargs('foo') .command( 'foo', 'desc', { bar: { describe: 'bar command', }, }, argv => { throw new YError('blah'); } ) .fail((message, error, yargs) => { yargs.showHelp(); }) .wrap(null) .parse() ); r.errors[0].should.contain('bar command'); }); describe('when check() throws error', () => { it('fail() is called with the original error object as the second parameter', () => { const r = checkUsage(() => { return yargs() .fail((message, error) => { console.log(error.message); }) .wrap(null) .check(() => { throw new Error('foo'); }) .parse(); }); r.logs.should.deep.equal(['foo']); r.should.have.property('exit').and.equal(false); }); }); describe('when command() throws error', () => { it('fail() is called with the original error object as the second parameter', () => { const r = checkUsage(() => { return yargs('test') .fail(() => { console.log('is triggered last'); }) .wrap(null) .command( 'test', 'test', subYargs => { subYargs .fail((message, error) => { console.log([error.name, error.message]); }) .exitProcess(false); }, argv => { throw new YError('foo'); } ) .parse(); }); r.logs.should.deep.equal([ "[ 'YError', 'foo' ]", 'is triggered last', ]); r.should.have.property('exit').and.equal(false); }); }); it('allows "false" to be provided to prevent exit/output', () => { try { yargs() .fail(false) .wrap(null) .check(argv => { throw new Error('sync error'); }) .parse(); throw Error('unreachable'); } catch (err) { err.message.should.equal('sync error'); } }); it('does not allow "true" as argument', () => { try { yargs() .fail(true) .wrap(null) .check(argv => { throw new Error('sync error'); }) .parse(); throw Error('unreachable'); } catch (err) { err.message.should.match(/Invalid first argument/); } }); }); }); it('should return a valid result when demanding a count of non-hyphenated values', () => { const r = checkUsage(() => yargs('1 2 3 --moo') .usage('Usage: $0 [x] [y] [z] {OPTIONS}') .demand(3) .parse() ); r.should.have.property('result'); r.should.have.property('errors').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit', false); r.result.should.have.property('_').and.deep.equal([1, 2, 3]); r.result.should.have.property('moo', true); }); it('should return a failure message when not enough non-hyphenated arguments are found after a demand count', () => { const r = checkUsage(() => yargs('1 2 --moo') .usage('Usage: $0 [x] [y] [z] {OPTIONS}') .demand(3) .wrap(null) .parse() ); r.should.have.property('result'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.result.should.have.property('_').and.deep.equal([1, 2]); r.result.should.have.property('moo', true); r.should.have.property('errors'); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [x] [y] [z] {OPTIONS}', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'Not enough non-option arguments: got 2, need at least 3', ]); }); it('should return a custom failure message when not enough non-hyphenated arguments are found after a demand count', () => { const r = checkUsage(() => yargs('src --moo') .usage('Usage: $0 [x] [y] [z] {OPTIONS} <src> <dest> [extra_files...]') .demand(2, 'src and dest files are both required') .wrap(null) .parse() ); r.should.have.property('result'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.result.should.have.property('_').and.deep.equal(['src']); r.result.should.have.property('moo', true); r.should.have.property('errors'); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [x] [y] [z] {OPTIONS} <src> <dest> [extra_files...]', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'src and dest files are both required', ]); }); it('should return a valid result when setting defaults for singles', () => { const r = checkUsage(() => yargs('--foo 50 --baz 70 --powsy') .default('foo', 5) .default('bar', 6) .default('baz', 7) .parse() ); r.should.have.property('result'); r.result.should.have.property('foo', 50); r.result.should.have.property('bar', 6); r.result.should.have.property('baz', 70); r.result.should.have.property('powsy', true); r.result.should.have.property('_').with.length(0); }); it('should return a valid result when default is set for an alias', () => { const r = checkUsage(() => yargs('').alias('f', 'foo').default('f', 5).parse() ); r.should.have.property('result'); r.result.should.have.property('f', 5); r.result.should.have.property('foo', 5); r.result.should.have.property('_').with.length(0); }); it('should print a single line when failing and default is set for an alias', () => { const r = checkUsage(() => yargs('').alias('f', 'foo').default('f', 5).demand(1).wrap(null).parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo [default: 5]', 'Not enough non-option arguments: got 0, need at least 1', ]); }); it('should allow you to set default values for a hash of options', () => { const r = checkUsage(() => yargs('--foo 50 --baz 70').default({foo: 10, bar: 20, quux: 30}).parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('foo', 50); r.result.should.have.property('baz', 70); r.result.should.have.property('bar', 20); r.result.should.have.property('quux', 30); }); describe('required arguments', () => { describe('with options object', () => { it('should show a failure message if a required option is missing', () => { const r = checkUsage(() => { const opts = { foo: {description: 'foo option', alias: 'f', requiresArg: true}, bar: {description: 'bar option', alias: 'b', requiresArg: true}, }; return yargs('-f --bar 20') .usage('Usage: $0 [options]') .options(opts) .wrap(null) .parse(); }); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [options]', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ' -b, --bar bar option', 'Not enough arguments following: f', ]); }); it('should show a failure message if more than one required option is missing', () => { const r = checkUsage(() => { const opts = { foo: {description: 'foo option', alias: 'f', requiresArg: true}, bar: {description: 'bar option', alias: 'b', requiresArg: true}, }; return yargs('-f --bar') .usage('Usage: $0 [options]') .options(opts) .wrap(null) .parse(); }); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [options]', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ' -b, --bar bar option', 'Not enough arguments following: bar', ]); }); }); describe('with requiresArg method', () => { it('should show a failure message if a required option is missing', () => { const r = checkUsage(() => { const opts = { foo: {description: 'foo option', alias: 'f'}, bar: {description: 'bar option', alias: 'b'}, }; return yargs('-f --bar 20') .usage('Usage: $0 [options]') .options(opts) .requiresArg(['foo', 'bar']) .wrap(null) .parse(); }); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [options]', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ' -b, --bar bar option', 'Not enough arguments following: f', ]); }); }); it("still requires argument if 'type' hints are given", () => { const r = checkUsage(() => yargs('--foo --bar') .requiresArg('foo') .string('foo') .requiresArg('bar') .array('bar') .wrap(null) .parse() ); r.errors[2].should.equal('Not enough arguments following: bar'); }); }); describe('with strict() option set', () => { it('should fail given an option argument that is not demanded', () => { const r = checkUsage(() => { const opts = { foo: {demand: 'foo option', alias: 'f'}, bar: {demand: 'bar option', alias: 'b'}, }; return yargs('-f 10 --bar 20 --baz 30') .usage('Usage: $0 [options]') .options(opts) .strict() .wrap(null) .parse(); }); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('f', 10); r.result.should.have.property('foo', 10); r.result.should.have.property('b', 20); r.result.should.have.property('bar', 20); r.result.should.have.property('baz', 30); r.should.have.property('errors'); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [options]', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo [required]', ' -b, --bar [required]', 'Unknown argument: baz', ]); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); }); describe('with hyphens in options', () => { it('fails when an invalid argument is provided', done => { return yargs('--foo-bar') .strict() .fail(msg => { return done(); }).argv; }); it('accepts valid options', () => { const r = checkUsage(() => { const opts = { '--foo-bar': {description: 'foo bar option'}, '--bar-baz': {description: 'bar baz option'}, }; return yargs('--foo-bar --bar-baz').options(opts).strict().parse(); }); r.result.should.have.property('foo-bar', true); r.result.should.have.property('fooBar', true); r.result.should.have.property('bar-baz', true); r.result.should.have.property('barBaz', true); }); it('works with aliases', () => { const r = checkUsage(() => { const opts = { '--foo-bar': {description: 'foo bar option', alias: 'f'}, '--bar-baz': {description: 'bar baz option', alias: 'b'}, }; return yargs('--foo-bar -b').options(opts).strict().parse(); }); r.result.should.have.property('foo-bar', true); r.result.should.have.property('fooBar', true); r.result.should.have.property('f', true); r.result.should.have.property('bar-baz', true); r.result.should.have.property('barBaz', true); r.result.should.have.property('b', true); }); it('accepts mixed options with values', () => { const r = checkUsage(() => { const opts = { '--foo-bar': {description: 'foo bar option', demand: true}, '--baz': {description: 'baz option', demand: true}, }; return yargs('--foo-bar 150 --baz').options(opts).strict().parse(); }); r.result.should.have.property('foo-bar', 150); r.result.should.have.property('fooBar', 150); r.result.should.have.property('baz', true); }); }); it('should fail given an option argument without a corresponding description', () => { const r = checkUsage(() => { const opts = { foo: {description: 'foo option', alias: 'f'}, bar: {description: 'bar option', alias: 'b'}, }; return yargs('-f 10 --bar 20 --baz 30') .usage('Usage: $0 [options]') .options(opts) .strict() .wrap(null) .parse(); }); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('f', 10); r.result.should.have.property('foo', 10); r.result.should.have.property('b', 20); r.result.should.have.property('bar', 20); r.result.should.have.property('baz', 30); r.should.have.property('errors'); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [options]', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ' -b, --bar bar option', 'Unknown argument: baz', ]); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); }); // Addresses: https://github.com/yargs/yargs/issues/2033 it('should wrap whitespace in quotes if provided as an unknown argument', () => { const r = checkUsage(() => { return yargs(['--opt1=hello', ' ', '--opt2=world']) .command({ command: '$0', desc: 'default description', builder: yargs => yargs .option('opt1', {type: 'string'}) .option('opt2', {type: 'string'}), handler: noop, }) .strict() .wrap(null) .parse(); }); r.errors.should.match(/Unknown argument: " "/); }); it('should fail given multiple option arguments without corresponding descriptions', () => { const r = checkUsage(() => { const opts = { foo: {description: 'foo option', alias: 'f'}, bar: {description: 'bar option', alias: 'b'}, }; return yargs('-f 10 --bar 20 --baz 30 -q 40') .usage('Usage: $0 [options]') .options(opts) .strict() .wrap(null) .parse(); }); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('f', 10); r.result.should.have.property('foo', 10); r.result.should.have.property('b', 20); r.result.should.have.property('bar', 20); r.result.should.have.property('baz', 30); r.result.should.have.property('q', 40); r.should.have.property('errors'); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage [options]', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ' -b, --bar bar option', 'Unknown arguments: baz, q', ]); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); }); it('should pass given option arguments with corresponding descriptions', () => { const r = checkUsage(() => { const opts = { foo: {description: 'foo option'}, bar: {description: 'bar option'}, }; return yargs('--foo 10 --bar 20') .usage('Usage: $0 [options]') .options(opts) .strict() .parse(); }); r.should.have.property('result'); r.result.should.have.property('foo', 10); r.result.should.have.property('bar', 20); r.result.should.have.property('_').with.length(0); r.should.have.property('errors').with.length(0); r.should.have.property('logs').with.length(0); r.should.have.property('exit', false); }); }); it('should display example on fail', () => { const r = checkUsage(() => yargs('') .example('$0 something', 'description') .example('$0 something else', 'other description') .demand(['y']) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -y [required]', 'Examples:', ' usage something description', ' usage something else other description', 'Missing required argument: y', ]); }); it('should display examples on fail when passing multiple examples at once', () => { const r = checkUsage(() => yargs('') .example([ ['$0 something', 'description'], ['$0 something else', 'other description'], ]) .demand(['y']) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -y [required]', 'Examples:', ' usage something description', ' usage something else other description', 'Missing required argument: y', ]); }); describe('demand option with boolean flag', () => { describe('with demand option', () => { it('should report missing required arguments', () => { const r = checkUsage(() => yargs('-y 10 -z 20') .usage('Usage: $0 -x NUM [-y NUM]') .options({ x: {description: 'an option', demand: true}, y: {description: 'another option', demand: false}, }) .wrap(null) .parse() ); r.result.should.have.property('y', 10); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n/) .should.deep.equal([ 'Usage: usage -x NUM [-y NUM]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -x an option [required]', ' -y another option', '', 'Missing required argument: x', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); }); describe('with required option', () => { it('should report missing required arguments', () => { const r = checkUsage(() => yargs('-y 10 -z 20') .usage('Usage: $0 -x NUM [-y NUM]') .options({ x: {description: 'an option', required: true}, y: {description: 'another option', required: false}, }) .wrap(null) .parse() ); r.result.should.have.property('y', 10); r.result.should.have.property('z', 20); r.result.should.have.property('_').with.length(0); r.errors .join('\n') .split(/\n/) .should.deep.equal([ 'Usage: usage -x NUM [-y NUM]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -x an option [required]', ' -y another option', '', 'Missing required argument: x', ]); r.logs.should.have.length(0); r.exit.should.equal(true); }); }); it('should not report missing required arguments when given an alias', () => { const r = checkUsage(() => yargs('-w 10') .usage('Usage: $0 --width NUM [--height NUM]') .options({ width: {description: 'Width', alias: 'w', demand: true}, height: {description: 'Height', alias: 'h', demand: false}, }) .wrap(null) .parse() ); r.result.should.have.property('w', 10); r.result.should.have.property('_').with.length(0); r.should.have.property('errors').with.length(0); r.logs.should.have.length(0); }); }); describe('help option', () => { it('should display usage', () => { const r = checkUsage(() => yargs(['--help']).demand(['y']).wrap(null).parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(1); r.should.have.property('exit').and.equal(true); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -y [required]', ]); }); it('should not show both dashed and camelCase aliases', () => { const r = checkUsage(() => yargs(['--help']) .usage('Usage: $0 options') .describe('some-opt', 'Some option') .default('some-opt', 2) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('exit').and.equal(true); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage options', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --some-opt Some option [default: 2]', ]); }); it('should use 2 dashes for general 1-digit usage', () => { const r = checkUsage(() => yargs(['--help']) .option('1', { type: 'string', description: 'First one', default: 'first', }) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('exit').and.equal(true); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --1 First one [string] [default: "first"]', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('should use single dashes for 1-digit boolean key usage', () => { const r = checkUsage(() => yargs(['--help']) .option('1', { type: 'boolean', description: 'Negative one', }) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('exit').and.equal(true); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' -1 Negative one [boolean]', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('should use single dashes for 1-digit boolean alias usage', () => { const r = checkUsage(() => yargs(['--help']) .option('negativeone', { alias: '1', type: 'boolean', description: 'Negative one', }) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('exit').and.equal(true); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -1, --negativeone Negative one [boolean]', ]); }); it('should use 2 dashes for multiple-digit alias usage', () => { const r = checkUsage(() => yargs(['--help']) .option('onehundred', { alias: '100', type: 'boolean', description: 'one hundred', }) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('exit').and.equal(true); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --onehundred, --100 one hundred [boolean]', ]); }); describe('when exitProcess is false', () => { it('should not validate arguments (required argument)', () => { const r = checkUsage(() => yargs(['--help']) .usage('Usage: $0 options') .alias('help', 'h') .describe('some-opt', 'Some option') .demand('some-opt') .wrap(null) .exitProcess(false) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('help').and.equal(true); r.result.should.have.property('h').and.equal(true); r.should.have.property('exit').and.equal(false); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage options', 'Options:', ' -h, --help Show help [boolean]', ' --version Show version number [boolean]', ' --some-opt Some option [required]', ]); }); // We need both this and the previous spec because a required argument // is a validation error and nargs is a parse error it('should not validate arguments (nargs)', () => { const r = checkUsage(() => yargs(['--help', '--some-opt']) .usage('Usage: $0 options') .alias('help', 'h') .describe('some-opt', 'Some option') .demand('some-opt') .nargs('some-opt', 3) .wrap(null) .exitProcess(false) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('help').and.equal(true); r.result.should.have.property('h').and.equal(true); r.should.have.property('exit').and.equal(false); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Usage: usage options', 'Options:', ' -h, --help Show help [boolean]', ' --version Show version number [boolean]', ' --some-opt Some option [required]', ]); }); }); }); describe('version option', () => { it('should display version', () => { const r = checkUsage(() => yargs(['--version']) .version('version', 'Show version number', '1.0.1') .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(1); r.should.have.property('exit').and.equal(true); r.logs[0].should.eql('1.0.1'); }); it('accepts version option as first argument, and version number as second argument', () => { const r = checkUsage(() => yargs(['--version']).version('version', '1.0.0').wrap(null).parse() ); r.logs[0].should.eql('1.0.0'); }); it("should default to 'version' as version option", () => { const r = checkUsage(() => yargs(['--version']).version('1.0.2').wrap(null).parse() ); r.logs[0].should.eql('1.0.2'); }); // Addresses: https://github.com/yargs/yargs/issues/1979 describe('when an option or alias "version" is set', () => { it('emits warning if version is not disabled', () => { const r = checkUsage(() => yargs .command('cmd1', 'cmd1 desc', yargs => yargs.option('v', { alias: 'version', describe: 'version desc', type: 'string', }) ) .fail(() => { expect.fail(); }) .wrap(null) .parse('cmd1 --version 0.25.10') ); r.should.have.property('emittedWarnings').with.length(1); r.emittedWarnings[0].should.match(/reserved word/); }); it('does not emit warning if version is disabled', () => { const r = checkUsage(() => yargs .command( 'cmd1', 'cmd1 desc', yargs => yargs.version(false).option('version', { alias: 'v', describe: 'version desc', type: 'string', }), argv => { argv.version.should.equal('0.25.10'); } ) .fail(() => { expect.fail(); }) .parse('cmd1 --version 0.25.10') ); r.should.have.property('emittedWarnings').with.length(0); }); }); describe('when exitProcess is false', () => { it('should not validate arguments (required argument)', () => { const r = checkUsage(() => yargs(['--version']) .version('version', 'Show version number', '1.0.1') .demand('some-opt') .wrap(null) .exitProcess(false) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('version').and.equal(true); r.should.have.property('exit').and.equal(false); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs[0].should.eql('1.0.1'); }); // We need both this and the previous spec because a required argument // is a validation error and nargs is a parse error it('should not validate arguments (nargs)', () => { const r = checkUsage(() => yargs(['--version', '--some-opt']) .nargs('some-opt', 3) .version('version', 'Show version number', '1.0.1') .wrap(null) .exitProcess(false) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.result.should.have.property('version').and.equal(true); r.should.have.property('exit').and.equal(false); r.should.have.property('errors').with.length(0); r.should.have.property('logs'); r.logs[0].should.eql('1.0.1'); }); }); }); describe('showHelpOnFail', () => { it('should display user supplied message', () => { const opts = { foo: {desc: 'foo option', alias: 'f'}, bar: {desc: 'bar option', alias: 'b'}, }; const r = checkUsage(() => yargs(['--foo']) .usage('Usage: $0 [options]') .options(opts) .demand(['foo', 'bar']) .showHelpOnFail(false, 'Specify --help for available options') .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n/) .should.deep.equal([ 'Missing required argument: bar', '', 'Specify --help for available options', ]); }); describe('should handle being chained both globally and on a command ', () => { const options = { opt1: { alias: 'o', demandOption: true, }, }; const cmdMessage = 'What have you done (command)????'; const globalMessage = 'What have you done (global)????'; const errorMessage = 'Unknown argument: extraOpt'; it('chained on to command', () => { const r = checkUsage(() => yargs('cmd1 --opt1 hello --extraOpt oops') .command( 'cmd1', 'cmd1 desc', yargs => yargs.options(options).showHelpOnFail(false, cmdMessage), argv => console.log(argv) ) .strict() .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(1); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n/) .should.deep.equal([errorMessage, '', cmdMessage]); }); it('chained on globally', () => { const r = checkUsage(() => yargs('cmd1 --opt1 hello --extraOpt oops') .command( 'cmd1', 'cmd1 desc', yargs => yargs.option(options), argv => console.log(argv) ) .showHelpOnFail(false, globalMessage) .strict() .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(1); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n/) .should.deep.equal([errorMessage, '', globalMessage]); }); it('chained on command and globally (priority given to command message)', () => { const r = checkUsage(() => yargs('cmd1 --opt1 hello --extraOpt oops') .command( 'cmd1', 'cmd1 desc', yargs => yargs.option(options).showHelpOnFail(false, cmdMessage), argv => console.log(argv) ) .showHelpOnFail(false, globalMessage) .strict() .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(1); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(true); r.errors .join('\n') .split(/\n/) .should.deep.equal([errorMessage, '', cmdMessage]); }); }); }); describe('exitProcess', () => { it('should not call process.exit on error if disabled', () => { const opts = { foo: {desc: 'foo option', alias: 'f'}, }; const r = checkUsage(() => yargs(['--foo']) .exitProcess(false) .usage('Usage: $0 [options]') .options(opts) .demand(['foo']) .wrap(null) .parse() ); r.should.have.property('result'); r.result.should.have.property('_').with.length(0); r.should.have.property('errors'); r.should.have.property('logs').with.length(0); r.should.have.property('exit').and.equal(false); }); }); describe('scriptName', () => { it('should display user supplied scriptName', () => { const r = checkUsage(() => yargs(['--help']).scriptName('custom').command('command').parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'custom [command]', 'Commands:', ' custom command', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); r.errors.should.have.length(0); r.exit.should.equal(true); }); it('should not alter the user supplied scriptName', () => { const r = checkUsage(() => yargs(['--help']).scriptName('./custom').command('command').parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ './custom [command]', 'Commands:', ' ./custom command', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); r.errors.should.have.length(0); r.exit.should.equal(true); }); }); it('should not print usage string if help() is called without arguments', () => { const r = checkUsage(() => yargs([]).usage('foo').help().parse()); r.logs.length.should.equal(0); }); it('should add --help as an option for printing usage text if help() is called without arguments', () => { const r = checkUsage(() => yargs(['--help']).usage('foo').help().parse()); r.logs.length.should.not.equal(0); }); describe('wrap', () => { it('should wrap argument descriptions onto multiple lines', () => { const r = checkUsage(() => yargs([]) .option('fairly-long-option', { alias: 'f', default: 'fairly-long-default', description: 'npm prefix used to locate globally installed npm packages', }) .demand('foo') .wrap(50) .parse() ); r.errors[0].split('\n').forEach((line, i) => { if (!i || !line) return; // ignore first and last line. line.length.should.lte(50); }); }); it('should wrap based on window-size if no wrap is provided', function () { if (!process.stdout.isTTY) { return this.skip(); } const width = process.stdout.columns; const r = checkUsage(() => yargs([]) .option('fairly-long-option', { alias: 'f', // create a giant string that should wrap. description: new Array((width + 1) * 5).join('s'), }) .demand('foo') .parse() ); // the long description should cause several line // breaks when wrapped. r.errors[0].split('\n').length.should.gte(5); }); it('should not wrap when YARGS_DISABLED_WRAP is provided', () => { const yargsInstance = yargs().wrap(99); process.env.YARGS_DISABLE_WRAP = 'true'; expect( yargsInstance.getInternalMethods().getUsageInstance().getWrap() ).to.equal(null); delete process.env.YARGS_DISABLE_WRAP; }); it('should not raise an exception when long default and description are provided', () => yargs([]) .option('fairly-long-option', { alias: 'f', default: 'npm prefix used to locate globally installed npm packages', description: 'npm prefix used to locate globally installed npm packages', }) .wrap(40) .help()); it('should wrap the left-hand-column if it takes up more than 50% of the screen', () => { const r = checkUsage(() => yargs([]) .example( 'i am a fairly long example', 'description that is also fairly long' ) .demand('foo') .wrap(40) .parse() ); // should split example usage onto multiple lines. r.errors[0].split('\n').length.should.equal(10); // should wrap within appropriate boundaries. r.errors[0].split('\n').forEach((line, i) => { // ignore headings and blank lines. if (!line || line.match('Examples:') || line.match('Options:')) return; line.length.should.lte(40); }); }); it('should not wrap left-hand-column if no description is provided', () => { const r = checkUsage(() => yargs([]) .example('i am a fairly long example that is like really long woooo') .demand('foo') .wrap(50) .parse() ); r.errors[0].split('\n').forEach((line, i) => { // ignore headings and blank lines. if (!line.match('i am a fairly long example')) return; // with two white space characters on the left, // line length should be 50 - 2 line.length.should.equal(48); }); }); it('should wrap the usage string', () => { const r = checkUsage(() => yargs([]) .usage('i am a fairly long usage string look at me go.') .demand('foo') .wrap(20) .parse() ); // the long usage string should cause line-breaks. r.errors[0].split('\n').length.should.gt(6); }); it('should align span columns when ansi colors are not used in a description', () => { const noColorAddedDescr = 'The file to add or remove'; const r = checkUsage(() => yargs(['-h']) .option('f', { alias: 'file', describe: noColorAddedDescr, demand: true, type: 'string', }) .help('h') .alias('h', 'help') .wrap(80) .parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --version Show version number [boolean]', ` -f, --file ${noColorAddedDescr} [string] [required]`, ' -h, --help Show help [boolean]', ]); }); it('should align span columns when ansi colors are used in a description', () => { const yellowDescription = chalk.yellow('The file to add or remove'); const r = checkUsage(() => yargs(['-h']) .option('f', { alias: 'file', describe: yellowDescription, demand: true, type: 'string', }) .help('h') .alias('h', 'help') .wrap(80) .parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --version Show version number [boolean]', ` -f, --file ${yellowDescription} [string] [required]`, ' -h, --help Show help [boolean]', ]); }); it('should not indent usage when no wrap is specified', () => { const expected = [ ' My greatest CLI App', 'Hello, world', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]; const r = checkUsage(() => yargs('--help') .usage( [ ' My greatest CLI App', 'Hello, world', ].join('\n') ) .wrap(null) .parse() ); // the leading whitespaces on the first line should not cause indentation to usage string r.logs[0].split('\n').should.deep.equal(expected); }); }); describe('commands', () => { it('should output a list of available commands', () => { const r = checkUsage(() => yargs('') .command('upload', 'upload something') .command('download', 'download something from somewhere') .demand('y') .wrap(null) .parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'usage [command]', 'Commands:', ' usage upload upload something', ' usage download download something from somewhere', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -y [required]', 'Missing required argument: y', ]); }); it('should not show hidden commands', () => { const r = checkUsage(() => yargs('') .command('upload', 'upload something') .command('secret', false) .demand('y') .wrap(null) .parse() ); r.errors .join('\n') .split(/\s+/) .should.deep.equal([ 'usage', '[command]', 'Commands:', 'usage', 'upload', 'upload', 'something', 'Options:', '--help', 'Show', 'help', '[boolean]', '--version', 'Show', 'version', 'number', '[boolean]', '-y', '[required]', 'Missing', 'required', 'argument:', 'y', ]); }); it('allows completion command to be hidden', () => { const r = checkUsage(() => yargs('') .command('upload', 'upload something') .completion('completion', false) .demand('y') .wrap(null) .parse() ); r.errors .join('\n') .split(/\s+/) .should.deep.equal([ 'usage', '[command]', 'Commands:', 'usage', 'upload', 'upload', 'something', 'Options:', '--help', 'Show', 'help', '[boolean]', '--version', 'Show', 'version', 'number', '[boolean]', '-y', '[required]', 'Missing', 'required', 'argument:', 'y', ]); }); it('preserves global wrap() for commands that do not override it', () => { const uploadCommand = 'upload <dest>'; const uploadDesc = 'Upload cwd to remote destination'; const uploadOpts = { force: { describe: 'Force overwrite of remote directory contents', type: 'boolean', }, }; const uploadHandler = argv => {}; const generalHelp = checkUsage(() => yargs('--help') .command(uploadCommand, uploadDesc, uploadOpts, uploadHandler) .wrap(null) .parse() ); const commandHelp = checkUsage(() => yargs('upload --help') .command(uploadCommand, uploadDesc, uploadOpts, uploadHandler) .wrap(null) .parse() ); generalHelp.logs[0] .split('\n') .should.deep.equal([ 'usage [command]', '', 'Commands:', ' usage upload <dest> Upload cwd to remote destination', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); commandHelp.logs[0] .split('\n') .should.deep.equal([ 'usage upload <dest>', '', 'Upload cwd to remote destination', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --force Force overwrite of remote directory contents [boolean]', ]); }); it('allows a command to override global wrap()', () => { const uploadCommand = 'upload <dest>'; const uploadDesc = 'Upload cwd'; const uploadBuilder = yargs => yargs .option('force', { describe: 'Force overwrite of remote directory contents', type: 'boolean', }) .wrap(46); const uploadHandler = argv => {}; const generalHelp = checkUsage(() => yargs('--help') .command(uploadCommand, uploadDesc, uploadBuilder, uploadHandler) .wrap(null) .parse() ); const commandHelp = checkUsage(() => yargs('upload --help') .command(uploadCommand, uploadDesc, uploadBuilder, uploadHandler) .wrap(null) .parse() ); generalHelp.logs[0] .split('\n') .should.deep.equal([ 'usage [command]', '', 'Commands:', ' usage upload <dest> Upload cwd', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); commandHelp.logs[0] .split('\n') .should.deep.equal([ 'usage upload <dest>', '', 'Upload cwd', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --force Force overwrite of remote', ' directory contents [boolean]', ]); }); it('resets groups for a command handler, respecting order', () => { const r = checkUsage(() => yargs(['upload', '-h']) .command('upload', 'upload something', yargs => yargs .option('q', { type: 'boolean', group: 'Flags:', }) .help('h') .group('h', 'Global Flags:') .wrap(null) ) .help('h') .group('h', 'Global Flags:') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage upload', '', 'upload something', '', 'Flags:', ' -q [boolean]', '', 'Global Flags:', ' -h Show help [boolean]', '', 'Options:', ' --version Show version number [boolean]', ]); }); it('allows global option to be disabled', () => { const r = checkUsage(() => yargs(['upload', '-h']) .command('upload', 'upload something', yargs => yargs .option('q', { type: 'boolean', group: 'Flags:', }) .wrap(null) ) .option('i', { type: 'boolean', global: true, group: 'Awesome Flags:', }) .option('j', { type: 'boolean', global: false, // not global so not preserved, even though the group is group: 'Awesome Flags:', }) .help('h') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage upload', '', 'upload something', '', 'Flags:', ' -q [boolean]', '', 'Awesome Flags:', ' -i [boolean]', '', 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ]); }); it('can add to preserved groups', () => { const r = checkUsage(() => yargs(['upload', '-h']) .command('upload', 'upload something', yargs => yargs .option('q', { type: 'boolean', group: 'Awesome Flags:', }) .wrap(null) ) .option('i', { type: 'boolean', global: true, group: 'Awesome Flags:', }) .help('h') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage upload', '', 'upload something', '', 'Awesome Flags:', ' -i [boolean]', ' -q [boolean]', '', 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ]); }); it('can bump up preserved groups', () => { const r = checkUsage(() => yargs(['upload', '-h']) .command('upload', 'upload something', yargs => yargs .group([], 'Awesome Flags:') .option('q', { type: 'boolean', group: 'Flags:', }) .wrap(null) ) .option('i', { type: 'boolean', global: true, group: 'Awesome Flags:', }) .option('j', { type: 'boolean', global: false, // not global so not preserved, even though the group is group: 'Awesome Flags:', }) .help('h') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage upload', '', 'upload something', '', 'Awesome Flags:', ' -i [boolean]', '', 'Flags:', ' -q [boolean]', '', 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ]); }); it('should display global non empty groups for commands', () => { const r = checkUsage(() => yargs(['upload', '-h']) .command('upload', 'upload something', yargs => yargs .option('q', { type: 'boolean', }) .wrap(null) ) .option('i', { type: 'boolean', global: true, }) .option('j', { type: 'boolean', global: false, // not global so not preserved, even though the group is }) .group(['i', 'j'], 'Awesome Flags:') .help('h') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage upload', '', 'upload something', '', 'Awesome Flags:', ' -i [boolean]', '', 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ' -q [boolean]', ]); }); it('should display global non empty groups for subcommands', () => { const r = checkUsage(() => yargs(['do', 'upload', '-h']) .command('do', 'do something', yargs => yargs .command('upload', 'upload something', yargs => yargs .option('q', { type: 'boolean', }) .wrap(null) ) .wrap(null) ) .option('i', { type: 'boolean', global: true, }) .option('j', { type: 'boolean', global: false, // not global so not preserved, even though the group is }) .group(['i', 'j'], 'Awesome Flags:') .help('h') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage do upload', '', 'upload something', '', 'Awesome Flags:', ' -i [boolean]', '', 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ' -q [boolean]', ]); }); it('should list a module command only once', () => { const r = checkUsage(() => yargs('--help') .command('upload', 'upload something', { builder(yargs) { return yargs; }, handler(argv) {}, }) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [command]', '', 'Commands:', ' usage upload upload something', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('allows a builder function to override default usage() string', () => { const r = checkUsage(() => yargs('upload --help') .command('upload', 'upload something', { builder(yargs) { return yargs .usage('Usage: program upload <something> [opts]') .demand(1); }, handler(argv) {}, }) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Usage: program upload <something> [opts]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('allows a builder function to disable default usage() with null', () => { const r = checkUsage(() => yargs('upload --help') .command( 'upload', 'upload something', yargs => yargs.usage(null), argv => {} ) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('displays given command chain with positional args in default usage for subcommand with builder object', () => { const r = checkUsage(() => yargs('one two --help') .command( 'one <sub>', 'level one, requires subcommand', yargs => yargs.command('two [next]', 'level two', {}, argv => {}), argv => {} ) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage one two [next]', '', 'level two', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('displays given command chain with positional args in default usage for subcommand with builder function', () => { const r = checkUsage(() => yargs('one two --help') .command( 'one <sub>', 'level one, requires subcommand', yargs => yargs.command( 'two [next]', 'level two', yargs => yargs, argv => {} ), argv => {} ) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage one two [next]', '', 'level two', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('displays aliases for commands that have them (no wrap)', () => { const r = checkUsage(() => yargs('help') .command(['copy <src> [dest]', 'cp', 'dupe'], 'Copy something') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [command]', '', 'Commands:', ' usage copy <src> [dest] Copy something [aliases: cp, dupe]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('displays aliases for commands that have them (with wrap)', () => { const r = checkUsage(() => yargs('help') .command(['copy <src> [dest]', 'cp', 'dupe'], 'Copy something') .wrap(80) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [command]', '', 'Commands:', ' usage copy <src> [dest] Copy something [aliases: cp, dupe]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('allows a builder to add more than one usage with multiple usage calls', () => { const r = checkUsage(() => yargs('upload --help') .command( 'upload', 'upload something', yargs => yargs .usage('$0 upload [something]') .usage('$0 upload [something else]'), argv => {} ) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage upload [something]', 'usage upload [something else]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('allows a builder to disable usage with null after mutiple usage calls', () => { const r = checkUsage(() => yargs('upload --help') .command( 'upload', 'upload something', yargs => yargs .usage('$0 upload [something]') .usage('$0 upload [something else]') .usage(null), argv => {} ) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('does not display $0 twice when default commands are enabled', () => { const r = checkUsage(() => yargs('-h') .usage('$0', 'do something', yargs => { yargs.alias('h', 'help'); }) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage', '', 'do something', '', 'Options:', ' --version Show version number [boolean]', ' -h, --help Show help [boolean]', ]); }); }); describe('epilogue', () => { it('should display an epilog message at the end of the usage instructions', () => { const r = checkUsage(() => yargs('') .epilog('for more info view the manual at http://example.com') .demand('y') .wrap(null) .parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -y [required]', 'for more info view the manual at http://example.com', 'Missing required argument: y', ]); }); it('supports multiple epilogs', () => { const r = checkUsage(() => yargs('') .epilog('for more info view the manual at http://example.com') .epilog( 'you can also find us on slack at http://devtoolscommunity.herokuapp.com' ) .epilog( 'keep up to date by reading our blog at http://yargs.js.org/blog.html' ) .demand('y') .wrap(null) .parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -y [required]', 'for more info view the manual at http://example.com', 'you can also find us on slack at http://devtoolscommunity.herokuapp.com', 'keep up to date by reading our blog at http://yargs.js.org/blog.html', 'Missing required argument: y', ]); }); it('replaces $0 in epilog string', () => { const r = checkUsage(() => yargs('') .epilog("Try '$0 --long-help' for more information") .demand('y') .wrap(null) .parse() ); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -y [required]', "Try 'usage --long-help' for more information", 'Missing required argument: y', ]); }); }); describe('default', () => { it('should indicate that the default is a generated-value, if function is provided', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .default('f', () => 99) .wrap(null) .parse() ); r.logs[0].should.include('default: (generated-value)'); }); it('if a named function is provided, should use name rather than (generated-value)', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .default('f', function randomNumber() { // eslint-disable-line return Math.random() * 256; }) .wrap(null) .parse() ); r.logs[0].should.include('default: (random-number)'); }); it('default-description take precedence if one is provided', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .default( 'f', () => { return Math.random() * 256; }, 'foo-description' ) .wrap(null) .parse() ); r.logs[0].should.include('default: foo-description'); }); it('serializes object and array defaults', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .default('a', []) .default('a2', [3]) .default('o', {a: '33'}) .wrap(null) .parse() ); r.logs[0].should.include('default: []'); r.logs[0].should.include('default: {"a":"33"}'); r.logs[0].should.include('default: [3]'); }); }); describe('defaultDescription', () => { describe('using option() without default()', () => { it('should output given desc with default value', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .option('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', default: 80, }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); it('should output given desc without default value', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .option('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); it('should prefer given desc over function desc', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .option('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', default: function determinePort() { return 80; }, }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); }); describe('using option() with default()', () => { it('should prefer default() desc when given last', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .option('port', { describe: 'The port value for URL', defaultDescription: 'depends on protocol', }) .default('port', null, '80 for HTTP and 443 for HTTPS') .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); it('should prefer option() desc when given last', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .default('port', null, '80 for HTTP and 443 for HTTPS') .option('port', { describe: 'The port value for URL', defaultDescription: 'depends on protocol', }) .wrap(null) .parse() ); r.logs[0].should.include('default: depends on protocol'); }); it('should prefer option() desc over default() function', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .option('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', }) .default('port', () => { return 80; }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); }); describe('using positional() without default()', () => { it('should output given desc with default value', () => { const r = checkUsage(() => yargs(['url', '-h']) .help('h') .command('url', 'Print a URL', yargs => { yargs.positional('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', default: 80, }); }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); it('should output given desc without default value', () => { const r = checkUsage(() => yargs(['url', '-h']) .help('h') .command('url', 'Print a URL', yargs => { yargs.positional('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', }); }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); it('should prefer given desc over function desc', () => { const r = checkUsage(() => yargs(['url', '-h']) .help('h') .command('url', 'Print a URL', yargs => { yargs.positional('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', default: function determinePort() { return 80; }, }); }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); }); describe('using positional() with default()', () => { it('should prefer default() desc when given last', () => { const r = checkUsage(() => yargs(['url', '-h']) .help('h') .command('url', 'Print a URL', yargs => { yargs .positional('port', { describe: 'The port value for URL', defaultDescription: 'depends on protocol', }) .default('port', null, '80 for HTTP and 443 for HTTPS'); }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); it('should prefer positional() desc when given last', () => { const r = checkUsage(() => yargs(['url', '-h']) .help('h') .command('url', 'Print a URL', yargs => { yargs .default('port', null, '80 for HTTP and 443 for HTTPS') .positional('port', { describe: 'The port value for URL', defaultDescription: 'depends on protocol', }); }) .wrap(null) .parse() ); r.logs[0].should.include('default: depends on protocol'); }); it('should prefer positional() desc over default() function', () => { const r = checkUsage(() => yargs(['url', '-h']) .help('h') .command('url', 'Print a URL', yargs => { yargs .positional('port', { describe: 'The port value for URL', defaultDescription: '80 for HTTP and 443 for HTTPS', }) .default('port', () => { return 80; }); }) .wrap(null) .parse() ); r.logs[0].should.include('default: 80 for HTTP and 443 for HTTPS'); }); }); }); describe('normalizeAliases', () => { // see #128 it("should display 'description' string in help message if set for alias", () => { const r = checkUsage(() => yargs(['-h']) .describe('foo', 'foo option') .alias('f', 'foo') .help('h') .wrap(null) .parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ' -f, --foo foo option', ]); }); it("should display 'required' string in help message if set for alias", () => { const r = checkUsage(() => yargs(['-h']) .demand('foo') .alias('f', 'foo') .help('h') .wrap(null) .parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ' -f, --foo [required]', ]); }); it("should display 'type' string in help message if set for alias", () => { const r = checkUsage(() => yargs(['-h']) .string('foo') .describe('foo', 'bar') .alias('f', 'foo') .help('h') .wrap(null) .parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ' -f, --foo bar [string]', ]); }); it("should display 'type' number in help message if set for alias", () => { const r = checkUsage(() => yargs(['-h']) .string('foo') .describe('foo', 'bar') .alias('f', 'foo') .number(['foo']) .help('h') .wrap(null) .parse() ); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ' -f, --foo bar [number]', ]); }); }); describe('showHelp', () => { // see #143. it('should show help regardless of whether argv has been called', () => { const r = checkUsage(() => { const y = yargs(['--foo']) .options('foo', { alias: 'f', describe: 'foo option', }) .wrap(null); y.showHelp(); }); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ]); }); it('should print the help using console.error when no arguments were specified', () => { const r = checkUsage(() => { const y = yargs(['--foo']) .options('foo', { alias: 'f', describe: 'foo option', }) .wrap(null); y.showHelp(); }); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ]); }); it('should call the correct console.log method when specified', () => { const r = checkUsage(() => { const y = yargs(['--foo']) .options('foo', { alias: 'f', describe: 'foo option', }) .wrap(null); y.showHelp('log'); }); r.errors.length.should.eql(0); r.logs .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ]); }); it('should call the callback to print when specified', done => { const y = yargs(['--foo']) .options('foo', { alias: 'f', describe: 'foo option', }) .wrap(null); y.showHelp(printCallback); function printCallback(msg) { msg .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' -f, --foo foo option', ]); return done(); } }); it('should not run handler or middleware', done => { let commandRun = false; let middlewareRun = false; const y = yargs(['foo']) .command( 'foo', 'foo command', () => {}, () => { commandRun = true; } ) .middleware(() => { middlewareRun = true; }); y.showHelp(printCallback); function printCallback(msg) { commandRun.should.equal(false); middlewareRun.should.equal(false); msg.should.match(/foo command/); return done(); } }); // See: https://github.com/yargs/yargs/issues/1791 it('should not run default command', done => { let executed = false; yargs.command( '$0', 'a default command', yargs => yargs, () => { executed = true; } ); yargs.showHelp(output => { executed.should.equal(false); output.should.match(/a default command/); return done(); }); }); }); describe('showVersion', () => { // see #143. it('should show version regardless of whether argv has been called', () => { const r = checkUsage(() => { const y = yargs().version('1.0.0').wrap(null); y.showVersion(); }); r.errors.join('\n').split(/\n+/).should.deep.equal(['1.0.0']); }); it('should call the correct console.log method when specified', () => { const r = checkUsage(() => { const y = yargs().version('1.0.0').wrap(null); y.showVersion('log'); }); r.errors.length.should.eql(0); r.logs.join('\n').split(/\n+/).should.deep.equal(['1.0.0']); }); it('should call the callback to print when specified', done => { const y = yargs().version('1.0.0').wrap(null); y.showVersion(printCallback); function printCallback(msg) { msg.split(/\n+/).should.deep.equal(['1.0.0']); return done(); } }); }); describe('$0', () => { function mockProcessArgv(argv, cb) { const argvOld = process.argv; process.argv = argv; // cb must be sync for now try { cb(); process.argv = argvOld; } catch (err) { process.argv = argvOld; throw err; } } it('is detected correctly for a basic script', () => { mockProcessArgv(['script.js'], () => { yargs([]).$0.should.equal('script.js'); }); }); it('is detected correctly when argv contains "node"', () => { mockProcessArgv(['node', 'script.js'], () => { yargs([]).$0.should.equal('script.js'); }); }); it('is detected correctly when dirname contains "node"', () => { mockProcessArgv(['/code/node/script.js'], () => { yargs([]).$0.should.equal('/code/node/script.js'); }); }); it('is detected correctly when dirname and argv contain "node"', () => { mockProcessArgv(['node', '/code/node/script.js'], () => { yargs([]).$0.should.equal('/code/node/script.js'); }); }); it('is detected correctly when argv contains "iojs"', () => { mockProcessArgv(['iojs', 'script.js'], () => { yargs([]).$0.should.equal('script.js'); }); }); it('is detected correctly when dirname contains "iojs"', () => { mockProcessArgv(['/code/iojs/script.js'], () => { yargs([]).$0.should.equal('/code/iojs/script.js'); }); }); it('is detected correctly when dirname and argv contain "iojs"', () => { mockProcessArgv(['iojs', '/code/iojs/script.js'], () => { yargs([]).$0.should.equal('/code/iojs/script.js'); }); }); it('is detected correctly when argv contains "node.exe"', () => { mockProcessArgv(['node.exe', 'script.js'], () => { yargs([]).$0.should.equal('script.js'); }); }); it('is detected correctly when argv contains "iojs.exe"', () => { mockProcessArgv(['iojs.exe', 'script.js'], () => { yargs([]).$0.should.equal('script.js'); }); }); if (process.platform !== 'win32') { it('is resolved to the relative path if it is shorter', () => { mockProcessArgv(['node', '/code/node/script.js'], () => { yargs([], '/code/python/').$0.should.equal('../node/script.js'); }); }); it('is not resolved to the relative path if it is larger', () => { mockProcessArgv(['node', '/script.js'], () => { yargs([], '/very/long/current/directory/').$0.should.equal( '/script.js' ); }); }); } if (process.platform === 'win32') { it('is resolved to the relative path if it is shorter, using Windows paths', () => { mockProcessArgv(['node.exe', 'C:\\code\\node\\script.js'], () => { yargs([], 'C:\\code\\python\\').$0.should.equal( '..\\node\\script.js' ); }); }); it('is not resolved to the relative path if it is larger, using Windows paths', () => { mockProcessArgv(['node', 'C:\\script.js'], () => { yargs([], 'C:\\very\\long\\current\\directory\\').$0.should.equal( 'C:\\script.js' ); }); }); } }); describe('choices', () => { it('should output choices when defined for non-hidden options', () => { const r = checkUsage(() => yargs(['--help']) .option('answer', { describe: 'does this look good?', choices: ['yes', 'no', 'maybe'], }) .option('confidence', { describe: 'percentage of confidence', choices: [0, 25, 50, 75, 100], }) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --answer does this look good? [choices: "yes", "no", "maybe"]', ' --confidence percentage of confidence [choices: 0, 25, 50, 75, 100]', ]); }); it('should not output choices when defined for hidden options', () => { const r = checkUsage(() => yargs(['--help']) .option('answer', { type: 'string', choices: ['yes', 'no', 'maybe'], hidden: true, }) .option('confidence', { choices: [0, 25, 50, 75, 100], hidden: true, }) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); }); describe('count', () => { it('should indicate when an option is a count', () => { const r = checkUsage(() => yargs(['--help']) .option('verbose', { describe: 'verbose level', count: true, }) .help('help') .wrap(null) .parse() ); r.logs.join(' ').should.match(/\[count]/); }); }); describe('array', () => { it('should indicate when an option is an array', () => { const r = checkUsage(() => yargs(['--help']) .option('arr', { describe: 'array option', array: true, }) .help('help') .wrap(null) .parse() ); r.logs.join(' ').should.match(/\[array]/); }); }); describe('group', () => { it('allows an an option to be placed in an alternative group', () => { const r = checkUsage(() => yargs(['--help']) .option('batman', { type: 'string', describe: "not the world's happiest guy", default: 'Bruce Wayne', }) .group('batman', 'Heroes:') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Heroes:', ' --batman not the world\'s happiest guy [string] [default: "Bruce Wayne"]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it("does not print the 'Options:' group if no keys are in it", () => { const r = checkUsage(() => yargs(['-h']) .string('batman') .describe('batman', "not the world's happiest guy") .default('batman', 'Bruce Wayne') .group('batman', 'Heroes:') .group('h', 'Heroes:') .help('h') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Heroes:', ' --batman not the world\'s happiest guy [string] [default: "Bruce Wayne"]', ' -h Show help [boolean]', '', 'Options:', ' --version Show version number [boolean]', ]); }); it('displays alias keys appropriately within a grouping', () => { const r = checkUsage(() => yargs(['-h']) .alias('h', 'help') .group('help', 'Magic Variable:') .group('version', 'Magic Variable:') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Magic Variable:', ' -h, --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('allows a group to be provided as the only information about an option', () => { const r = checkUsage(() => yargs(['--help']).group('batman', 'Heroes:').wrap(null).parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Heroes:', ' --batman', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('allows multiple options to be grouped at the same time', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .group('h', 'Options:') .group(['batman', 'robin'], 'Heroes:') .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' -h Show help [boolean]', ' --version Show version number [boolean]', '', 'Heroes:', ' --batman', ' --robin', ]); }); it('allows group to be provided in the options object', () => { const r = checkUsage(() => yargs(['-h']) .help('h') .option('batman', { group: 'Heroes:', string: true, }) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Heroes:', ' --batman [string]', '', 'Options:', ' --version Show version number [boolean]', ' -h Show help [boolean]', ]); }); it('only displays a duplicated option once per group', () => { const r = checkUsage(() => yargs(['--help']) .group(['batman', 'batman'], 'Heroes:') .group('robin', 'Heroes:') .option('robin', { group: 'Heroes:', }) .wrap(null) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Heroes:', ' --batman', ' --robin', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); }); describe('cjk', () => { it('should calculate width of cjk text correctly', () => { const r = checkUsage(() => { const y = yargs() .example( '안녕하세요 선생님 안녕 친구야', '인사하는 어린이 착한 어린이' ) .wrap(80); y.showHelp(); }); r.errors .join('\n') .split(/\n+/) .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', 'Examples:', ' 안녕하세요 선생님 안녕 친구야 인사하는 어린이 착한 어린이', ]); }); }); describe('default command', () => { it('should display top-level help with no command given', () => { const r = checkUsage(() => yargs('--help') .command( ['list [pattern]', 'ls', '*'], 'List key-value pairs for pattern', {}, noop ) .command('get <key>', 'Get value for key', {}, noop) .command('set <key> [value]', 'Set value for key', {}, noop) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [pattern]', '', 'List key-value pairs for pattern', '', 'Commands:', ' usage list [pattern] List key-value pairs for pattern', ' [default] [aliases: ls]', ' usage get <key> Get value for key', ' usage set <key> [value] Set value for key', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('should display top-level help with sorting with no command given if sorting enabled', () => { const r = checkUsage(() => yargs('--help') .command( ['list [pattern]', 'ls', '*'], 'List key-value pairs for pattern', {}, noop ) .command('get <key>', 'Get value for key', {}, noop) .command('set <key> [value]', 'Set value for key', {}, noop) .parserConfiguration({'sort-commands': true}) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [pattern]', '', 'List key-value pairs for pattern', '', 'Commands:', ' usage get <key> Get value for key', ' usage list [pattern] List key-value pairs for pattern', ' [default] [aliases: ls]', ' usage set <key> [value] Set value for key', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('should display default command as ./$0 if it has no aliases', () => { const r = checkUsage(() => yargs('--help') .command('* [pattern]', 'List key-value pairs for pattern', {}, noop) .command('get <key>', 'Get value for key', {}, noop) .command('set <key> [value]', 'Set value for key', {}, noop) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [pattern]', '', 'List key-value pairs for pattern', '', 'Commands:', ' usage [pattern] List key-value pairs for pattern [default]', ' usage get <key> Get value for key', ' usage set <key> [value] Set value for key', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('should display positionals that have been configured', () => { const r = checkUsage(() => yargs('--help') .command( '* [pattern]', 'List key-value pairs for pattern', yargs => { yargs.positional('pattern', { type: 'string', default: '.*', }); }, noop ) .command('get <key>', 'Get value for key', {}, noop) .command('set <key> [value]', 'Set value for key', {}, noop) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [pattern]', '', 'List key-value pairs for pattern', '', 'Commands:', ' usage [pattern] List key-value pairs for pattern [default]', ' usage get <key> Get value for key', ' usage set <key> [value] Set value for key', '', 'Positionals:', ' pattern [string] [default: ".*"]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('should display options that have been configured', () => { const r = checkUsage(() => yargs('--help') .command( '* [pattern]', 'List key-value pairs for pattern', {uuid: {required: true}}, noop ) .command('get <key>', 'Get value for key', {}, noop) .command('set <key> [value]', 'Set value for key', {}, noop) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage [pattern]', '', 'List key-value pairs for pattern', '', 'Commands:', ' usage [pattern] List key-value pairs for pattern [default]', ' usage get <key> Get value for key', ' usage set <key> [value] Set value for key', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --uuid [required]', ]); }); // Addresses: https://github.com/yargs/yargs/issues/2030 it('should display options (with descriptions) on failed default command', () => { const r = checkUsage(() => yargs('') .command({ command: '$0 <arg1>', desc: 'default desc', builder: yargs => yargs .option('arg1', { type: 'string', desc: 'arg1 desc', demandOption: true, }) .option('arg2', { type: 'string', desc: 'arg2 desc', }), handler: noop, }) .strict() .parse() ); r.errors[0] .split('\n') .should.deep.equal([ 'usage <arg1>', '', 'default desc', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --arg1 arg1 desc [string] [required]', ' --arg2 arg2 desc [string]', ]); }); }); describe('positional', () => { it('should display help section for positionals', () => { const r = checkUsage(() => yargs('--help list') .command( 'list [pattern]', 'List key-value pairs for pattern', yargs => { yargs.positional('pattern', { describe: 'the pattern to list keys for', }); } ) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage list [pattern]', '', 'List key-value pairs for pattern', '', 'Positionals:', ' pattern the pattern to list keys for', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('shows that variadic positional arguments are arrays', () => { const r = checkUsage(() => yargs('--help list') .command( 'list [pattern...]', 'List key-value pairs for pattern', yargs => { yargs.positional('pattern', { describe: 'the pattern to list keys for', }); } ) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage list [pattern...]', '', 'List key-value pairs for pattern', '', 'Positionals:', ' pattern the pattern to list keys for [array] [default: []]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('indicates that <foo> positional arguments are required', () => { const r = checkUsage(() => yargs('--help list') .command( 'list <pattern>', 'List key-value pairs for pattern', yargs => { yargs.positional('pattern', { describe: 'the pattern to list keys for', }); } ) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage list <pattern>', '', 'List key-value pairs for pattern', '', 'Positionals:', ' pattern the pattern to list keys for [required]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('displays aliases appropriately', () => { const r = checkUsage(() => yargs('--help list') .command( 'list [pattern|thingy]', 'List key-value pairs for pattern', yargs => { yargs.positional('pattern', { describe: 'the pattern to list keys for', }); } ) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage list [pattern|thingy]', '', 'List key-value pairs for pattern', '', 'Positionals:', ' pattern, thingy the pattern to list keys for', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('displays type information', () => { const r = checkUsage(() => yargs('--help list') .command( 'list [pattern]', 'List key-value pairs for pattern', yargs => { yargs.positional('pattern', { describe: 'the pattern to list keys for', type: 'string', }); } ) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage list [pattern]', '', 'List key-value pairs for pattern', '', 'Positionals:', ' pattern the pattern to list keys for [string]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); it('displays choices array', () => { const r = checkUsage(() => yargs('--help list') .command( 'list [pattern]', 'List key-value pairs for pattern', yargs => { yargs.positional('pattern', { describe: 'the pattern to list keys for', choices: ['foo', 'bar'], }); } ) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'usage list [pattern]', '', 'List key-value pairs for pattern', '', 'Positionals:', ' pattern the pattern to list keys for [choices: "foo", "bar"]', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); }); }); describe('hidden options', () => { it('--help should display all options except for hidden ones', () => { const r = checkUsage(() => yargs('--help') .options({ foo: { describe: 'FOO', }, bar: {}, baz: { describe: 'BAZ', hidden: true, }, }) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --foo FOO', ' --bar', ]); }); it('--help should display all options except for hidden ones even with a default', () => { const r = checkUsage(() => yargs('--help') .options({ foo: { describe: 'FOO', hidden: true, }, bar: {}, baz: { type: 'number', describe: 'BAZ', default: 1, hidden: true, }, }) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --bar', ]); }); it('--help should display all options except for hidden ones even in a group', () => { const r = checkUsage(() => yargs('--help') .options({ foo: { describe: 'FOO', hidden: true, }, bar: {}, baz: { describe: 'BAZ', group: 'Hidden:', hidden: true, }, }) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --bar', ]); }); it('--help should display all groups except for ones with only hidden options', () => { const r = checkUsage(() => yargs('--help') .options({ foo: { describe: 'FOO', group: 'Hidden:', hidden: true, }, bar: {}, baz: { describe: 'BAZ', group: 'Hidden:', hidden: true, }, qux: { group: 'Shown:', }, }) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Shown:', ' --qux', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --bar', ]); }); it('--help should display all options (including hidden ones) with --show-hidden', () => { const r = checkUsage(() => yargs('--help --show-hidden --mama ama') .options({ foo: { describe: 'FOO', }, bar: {}, baz: { describe: 'BAZ', hidden: true, }, }) .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --foo FOO', ' --bar', ' --baz BAZ', ]); }); it('--help should display all groups (including ones with only hidden options) with --show-hidden', () => { const r = checkUsage( () => yargs('--help --show-hidden').options({ foo: { describe: 'FOO', group: 'Hidden:', hidden: true, }, bar: {}, baz: { describe: 'BAZ', group: 'Hidden:', hidden: true, }, qux: { group: 'Shown:', }, }).argv ); r.logs[0] .split('\n') .should.deep.equal([ 'Hidden:', ' --foo FOO', ' --baz BAZ', '', 'Shown:', ' --qux', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --bar', ]); }); it('--help should display --custom-show-hidden', () => { const r = checkUsage(() => yargs('--help') .options({ foo: { describe: 'FOO', }, bar: {}, baz: { describe: 'BAZ', hidden: true, }, }) .showHidden('custom-show-hidden') .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --foo FOO', ' --bar', ' --custom-show-hidden Show hidden options [boolean]', ]); }); it('--help should display all options with --custom-show-hidden', () => { const r = checkUsage(() => yargs('--help --custom-show-hidden') .options({ foo: { describe: 'FOO', }, bar: {}, baz: { describe: 'BAZ', hidden: true, }, }) .showHidden('custom-show-hidden') .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --foo FOO', ' --bar', ' --baz BAZ', ' --custom-show-hidden Show hidden options [boolean]', ]); }); }); describe('help message caching', () => { it('should display proper usage when an async handler fails', done => { const y = yargs() .scriptName('mocha') .command('cmd', 'test command', {}, () => { return new Promise((resolve, reject) => setTimeout(reject, 10)); }) .exitProcess(false); checkUsage( () => { y.parse('cmd'); setTimeout(() => process.exit(1), 100); // eslint-disable-line }, undefined, (err, r) => { should.not.exist(err); should.exist(r.errors[0]); r.errors[0] .split('\n') .should.deep.equal([ 'mocha cmd', '', 'test command', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); done(); } ); }); it('should not display a cached help message for the next parsing', done => { const y = yargs() .scriptName('mocha') .command('cmd', 'test command', {}, () => { return new Promise((resolve, _reject) => setTimeout(resolve, 10)); }) .demandCommand(1, 'You need at least one command before moving on') .exitProcess(false); checkUsage( () => { y.parse('cmd'); setTimeout(() => { y.parse(''); setTimeout(() => process.exit(1), 100); // eslint-disable-line }, 100); }, undefined, (err, r) => { should.exist(err); err.message.should.equal( 'You need at least one command before moving on' ); should.exist(r.errors[0]); r.errors[0] .split('\n') .should.deep.equal([ 'mocha <command>', '', 'Commands:', ' mocha cmd test command', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]); done(); } ); }); }); it('should allow setting the same description for several keys', () => { const r = checkUsage(() => yargs('--help').describe(['big', 'small'], 'Packet size').parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ' --big Packet size', ' --small Packet size', ]); }); // Refs: https://github.com/yargs/yargs/pull/1826 describe('usage for default command', () => { describe('default only', () => { const expected = [ 'usage', '', 'Default command description', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]; it('should contain the expected output for --help', () => { const r = checkUsage(() => yargs('--help') .scriptName('usage') .command('*', 'Default command description') .parse() ); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for showhelp', () => { const r = checkUsage(() => { const y = yargs() .scriptName('usage') .command('*', 'Default command description'); y.showHelp('log'); }); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for getHelp', async () => { const y = yargs() .scriptName('usage') .command('*', 'Default command description'); const help = await y.getHelp(); help.split('\n').should.deep.equal(expected); }); it('should contain the expected output for getHelp when called from within handler', async () => { let help = ''; const y = yargs() .scriptName('usage') .command('*', 'Default command description', {}, async () => { help = await y.getHelp(); }); await y.parse(); help.split('\n').should.deep.equal(expected); }); it('should contain the expected output for showHelp when called from within handler', () => { const r = checkUsage(() => yargs() .scriptName('usage') .command('*', 'Default command description', {}, () => yargs.showHelp('log') ) .parse('') ); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for showHelp, when exception occurs', () => { const r = checkUsage(() => yargs() .scriptName('usage') .command('*', 'Default command description', {}, () => yargs.showHelp('log') ) .check(() => { return false; }) .parse('') ); r.errors[0].split('\n').should.deep.equal(expected); }); }); describe('multiple', () => { const expected = [ 'Hello, world!', '', 'Commands:', ' usage Default command description [default]', ' usage foo Foo command description', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]; it('should contain the expected output for --help', () => { const r = checkUsage(() => yargs('--help') .scriptName('usage') .usage('Hello, world!') .commands([ {command: '*', desc: 'Default command description'}, {command: 'foo', desc: 'Foo command description'}, ]) .parse() ); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for showHelp', () => { const r = checkUsage(() => { yargs() .scriptName('usage') .usage('Hello, world!') .commands([ {command: '*', desc: 'Default command description'}, {command: 'foo', desc: 'Foo command description'}, ]) .parse(); yargs.showHelp('log'); }); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for showHelp when called from within handler', () => { const r = checkUsage( () => yargs() .scriptName('usage') .usage('Hello, world!') .commands([ { command: '*', desc: 'Default command description', handler: _ => yargs.showHelp('log'), }, {command: 'foo', desc: 'Foo command description'}, ]).argv ); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for getHelp', async () => { const y = yargs() .scriptName('usage') .usage('Hello, world!') .commands([ { command: '*', desc: 'Default command description', handler: () => {}, }, {command: 'foo', desc: 'Foo command description'}, ]); const help = await y.getHelp(); help.split('\n').should.deep.equal(expected); }); it('should contain the expected output for getHelp when called from within handler', async () => { let help = ''; const y = yargs() .scriptName('usage') .usage('Hello, world!') .commands([ { command: '*', desc: 'Default command description', handler: async () => { help = await y.getHelp(); }, }, {command: 'foo', desc: 'Foo command description'}, ]); await y.argv; help.split('\n').should.deep.equal(expected); }); }); // Refs: https://github.com/yargs/yargs/issues/1912 describe('positional', () => { const expected = [ 'Hello, world!', '', 'Commands:', ' usage [foo] Default command description [default]', ' usage foo Foo command description', '', 'Positionals:', ' foo foo parameter', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]; it('should contain the expected output for --help', () => { const r = checkUsage(() => yargs('--help') .scriptName('usage') .usage('Hello, world!') .command('* [foo]', 'Default command description', yargs => { yargs.positional('foo', { describe: 'foo parameter', }); }) .commands([{command: 'foo', desc: 'Foo command description'}]) .parse() ); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for showHelp', () => { const r = checkUsage(() => { const y = yargs() .scriptName('usage') .usage('Hello, world!') .command('* [foo]', 'Default command description', yargs => { yargs.positional('foo', { describe: 'foo parameter', }); }) .commands([{command: 'foo', desc: 'Foo command description'}]); y.parse(); y.showHelp('log'); }); r.logs[0].split('\n').should.deep.equal(expected); }); }); }); describe('async builder', async () => { it('shows appropriate usage instructions for nested command', async () => { // With --help flag: { const r = await checkUsage(() => { return yargs(['cmd', '--help']) .command('cmd <foo>', 'a test command', async yargs => { await wait(); yargs.positional('foo', { type: 'string', default: 'hello', }); }) .parse(); }); const logs = r.logs.join('\n'); logs.should.match(/default: "hello"/); logs.should.match(/a test command/); } // Using showHelp(): { const r = await checkUsage(() => { yargs(['cmd']) .command('cmd <foo>', 'a test command', async yargs => { await wait(); yargs.positional('foo', { type: 'string', default: 'hello', }); }) .showHelp('log'); return wait(20); }); const logs = r.logs.join('\n'); logs.should.match(/default: "hello"/); logs.should.match(/a test command/); } }); // Refs: https://github.com/yargs/yargs/issues/1912 describe('positional', () => { const expected = [ 'Hello, world!', '', 'Commands:', ' usage [foo] Default command description [default]', ' usage foo Foo command description', '', 'Positionals:', ' foo foo parameter', '', 'Options:', ' --help Show help [boolean]', ' --version Show version number [boolean]', ]; it('should contain the expected output for --help', async () => { const r = await checkUsage(() => { yargs('--help') .scriptName('usage') .usage('Hello, world!') .command('* [foo]', 'Default command description', async yargs => { await wait(); yargs.positional('foo', { describe: 'foo parameter', }); }) .commands([{command: 'foo', desc: 'Foo command description'}]) .parse(); return wait(20); }); r.logs[0].split('\n').should.deep.equal(expected); }); it('should contain the expected output for getHelp', async () => { const y = yargs() .scriptName('usage') .usage('Hello, world!') .command('* [foo]', 'Default command description', async yargs => { // await wait(); yargs.positional('foo', { describe: 'foo parameter', }); }) .commands([{command: 'foo', desc: 'Foo command description'}]); await y.parse(''); await wait(); const help = await y.getHelp(); help.split('\n').should.deep.equal(expected); }); }); }); // Refs: https://github.com/yargs/yargs/issues/1820 it('allows setting help and version with aliases and custom description', () => { const r = checkUsage(() => yargs('--help') .describe('help', 'Custom help description') .describe('version', 'Custom version description') .alias('help', 'h') .alias('v', 'version') .parse() ); r.logs[0] .split('\n') .should.deep.equal([ 'Options:', ' -h, --help Custom help description [boolean]', ' -v, --version Custom version description [boolean]', ]); }); // https://github.com/yargs/yargs/issues/2169 it('allows multiple option calls to not clobber description', () => { const r = checkUsage(() => yargs('--help') .options({ arg: {desc: 'Old description', type: 'string', default: 'old'}, }) .options({arg: {default: 'new'}}) .wrap(null) .parse() ); r.logs[0] .split('\n') .slice(-1)[0] .replace(/\s+/g, ' ') .trim() .should.equal('--arg Old description [string] [default: "new"]'); });});
Version Info