Skip to main content
Module

x/yargs/test/usage.cjs

yargs the modern, pirate-themed successor to optimist.
Very Popular
Go to Latest
File
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198
'use strict';/* global describe, it, beforeEach */
const checkUsage = require('./helpers/utils.cjs').checkOutput;const chalk = require('chalk');const path = require('path');const yargs = require('../index.cjs');const {rebase, YError} = require('../build/index.cjs');
const should = require('chai').should();
const noop = () => {};
describe('usage tests', () => { beforeEach(() => { yargs.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); }) .exitProcess(false) .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(); }) .exitProcess(false) .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); }) .exitProcess(false) .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'); }) .exitProcess(false) .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('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); }); 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'); }); 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('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 succeed when rebase', () => { rebase( ['home', 'chevex'].join(path.sep), ['home', 'chevex', 'foo', 'bar', 'baz'].join(path.sep) ).should.equal(['foo', 'bar', 'baz'].join(path.sep)); rebase( ['home', 'chevex', 'foo', 'bar', 'baz'].join(path.sep), ['home', 'chevex'].join(path.sep) ).should.equal(['..', '..', '..'].join(path.sep)); rebase( ['home', 'chevex', 'foo'].join(path.sep), ['home', 'chevex', 'pow', 'zoom.txt'].join(path.sep) ).should.equal(['..', 'pow', 'zoom.txt'].join(path.sep)); }); 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(4); }); 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]', ]); }); }); 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 mutiple 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(); } }); }); 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]', ]); }); }); 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() .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() .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', ]); });});