Skip to main content
Module

x/yargs/test/middleware.cjs

yargs the modern, pirate-themed successor to optimist.
Very Popular
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
'use strict';/* global describe, it, beforeEach, afterEach *//* eslint-disable no-unused-vars */
const assert = require('assert');const {expect} = require('chai');let yargs;require('chai').should();
function clearRequireCache() { delete require.cache[require.resolve('../index.cjs')]; delete require.cache[require.resolve('../build/index.cjs')];}
async function wait() { return new Promise(resolve => { setTimeout(resolve, 10); });}
describe('middleware', () => { beforeEach(() => { yargs = require('../index.cjs'); }); afterEach(() => { clearRequireCache(); });
it('runs the middleware before reaching the handler', done => { yargs(['mw']) .middleware(argv => { argv.mw = 'mw'; }) .command( 'mw', 'adds func to middleware', () => {}, argv => { argv.mw.should.equal('mw'); return done(); } ) .parse(); });
it('runs all middleware before reaching the handler', done => { yargs(['mw']) .middleware([ function (argv) { argv.mw1 = 'mw1'; }, function (argv) { argv.mw2 = 'mw2'; }, ]) .command( 'mw', 'adds func list to middleware', () => {}, argv => { argv.mw1.should.equal('mw1'); argv.mw2.should.equal('mw2'); return done(); } ) .exitProcess(false) .parse(); });
it('should be able to register middleware regardless of when middleware is called', done => { yargs(['mw']) .middleware(argv => { argv.mw1 = 'mw1'; }) .command( 'mw', 'adds func list to middleware', () => {}, argv => { // we should get the argv filled with data from the middleware argv.mw1.should.equal('mw1'); argv.mw2.should.equal('mw2'); argv.mw3.should.equal('mw3'); argv.mw4.should.equal('mw4'); return done(); } ) .middleware(argv => { argv.mw2 = 'mw2'; }) .middleware([ function (argv) { argv.mw3 = 'mw3'; }, function (argv) { argv.mw4 = 'mw4'; }, ]) .exitProcess(false) .parse(); });
// see: https://github.com/yargs/yargs/issues/1281 it("doesn't modify globalMiddleware array when executing middleware", () => { let count = 0; yargs('bar') .middleware(argv => { count++; }) .command( 'foo', 'foo command', () => {}, () => {}, [ () => { count++; }, ] ) .command( 'bar', 'bar command', () => {}, () => {}, [ () => { count++; }, ] ) .exitProcess(false) .parse(); count.should.equal(2); });
it('allows middleware to be added in builder', done => { yargs(['mw']) .command( 'mw', 'adds func to middleware', yargs => { yargs.middleware(argv => { argv.mw = 'mw'; }); }, argv => { argv.mw.should.equal('mw'); return done(); } ) .exitProcess(false) .parse(); });
it('passes yargs object to middleware', done => { yargs(['mw']) .command( 'mw', 'adds func to middleware', yargs => { yargs.middleware((argv, yargs) => { expect(typeof yargs.help).to.equal('function'); argv.mw = 'mw'; }); }, argv => { argv.mw.should.equal('mw'); return done(); } ) .exitProcess(false) .parse(); });
it('applies aliases before middleware is called', done => { yargs(['mw', '--foo', '99']) .middleware(argv => { argv.f.should.equal(99); argv.mw = 'mw'; }) .command( 'mw', 'adds func to middleware', yargs => { yargs.middleware(argv => { argv.f.should.equal(99); argv.mw2 = 'mw2'; }); }, argv => { argv.mw.should.equal('mw'); argv.mw2.should.equal('mw2'); return done(); } ) .alias('foo', 'f') .exitProcess(false) .parse(); });
describe('applyBeforeValidation=true', () => { it('runs before validation', done => { yargs(['mw']) .middleware(argv => { argv.mw = 'mw'; }, true) .command( 'mw', 'adds func to middleware', { mw: { demand: true, string: true, }, }, argv => { argv.mw.should.equal('mw'); return done(); } ) .exitProcess(false) .parse(); });
it('resolves async middleware, before applying validation', async () => { const argv = await yargs(['mw']) .fail(false) .middleware( [ async function (argv) { return new Promise(resolve => { setTimeout(() => { argv.mw = 'mw'; argv.other = true; return resolve(argv); }, 5); }); }, ], true ) .command('mw', 'adds func to middleware', { mw: { demand: true, string: true, }, }) .parse(); argv.other.should.equal(true); argv.mw.should.equal('mw'); });
it('still throws error when async middleware is used', async () => { try { const argv = await yargs(['mw']) .fail(false) .middleware( [ async function (argv) { return new Promise(resolve => { setTimeout(() => { argv.other = true; return resolve(argv); }, 5); }); }, ], true ) .command('mw', 'adds func to middleware', { mw: { demand: true, string: true, }, }) .parse(); throw Error('unreachable'); } catch (err) { err.message.should.match(/Missing required argument/); } });
// Addresses: https://github.com/yargs/yargs/issues/2124 // This test will fail if result of async middleware is not handled like a promise it('does not cause an unexpected error when async middleware and strict are both used', done => { const input = 'cmd1'; yargs(input) .command( 'cmd1', 'cmd1 desc', yargs => yargs.middleware(async argv => argv, true), _argv => { done(); } ) .fail(msg => { done(new Error(msg)); }) .strict() .parse(); });
it('runs before validation, when middleware is added in builder', done => { yargs(['mw']) .command( 'mw', 'adds func to middleware', yargs => { // we know that this middleware is being run in the context of the // mw command. yargs.middleware(argv => { argv.mw = 'mw'; }, true); }, argv => { argv.mw.should.equal('mw'); return done(); } ) .demand('mw') .exitProcess(false) .parse(); });
it('applies aliases before middleware is called, for global middleware', done => { yargs(['mw', '--foo', '99']) .middleware(argv => { argv.f.should.equal(99); argv.mw = 'mw'; }, true) .command( 'mw', 'adds func to middleware', { mw: { demand: true, }, }, argv => { argv.mw.should.equal('mw'); return done(); } ) .alias('foo', 'f') .exitProcess(false) .parse(); });
it('applies aliases before middleware is called, when middleware is added in builder', done => { yargs(['mw', '--foo', '99']) .command( 'mw', 'adds func to middleware', yargs => { yargs .middleware(argv => { argv.f.should.equal(99); argv.mw = 'mw'; }, true) .demand('mw'); }, argv => { argv.mw.should.equal('mw'); return done(); } ) .alias('foo', 'f') .exitProcess(false) .parse(); }); });
// addresses https://github.com/yargs/yargs/issues/1237 describe('async', () => { it('fails when the promise returned by the middleware rejects', done => { const error = new Error(); const handlerErr = new Error('should not have been called'); yargs('foo') .command( 'foo', 'foo command', () => {}, argv => done(handlerErr), [argv => Promise.reject(error)] ) .fail((msg, err) => { expect(msg).to.equal(null); expect(err).to.equal(error); done(); }) .parse(); });
it('it allows middleware rejection to be caught', async () => { const argvPromise = yargs('foo') .command( 'foo', 'foo command', () => {}, () => {} ) .middleware(async () => { return new Promise((resolve, reject) => { setTimeout(() => { return reject(Error('error from middleware')); }, 5); }); }) .fail(false) .parse(); try { await argvPromise; throw Error('unreachable'); } catch (err) { err.message.should.match(/error from middleware/); } });
it('it awaits middleware before awaiting handler, when applyBeforeValidation is "false"', async () => { let log = ''; const argvPromise = yargs('foo --bar') .command( 'foo', 'foo command', () => {}, async () => { return new Promise(resolve => { setTimeout(() => { log += 'handler'; return resolve(); }, 5); }); } ) .middleware(async argv => { return new Promise(resolve => { setTimeout(() => { log += 'middleware'; argv.fromMiddleware = 99; return resolve(); }, 20); }); }, false) .parse(); const argv = await argvPromise; log.should.equal('middlewarehandler'); argv.fromMiddleware.should.equal(99); argv.bar.should.equal(true); });
it('calls the command handler when all middleware promises resolve', done => { const middleware = (key, value) => () => new Promise((resolve, reject) => { setTimeout(() => { return resolve({[key]: value}); }, 5); }); yargs('foo hello') .command( 'foo <pos>', 'foo command', () => {}, argv => { argv.hello.should.equal('world'); argv.foo.should.equal('bar'); done(); }, [middleware('hello', 'world'), middleware('foo', 'bar')] ) .fail((msg, err) => { return done(Error('should not have been called')); }) .exitProcess(false) .parse(); });
it('calls an async middleware only once for nested subcommands', done => { let callCount = 0; const argv = yargs('cmd subcmd') .command('cmd', 'cmd command', yargs => { yargs.command('subcmd', 'subcmd command', yargs => {}); }) .middleware( argv => new Promise(resolve => { callCount++; resolve(argv); }) ) .parse();
if (!(argv instanceof Promise)) done(Error('argv should be a Promise'));
argv .then(() => { callCount.should.equal(1); done(); }) .catch(err => done(err)); });
describe('$0', () => { it('applies global middleware when no commands are provided, with implied $0', async () => { const argv = await yargs('--foo 99') .middleware(argv => { return new Promise(resolve => { setTimeout(() => { argv.foo = argv.foo * 3; return resolve(); }, 20); }); }) .parse(); argv.foo.should.equal(297); });
it('applies middleware before performing validation, with implied $0', async () => { const argvEventual = yargs('--foo 100') .option('bar', { demand: true, }) .middleware(async argv => { return new Promise(resolve => { setTimeout(() => { argv.foo = argv.foo * 2; argv.bar = 'hello'; return resolve(); }, 100); }); }, true) .check(argv => argv.foo > 100) .parse(); const argv = await argvEventual; argv.foo.should.equal(200); argv.bar.should.equal('hello'); });
it('applies middleware before performing validation, with explicit $0', async () => { const argvEventual = yargs('--foo 100') .usage('$0', 'usage', () => {}) .option('bar', { demand: true, }) .middleware(async argv => { return new Promise(resolve => { setTimeout(() => { argv.foo = argv.foo * 2; argv.bar = 'hello'; return resolve(); }, 100); }); }, true) .check(argv => argv.foo > 100) .parse(); const argv = await argvEventual; argv.foo.should.equal(200); argv.bar.should.equal('hello'); }); }); });
describe('synchronous $0', () => { it('applies global middleware when no commands are provided', () => { const argv = yargs('--foo 99') .middleware(argv => { argv.foo = argv.foo * 2; }) .parse(); argv.foo.should.equal(198); }); it('applies global middleware when default command is provided, with explicit $0', () => { const argv = yargs('--foo 100') .usage( '$0', 'usage', () => {}, argv => { argv.foo = argv.foo * 3; } ) .middleware(argv => { argv.foo = argv.foo * 2; }) .parse(); argv.foo.should.equal(600); }); it('applies middleware before performing validation, with implicit $0', () => { const argv = yargs('--foo 100') .option('bar', { demand: true, }) .middleware(argv => { argv.foo = argv.foo * 2; argv.bar = 'hello'; }, true) .check(argv => argv.foo > 100) .parse(); argv.foo.should.equal(200); argv.bar.should.equal('hello'); }); });
// Refs: https://github.com/yargs/yargs/issues/1351 it('should run even if no command is matched', () => { const argv = yargs('snuh --foo 99') .middleware(argv => { argv.foo = argv.foo * 2; }) .command( 'bar', 'bar command', () => {}, () => {} ) .parse(); argv.foo.should.equal(198); });
it('throws error if middleware not function', () => { let err; assert.throws(() => { yargs('snuh --foo 99').middleware(['hello']).parse(); }, /middleware must be a function/); });
describe('async check', () => { describe('success', () => { it('returns promise if check is async', async () => { const argvPromise = yargs('--foo 100') .middleware(argv => { argv.foo *= 2; }, true) .check(async argv => { wait(); return argv.foo >= 200; }) .parse(); (!!argvPromise.then).should.equal(true); const argv = await argvPromise; argv.foo.should.equal(200); }); it('returns promise if check and middleware is async', async () => { const argvPromise = yargs('--foo 100') .middleware(async argv => { wait(); argv.foo *= 2; }, true) .check(async argv => { wait(); return argv.foo >= 200; }) .parse(); (!!argvPromise.then).should.equal(true); const argv = await argvPromise; argv.foo.should.equal(200); }); it('allows async check to be used with command', async () => { let output = ''; const argv = await yargs('cmd --foo 300') .command( 'cmd', 'a command', yargs => { yargs.check(async argv => { wait(); output += 'first'; return argv.foo >= 200; }); }, async argv => { wait(); output += 'second'; } ) .parse(); argv._.should.include('cmd'); argv.foo.should.equal(300); output.should.equal('firstsecond'); }); it('allows async check to be used with command and middleware', async () => { let output = ''; const argv = await yargs('cmd --foo 100') .command( 'cmd', 'a command', yargs => { yargs.check(async argv => { wait(); output += 'second'; return argv.foo >= 200; }); }, async argv => { wait(); output += 'fourth'; }, [ async argv => { wait(); output += 'third'; argv.foo *= 2; }, ] ) .middleware(async argv => { wait(); output += 'first'; argv.foo *= 2; }, true) .parse(); argv._.should.include('cmd'); argv.foo.should.equal(400); output.should.equal('firstsecondthirdfourth'); }); }); describe('failure', () => { it('allows failed check to be caught', async () => { await assert.rejects( yargs('--f 33') .alias('foo', 'f') .fail(false) .check(async argv => { wait(); return argv.foo > 50; }) .parse(), /Argument check failed/ ); }); it('allows error to be caught before calling command', async () => { let output = ''; await assert.rejects( yargs('cmd --foo 100') .fail(false) .command( 'cmd', 'a command', yargs => { yargs.check(async argv => { wait(); output += 'first'; return argv.foo >= 200; }); }, async argv => { wait(); output += 'second'; } ) .parse(), /Argument check failed/ ); output.should.equal('first'); }); it('allows error to be caught before calling command and middleware', async () => { let output = ''; await assert.rejects( yargs('cmd --foo 10') .fail(false) .command( 'cmd', 'a command', yargs => { yargs.check(async argv => { wait(); output += 'second'; return argv.foo >= 200; }); }, async argv => { wait(); output += 'fourth'; }, [ async argv => { wait(); output += 'third'; argv.foo *= 2; }, ] ) .middleware(async argv => { wait(); output += 'first'; argv.foo *= 2; }, true) .parse(), /Argument check failed/ ); output.should.equal('firstsecond'); }); }); it('applies aliases prior to calling check', async () => { const argv = await yargs('--f 99') .alias('foo', 'f') .check(async argv => { wait(); return argv.foo > 50; }) .parse(); argv.foo.should.equal(99); }); });
describe('async coerce', () => { it('allows two commands to register different coerce methods', async () => { const y = yargs() .command('command1', 'first command', yargs => { yargs.coerce('foo', async arg => { wait(); return new Date(arg); }); }) .command('command2', 'second command', yargs => { yargs.coerce('foo', async arg => { wait(); return new Number(arg); }); }) .coerce('foo', async _arg => { return 'hello'; }); const r1 = await y.parse('command1 --foo 2020-10-10'); expect(r1.foo).to.be.an.instanceof(Date); const r2 = await y.parse('command2 --foo 302'); r2.foo.should.equal(302); }); it('coerce is applied to argument and aliases', async () => { let callCount = 0; const argvPromise = yargs() .alias('f', 'foo') .coerce('foo', async arg => { wait(); callCount++; return new Date(arg.toString()); }) .parse('-f 2014'); (!!argvPromise.then).should.equal(true); const argv = await argvPromise; callCount.should.equal(1); expect(argv.foo).to.be.an.instanceof(Date); expect(argv.f).to.be.an.instanceof(Date); }); it('applies coerce before validation', async () => { const argv = await yargs() .option('foo', { choices: [10, 20, 30], }) .coerce('foo', async arg => { wait(); return (arg *= 2); }) .parse('--foo 5'); argv.foo.should.equal(10); }); it('allows error to be caught', async () => { await assert.rejects( yargs() .fail(false) .option('foo', { choices: [10, 20, 30], }) .coerce('foo', async arg => { await wait(); return (arg *= 2); }) .parse('--foo 2'), /Choices: 10, 20, 30/ ); }); });});