Merge pull request #168 from braydonf/cli-cmds
Added CLI commands "remove" and "call"
This commit is contained in:
commit
95a65da87d
51
cli/main.js
51
cli/main.js
@ -5,12 +5,14 @@ var path = require('path');
|
|||||||
var bitcorenode = require('..');
|
var bitcorenode = require('..');
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
|
/* jshint maxstatements: 100 */
|
||||||
|
|
||||||
// local commands
|
|
||||||
var version = bitcorenode.version;
|
var version = bitcorenode.version;
|
||||||
var create = bitcorenode.scaffold.create;
|
var create = bitcorenode.scaffold.create;
|
||||||
var add = bitcorenode.scaffold.add;
|
var add = bitcorenode.scaffold.add;
|
||||||
var start = bitcorenode.scaffold.start;
|
var start = bitcorenode.scaffold.start;
|
||||||
|
var remove = bitcorenode.scaffold.remove;
|
||||||
|
var callMethod = bitcorenode.scaffold.callMethod;
|
||||||
var findConfig = bitcorenode.scaffold.findConfig;
|
var findConfig = bitcorenode.scaffold.findConfig;
|
||||||
var defaultConfig = bitcorenode.scaffold.defaultConfig;
|
var defaultConfig = bitcorenode.scaffold.defaultConfig;
|
||||||
|
|
||||||
@ -69,7 +71,7 @@ function main() {
|
|||||||
modules: modules
|
modules: modules
|
||||||
};
|
};
|
||||||
add(opts, function() {
|
add(opts, function() {
|
||||||
console.log('Successfully added modules: ', modules.join(', '));
|
console.log('Successfully added module(s):', modules.join(', '));
|
||||||
});
|
});
|
||||||
}).on('--help', function() {
|
}).on('--help', function() {
|
||||||
console.log(' Examples:');
|
console.log(' Examples:');
|
||||||
@ -79,6 +81,51 @@ function main() {
|
|||||||
console.log();
|
console.log();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('remove <modules...>')
|
||||||
|
.alias('uninstall')
|
||||||
|
.description('Uninstall a module for the current node')
|
||||||
|
.action(function(modules){
|
||||||
|
var configInfo = findConfig(process.cwd());
|
||||||
|
if (!configInfo) {
|
||||||
|
throw new Error('Could not find configuration, see `bitcore-node create --help`');
|
||||||
|
}
|
||||||
|
var opts = {
|
||||||
|
path: configInfo.path,
|
||||||
|
modules: modules
|
||||||
|
};
|
||||||
|
remove(opts, function() {
|
||||||
|
console.log('Successfully removed module(s):', modules.join(', '));
|
||||||
|
});
|
||||||
|
}).on('--help', function() {
|
||||||
|
console.log(' Examples:');
|
||||||
|
console.log();
|
||||||
|
console.log(' $ bitcore-node remove wallet-service');
|
||||||
|
console.log(' $ bitcore-node remove insight-api');
|
||||||
|
console.log();
|
||||||
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('call <method> [params...]')
|
||||||
|
.description('Call an API method')
|
||||||
|
.action(function(method, params) {
|
||||||
|
var configInfo = findConfig(process.cwd());
|
||||||
|
if (!configInfo) {
|
||||||
|
configInfo = defaultConfig();
|
||||||
|
}
|
||||||
|
var options = {
|
||||||
|
protocol: 'http',
|
||||||
|
host: 'localhost',
|
||||||
|
port: configInfo.config.port
|
||||||
|
};
|
||||||
|
callMethod(options, method, params, function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
console.log(JSON.stringify(data, null, 2));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
program.parse(process.argv);
|
program.parse(process.argv);
|
||||||
|
|
||||||
if (process.argv.length === 2) {
|
if (process.argv.length === 2) {
|
||||||
|
|||||||
2
index.js
2
index.js
@ -15,7 +15,9 @@ module.exports.modules.AddressModule = require('./lib/modules/address');
|
|||||||
module.exports.scaffold = {};
|
module.exports.scaffold = {};
|
||||||
module.exports.scaffold.create = require('./lib/scaffold/create');
|
module.exports.scaffold.create = require('./lib/scaffold/create');
|
||||||
module.exports.scaffold.add = require('./lib/scaffold/add');
|
module.exports.scaffold.add = require('./lib/scaffold/add');
|
||||||
|
module.exports.scaffold.remove = require('./lib/scaffold/remove');
|
||||||
module.exports.scaffold.start = require('./lib/scaffold/start');
|
module.exports.scaffold.start = require('./lib/scaffold/start');
|
||||||
|
module.exports.scaffold.callMethod = require('./lib/scaffold/call-method');
|
||||||
module.exports.scaffold.findConfig = require('./lib/scaffold/find-config');
|
module.exports.scaffold.findConfig = require('./lib/scaffold/find-config');
|
||||||
module.exports.scaffold.defaultConfig = require('./lib/scaffold/default-config');
|
module.exports.scaffold.defaultConfig = require('./lib/scaffold/default-config');
|
||||||
|
|
||||||
|
|||||||
@ -81,6 +81,14 @@ DB.prototype.stop = function(callback) {
|
|||||||
setImmediate(callback);
|
setImmediate(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DB.prototype.getInfo = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
setImmediate(function() {
|
||||||
|
var info = self.node.bitcoind.getInfo();
|
||||||
|
callback(null, info);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
DB.prototype.getBlock = function(hash, callback) {
|
DB.prototype.getBlock = function(hash, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -289,6 +297,7 @@ DB.prototype.blockHandler = function(block, add, callback) {
|
|||||||
|
|
||||||
DB.prototype.getAPIMethods = function() {
|
DB.prototype.getAPIMethods = function() {
|
||||||
var methods = [
|
var methods = [
|
||||||
|
['getInfo', this, this.getInfo, 0],
|
||||||
['getBlock', this, this.getBlock, 1],
|
['getBlock', this, this.getBlock, 1],
|
||||||
['getTransaction', this, this.getTransaction, 2],
|
['getTransaction', this, this.getTransaction, 2],
|
||||||
['sendTransaction', this, this.sendTransaction, 1],
|
['sendTransaction', this, this.sendTransaction, 1],
|
||||||
|
|||||||
43
lib/scaffold/call-method.js
Normal file
43
lib/scaffold/call-method.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var socketClient = require('socket.io-client');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls a remote node with a method and params
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {String} method - The name of the method to call
|
||||||
|
* @param {Array} params - An array of the params for the method
|
||||||
|
* @param {Function} done - The callback function
|
||||||
|
*/
|
||||||
|
function callMethod(options, method, params, done) {
|
||||||
|
|
||||||
|
var host = options.host;
|
||||||
|
var protocol = options.protocol;
|
||||||
|
var port = options.port;
|
||||||
|
var url = protocol + '://' + host + ':' + port;
|
||||||
|
var socketOptions = {
|
||||||
|
reconnection: false,
|
||||||
|
connect_timeout: 5000
|
||||||
|
};
|
||||||
|
var socket = socketClient(url, socketOptions);
|
||||||
|
|
||||||
|
socket.on('connect', function(){
|
||||||
|
socket.send({
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
}, function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
return done(new Error(response.error.message));
|
||||||
|
}
|
||||||
|
socket.close();
|
||||||
|
done(null, response.result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('connect_error', done);
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = callMethod;
|
||||||
144
lib/scaffold/remove.js
Normal file
144
lib/scaffold/remove.js
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
var fs = require('fs');
|
||||||
|
var npm = require('npm');
|
||||||
|
var path = require('path');
|
||||||
|
var spawn = require('child_process').spawn;
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var $ = bitcore.util.preconditions;
|
||||||
|
var _ = bitcore.deps._;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will remove a module from bitcore-node.json
|
||||||
|
* @param {String} configFilePath - The absolute path to the configuration file
|
||||||
|
* @param {String} module - The name of the module
|
||||||
|
* @param {Function} done
|
||||||
|
*/
|
||||||
|
function removeConfig(configFilePath, module, done) {
|
||||||
|
$.checkArgument(path.isAbsolute(configFilePath), 'An absolute path is expected');
|
||||||
|
fs.readFile(configFilePath, function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
var config = JSON.parse(data);
|
||||||
|
$.checkState(
|
||||||
|
Array.isArray(config.modules),
|
||||||
|
'Configuration file is expected to have a modules array.'
|
||||||
|
);
|
||||||
|
// remove the module from the configuration
|
||||||
|
for (var i = 0; i < config.modules.length; i++) {
|
||||||
|
if (config.modules[i] === module) {
|
||||||
|
config.modules.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.modules = _.unique(config.modules);
|
||||||
|
config.modules.sort(function(a, b) {
|
||||||
|
return a > b;
|
||||||
|
});
|
||||||
|
fs.writeFile(configFilePath, JSON.stringify(config, null, 2), done);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will uninstall a Node.js module and remove from package.json.
|
||||||
|
* @param {String} configDir - The absolute configuration directory path
|
||||||
|
* @param {String} module - The name of the module
|
||||||
|
* @param {Function} done
|
||||||
|
*/
|
||||||
|
function uninstallModule(configDir, module, done) {
|
||||||
|
$.checkArgument(path.isAbsolute(configDir), 'An absolute path is expected');
|
||||||
|
$.checkArgument(_.isString(module), 'A string is expected for the module argument');
|
||||||
|
|
||||||
|
var child = spawn('npm', ['uninstall', module, '--save'], {cwd: configDir});
|
||||||
|
|
||||||
|
child.stdout.on('data', function(data) {
|
||||||
|
process.stdout.write(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on('data', function(data) {
|
||||||
|
process.stderr.write(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', function(code) {
|
||||||
|
if (code !== 0) {
|
||||||
|
return done(new Error('There was an error uninstalling module: ' + module));
|
||||||
|
} else {
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will remove a Node.js module if it is installed.
|
||||||
|
* @param {String} configDir - The absolute configuration directory path
|
||||||
|
* @param {String} module - The name of the module
|
||||||
|
* @param {Function} done
|
||||||
|
*/
|
||||||
|
function removeModule(configDir, module, done) {
|
||||||
|
$.checkArgument(path.isAbsolute(configDir), 'An absolute path is expected');
|
||||||
|
$.checkArgument(_.isString(module), 'A string is expected for the module argument');
|
||||||
|
|
||||||
|
// check if the module is installed
|
||||||
|
npm.load(function(err) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
npm.commands.ls([module], true /*silent*/, function(err, data, lite) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
if (lite.dependencies) {
|
||||||
|
uninstallModule(configDir, module, done);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will remove the Node.js module and from the bitcore-node configuration.
|
||||||
|
* @param {String} options.cwd - The current working directory
|
||||||
|
* @param {String} options.dirname - The bitcore-node configuration directory
|
||||||
|
* @param {Array} options.modules - An array of strings of module names
|
||||||
|
* @param {Function} done - A callback function called when finished
|
||||||
|
*/
|
||||||
|
function remove(options, done) {
|
||||||
|
$.checkArgument(_.isObject(options));
|
||||||
|
$.checkArgument(_.isFunction(done));
|
||||||
|
$.checkArgument(
|
||||||
|
_.isString(options.path) && path.isAbsolute(options.path),
|
||||||
|
'An absolute path is expected'
|
||||||
|
);
|
||||||
|
$.checkArgument(Array.isArray(options.modules));
|
||||||
|
|
||||||
|
var configPath = options.path;
|
||||||
|
var modules = options.modules;
|
||||||
|
|
||||||
|
var bitcoreConfigPath = path.resolve(configPath, 'bitcore-node.json');
|
||||||
|
var packagePath = path.resolve(configPath, 'package.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(bitcoreConfigPath) || !fs.existsSync(packagePath)) {
|
||||||
|
return done(
|
||||||
|
new Error('Directory does not have a bitcore-node.json and/or package.json file.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachSeries(
|
||||||
|
modules,
|
||||||
|
function(module, next) {
|
||||||
|
// if the module is installed remove it
|
||||||
|
removeModule(configPath, module, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
// remove module to bitcore-node.json
|
||||||
|
removeConfig(bitcoreConfigPath, module, next);
|
||||||
|
});
|
||||||
|
}, done
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = remove;
|
||||||
@ -56,8 +56,10 @@
|
|||||||
"memdown": "^1.0.0",
|
"memdown": "^1.0.0",
|
||||||
"mkdirp": "0.5.0",
|
"mkdirp": "0.5.0",
|
||||||
"nan": "1.3.0",
|
"nan": "1.3.0",
|
||||||
|
"npm": "^2.14.1",
|
||||||
"semver": "^5.0.1",
|
"semver": "^5.0.1",
|
||||||
"socket.io": "^1.3.6"
|
"socket.io": "^1.3.6",
|
||||||
|
"socket.io-client": "^1.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"aws-sdk": "~2.0.0-rc.15",
|
"aws-sdk": "~2.0.0-rc.15",
|
||||||
|
|||||||
@ -369,7 +369,7 @@ describe('Bitcoin DB', function() {
|
|||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
var methods = db.getAPIMethods();
|
var methods = db.getAPIMethods();
|
||||||
methods.length.should.equal(4);
|
methods.length.should.equal(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
93
test/scaffold/call-method.unit.js
Normal file
93
test/scaffold/call-method.unit.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var should = require('chai').should();
|
||||||
|
var sinon = require('sinon');
|
||||||
|
var proxyquire = require('proxyquire');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
describe('#callMethod', function() {
|
||||||
|
|
||||||
|
var expectedUrl = 'http://localhost:3001';
|
||||||
|
var expectedOptions = {
|
||||||
|
reconnection: false,
|
||||||
|
connect_timeout: 5000
|
||||||
|
};
|
||||||
|
|
||||||
|
var callOptions = {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 3001,
|
||||||
|
protocol: 'http'
|
||||||
|
};
|
||||||
|
|
||||||
|
var callMethod;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
callMethod = proxyquire('../../lib/scaffold/call-method', {
|
||||||
|
'socket.io-client': function(url, options) {
|
||||||
|
url.should.equal(expectedUrl);
|
||||||
|
options.should.deep.equal(expectedOptions);
|
||||||
|
return new EventEmitter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handle a connection error', function(done) {
|
||||||
|
var socket = callMethod(callOptions, 'getInfo', null, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.message.should.equal('connect');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
socket.emit('connect_error', new Error('connect'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('give an error response', function(done) {
|
||||||
|
var socket = callMethod(callOptions, 'getInfo', null, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.message.should.equal('response');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
socket.send = function(opts, callback) {
|
||||||
|
opts.method.should.equal('getInfo');
|
||||||
|
should.equal(opts.params, null);
|
||||||
|
var response = {
|
||||||
|
error: {
|
||||||
|
message: 'response'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
callback(response);
|
||||||
|
};
|
||||||
|
socket.emit('connect');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('give result and close socket', function(done) {
|
||||||
|
var expectedData = {
|
||||||
|
version: 110000,
|
||||||
|
protocolversion: 70002,
|
||||||
|
blocks: 258614,
|
||||||
|
timeoffset: -2,
|
||||||
|
connections: 8,
|
||||||
|
difficulty: 112628548.66634709,
|
||||||
|
testnet: false,
|
||||||
|
relayfee: 1000,
|
||||||
|
errors: ''
|
||||||
|
};
|
||||||
|
var socket = callMethod(callOptions, 'getInfo', null, function(err, data) {
|
||||||
|
should.not.exist(err);
|
||||||
|
data.should.deep.equal(expectedData);
|
||||||
|
socket.close.callCount.should.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
socket.close = sinon.stub();
|
||||||
|
socket.send = function(opts, callback) {
|
||||||
|
opts.method.should.equal('getInfo');
|
||||||
|
should.equal(opts.params, null);
|
||||||
|
var response = {
|
||||||
|
error: null,
|
||||||
|
result: expectedData
|
||||||
|
};
|
||||||
|
callback(response);
|
||||||
|
};
|
||||||
|
socket.emit('connect');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
138
test/scaffold/remove.integration.js
Normal file
138
test/scaffold/remove.integration.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var should = require('chai').should();
|
||||||
|
var sinon = require('sinon');
|
||||||
|
var path = require('path');
|
||||||
|
var fs = require('fs');
|
||||||
|
var proxyquire = require('proxyquire');
|
||||||
|
var mkdirp = require('mkdirp');
|
||||||
|
var rimraf = require('rimraf');
|
||||||
|
var remove = require('../../lib/scaffold/remove');
|
||||||
|
|
||||||
|
describe('#remove', function() {
|
||||||
|
|
||||||
|
var basePath = path.resolve(__dirname, '..');
|
||||||
|
var testDir = path.resolve(basePath, 'temporary-test-data');
|
||||||
|
var startConfig = {
|
||||||
|
name: 'My Node',
|
||||||
|
modules: ['a', 'b', 'c']
|
||||||
|
};
|
||||||
|
var startPackage = {};
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
mkdirp(testDir + '/s0/s1', function(err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
fs.writeFile(
|
||||||
|
testDir + '/s0/s1/bitcore-node.json',
|
||||||
|
JSON.stringify(startConfig),
|
||||||
|
function(err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
fs.writeFile(
|
||||||
|
testDir + '/s0/s1/package.json',
|
||||||
|
JSON.stringify(startPackage),
|
||||||
|
done
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function(done) {
|
||||||
|
// cleanup testing directories
|
||||||
|
rimraf(testDir, function(err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('will modify scaffold files', function() {
|
||||||
|
|
||||||
|
it('will give an error if expected files do not exist', function(done) {
|
||||||
|
remove({
|
||||||
|
path: path.resolve(testDir, 's0'),
|
||||||
|
modules: ['b']
|
||||||
|
}, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.message.match(/^Invalid state/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('will update bitcore-node.json modules', function(done) {
|
||||||
|
var spawn = sinon.stub().returns({
|
||||||
|
stdout: {
|
||||||
|
on: sinon.stub()
|
||||||
|
},
|
||||||
|
stderr: {
|
||||||
|
on: sinon.stub()
|
||||||
|
},
|
||||||
|
on: sinon.stub().callsArgWith(1, 0)
|
||||||
|
});
|
||||||
|
var removetest = proxyquire('../../lib/scaffold/remove', {
|
||||||
|
'child_process': {
|
||||||
|
spawn: spawn
|
||||||
|
},
|
||||||
|
'npm': {
|
||||||
|
load: sinon.stub().callsArg(0),
|
||||||
|
commands: {
|
||||||
|
ls: sinon.stub().callsArgWith(2, null, {}, {
|
||||||
|
dependencies: {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
removetest({
|
||||||
|
path: path.resolve(testDir, 's0/s1/'),
|
||||||
|
modules: ['b']
|
||||||
|
}, function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
var configPath = path.resolve(testDir, 's0/s1/bitcore-node.json');
|
||||||
|
var config = JSON.parse(fs.readFileSync(configPath));
|
||||||
|
config.modules.should.deep.equal(['a', 'c']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('will receive error from `npm uninstall`', function(done) {
|
||||||
|
var spawn = sinon.stub().returns({
|
||||||
|
stdout: {
|
||||||
|
on: sinon.stub()
|
||||||
|
},
|
||||||
|
stderr: {
|
||||||
|
on: sinon.stub()
|
||||||
|
},
|
||||||
|
on: sinon.stub().callsArgWith(1, 1)
|
||||||
|
});
|
||||||
|
var removetest = proxyquire('../../lib/scaffold/remove', {
|
||||||
|
'child_process': {
|
||||||
|
spawn: spawn
|
||||||
|
},
|
||||||
|
'npm': {
|
||||||
|
load: sinon.stub().callsArg(0),
|
||||||
|
commands: {
|
||||||
|
ls: sinon.stub().callsArgWith(2, null, {}, {
|
||||||
|
dependencies: {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
removetest({
|
||||||
|
path: path.resolve(testDir, 's0/s1/'),
|
||||||
|
modules: ['b']
|
||||||
|
}, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.message.should.equal('There was an error uninstalling module: b');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user