[CCSDK-28] populated the seed code for dgbuilder
[ccsdk/distribution.git] / dgbuilder / test / red / nodes / registry_spec.js
1 /**
2  * Copyright 2014 IBM Corp.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  **/
16
17 var should = require("should");
18 var sinon = require("sinon");
19 var path = require("path");
20 var when = require("when");
21
22 var RedNodes = require("../../../red/nodes");
23 var RedNode = require("../../../red/nodes/Node");
24 var typeRegistry = require("../../../red/nodes/registry");
25 var events = require("../../../red/events");
26
27 afterEach(function() {
28     typeRegistry.clear();
29 });
30
31 describe('NodeRegistry', function() {
32     
33     var resourcesDir = __dirname+ path.sep + "resources" + path.sep;
34     
35     function stubSettings(s,available) {
36         s.available =  function() {return available;}
37         s.set = function(s,v) { return when.resolve()},
38         s.get = function(s) { return null;}
39         return s
40     }
41     var settings = stubSettings({},false);
42     var settingsWithStorage = stubSettings({},true);
43     
44     it('automatically registers new nodes',function() {
45         var testNode = RedNodes.getNode('123');
46         should.not.exist(n);
47         var n = new RedNode({id:'123',type:'abc'});
48         
49         var newNode = RedNodes.getNode('123');
50         
51         should.strictEqual(n,newNode);
52     });
53     
54     it('handles nodes that export a function', function(done) {
55         typeRegistry.init(settings);
56         typeRegistry.load(resourcesDir + "TestNode1",true).then(function() {
57             var list = typeRegistry.getNodeList();
58             list.should.be.an.Array.and.have.lengthOf(1);
59             list[0].should.have.property("id");
60             list[0].should.have.property("name","TestNode1.js");
61             list[0].should.have.property("types",["test-node-1"]);
62             list[0].should.have.property("enabled",true);
63             list[0].should.not.have.property("err");
64             
65             var nodeConstructor = typeRegistry.get("test-node-1");
66             nodeConstructor.should.be.type("function");
67             
68             done();
69         }).catch(function(e) {
70             done(e);
71         });
72             
73     });
74     
75     
76     it('handles nodes that export a function returning a resolving promise', function(done) {
77         typeRegistry.init(settings);
78         typeRegistry.load(resourcesDir + "TestNode2",true).then(function() {
79             var list = typeRegistry.getNodeList();
80             list.should.be.an.Array.and.have.lengthOf(1);
81             list[0].should.have.property("id");
82             list[0].should.have.property("name","TestNode2.js");
83             list[0].should.have.property("types",["test-node-2"]);
84             list[0].should.have.property("enabled",true);
85             list[0].should.not.have.property("err");
86             var nodeConstructor = typeRegistry.get("test-node-2");
87             nodeConstructor.should.be.type("function");
88             
89             done();
90         }).catch(function(e) {
91             done(e);
92         });
93             
94     });
95     
96     it('handles nodes that export a function returning a rejecting promise', function(done) {
97         typeRegistry.init(settings);
98         typeRegistry.load(resourcesDir + "TestNode3",true).then(function() {
99             var list = typeRegistry.getNodeList();
100             list.should.be.an.Array.and.have.lengthOf(1);
101             list[0].should.have.property("id");
102             list[0].should.have.property("name","TestNode3.js");
103             list[0].should.have.property("types",["test-node-3"]);
104             list[0].should.have.property("enabled",true);
105             list[0].should.have.property("err","fail");
106
107             var nodeConstructor = typeRegistry.get("test-node-3");
108             (nodeConstructor === null).should.be.true;
109             
110             done();
111         }).catch(function(e) {
112             done(e);
113         });
114             
115     });
116     
117     it('handles files containing multiple nodes', function(done) {
118         typeRegistry.init(settings);
119         typeRegistry.load(resourcesDir + "MultipleNodes1",true).then(function() {
120             var list = typeRegistry.getNodeList();
121             list.should.be.an.Array.and.have.lengthOf(1);
122             list[0].should.have.property("id");
123             list[0].should.have.property("name","MultipleNodes1.js");
124             list[0].should.have.property("types",["test-node-multiple-1a","test-node-multiple-1b"]);
125             list[0].should.have.property("enabled",true);
126             list[0].should.not.have.property("err");
127             
128             var nodeConstructor = typeRegistry.get("test-node-multiple-1a");
129             nodeConstructor.should.be.type("function");
130
131             nodeConstructor = typeRegistry.get("test-node-multiple-1b");
132             nodeConstructor.should.be.type("function");
133             
134             done();
135         }).catch(function(e) {
136             done(e);
137         });
138     });
139     
140     it('handles nested directories', function(done) {
141         typeRegistry.init(settings);
142         typeRegistry.load(resourcesDir + "NestedDirectoryNode",true).then(function() {
143             var list = typeRegistry.getNodeList();
144             list.should.be.an.Array.and.have.lengthOf(1);
145             list[0].should.have.property("id");
146             list[0].should.have.property("name","NestedNode.js");
147             list[0].should.have.property("types",["nested-node-1"]);
148             list[0].should.have.property("enabled",true);
149             list[0].should.not.have.property("err");
150             done();
151         }).catch(function(e) {
152             done(e);
153         });
154     });
155     
156     it('emits type-registered and node-icon-dir events', function(done) {
157         var eventEmitSpy = sinon.spy(events,"emit");
158         typeRegistry.init(settings);
159         typeRegistry.load(resourcesDir + "NestedDirectoryNode",true).then(function() {
160             var list = typeRegistry.getNodeList();
161             list.should.be.an.Array.and.have.lengthOf(1);
162             list[0].should.have.property("name","NestedNode.js");
163             list[0].should.have.property("types",["nested-node-1"]);
164             list[0].should.have.property("enabled",true);
165             list[0].should.not.have.property("err");
166             
167             eventEmitSpy.callCount.should.equal(2);
168             
169             eventEmitSpy.firstCall.args[0].should.be.equal("node-icon-dir");
170             eventEmitSpy.firstCall.args[1].should.be.equal(
171                     resourcesDir + "NestedDirectoryNode" + path.sep + "NestedNode" + path.sep + "icons");
172
173             eventEmitSpy.secondCall.args[0].should.be.equal("type-registered");
174             eventEmitSpy.secondCall.args[1].should.be.equal("nested-node-1");
175             
176             done();
177         }).catch(function(e) {
178             done(e);
179         }).finally(function() {
180             eventEmitSpy.restore();
181         });
182     });
183     
184     it('rejects a duplicate node type registration', function(done) {
185         
186         typeRegistry.init(stubSettings({
187             nodesDir:[resourcesDir + "TestNode1",resourcesDir + "DuplicateTestNode"]
188         },false));
189         typeRegistry.load("wontexist",true).then(function() {
190             var list = typeRegistry.getNodeList();
191             
192             list.should.be.an.Array.and.have.lengthOf(2);
193             list[0].should.have.property("id");
194             list[0].should.have.property("name","TestNode1.js");
195             list[0].should.have.property("types",["test-node-1"]);
196             list[0].should.have.property("enabled",true);
197             list[0].should.not.have.property("err");
198             
199             list[1].should.have.property("id");
200             list[1].id.should.not.equal(list[0].id);
201             
202             list[1].should.have.property("name","TestNode1.js");
203             list[1].should.have.property("types",["test-node-1"]);
204             list[1].should.have.property("enabled",true);
205             list[1].should.have.property("err");
206             /already registered/.test(list[1].err).should.be.true;
207
208             var nodeConstructor = typeRegistry.get("test-node-1");
209             // Verify the duplicate node hasn't replaced the original one
210             nodeConstructor.name.should.be.equal("TestNode");
211             
212             done();
213         }).catch(function(e) {
214             done(e);
215         });
216     });
217     
218     it('handles nodesDir as a string', function(done) {
219
220         typeRegistry.init(stubSettings({
221             nodesDir :resourcesDir + "TestNode1"
222         },false));
223         typeRegistry.load("wontexist",true).then(function(){
224             var list = typeRegistry.getNodeList();
225             list.should.be.an.Array.and.have.lengthOf(1);
226             list[0].should.have.property("types",["test-node-1"]);
227             done();
228         }).catch(function(e) {
229             done("Loading of non-existing nodesDir should succeed");
230         });
231             
232     });
233     
234     it('handles invalid nodesDir',function(done) {
235
236         typeRegistry.init(stubSettings({
237             nodesDir : "wontexist"
238         },false));
239         typeRegistry.load("wontexist",true).then(function(){
240             var list = typeRegistry.getNodeList();
241             list.should.be.an.Array.and.be.empty;
242             done();
243         }).catch(function(e) {
244             done("Loading of non-existing nodesDir should succeed");
245         });
246     });
247     
248     it('returns nothing for an unregistered type config', function() {
249         typeRegistry.init(settings);
250         typeRegistry.load("wontexist",true).then(function(){
251             var config = typeRegistry.getNodeConfig("imaginary-shark");
252             (config === null).should.be.true;
253         }).catch(function(e) {
254             done(e);
255         });
256     });
257     
258     it('excludes node files listed in nodesExcludes',function(done) {
259         typeRegistry.init(stubSettings({
260             nodesExcludes: [ "TestNode1.js" ],
261             nodesDir:[resourcesDir + "TestNode1",resourcesDir + "TestNode2"]
262         },false));
263         typeRegistry.load("wontexist",true).then(function() {
264             var list = typeRegistry.getNodeList();
265             list.should.be.an.Array.and.have.lengthOf(1);
266             list[0].should.have.property("types",["test-node-2"]);
267             done();
268         }).catch(function(e) {
269             done(e);
270         });
271     });
272     
273     it('returns the node configurations', function(done) {
274         typeRegistry.init(stubSettings({
275             nodesDir:[resourcesDir + "TestNode1",resourcesDir + "TestNode2"]
276         },false));
277         typeRegistry.load("wontexist",true).then(function() {
278             var list = typeRegistry.getNodeList();
279             
280             var nodeConfigs = typeRegistry.getNodeConfigs();
281             
282             // TODO: this is brittle...
283             nodeConfigs.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/x-red\" data-template-name=\"test-node-2\"></script>\n<script type=\"text/x-red\" data-help-name=\"test-node-2\"></script>\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-2',{});</script>\n<style></style>\n");
284             
285             var nodeId = list[0].id;
286             var nodeConfig = typeRegistry.getNodeConfig(nodeId);
287             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");
288             done();
289         }).catch(function(e) {
290             done(e);
291         });
292     });
293     
294     it('stores the node list', function(done) {
295         var settings = {
296             nodesDir:[resourcesDir + "TestNode1",resourcesDir + "TestNode2",resourcesDir + "TestNode3"],
297             available: function() { return true; },
298             set: function(s,v) {return when.resolve();},
299             get: function(s) { return null;}
300         }
301         var settingsSave = sinon.spy(settings,"set");
302         typeRegistry.init(settings);
303         typeRegistry.load("wontexist",true).then(function() {
304             var list = typeRegistry.getNodeList();
305             list.should.be.Array.and.have.length(3);
306             
307             settingsSave.callCount.should.equal(1);
308             settingsSave.firstCall.args[0].should.be.equal("nodes");
309             var savedList = settingsSave.firstCall.args[1];
310             
311             savedList[list[0].id].name == list[0].name;
312             savedList[list[1].id].name == list[1].name;
313             savedList[list[2].id].name == list[2].name;
314             
315             savedList[list[0].id].should.not.have.property("err");
316             savedList[list[1].id].should.not.have.property("err");
317             savedList[list[2].id].should.not.have.property("err");
318             
319             done();
320         }).catch(function(e) {
321             done(e);
322         }).finally(function() {
323             settingsSave.restore();
324         });
325             
326     });
327     
328     it('allows nodes to be added by filename', function(done) {
329         var settings = {
330             available: function() { return true; },
331             set: function(s,v) {return when.resolve();},
332             get: function(s) { return null;}
333         }            
334         typeRegistry.init(settings);
335         typeRegistry.load("wontexist",true).then(function(){
336             var list = typeRegistry.getNodeList();
337             list.should.be.an.Array.and.be.empty;
338             
339             typeRegistry.addNode(resourcesDir + "TestNode1/TestNode1.js").then(function(node) {
340                 list = typeRegistry.getNodeList();
341                 list[0].should.have.property("id");
342                 list[0].should.have.property("name","TestNode1.js");
343                 list[0].should.have.property("types",["test-node-1"]);
344                 list[0].should.have.property("enabled",true);
345                 list[0].should.not.have.property("err");
346                 
347                 node.should.be.an.Array.and.have.lengthOf(1);
348                 node.should.eql(list);
349                 
350                 done();
351             }).catch(function(e) {
352                 done(e);
353             });
354             
355         }).catch(function(e) {
356             done(e);
357         });
358     });
359     
360     it('fails to add non-existent filename', function(done) {
361         typeRegistry.init(settingsWithStorage);
362         typeRegistry.load("wontexist",true).then(function(){
363             var list = typeRegistry.getNodeList();
364             list.should.be.an.Array.and.be.empty;
365             typeRegistry.addNode(resourcesDir + "DoesNotExist/DoesNotExist.js").then(function(nodes) {
366                 nodes.should.be.an.Array.and.have.lengthOf(1);
367                 nodes[0].should.have.property("id");
368                 nodes[0].should.have.property("types",[]);
369                 nodes[0].should.have.property("err");
370                 done();
371             }).otherwise(function(e) {
372                 done(e);
373             });
374             
375         }).catch(function(e) {
376             done(e);
377         });
378     });
379     
380     it('returns node info by type or id', function(done) {
381         typeRegistry.init(settings);
382         typeRegistry.load(resourcesDir + "TestNode1",true).then(function() {
383             var list = typeRegistry.getNodeList();
384             list.should.be.an.Array.and.have.lengthOf(1);
385             
386             var id = list[0].id;
387             var type = list[0].types[0];
388             
389             list[0].should.have.property("id");
390             list[0].should.have.property("name","TestNode1.js");
391             list[0].should.have.property("types",["test-node-1"]);
392             list[0].should.have.property("enabled",true);
393             list[0].should.not.have.property("err");
394             
395             var info = typeRegistry.getNodeInfo(id);
396             list[0].should.eql(info);
397
398             var info2 = typeRegistry.getNodeInfo(type);
399             list[0].should.eql(info2);
400             
401             done();
402         }).catch(function(e) {
403             done(e);
404         });
405             
406     });
407     
408     
409     it('rejects adding duplicate nodes', function(done) {
410         typeRegistry.init(settingsWithStorage);
411         typeRegistry.load(resourcesDir + "TestNode1",true).then(function(){
412             var list = typeRegistry.getNodeList();
413             list.should.be.an.Array.and.have.lengthOf(1);
414             
415             typeRegistry.addNode({file:resourcesDir + "TestNode1" + path.sep + "TestNode1.js"}).then(function(node) {
416                 done(new Error("duplicate node loaded"));
417             }).otherwise(function(e) {
418                 var list = typeRegistry.getNodeList();
419                 list.should.be.an.Array.and.have.lengthOf(1);
420                 done();
421             });
422             
423         }).catch(function(e) {
424             done(e);
425         });
426     });
427     
428     it('removes nodes from the registry', function(done) {
429         typeRegistry.init(settingsWithStorage);
430         typeRegistry.load(resourcesDir + "TestNode1",true).then(function() {
431             var list = typeRegistry.getNodeList();
432             list.should.be.an.Array.and.have.lengthOf(1);
433             list[0].should.have.property("id");
434             list[0].should.have.property("name","TestNode1.js");
435             list[0].should.have.property("types",["test-node-1"]);
436             list[0].should.have.property("enabled",true);
437             list[0].should.have.property("loaded",true);
438
439             typeRegistry.getNodeConfigs().length.should.be.greaterThan(0);
440             
441             var info = typeRegistry.removeNode(list[0].id);
442             
443             info.should.have.property("id",list[0].id);
444             info.should.have.property("enabled",false);
445             info.should.have.property("loaded",false);
446             
447             typeRegistry.getNodeList().should.be.an.Array.and.be.empty;
448             typeRegistry.getNodeConfigs().length.should.equal(0);
449             
450             var nodeConstructor = typeRegistry.get("test-node-1");
451             (typeof nodeConstructor).should.be.equal("undefined");
452             
453             
454             done();
455         }).catch(function(e) {
456             done(e);
457         });
458     });
459     
460     it('rejects removing unknown nodes from the registry', function(done) {
461         typeRegistry.init(settings);
462         typeRegistry.load("wontexist",true).then(function() {
463             var list = typeRegistry.getNodeList();
464             list.should.be.an.Array.and.be.empty;
465
466             
467             /*jshint immed: false */
468             (function() {
469                 typeRegistry.removeNode("1234");
470             }).should.throw();
471
472             done();
473         }).catch(function(e) {
474             done(e);
475         });
476     });
477     
478     it('scans the node_modules path for node files', function(done) {
479         var fs = require("fs");
480         var path = require("path");
481         
482         var eventEmitSpy = sinon.spy(events,"emit");
483         var pathJoin = (function() {
484             var _join = path.join;
485             return sinon.stub(path,"join",function() {
486                 if (arguments.length  == 3 && arguments[2] == "package.json") {
487                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]);
488                 }
489                 if (arguments.length == 2 && arguments[1] == "TestNodeModule") {
490                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]);
491                 }
492                 return _join.apply(this,arguments);
493             });
494         })();
495         
496         var readdirSync = (function() {
497             var originalReaddirSync = fs.readdirSync;
498             var callCount = 0;
499             return sinon.stub(fs,"readdirSync",function(dir) {
500                 var result = [];
501                 if (callCount == 1) {
502                     result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules");
503                 }
504                 callCount++;
505                 return result;
506             });
507         })();
508         
509         typeRegistry.init(settings);
510         typeRegistry.load("wontexist",false).then(function(){
511             var list = typeRegistry.getNodeList();
512             list.should.be.an.Array.and.have.lengthOf(2);
513             list[0].should.have.property("id");
514             list[0].should.have.property("name","TestNodeModule:TestNodeMod1");
515             list[0].should.have.property("types",["test-node-mod-1"]);
516             list[0].should.have.property("enabled",true);
517             list[0].should.not.have.property("err");
518
519             list[1].should.have.property("id");
520             list[1].should.have.property("name","TestNodeModule:TestNodeMod2");
521             list[1].should.have.property("types",["test-node-mod-2"]);
522             list[1].should.have.property("enabled",true);
523             list[1].should.have.property("err");
524             
525             
526             eventEmitSpy.callCount.should.equal(2);
527             
528             eventEmitSpy.firstCall.args[0].should.be.equal("node-icon-dir");
529             eventEmitSpy.firstCall.args[1].should.be.equal(
530                     resourcesDir + "TestNodeModule" + path.sep+ "node_modules" + path.sep + "TestNodeModule" + path.sep + "icons");
531
532             eventEmitSpy.secondCall.args[0].should.be.equal("type-registered");
533             eventEmitSpy.secondCall.args[1].should.be.equal("test-node-mod-1");
534             
535             done();
536         }).catch(function(e) {
537             done(e);
538         }).finally(function() {
539             readdirSync.restore();
540             pathJoin.restore();
541             eventEmitSpy.restore();
542         });
543     });
544     
545     it('allows nodes to be added by module name', function(done) {
546         var fs = require("fs");
547         var path = require("path");
548
549         var pathJoin = (function() {
550             var _join = path.join;
551             return sinon.stub(path,"join",function() {
552                 if (arguments.length  == 3 && arguments[2] == "package.json") {
553                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]);
554                 }
555                 if (arguments.length == 2 && arguments[1] == "TestNodeModule") {
556                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]);
557                 }
558                 return _join.apply(this,arguments);
559             });
560         })();
561         
562         var readdirSync = (function() {
563             var originalReaddirSync = fs.readdirSync;
564             var callCount = 0;
565             return sinon.stub(fs,"readdirSync",function(dir) {
566                 var result = [];
567                 if (callCount == 1) {
568                     result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules");
569                 }
570                 callCount++;
571                 return result;
572             });
573         })();
574         typeRegistry.init(settingsWithStorage);
575         typeRegistry.load("wontexist",true).then(function(){
576             var list = typeRegistry.getNodeList();
577             list.should.be.an.Array.and.be.empty;
578             
579             typeRegistry.addModule("TestNodeModule").then(function(node) {
580                 list = typeRegistry.getNodeList();
581                 list.should.be.an.Array.and.have.lengthOf(2);
582                 list[0].should.have.property("id");
583                 list[0].should.have.property("name","TestNodeModule:TestNodeMod1");
584                 list[0].should.have.property("types",["test-node-mod-1"]);
585                 list[0].should.have.property("enabled",true);
586                 list[0].should.not.have.property("err");
587
588                 list[1].should.have.property("id");
589                 list[1].should.have.property("name","TestNodeModule:TestNodeMod2");
590                 list[1].should.have.property("types",["test-node-mod-2"]);
591                 list[1].should.have.property("enabled",true);
592                 list[1].should.have.property("err");
593                 
594                 node.should.eql(list);
595                 
596                 done();
597             }).catch(function(e) {
598                 done(e);
599             });
600             
601         }).catch(function(e) {
602             done(e);
603         }).finally(function() {
604             readdirSync.restore();
605             pathJoin.restore();
606         });
607     });
608     
609     
610     it('rejects adding duplicate node modules', function(done) {
611         var fs = require("fs");
612         var path = require("path");
613
614         var pathJoin = (function() {
615             var _join = path.join;
616             return sinon.stub(path,"join",function() {
617                 if (arguments.length  == 3 && arguments[2] == "package.json") {
618                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]);
619                 }
620                 if (arguments.length == 2 && arguments[1] == "TestNodeModule") {
621                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]);
622                 }
623                 return _join.apply(this,arguments);
624             });
625         })();
626         
627         var readdirSync = (function() {
628             var originalReaddirSync = fs.readdirSync;
629             var callCount = 0;
630             return sinon.stub(fs,"readdirSync",function(dir) {
631                 var result = [];
632                 if (callCount == 1) {
633                     result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules");
634                 }
635                 callCount++;
636                 return result;
637             });
638         })();
639
640         typeRegistry.init(settingsWithStorage);
641         typeRegistry.load('wontexist',false).then(function(){
642             var list = typeRegistry.getNodeList();
643             list.should.be.an.Array.and.have.lengthOf(2);
644             typeRegistry.addModule("TestNodeModule").then(function(node) {
645                 done(new Error("addModule resolved"));
646             }).otherwise(function(err) {
647                 done();
648             });
649         }).catch(function(e) {
650             done(e);
651         }).finally(function() {
652             readdirSync.restore();
653             pathJoin.restore();
654         });
655     });
656     
657     
658     it('fails to add non-existent module name', function(done) {
659         typeRegistry.init(settingsWithStorage);
660         typeRegistry.load("wontexist",true).then(function(){
661             var list = typeRegistry.getNodeList();
662             list.should.be.an.Array.and.be.empty;
663             
664             typeRegistry.addModule("DoesNotExistModule").then(function(node) {
665                 done(new Error("ENOENT not thrown"));
666             }).otherwise(function(e) {
667                 e.code.should.eql("MODULE_NOT_FOUND");
668                 done();
669             });
670             
671         }).catch(function(e) {
672             done(e);
673         });
674     });
675     
676     it('removes nodes from the registry by module', function(done) {
677         var fs = require("fs");
678         var path = require("path");
679
680         var pathJoin = (function() {
681             var _join = path.join;
682             return sinon.stub(path,"join",function() {
683                 if (arguments.length  == 3 && arguments[2] == "package.json") {
684                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1],arguments[2]);
685                 }
686                 if (arguments.length == 2 && arguments[1] == "TestNodeModule") {
687                     return _join(resourcesDir,"TestNodeModule" + path.sep + "node_modules" + path.sep,arguments[1]);
688                 }
689                 return _join.apply(this,arguments);
690             });
691         })();
692         
693         var readdirSync = (function() {
694             var originalReaddirSync = fs.readdirSync;
695             var callCount = 0;
696             return sinon.stub(fs,"readdirSync",function(dir) {
697                 var result = [];
698                 if (callCount == 1) {
699                     result = originalReaddirSync(resourcesDir + "TestNodeModule" + path.sep + "node_modules");
700                 }
701                 callCount++;
702                 return result;
703             });
704         })();
705
706         typeRegistry.init(settingsWithStorage);
707         typeRegistry.load('wontexist',false).then(function(){
708             var list = typeRegistry.getNodeList();
709             list.should.be.an.Array.and.have.lengthOf(2);
710             var res = typeRegistry.removeModule("TestNodeModule");
711             
712             res.should.be.an.Array.and.have.lengthOf(2);
713             res[0].should.have.a.property("id",list[0].id);
714             res[1].should.have.a.property("id",list[1].id);
715             
716             list = typeRegistry.getNodeList();
717             list.should.be.an.Array.and.be.empty;
718             
719             done();
720         }).catch(function(e) {
721             done(e);
722         }).finally(function() {
723             readdirSync.restore();
724             pathJoin.restore();
725         });
726             
727     });
728     
729     it('fails to remove non-existent module name', function(done) {
730         typeRegistry.init(settings);
731         typeRegistry.load("wontexist",true).then(function(){
732             var list = typeRegistry.getNodeList();
733             list.should.be.an.Array.and.be.empty;
734             
735             /*jshint immed: false */
736             (function() {
737                 typeRegistry.removeModule("DoesNotExistModule");
738             }).should.throw();
739             
740             done();
741             
742         }).catch(function(e) {
743             done(e);
744         });
745     });
746     
747     
748     it('allows nodes to be enabled and disabled', function(done) {
749         typeRegistry.init(settingsWithStorage);
750         typeRegistry.load(resourcesDir+path.sep+"TestNode1",true).then(function() {
751             var list = typeRegistry.getNodeList();
752             list.should.be.an.Array.and.have.lengthOf(1);
753             list[0].should.have.property("id");
754             list[0].should.have.property("name","TestNode1.js");
755             list[0].should.have.property("enabled",true);
756             
757             var nodeConfig = typeRegistry.getNodeConfigs();
758             nodeConfig.length.should.be.greaterThan(0);
759             
760             var info = typeRegistry.disableNode(list[0].id);
761             info.should.have.property("id",list[0].id);
762             info.should.have.property("enabled",false);
763             
764             var list2 = typeRegistry.getNodeList();
765             list2.should.be.an.Array.and.have.lengthOf(1);
766             list2[0].should.have.property("enabled",false);
767             
768             typeRegistry.getNodeConfigs().length.should.equal(0);
769             
770             var info2 = typeRegistry.enableNode(list[0].id);
771             info2.should.have.property("id",list[0].id);
772             info2.should.have.property("enabled",true);
773             
774             var list3 = typeRegistry.getNodeList();
775             list3.should.be.an.Array.and.have.lengthOf(1);
776             list3[0].should.have.property("enabled",true);
777             
778             var nodeConfig2 = typeRegistry.getNodeConfigs();
779             nodeConfig2.should.eql(nodeConfig);
780
781             done();
782         }).catch(function(e) {
783             done(e);
784         });
785     });
786     
787     it('fails to enable/disable non-existent nodes', function(done) {
788         typeRegistry.init(settings);
789         typeRegistry.load("wontexist",true).then(function() {
790             var list = typeRegistry.getNodeList();
791             list.should.be.an.Array.and.be.empty;
792             
793             /*jshint immed: false */
794             (function() {
795                     typeRegistry.disableNode("123");
796             }).should.throw();
797             
798             /*jshint immed: false */
799             (function() {
800                 typeRegistry.enableNode("123");
801             }).should.throw();
802             
803             done();
804         }).catch(function(e) {
805             done(e);
806         });
807     });
808 });