From afef8b25d3752877a7d6e4c1decc2a853709f6ff Mon Sep 17 00:00:00 2001 From: Matthias ETIENNE Date: Mon, 27 Feb 2017 01:30:59 +0100 Subject: [PATCH] feat(actions): Implement async actions Actions can now be function that returns a Promise. --- lib/command.js | 10 +++- tests/actions.js | 128 +++++++++++++++++++++++++++++++++++++++++++++ tests/no-action.js | 28 ---------- 3 files changed, 136 insertions(+), 30 deletions(-) create mode 100644 tests/actions.js delete mode 100644 tests/no-action.js diff --git a/lib/command.js b/lib/command.js index 44909bb..8472583 100644 --- a/lib/command.js +++ b/lib/command.js @@ -9,6 +9,7 @@ const InvalidArgumentValueError = require('./error/invalid-argument-value'); const MissingOptionError = require('./error/missing-option'); const NoActionError = require('./error/no-action-error'); const WrongNumberOfArgumentError = require('./error/wrong-num-of-arg'); +const Promise = require('bluebird'); /** * Command class @@ -381,7 +382,6 @@ class Command extends GetterSetter { * * @param {Object} args - Arguments * @param {Object} options - Options - * @returns {*} * @private */ _run(args, options) { @@ -392,7 +392,13 @@ class Command extends GetterSetter { this._program )); } - return this._action.apply(this, [args, options, this._logger]); + const actionResults = this._action.apply(this, [args, options, this._logger]); + const response = Promise.resolve(actionResults); + response + .catch(err => { + err = err instanceof Error ? err : new Error(err); + return this._program.fatalError(err); + }) } /** diff --git a/tests/actions.js b/tests/actions.js new file mode 100644 index 0000000..76af41e --- /dev/null +++ b/tests/actions.js @@ -0,0 +1,128 @@ +"use strict"; + +/* global Program, logger, should, makeArgv, sinon */ + +const Promise = require('bluebird'); + +describe('Setting up no action()', () => { + + it(`should throw NoActionError`, () => { + + const program = new Program(); + + program + .logger(logger) + .version('1.0.0') + .command('foo', 'My foo'); + + const error = sinon.stub(program, "fatalError", function(err) { + should(err.name).eql('NoActionError'); + }); + + program.parse(makeArgv('foo')); + + const count = error.callCount; + error.restore(); + should(count).be.eql(1); + program.reset(); + }); + +}); + + +describe('Setting up a sync action', () => { + + it(`should call this action`, () => { + + const program = new Program(); + const action = sinon.spy(); + + program + .logger(logger) + .version('1.0.0') + .command('foo', 'My foo') + .action(action); + + program.parse(makeArgv('foo')); + + should(action.callCount).be.eql(1); + + program.reset(); + }); + +}); + + +describe('Setting up a async action', () => { + + it(`should succeed for a resolved promise`, () => { + + const program = new Program(); + const action = function() { + return Promise.resolve('foo') + }; + const stub = sinon.spy(action); + + program + .logger(logger) + .version('1.0.0') + .command('foo', 'My foo') + .action(stub); + + program.parse(makeArgv('foo')); + + should(stub.callCount).be.eql(1); + program.reset(); + + }); + + it(`should fatalError() for a rejected promise (error string)`, (done) => { + + const program = new Program(); + const action = function() { + return Promise.reject('Failed!') + }; + const stub = sinon.spy(action); + const fatalError = sinon.stub(program, "fatalError"); + + program + .logger(logger) + .version('1.0.0') + .command('foo', 'My foo') + .action(stub); + + program.parse(makeArgv('foo')); + + setImmediate(function () { + should(stub.callCount).be.eql(1); + should(fatalError.callCount).be.eql(1); + done() + }); + + }); + it(`should fatalError() for a rejected promise (error object)`, (done) => { + + const program = new Program(); + const action = function() { + return Promise.reject(new Error('Failed!')) + }; + const stub = sinon.spy(action); + const fatalError = sinon.stub(program, "fatalError"); + + program + .logger(logger) + .version('1.0.0') + .command('foo', 'My foo') + .action(stub); + + program.parse(makeArgv('foo')); + + setImmediate(function () { + should(stub.callCount).be.eql(1); + should(fatalError.callCount).be.eql(1); + done() + }); + + }); + +}); diff --git a/tests/no-action.js b/tests/no-action.js deleted file mode 100644 index c629a4e..0000000 --- a/tests/no-action.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; - -/* global Program, logger, should, makeArgv, sinon */ - -const program = new Program(); - -program - .logger(logger) - .version('1.0.0') - .command('foo', 'My foo'); - -describe('Setting up no action()', () => { - it(`should throw NoActionError`, () => { - - const error = sinon.stub(program, "fatalError", function(err) { - should(err.name).eql('NoActionError'); - }); - - program.parse(makeArgv('foo')); - - const count = error.callCount; - error.restore(); - should(count).be.eql(1); - program.reset(); - }); -}); - -