From f7f58a2347e3056d11321704389089b18b040ef9 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Sun, 3 Aug 2014 21:17:24 +0100 Subject: [PATCH] Add registry test for node module loading --- red/nodes/registry.js | 7 +- test/red/nodes/registry_spec.js | 88 ++++++++++++++++--- .../TestNodeModule/TestNodeModule.html | 5 ++ .../TestNodeModule/TestNodeModule.js | 5 ++ .../TestNodeModule/icons/file.txt | 3 + .../node_modules/TestNodeModule/package.json | 10 +++ 6 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html create mode 100644 test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js create mode 100644 test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/icons/file.txt create mode 100644 test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json diff --git a/red/nodes/registry.js b/red/nodes/registry.js index 25ab1e638..f3080c044 100644 --- a/red/nodes/registry.js +++ b/red/nodes/registry.js @@ -219,7 +219,8 @@ function loadNodesFromModule(moduleDir,pkg) { var iconDirs = []; for (var n in nodes) { if (nodes.hasOwnProperty(n)) { - results.push(loadNodeConfig(path.join(moduleDir,nodes[n]),pkg.name+":"+n)); + var file = path.join(moduleDir,nodes[n]); + results.push(loadNodeConfig(file,pkg.name+":"+n)); var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons"); if (iconDirs.indexOf(iconDir) == -1) { if (fs.existsSync(iconDir)) { @@ -302,7 +303,7 @@ function loadNodeConfig(file,name) { * location of nodeFiles - used by the tests * @return a promise that resolves on completion of loading */ -function load(defaultNodesDir) { +function load(defaultNodesDir,disableNodePathScan) { return when.promise(function(resolve,reject) { // Find all of the nodes to load var nodeFiles; @@ -330,7 +331,7 @@ function load(defaultNodesDir) { // This indicates a test is being run - don't want to pick up // unexpected nodes. // Urgh. - if (!defaultNodesDir) { + if (!disableNodePathScan) { // Find all of the modules containing nodes var moduleFiles = scanTreeForNodesModules(); moduleFiles.forEach(function(moduleFile) { diff --git a/test/red/nodes/registry_spec.js b/test/red/nodes/registry_spec.js index 00052b828..c640d5488 100644 --- a/test/red/nodes/registry_spec.js +++ b/test/red/nodes/registry_spec.js @@ -39,7 +39,7 @@ describe('NodeRegistry', function() { it('handles nodes that export a function', function(done) { typeRegistry.init({}); - typeRegistry.load(__dirname+"/resources/TestNode1").then(function() { + typeRegistry.load(__dirname+"/resources/TestNode1",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("id"); @@ -61,7 +61,7 @@ describe('NodeRegistry', function() { it('handles nodes that export a function returning a resolving promise', function(done) { typeRegistry.init({}); - typeRegistry.load(__dirname+"/resources/TestNode2").then(function() { + typeRegistry.load(__dirname+"/resources/TestNode2",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("id"); @@ -81,7 +81,7 @@ describe('NodeRegistry', function() { it('handles nodes that export a function returning a rejecting promise', function(done) { typeRegistry.init({}); - typeRegistry.load(__dirname+"/resources/TestNode3").then(function() { + typeRegistry.load(__dirname+"/resources/TestNode3",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("id"); @@ -103,7 +103,7 @@ describe('NodeRegistry', function() { it('handles files containing multiple nodes', function(done) { typeRegistry.init({}); - typeRegistry.load(__dirname+"/resources/MultipleNodes1").then(function() { + typeRegistry.load(__dirname+"/resources/MultipleNodes1",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("id"); @@ -126,7 +126,7 @@ describe('NodeRegistry', function() { it('handles nested directories', function(done) { typeRegistry.init({}); - typeRegistry.load(__dirname+"/resources/NestedDirectoryNode").then(function() { + typeRegistry.load(__dirname+"/resources/NestedDirectoryNode",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("id"); @@ -143,7 +143,7 @@ describe('NodeRegistry', function() { it('emits type-registered and node-icon-dir events', function(done) { var eventEmitSpy = sinon.spy(events,"emit"); typeRegistry.init({}); - typeRegistry.load(__dirname+"/resources/NestedDirectoryNode").then(function() { + typeRegistry.load(__dirname+"/resources/NestedDirectoryNode",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("name","NestedNode.js"); @@ -171,7 +171,7 @@ describe('NodeRegistry', function() { typeRegistry.init({ nodesDir:[__dirname+"/resources/TestNode1",__dirname+"/resources/DuplicateTestNode"] }); - typeRegistry.load("wontexist").then(function() { + typeRegistry.load("wontexist",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(2); @@ -206,7 +206,7 @@ describe('NodeRegistry', function() { } typeRegistry.init(settings); - typeRegistry.load("wontexist").then(function(){ + typeRegistry.load("wontexist",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("types",["test-node-1"]); @@ -223,7 +223,7 @@ describe('NodeRegistry', function() { } typeRegistry.init(settings); - typeRegistry.load("wontexist").then(function(){ + typeRegistry.load("wontexist",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; done(); @@ -234,7 +234,7 @@ describe('NodeRegistry', function() { it('returns nothing for an unregistered type config', function() { typeRegistry.init({}); - typeRegistry.load("wontexist").then(function(){ + typeRegistry.load("wontexist",true).then(function(){ var config = typeRegistry.getNodeConfig("imaginary-shark"); (config === null).should.be.true; }).catch(function(e) { @@ -247,7 +247,7 @@ describe('NodeRegistry', function() { nodesExcludes: [ "TestNode1.js" ], nodesDir:[__dirname+"/resources/TestNode1",__dirname+"/resources/TestNode2"] }); - typeRegistry.load("wontexist").then(function() { + typeRegistry.load("wontexist",true).then(function() { var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); list[0].should.have.property("types",["test-node-2"]); @@ -261,7 +261,7 @@ describe('NodeRegistry', function() { typeRegistry.init({ nodesDir:[__dirname+"/resources/TestNode1",__dirname+"/resources/TestNode2"] }); - typeRegistry.load("wontexist").then(function() { + typeRegistry.load("wontexist",true).then(function() { var list = typeRegistry.getNodeList(); var nodeConfigs = typeRegistry.getNodeConfigs(); @@ -280,7 +280,7 @@ describe('NodeRegistry', function() { it('allows nodes to be added', function(done) { typeRegistry.init({}); - typeRegistry.load("wontexist").then(function(){ + typeRegistry.load("wontexist",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.be.empty; @@ -306,7 +306,7 @@ describe('NodeRegistry', function() { it('rejects adding duplicate nodes', function(done) { typeRegistry.init({}); - typeRegistry.load(__dirname+"/resources/TestNode1").then(function(){ + typeRegistry.load(__dirname+"/resources/TestNode1",true).then(function(){ var list = typeRegistry.getNodeList(); list.should.be.an.Array.and.have.lengthOf(1); @@ -323,4 +323,64 @@ describe('NodeRegistry', function() { }); }); + it('scans the node_modules path for node files', function(done) { + var fs = require("fs"); + var path = require("path"); + + var eventEmitSpy = sinon.spy(events,"emit"); + var pathJoin = (function() { + var _join = path.join; + return sinon.stub(path,"join",function() { + if (arguments.length == 3 && arguments[2] == "package.json") { + return _join(__dirname,"/resources/TestNodeModule/node_modules/",arguments[1],arguments[2]); + } + if (arguments.length == 2 && arguments[1] == "TestNodeModule") { + return _join(__dirname,"/resources/TestNodeModule/node_modules/",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(__dirname+"/resources/TestNodeModule/node_modules"); + } + callCount++; + return result; + }); + })(); + + typeRegistry.init({}); + typeRegistry.load("wontexist",false).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","TestNodeModule:TestNodeMod1"); + list[0].should.have.property("types",["test-node-mod-1"]); + list[0].should.have.property("enabled",true); + list[0].should.not.have.property("err"); + + + eventEmitSpy.callCount.should.equal(2); + + eventEmitSpy.firstCall.args[0].should.be.equal("node-icon-dir"); + eventEmitSpy.firstCall.args[1].should.be.equal(__dirname+"/resources/TestNodeModule/node_modules/TestNodeModule/icons"); + + eventEmitSpy.secondCall.args[0].should.be.equal("type-registered"); + eventEmitSpy.secondCall.args[1].should.be.equal("test-node-mod-1"); + + done(); + }).catch(function(e) { + done(e); + }).finally(function() { + readdirSync.restore(); + pathJoin.restore(); + eventEmitSpy.restore(); + }); + }); + }); diff --git a/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html new file mode 100644 index 000000000..17483f7ca --- /dev/null +++ b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html @@ -0,0 +1,5 @@ + + + + +

this should be filtered out

diff --git a/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js new file mode 100644 index 000000000..c79c2ceb6 --- /dev/null +++ b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js @@ -0,0 +1,5 @@ +// A test node that exports a function +module.exports = function(RED) { + function TestNode(n) {} + RED.nodes.registerType("test-node-mod-1",TestNode); +} diff --git a/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/icons/file.txt b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/icons/file.txt new file mode 100644 index 000000000..59a29af14 --- /dev/null +++ b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/icons/file.txt @@ -0,0 +1,3 @@ +This file exists just to ensure the 'icons' directory is in the repository. +TODO: a future test needs to ensure the right icon files are loaded - this + directory can be used for that diff --git a/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json new file mode 100644 index 000000000..da483f645 --- /dev/null +++ b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json @@ -0,0 +1,10 @@ +{ + "name" : "TestNodeModule", + "version" : "0.0.1", + "description" : "A test node module", + "node-red" : { + "nodes": { + "TestNodeMod1": "TestNodeModule.js" + } + } +}