Repository: donejs/donejs
Pull request #608 (2016-04-04 13:09:24)

Fix a couple of issues with `donejs add`.
`donejs add app [folder]` was creating an empty folder
`donejs add component [name]` was trying to install the generator instead of using the built-in.


Patch:
diff --git a/lib/cli/cmd-add.js b/lib/cli/cmd-add.js
index b643633..9221b71 100644
--- a/lib/cli/cmd-add.js
+++ b/lib/cli/cmd-add.js
@@ -1,3 +1,4 @@
+var cmdInit = require('./cmd-init');
 var runBinary = require('./run-binary');
 var types = ['app', 'plugin', 'generator'];
 
@@ -15,11 +16,11 @@ module.exports = function(type, params, options) {
       options.type = type;
     }
 
-    return runBinary(['init', folder], options);
-
+    return cmdInit(folder, options);
+  }
   // handles commands with the following shape `donejs add <name> [params...]`
-  } else {
-    var args = ['add'].concat(params);
+  else {
+    var args = ['add', type].concat(params);
     return runBinary(args, options);
   }
 };
diff --git a/lib/cli/cmd-init.js b/lib/cli/cmd-init.js
index 30f1a6d..0d728b5 100644
--- a/lib/cli/cmd-init.js
+++ b/lib/cli/cmd-init.js
@@ -37,6 +37,10 @@ function installCli(version, options) {
 function runCliInit(folderPath, options) {
   var initArgs = ['init'];
 
+  // cd into the newly created folder, this way runBinary
+  // gets the root folder correctly.
+  process.chdir(folderPath);
+
   if (options.skipInstall) {
     initArgs.push('--skip-install');
   }
diff --git a/lib/utils.js b/lib/utils.js
index 6793af5..0b0b543 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -35,9 +35,15 @@ exports.mkdirp = function(folder) {
 // Run a command and pipe the output.
 // The returned promise will reject if there is a non-zero exist status
 exports.spawn = function(cmd, args, options) {
+  options = options || {};
+
   options.stdio = 'inherit';
 
-  var child = spawn(cmd, args, options || { cwd: process.cwd() });
+  if (!options.cwd) {
+    options.cwd = process.cwd();
+  }
+
+  var child = spawn(cmd, args, options);
   var deferred = Q.defer();
 
   child.on('exit', function(status) {
@@ -70,15 +76,17 @@ exports.projectRoot = function() {
 
 // Log error messages and exit application
 exports.log = function(promise) {
-  return promise.then(function() {
-    process.exit(0);
-  }, function(error) {
-    console.log();
-    console.error(error.stack || error.message || error);
-    console.log();
-
-    process.exit(1);
-  });
+  return promise
+    .then(function() {
+      process.exit(0);
+    })
+    .catch(function(error) {
+      console.log();
+      console.error(error.stack || error.message || error);
+      console.log();
+
+      process.exit(1);
+    });
 };
 
 // Takes an exact version like 0.5.7 and turns into a range like ^0.5.0


Test patch:
diff --git a/test/cmd-add-test.js b/test/cmd-add-test.js
index 3cb717f..78ced19 100644
--- a/test/cmd-add-test.js
+++ b/test/cmd-add-test.js
@@ -5,8 +5,10 @@ var mockery = require('mockery');
 describe('cmd add test', function() {
   var add;
   var binaryArgs;
+  var cmdInitArgs;
 
   beforeEach(function() {
+    cmdInitArgs = {};
     var cmdAddPath = '../lib/cli/cmd-add';
 
     mockery.enable({
@@ -16,6 +18,12 @@ describe('cmd add test', function() {
 
     mockery.registerAllowable(cmdAddPath);
 
+    mockery.registerMock('./cmd-init', function() {
+      cmdInitArgs.folder = arguments[0];
+      cmdInitArgs.options = arguments[1];
+      return Q(true);
+    });
+
     mockery.registerMock('./run-binary', function() {
       binaryArgs = arguments;
       return Q(true);
@@ -29,22 +37,23 @@ describe('cmd add test', function() {
     mockery.deregisterAll();
   });
 
-  it('when type is "app", runs binary with the right args', function() {
+  it('when type is "app", runs init command with the right args', function() {
     var folder = 'my-awesome-app';
 
     return add('app', [folder], {})
       .then(function() {
-        assert.deepEqual(binaryArgs[0], ['init', folder]);
+        assert.equal(cmdInitArgs.folder, folder);
+        assert.deepEqual(cmdInitArgs.options, {});
       });
   });
 
-  it('when type is "plugin", runs binary with the right args', function() {
+  it('when type is "plugin", runs init command with the right args', function() {
     var folder = 'my-awesome-app';
 
     return add('plugin', [folder], {})
       .then(function() {
-        assert.deepEqual(binaryArgs[0], ['init', folder]);
-        assert.deepEqual(binaryArgs[1].type, 'plugin');
+        assert.equal(cmdInitArgs.folder, folder);
+        assert.equal(cmdInitArgs.options.type, 'plugin');
       });
   });
 
@@ -53,15 +62,18 @@ describe('cmd add test', function() {
 
     return add('generator', [name], {})
       .then(function() {
-        assert.deepEqual(binaryArgs[0], ['init', name]);
-        assert.deepEqual(binaryArgs[1].type, 'generator');
+        assert.equal(cmdInitArgs.folder, name);
+        assert.equal(cmdInitArgs.options.type, 'generator');
       });
   });
 
   it('calls the "donejs-cli add" for other [type] values', function() {
     return add('component', ['my-component', '0.0.1'], {})
       .then(function() {
-        assert.deepEqual(binaryArgs[0], ['add', 'my-component', '0.0.1']);
+        assert.deepEqual(
+          binaryArgs[0],
+          ['add', 'component', 'my-component', '0.0.1']
+        );
       });
   });
 });
diff --git a/test/cmd-init-test.js b/test/cmd-init-test.js
index 3f167f0..c5b4f74 100644
--- a/test/cmd-init-test.js
+++ b/test/cmd-init-test.js
@@ -7,6 +7,7 @@ var mockery = require('mockery');
 var utils = require('../lib/utils');
 
 describe('cli init cmd', function() {
+  var cwd;
   var init;
   var spawnCalls;
   var runBinaryCall;
@@ -16,6 +17,7 @@ describe('cli init cmd', function() {
   beforeEach(function() {
     spawnCalls = [];
     runBinaryCall = {};
+    cwd = process.cwd();
 
     deleteFolder();
 
@@ -58,6 +60,7 @@ describe('cli init cmd', function() {
 
     init(folder, {})
       .then(function() {
+        process.chdir(cwd);
         assert(fs.existsSync(folder), 'should create folder');
         done();
       })
@@ -70,6 +73,7 @@ describe('cli init cmd', function() {
   it('creates {folder}/node_modules for deps to be installed', function(done) {
     init(folder, {})
       .then(function() {
+        process.chdir(cwd);
         var folderModules = path.join(folder, 'node_modules');
         assert(fs.existsSync(folderModules), 'should create node_modules');
         done();
@@ -81,7 +85,7 @@ describe('cli init cmd', function() {
     init(folder, {})
       .then(function() {
         var installCliCall = spawnCalls[0];
-        var folderPath = path.join(process.cwd(), folder);
+        var folderPath = path.join(cwd, folder);
 
         assert.equal(installCliCall.binary, 'npm');
         assert.deepEqual(installCliCall.args, [
@@ -100,6 +104,17 @@ describe('cli init cmd', function() {
       .catch(done);
   });
 
+  it('passes options to donejs-init properly', function(done) {
+    init(folder, { skipInstall: true, type: 'foobar' })
+      .then(function() {
+        assert.deepEqual(runBinaryCall.args, [
+          'init', '--skip-install', '--type', 'foobar'
+        ]);
+        done();
+      })
+      .catch(done);
+  });
+
   function deleteFolder() {
     if (fs.existsSync(folder)) {
       rimraf.sync(folder);
diff --git a/test/utils-test.js b/test/utils-test.js
index ba061cf..c248b43 100644
--- a/test/utils-test.js
+++ b/test/utils-test.js
@@ -1,6 +1,7 @@
-var assert = require('assert');
-var path = require('path');
+var Q = require('q');
 var fs = require('fs');
+var path = require('path');
+var assert = require('assert');
 var utils = require('../lib/utils');
 
 describe('utils tests', function() {
@@ -16,10 +17,69 @@ describe('utils tests', function() {
     });
   });
 
+  it('mkdirp returns cwd if folder is not provided', function() {
+    return utils.mkdirp()
+      .then(function(folderPath) {
+        assert.equal(folderPath, process.cwd());
+      });
+  });
+
   it('spawns successfully', function(done) {
-    utils.spawn('npm', ['run', 'verify'], {}).then(function(child) {
-      assert.equal(child.exitCode, 0);
-      done();
+    var cwd = path.join(__dirname, '..');
+
+    utils.spawn('npm', ['run', 'verify'], { cwd: cwd })
+      .then(function(child) {
+        assert.equal(child.exitCode, 0);
+        done();
+      })
+      .catch(done);
+  });
+
+  it('spawns rejects if cannot execute command', function(done) {
+    var cwd = path.join(__dirname, '..');
+
+    utils.spawn('npm', ['run', 'invalid-script'], { cwd: cwd })
+      .then(function() {
+        assert(false, 'should reject promise');
+        done();
+      })
+      .catch(function(err) {
+        assert.equal(err.message, 'Command `npm` did not complete successfully');
+        done();
+      });
+  });
+
+  describe('utils.log', function() {
+    var _exit;
+    var exitCode;
+
+    beforeEach(function() {
+      _exit = process.exit;
+
+      Object.defineProperty(process, 'exit', {
+        value: function(code) {
+          exitCode = code;
+        }
+      });
+    });
+
+    afterEach(function() {
+      Object.defineProperty(process, 'exit', { value: _exit });
+    });
+
+    it('ends process successfully if promise resolves', function() {
+      return utils.log(Q(true))
+        .then(function() {
+          assert.equal(exitCode, 0);
+        });
+    });
+
+    it('ends process with error if promise rejects', function(done) {
+      utils.log(Q.reject(new Error('foobar')))
+        .finally(function() {
+          assert.equal(exitCode, 1);
+          done();
+        });
     });
   });