mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Add dynamic node api
Closes #322 - nodes modules can be installed/removed dynamically at runtime - nodes can be enabled/disabled - onpaletteadd/onpaletteremove api added to node definitions - initial implementation of nr-cli
This commit is contained in:
@@ -51,36 +51,36 @@ var walkDirectory = function(dir, topdir, done) {
|
||||
errReturned = true;
|
||||
return done(error);
|
||||
}
|
||||
}
|
||||
|
||||
file = path.resolve(dir, file);
|
||||
fs.stat(file, function(err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
walkDirectory(file, false, function(err) {
|
||||
if (!error) {
|
||||
error = err;
|
||||
} else {
|
||||
file = path.resolve(dir, file);
|
||||
fs.stat(file, function(err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
walkDirectory(file, false, function(err) {
|
||||
if (!error) {
|
||||
error = err;
|
||||
}
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
if (path.extname(file) === ".js") {
|
||||
var testFile = file.replace(jsdir, testdir).replace(".js", "_spec.js");
|
||||
fs.exists(testFile, function (exists) {
|
||||
try {
|
||||
exists.should.equal(true, testFile + " does not exist");
|
||||
} catch (err) {
|
||||
if (!topdir) {
|
||||
return done(err);
|
||||
} else {
|
||||
error = err;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
if (path.extname(file) === ".js") {
|
||||
var testFile = file.replace(jsdir, testdir).replace(".js", "_spec.js");
|
||||
fs.exists(testFile, function (exists) {
|
||||
try {
|
||||
exists.should.equal(true, testFile + " does not exist");
|
||||
} catch (err) {
|
||||
if (!topdir) {
|
||||
return done(err);
|
||||
} else {
|
||||
error = err;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
});
|
||||
};
|
||||
|
@@ -51,7 +51,11 @@ module.exports = {
|
||||
return defer.promise;
|
||||
},
|
||||
};
|
||||
redNodes.init({}, storage);
|
||||
var settings = {
|
||||
available: function() { return false; }
|
||||
}
|
||||
|
||||
redNodes.init(settings, storage);
|
||||
RED.nodes.registerType("helper", helperNode);
|
||||
testNode(RED);
|
||||
flows.load().then(function() {
|
||||
|
0
test/red/bin/nr-cli_spec.js
Normal file
0
test/red/bin/nr-cli_spec.js
Normal file
@@ -170,6 +170,12 @@ describe('Credentials', function() {
|
||||
},
|
||||
saveCredentials: function(creds) {
|
||||
return when(true);
|
||||
},
|
||||
getSettings: function() {
|
||||
return when({});
|
||||
},
|
||||
saveSettings: function(s) {
|
||||
return when();
|
||||
}
|
||||
};
|
||||
function TestNode(n) {
|
||||
@@ -188,8 +194,10 @@ describe('Credentials', function() {
|
||||
sinon.stub(util, 'log', function(msg) {
|
||||
logmsg = msg;
|
||||
});
|
||||
|
||||
index.init({}, storage);
|
||||
var settings = {
|
||||
available: function() { return false;}
|
||||
}
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
var testnode = new TestNode({id:'tab1',type:'test',name:'barney'});
|
||||
|
@@ -15,26 +15,29 @@
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var sinon = require("sinon");
|
||||
var when = require("when");
|
||||
var flows = require("../../../red/nodes/flows");
|
||||
var RedNode = require("../../../red/nodes/Node");
|
||||
var RED = require("../../../red/nodes");
|
||||
var events = require("../../../red/events");
|
||||
var typeRegistry = require("../../../red/nodes/registry");
|
||||
|
||||
|
||||
var settings = {
|
||||
available: function() { return false; }
|
||||
}
|
||||
|
||||
function loadFlows(testFlows, cb) {
|
||||
var storage = {
|
||||
getFlows: function() {
|
||||
var defer = when.defer();
|
||||
defer.resolve(testFlows);
|
||||
return defer.promise;
|
||||
return when.resolve(testFlows);
|
||||
},
|
||||
getCredentials: function() {
|
||||
var defer = when.defer();
|
||||
defer.resolve({});
|
||||
return defer.promise;
|
||||
},
|
||||
return when.resolve({});
|
||||
}
|
||||
};
|
||||
RED.init({}, storage);
|
||||
RED.init(settings, storage);
|
||||
flows.load().then(function() {
|
||||
should.deepEqual(testFlows, flows.getFlows());
|
||||
cb();
|
||||
@@ -76,24 +79,34 @@ describe('flows', function() {
|
||||
});
|
||||
|
||||
it('should load and start an empty tab flow',function(done) {
|
||||
loadFlows([{"type":"tab","id":"tab1","label":"Sheet 1"}],
|
||||
function() {});
|
||||
loadFlows([{"type":"tab","id":"tab1","label":"Sheet 1"}], function() {});
|
||||
events.once('nodes-started', function() { done(); });
|
||||
});
|
||||
|
||||
it('should load and start a registered node type', function(done) {
|
||||
RED.registerType('debug', function() {});
|
||||
var typeRegistryGet = sinon.stub(typeRegistry,"get",function(nt) {
|
||||
return function() {};
|
||||
});
|
||||
loadFlows([{"id":"n1","type":"debug"}], function() { });
|
||||
events.once('nodes-started', function() { done(); });
|
||||
events.once('nodes-started', function() {
|
||||
typeRegistryGet.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load and start when node type is registered',
|
||||
function(done) {
|
||||
loadFlows([{"id":"n2","type":"inject"}],
|
||||
function() {
|
||||
RED.registerType('inject', function() { });
|
||||
});
|
||||
events.once('nodes-started', function() { done(); });
|
||||
it('should load and start when node type is registered', function(done) {
|
||||
var typeRegistryGet = sinon.stub(typeRegistry,"get");
|
||||
typeRegistryGet.onCall(0).returns(null);
|
||||
typeRegistryGet.returns(function(){});
|
||||
|
||||
loadFlows([{"id":"n2","type":"inject"}], function() {
|
||||
events.emit('type-registered','inject');
|
||||
});
|
||||
events.once('nodes-started', function() {
|
||||
typeRegistryGet.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -112,7 +125,7 @@ describe('flows', function() {
|
||||
return when(true);
|
||||
}
|
||||
};
|
||||
RED.init({}, storage);
|
||||
RED.init(settings, storage);
|
||||
flows.setFlows(testFlows);
|
||||
events.once('nodes-started', function() { done(); });
|
||||
});
|
||||
|
@@ -43,7 +43,11 @@ describe("red/nodes/index", function() {
|
||||
saveCredentials: function(creds) {
|
||||
return when(true);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var settings = {
|
||||
available: function() { return false }
|
||||
};
|
||||
|
||||
function TestNode(n) {
|
||||
index.createNode(this, n);
|
||||
@@ -55,7 +59,7 @@ describe("red/nodes/index", function() {
|
||||
|
||||
it('nodes are initialised with credentials',function(done) {
|
||||
|
||||
index.init({}, storage);
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
var testnode = new TestNode({id:'tab1',type:'test',name:'barney'});
|
||||
@@ -69,7 +73,7 @@ describe("red/nodes/index", function() {
|
||||
});
|
||||
|
||||
it('flows should be initialised',function(done) {
|
||||
index.init({}, storage);
|
||||
index.init(settings, storage);
|
||||
index.loadFlows().then(function() {
|
||||
should.deepEqual(testFlows, index.getFlows());
|
||||
done();
|
||||
@@ -131,7 +135,7 @@ describe("red/nodes/index", function() {
|
||||
|
||||
});
|
||||
|
||||
describe('allows nodes to be removed from the registry', function() {
|
||||
describe('allows nodes to be added/remove/enabled/disabled from the registry', function() {
|
||||
var registry = require("../../../red/nodes/registry");
|
||||
var randomNodeInfo = {id:"5678",types:["random"]};
|
||||
|
||||
@@ -148,17 +152,21 @@ describe("red/nodes/index", function() {
|
||||
sinon.stub(registry,"removeNode",function(id) {
|
||||
return randomNodeInfo;
|
||||
});
|
||||
sinon.stub(registry,"disableNode",function(id) {
|
||||
return randomNodeInfo;
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
registry.getNodeInfo.restore();
|
||||
registry.removeNode.restore();
|
||||
registry.disableNode.restore();
|
||||
});
|
||||
|
||||
it(': allows an unused node type to be removed',function(done) {
|
||||
index.init({}, storage);
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
var info = index.removeNode("random");
|
||||
var info = index.removeNode("5678");
|
||||
registry.removeNode.calledOnce.should.be.true;
|
||||
registry.removeNode.calledWith("5678").should.be.true;
|
||||
info.should.eql(randomNodeInfo);
|
||||
@@ -167,9 +175,23 @@ describe("red/nodes/index", function() {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
it(': allows an unused node type to be disabled',function(done) {
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
var info = index.disableNode("5678");
|
||||
registry.disableNode.calledOnce.should.be.true;
|
||||
registry.disableNode.calledWith("5678").should.be.true;
|
||||
info.should.eql(randomNodeInfo);
|
||||
done();
|
||||
}).otherwise(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
it(': prevents removing a node type that is in use',function(done) {
|
||||
index.init({}, storage);
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
/*jshint immed: false */
|
||||
@@ -183,8 +205,23 @@ describe("red/nodes/index", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it(': prevents disabling a node type that is in use',function(done) {
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
index.disabledNode("test");
|
||||
}).should.throw();
|
||||
|
||||
done();
|
||||
}).otherwise(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
it(': prevents removing a node type that is unknown',function(done) {
|
||||
index.init({}, storage);
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
/*jshint immed: false */
|
||||
@@ -192,6 +229,20 @@ describe("red/nodes/index", function() {
|
||||
index.removeNode("doesnotexist");
|
||||
}).should.throw();
|
||||
|
||||
done();
|
||||
}).otherwise(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
it(': prevents disabling a node type that is unknown',function(done) {
|
||||
index.init(settings, storage);
|
||||
index.registerType('test', TestNode);
|
||||
index.loadFlows().then(function() {
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
index.disableNode("doesnotexist");
|
||||
}).should.throw();
|
||||
|
||||
done();
|
||||
}).otherwise(function(err) {
|
||||
done(err);
|
||||
|
@@ -31,6 +31,15 @@ describe('NodeRegistry', function() {
|
||||
|
||||
var resourcesDir = __dirname+ path.sep + "resources" + path.sep;
|
||||
|
||||
function stubSettings(s,available) {
|
||||
s.available = function() {return available;}
|
||||
s.set = function(s,v) {},
|
||||
s.get = function(s) { return null;}
|
||||
return s
|
||||
}
|
||||
var settings = stubSettings({},false);
|
||||
var settingsWithStorage = stubSettings({},true);
|
||||
|
||||
it('automatically registers new nodes',function() {
|
||||
var testNode = RedNodes.getNode('123');
|
||||
should.not.exist(n);
|
||||
@@ -42,7 +51,7 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('handles nodes that export a function', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load(resourcesDir + "TestNode1",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -64,7 +73,7 @@ describe('NodeRegistry', function() {
|
||||
|
||||
|
||||
it('handles nodes that export a function returning a resolving promise', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load(resourcesDir + "TestNode2",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -84,15 +93,14 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('handles nodes that export a function returning a rejecting promise', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load(resourcesDir + "TestNode3",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
list[0].should.have.property("id");
|
||||
list[0].should.have.property("name","TestNode3.js");
|
||||
list[0].should.have.property("types",["test-node-3"]);
|
||||
list[0].should.have.property("enabled",false);
|
||||
|
||||
list[0].should.have.property("enabled",true);
|
||||
list[0].should.have.property("err","fail");
|
||||
|
||||
var nodeConstructor = typeRegistry.get("test-node-3");
|
||||
@@ -106,7 +114,7 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('handles files containing multiple nodes', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load(resourcesDir + "MultipleNodes1",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -129,7 +137,7 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('handles nested directories', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load(resourcesDir + "NestedDirectoryNode",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -146,7 +154,7 @@ describe('NodeRegistry', function() {
|
||||
|
||||
it('emits type-registered and node-icon-dir events', function(done) {
|
||||
var eventEmitSpy = sinon.spy(events,"emit");
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load(resourcesDir + "NestedDirectoryNode",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -173,9 +181,10 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('rejects a duplicate node type registration', function(done) {
|
||||
typeRegistry.init({
|
||||
|
||||
typeRegistry.init(stubSettings({
|
||||
nodesDir:[resourcesDir + "TestNode1",resourcesDir + "DuplicateTestNode"]
|
||||
});
|
||||
},false));
|
||||
typeRegistry.load("wontexist",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
|
||||
@@ -191,7 +200,7 @@ describe('NodeRegistry', function() {
|
||||
|
||||
list[1].should.have.property("name","TestNode1.js");
|
||||
list[1].should.have.property("types",["test-node-1"]);
|
||||
list[1].should.have.property("enabled",false);
|
||||
list[1].should.have.property("enabled",true);
|
||||
list[1].should.have.property("err");
|
||||
/already registered/.test(list[1].err).should.be.true;
|
||||
|
||||
@@ -206,11 +215,10 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('handles nodesDir as a string', function(done) {
|
||||
var settings = {
|
||||
nodesDir :resourcesDir + "TestNode1"
|
||||
}
|
||||
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.init(stubSettings({
|
||||
nodesDir :resourcesDir + "TestNode1"
|
||||
},false));
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -223,11 +231,10 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('handles invalid nodesDir',function(done) {
|
||||
var settings = {
|
||||
nodesDir : "wontexist"
|
||||
}
|
||||
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.init(stubSettings({
|
||||
nodesDir : "wontexist"
|
||||
},false));
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
@@ -238,7 +245,7 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('returns nothing for an unregistered type config', function() {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var config = typeRegistry.getNodeConfig("imaginary-shark");
|
||||
(config === null).should.be.true;
|
||||
@@ -248,10 +255,10 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('excludes node files listed in nodesExcludes',function(done) {
|
||||
typeRegistry.init({
|
||||
typeRegistry.init(stubSettings({
|
||||
nodesExcludes: [ "TestNode1.js" ],
|
||||
nodesDir:[resourcesDir + "TestNode1",resourcesDir + "TestNode2"]
|
||||
});
|
||||
},false));
|
||||
typeRegistry.load("wontexist",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -263,9 +270,9 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('returns the node configurations', function(done) {
|
||||
typeRegistry.init({
|
||||
typeRegistry.init(stubSettings({
|
||||
nodesDir:[resourcesDir + "TestNode1",resourcesDir + "TestNode2"]
|
||||
});
|
||||
},false));
|
||||
typeRegistry.load("wontexist",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
|
||||
@@ -276,20 +283,59 @@ describe('NodeRegistry', function() {
|
||||
|
||||
var nodeId = list[0].id;
|
||||
var nodeConfig = typeRegistry.getNodeConfig(nodeId);
|
||||
nodeConfig.should.equal("<script type=\"text/x-red\" data-template-name=\"test-node-1\"></script>\n<script type=\"text/x-red\" data-help-name=\"test-node-1\"></script>\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-1',{});</script>\n<style></style>\n<p>this should be filtered out</p>\n<script type=\"text/javascript\"></script>");
|
||||
nodeConfig.should.equal("<script type=\"text/x-red\" data-template-name=\"test-node-1\"></script>\n<script type=\"text/x-red\" data-help-name=\"test-node-1\"></script>\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-1',{});</script>\n<style></style>\n<p>this should be filtered out</p>\n");
|
||||
done();
|
||||
}).catch(function(e) {
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
|
||||
it('stores the node list', function(done) {
|
||||
var settings = {
|
||||
nodesDir:[resourcesDir + "TestNode1",resourcesDir + "TestNode2",resourcesDir + "TestNode3"],
|
||||
available: function() { return true; },
|
||||
set: function(s,v) {},
|
||||
get: function(s) { return null;}
|
||||
}
|
||||
var settingsSave = sinon.spy(settings,"set");
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load("wontexist",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.Array.and.have.length(3);
|
||||
|
||||
settingsSave.callCount.should.equal(1);
|
||||
settingsSave.firstCall.args[0].should.be.equal("nodes");
|
||||
var savedList = settingsSave.firstCall.args[1];
|
||||
|
||||
savedList[list[0].id].name == list[0].name;
|
||||
savedList[list[1].id].name == list[1].name;
|
||||
savedList[list[2].id].name == list[2].name;
|
||||
|
||||
savedList[list[0].id].should.not.have.property("err");
|
||||
savedList[list[1].id].should.not.have.property("err");
|
||||
savedList[list[2].id].should.not.have.property("err");
|
||||
|
||||
done();
|
||||
}).catch(function(e) {
|
||||
done(e);
|
||||
}).finally(function() {
|
||||
settingsSave.restore();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('allows nodes to be added by filename', function(done) {
|
||||
typeRegistry.init({});
|
||||
var settings = {
|
||||
available: function() { return true; },
|
||||
set: function(s,v) {},
|
||||
get: function(s) { return null;}
|
||||
}
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
|
||||
typeRegistry.addNode({file: resourcesDir + "TestNode1/TestNode1.js"}).then(function(node) {
|
||||
typeRegistry.addNode(resourcesDir + "TestNode1/TestNode1.js").then(function(node) {
|
||||
list = typeRegistry.getNodeList();
|
||||
list[0].should.have.property("id");
|
||||
list[0].should.have.property("name","TestNode1.js");
|
||||
@@ -311,11 +357,11 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('fails to add non-existent filename', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
typeRegistry.addNode({file: resourcesDir + "DoesNotExist/DoesNotExist.js"}).then(function(node) {
|
||||
typeRegistry.addNode(resourcesDir + "DoesNotExist/DoesNotExist.js").then(function(node) {
|
||||
done(new Error("ENOENT not thrown"));
|
||||
}).otherwise(function(e) {
|
||||
e.code.should.eql("ENOENT");
|
||||
@@ -328,7 +374,7 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('returns node info by type or id', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load(resourcesDir + "TestNode1",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -357,7 +403,7 @@ describe('NodeRegistry', function() {
|
||||
|
||||
|
||||
it('rejects adding duplicate nodes', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load(resourcesDir + "TestNode1",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -376,18 +422,23 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('removes nodes from the registry', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load(resourcesDir + "TestNode1",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
list[0].should.have.property("id");
|
||||
list[0].should.have.property("name","TestNode1.js");
|
||||
list[0].should.have.property("types",["test-node-1"]);
|
||||
list[0].should.have.property("enabled",true);
|
||||
list[0].should.have.property("loaded",true);
|
||||
|
||||
typeRegistry.getNodeConfigs().length.should.be.greaterThan(0);
|
||||
|
||||
var info = typeRegistry.removeNode(list[0].id);
|
||||
info.should.eql(list[0]);
|
||||
|
||||
info.should.have.property("id",list[0].id);
|
||||
info.should.have.property("enabled",false);
|
||||
info.should.have.property("loaded",false);
|
||||
|
||||
typeRegistry.getNodeList().should.be.an.Array.and.be.empty;
|
||||
typeRegistry.getNodeConfigs().length.should.equal(0);
|
||||
@@ -403,7 +454,7 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
|
||||
it('rejects removing unknown nodes from the registry', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load("wontexist",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
@@ -451,7 +502,7 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
})();
|
||||
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load("wontexist",false).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(2);
|
||||
@@ -464,7 +515,7 @@ describe('NodeRegistry', function() {
|
||||
list[1].should.have.property("id");
|
||||
list[1].should.have.property("name","TestNodeModule:TestNodeMod2");
|
||||
list[1].should.have.property("types",["test-node-mod-2"]);
|
||||
list[1].should.have.property("enabled",false);
|
||||
list[1].should.have.property("enabled",true);
|
||||
list[1].should.have.property("err");
|
||||
|
||||
|
||||
@@ -516,13 +567,12 @@ describe('NodeRegistry', function() {
|
||||
return result;
|
||||
});
|
||||
})();
|
||||
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
|
||||
typeRegistry.addNode({module: "TestNodeModule"}).then(function(node) {
|
||||
typeRegistry.addModule("TestNodeModule").then(function(node) {
|
||||
list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(2);
|
||||
list[0].should.have.property("id");
|
||||
@@ -534,7 +584,7 @@ describe('NodeRegistry', function() {
|
||||
list[1].should.have.property("id");
|
||||
list[1].should.have.property("name","TestNodeModule:TestNodeMod2");
|
||||
list[1].should.have.property("types",["test-node-mod-2"]);
|
||||
list[1].should.have.property("enabled",false);
|
||||
list[1].should.have.property("enabled",true);
|
||||
list[1].should.have.property("err");
|
||||
|
||||
node.should.eql(list);
|
||||
@@ -552,13 +602,62 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('rejects adding duplicate node modules', function(done) {
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
var pathJoin = (function() {
|
||||
var _join = path.join;
|
||||
return sinon.stub(path,"join",function() {
|
||||
if (arguments.length == 3 && arguments[2] == "package.json") {
|
||||
return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]);
|
||||
}
|
||||
if (arguments.length == 2 && arguments[1] == "TestNodeModule") {
|
||||
return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]);
|
||||
}
|
||||
return _join.apply(this,arguments);
|
||||
});
|
||||
})();
|
||||
|
||||
var readdirSync = (function() {
|
||||
var originalReaddirSync = fs.readdirSync;
|
||||
var callCount = 0;
|
||||
return sinon.stub(fs,"readdirSync",function(dir) {
|
||||
var result = [];
|
||||
if (callCount == 1) {
|
||||
result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules");
|
||||
}
|
||||
callCount++;
|
||||
return result;
|
||||
});
|
||||
})();
|
||||
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load('wontexist',false).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(2);
|
||||
typeRegistry.addModule("TestNodeModule").then(function(node) {
|
||||
done(new Error("addModule resolved"));
|
||||
}).otherwise(function(err) {
|
||||
done();
|
||||
});
|
||||
}).catch(function(e) {
|
||||
done(e);
|
||||
}).finally(function() {
|
||||
readdirSync.restore();
|
||||
pathJoin.restore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('fails to add non-existent module name', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
|
||||
typeRegistry.addNode({module: "DoesNotExistModule"}).then(function(node) {
|
||||
typeRegistry.addModule("DoesNotExistModule").then(function(node) {
|
||||
done(new Error("ENOENT not thrown"));
|
||||
}).otherwise(function(e) {
|
||||
e.code.should.eql("MODULE_NOT_FOUND");
|
||||
@@ -570,9 +669,80 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('removes nodes from the registry by module', function(done) {
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
var pathJoin = (function() {
|
||||
var _join = path.join;
|
||||
return sinon.stub(path,"join",function() {
|
||||
if (arguments.length == 3 && arguments[2] == "package.json") {
|
||||
return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]);
|
||||
}
|
||||
if (arguments.length == 2 && arguments[1] == "TestNodeModule") {
|
||||
return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]);
|
||||
}
|
||||
return _join.apply(this,arguments);
|
||||
});
|
||||
})();
|
||||
|
||||
var readdirSync = (function() {
|
||||
var originalReaddirSync = fs.readdirSync;
|
||||
var callCount = 0;
|
||||
return sinon.stub(fs,"readdirSync",function(dir) {
|
||||
var result = [];
|
||||
if (callCount == 1) {
|
||||
result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules");
|
||||
}
|
||||
callCount++;
|
||||
return result;
|
||||
});
|
||||
})();
|
||||
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load('wontexist',false).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(2);
|
||||
var res = typeRegistry.removeModule("TestNodeModule");
|
||||
|
||||
res.should.be.an.Array.and.have.lengthOf(2);
|
||||
res[0].should.have.a.property("id",list[0].id);
|
||||
res[1].should.have.a.property("id",list[1].id);
|
||||
|
||||
list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
|
||||
done();
|
||||
}).catch(function(e) {
|
||||
done(e);
|
||||
}).finally(function() {
|
||||
readdirSync.restore();
|
||||
pathJoin.restore();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('fails to remove non-existent module name', function(done) {
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load("wontexist",true).then(function(){
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
typeRegistry.removeModule("DoesNotExistModule");
|
||||
}).should.throw();
|
||||
|
||||
done();
|
||||
|
||||
}).catch(function(e) {
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('allows nodes to be enabled and disabled', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.init(settingsWithStorage);
|
||||
typeRegistry.load(resourcesDir+path.sep+"TestNode1",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -583,7 +753,9 @@ describe('NodeRegistry', function() {
|
||||
var nodeConfig = typeRegistry.getNodeConfigs();
|
||||
nodeConfig.length.should.be.greaterThan(0);
|
||||
|
||||
typeRegistry.disableNode(list[0].id);
|
||||
var info = typeRegistry.disableNode(list[0].id);
|
||||
info.should.have.property("id",list[0].id);
|
||||
info.should.have.property("enabled",false);
|
||||
|
||||
var list2 = typeRegistry.getNodeList();
|
||||
list2.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -591,7 +763,9 @@ describe('NodeRegistry', function() {
|
||||
|
||||
typeRegistry.getNodeConfigs().length.should.equal(0);
|
||||
|
||||
typeRegistry.enableNode(list[0].id);
|
||||
var info2 = typeRegistry.enableNode(list[0].id);
|
||||
info2.should.have.property("id",list[0].id);
|
||||
info2.should.have.property("enabled",true);
|
||||
|
||||
var list3 = typeRegistry.getNodeList();
|
||||
list3.should.be.an.Array.and.have.lengthOf(1);
|
||||
@@ -606,27 +780,25 @@ describe('NodeRegistry', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not allow a node with error to be enabled', function(done) {
|
||||
typeRegistry.init({});
|
||||
typeRegistry.load(resourcesDir+path.sep+"TestNode3",true).then(function() {
|
||||
it('fails to enable/disable non-existent nodes', function(done) {
|
||||
typeRegistry.init(settings);
|
||||
typeRegistry.load("wontexist",true).then(function() {
|
||||
var list = typeRegistry.getNodeList();
|
||||
list.should.be.an.Array.and.have.lengthOf(1);
|
||||
list[0].should.have.property("id");
|
||||
list[0].should.have.property("name","TestNode3.js");
|
||||
list[0].should.have.property("enabled",false);
|
||||
list[0].should.have.property("err");
|
||||
list.should.be.an.Array.and.be.empty;
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
typeRegistry.enable(list[0].id);
|
||||
typeRegistry.disableNode("123");
|
||||
}).should.throw();
|
||||
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
typeRegistry.enableNode("123");
|
||||
}).should.throw();
|
||||
|
||||
done();
|
||||
}).catch(function(e) {
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
109
test/red/settings_spec.js
Normal file
109
test/red/settings_spec.js
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright 2014 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var should = require("should");
|
||||
var when = require("when");
|
||||
|
||||
var settings = require("../../red/settings");
|
||||
|
||||
|
||||
describe("red/settings", function() {
|
||||
it('wraps the user settings as read-only properties', function() {
|
||||
var userSettings = {
|
||||
a: 123,
|
||||
b: "test",
|
||||
c: [1,2,3]
|
||||
}
|
||||
settings.init(userSettings);
|
||||
|
||||
settings.available().should.be.false;
|
||||
|
||||
settings.a.should.equal(123);
|
||||
settings.b.should.equal("test");
|
||||
settings.c.should.be.an.Array.with.lengthOf(3);
|
||||
|
||||
settings.get("a").should.equal(123);
|
||||
settings.get("b").should.equal("test");
|
||||
settings.get("c").should.be.an.Array.with.lengthOf(3);
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
settings.a = 456;
|
||||
}).should.throw();
|
||||
|
||||
settings.c.push(5);
|
||||
settings.c.should.be.an.Array.with.lengthOf(4);
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
settings.set("a",456);
|
||||
}).should.throw();
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
settings.set("a",456);
|
||||
}).should.throw();
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
settings.get("unknown");
|
||||
}).should.throw();
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
settings.set("unknown",456);
|
||||
}).should.throw();
|
||||
|
||||
});
|
||||
|
||||
it('loads global settings from storage', function(done) {
|
||||
var userSettings = {
|
||||
a: 123,
|
||||
b: "test",
|
||||
c: [1,2,3]
|
||||
}
|
||||
var savedSettings = null;
|
||||
var storage = {
|
||||
getSettings: function() {
|
||||
return when.resolve({globalA:789});
|
||||
},
|
||||
saveSettings: function(settings) {
|
||||
savedSettings = settings;
|
||||
return when.resolve();
|
||||
}
|
||||
}
|
||||
settings.init(userSettings);
|
||||
|
||||
settings.available().should.be.false;
|
||||
|
||||
/*jshint immed: false */
|
||||
(function() {
|
||||
settings.get("unknown");
|
||||
}).should.throw();
|
||||
|
||||
settings.load(storage).then(function() {
|
||||
settings.available().should.be.true;
|
||||
settings.get("globalA").should.equal(789);
|
||||
settings.set("globalA","abc").then(function() {
|
||||
savedSettings.globalA.should.equal("abc");
|
||||
done();
|
||||
});
|
||||
}).otherwise(function(err) {
|
||||
done(err);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user