node-red/test/red/runtime/nodes/flows/Flow_spec.js

1056 lines
45 KiB
JavaScript
Raw Normal View History

2015-01-15 18:10:32 +01:00
/**
* Copyright 2015 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.
**/
2015-01-15 18:10:32 +01:00
var should = require("should");
var sinon = require('sinon');
var clone = require('clone');
2015-01-16 16:43:47 +01:00
var util = require("util");
var flowUtils = require("../../../../../red/runtime/nodes/flows/util");
var Flow = require("../../../../../red/runtime/nodes/flows/Flow");
var flows = require("../../../../../red/runtime/nodes/flows");
var Node = require("../../../../../red/runtime/nodes/Node");
var typeRegistry = require("../../../../../red/runtime/nodes/registry");
describe('Flow', function() {
var getType;
var getNode;
var stoppedNodes = {};
var currentNodes = {};
var rewiredNodes = {};
var createCount = 0;
beforeEach(function() {
currentNodes = {};
stoppedNodes = {};
rewiredNodes = {};
createCount = 0;
});
var TestNode = function(n) {
Node.call(this,n);
this._index = createCount++;
this.scope = n.scope;
var node = this;
this.foo = n.foo;
this.handled = 0;
this.stopped = false;
currentNodes[node.id] = node;
this.on('input',function(msg) {
node.handled++;
node.send(msg);
});
this.on('close',function() {
node.stopped = true;
stoppedNodes[node.id] = node;
delete currentNodes[node.id];
});
this.__updateWires = this.updateWires;
this.updateWires = function(newWires) {
rewiredNodes[node.id] = node;
node.newWires = newWires;
node.__updateWires[newWires];
};
}
util.inherits(TestNode,Node);
var TestAsyncNode = function(n) {
Node.call(this,n);
var node = this;
this.scope = n.scope;
this.foo = n.foo;
this.handled = 0;
this.messages = [];
this.stopped = false;
currentNodes[node.id] = node;
this.on('input',function(msg) {
node.handled++;
node.messages.push(msg);
node.send(msg);
});
this.on('close',function(done) {
setTimeout(function() {
node.stopped = true;
stoppedNodes[node.id] = node;
delete currentNodes[node.id];
done();
},50);
2015-01-15 18:10:32 +01:00
});
}
util.inherits(TestAsyncNode,Node);
before(function() {
getType = sinon.stub(typeRegistry,"get",function(type) {
if (type=="test") {
return TestNode;
} else {
return TestAsyncNode;
2015-01-15 18:10:32 +01:00
}
});
getNode = sinon.stub(flows,"get",function(id) {
return currentNodes[id];
2015-01-15 18:10:32 +01:00
});
2015-01-15 18:10:32 +01:00
});
after(function() {
getType.restore();
getNode.restore();
2015-01-15 18:10:32 +01:00
});
describe('#constructor',function() {
it('called with an empty flow',function() {
var config = flowUtils.parseConfig([]);
var flow = Flow.create(config);
var nodeCount = 0;
Object.keys(flow.getActiveNodes()).length.should.equal(0);
});
});
describe('#start',function() {
it("instantiates an initial configuration and stops it",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"4",z:"t1",type:"test",foo:"a"}
]);
var flow = Flow.create(config,config.flows["t1"]);
flow.start();
Object.keys(flow.getActiveNodes()).should.have.length(4);
flow.getNode('1').should.have.a.property('id','1');
flow.getNode('2').should.have.a.property('id','2');
flow.getNode('3').should.have.a.property('id','3');
flow.getNode('4').should.have.a.property('id','4');
currentNodes.should.have.a.property("1");
currentNodes.should.have.a.property("2");
currentNodes.should.have.a.property("3");
currentNodes.should.have.a.property("4");
currentNodes["1"].should.have.a.property("handled",0);
currentNodes["2"].should.have.a.property("handled",0);
currentNodes["3"].should.have.a.property("handled",0);
currentNodes["1"].receive({payload:"test"});
currentNodes["1"].should.have.a.property("handled",1);
currentNodes["2"].should.have.a.property("handled",1);
currentNodes["3"].should.have.a.property("handled",1);
flow.stop().then(function() {
currentNodes.should.not.have.a.property("1");
currentNodes.should.not.have.a.property("2");
currentNodes.should.not.have.a.property("3");
currentNodes.should.not.have.a.property("4");
stoppedNodes.should.have.a.property("1");
stoppedNodes.should.have.a.property("2");
stoppedNodes.should.have.a.property("3");
stoppedNodes.should.have.a.property("4");
done();
});
});
it("instantiates config nodes in the right order",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"4",z:"t1",type:"test",foo:"5"}, // This node depends on #5
{id:"5",z:"t1",type:"test"}
]);
var flow = Flow.create(config,config.flows["t1"]);
flow.start();
Object.keys(flow.getActiveNodes()).should.have.length(5);
currentNodes.should.have.a.property("1");
currentNodes.should.have.a.property("2");
currentNodes.should.have.a.property("3");
currentNodes.should.have.a.property("4");
currentNodes.should.have.a.property("5");
currentNodes["1"].should.have.a.property("_index",2);
currentNodes["2"].should.have.a.property("_index",3);
currentNodes["3"].should.have.a.property("_index",4);
currentNodes["4"].should.have.a.property("_index",1);
currentNodes["5"].should.have.a.property("_index",0);
flow.stop().then(function() {
currentNodes.should.not.have.a.property("1");
currentNodes.should.not.have.a.property("2");
currentNodes.should.not.have.a.property("3");
currentNodes.should.not.have.a.property("4");
currentNodes.should.not.have.a.property("5");
stoppedNodes.should.have.a.property("1");
stoppedNodes.should.have.a.property("2");
stoppedNodes.should.have.a.property("3");
stoppedNodes.should.have.a.property("4");
stoppedNodes.should.have.a.property("5");
done();
});
});
it("detects dependency loops in config nodes",function() {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"node1",z:"t1",type:"test",foo:"node2"}, // This node depends on #5
{id:"node2",z:"t1",type:"test",foo:"node1"}
]);
var flow = Flow.create(config,config.flows["t1"]);
/*jshint immed: false */
(function(){
flow.start();
}).should.throw("Circular config node dependency detected: node1");
});
it("instantiates a subflow and stops it",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3","4"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]},{"wires":[{"id":"sf1","port":0}]}]},
{id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]},
{id:"sf1-2",type:"test","z":"sf1",foo:"sf1-cn",x:166,y:99,"wires":[[]]},
{id:"sf1-cn",type:"test","z":"sf1"}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
flow.start();
var activeNodes = flow.getActiveNodes();
Object.keys(activeNodes).should.have.length(7);
var sfInstanceId = Object.keys(activeNodes)[5];
var sfInstanceId2 = Object.keys(activeNodes)[6];
var sfConfigId = Object.keys(activeNodes)[4];
flow.getNode('1').should.have.a.property('id','1');
flow.getNode('2').should.have.a.property('id','2');
flow.getNode('3').should.have.a.property('id','3');
flow.getNode('4').should.have.a.property('id','4');
flow.getNode(sfInstanceId).should.have.a.property('id',sfInstanceId);
flow.getNode(sfInstanceId2).should.have.a.property('id',sfInstanceId2);
flow.getNode(sfConfigId).should.have.a.property('id',sfConfigId);
flow.getNode(sfInstanceId2).should.have.a.property('foo',sfConfigId);
currentNodes.should.have.a.property("1");
currentNodes.should.not.have.a.property("2");
currentNodes.should.have.a.property("3");
currentNodes.should.have.a.property("4");
currentNodes.should.have.a.property(sfInstanceId);
currentNodes.should.have.a.property(sfInstanceId2);
currentNodes.should.have.a.property(sfConfigId);
currentNodes["1"].should.have.a.property("handled",0);
currentNodes["3"].should.have.a.property("handled",0);
currentNodes["4"].should.have.a.property("handled",0);
currentNodes[sfInstanceId].should.have.a.property("handled",0);
currentNodes[sfInstanceId2].should.have.a.property("handled",0);
currentNodes["1"].receive({payload:"test"});
currentNodes["1"].should.have.a.property("handled",1);
currentNodes[sfInstanceId].should.have.a.property("handled",1);
currentNodes[sfInstanceId2].should.have.a.property("handled",1);
currentNodes["3"].should.have.a.property("handled",1);
currentNodes["4"].should.have.a.property("handled",1);
flow.stop().then(function() {
currentNodes.should.not.have.a.property("1");
currentNodes.should.not.have.a.property("3");
currentNodes.should.not.have.a.property("4");
currentNodes.should.not.have.a.property(sfInstanceId);
currentNodes.should.not.have.a.property(sfInstanceId2);
currentNodes.should.not.have.a.property(sfConfigId);
stoppedNodes.should.have.a.property("1");
stoppedNodes.should.have.a.property("3");
stoppedNodes.should.have.a.property("4");
stoppedNodes.should.have.a.property(sfInstanceId);
stoppedNodes.should.have.a.property(sfInstanceId2);
stoppedNodes.should.have.a.property(sfConfigId);
done();
});
});
it("instantiates a subflow inside a subflow and stops it",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3","4"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 1","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]}]},
{id:"sf2",type:"subflow","name":"Subflow 2","info":"",
"in":[{wires:[]}],"out":[{"wires":[{"id":"sf2","port":0}]}]},
{id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]},
{id:"sf1-2",type:"subflow:sf2","z":"sf1",x:166,y:99,"wires":[[]]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
flow.start();
currentNodes["1"].should.have.a.property("handled",0);
currentNodes["3"].should.have.a.property("handled",0);
currentNodes["1"].receive({payload:"test"});
currentNodes["1"].should.have.a.property("handled",1);
currentNodes["3"].should.have.a.property("handled",1);
flow.stop().then(function() {
Object.keys(currentNodes).should.have.length(0);
done();
});
});
it("rewires nodes specified by diff",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
createCount.should.equal(0);
flow.start();
//TODO: use update to pass in new wiring and verify the change
createCount.should.equal(3);
flow.start({rewired:["2"]});
createCount.should.equal(3);
rewiredNodes.should.have.a.property("2");
done();
});
it("rewires a subflow node on update/start",function(done){
var rawConfig = [
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]}]},
{id:"sf1-1",type:"test1","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]},
{id:"sf1-2",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]}
];
var config = flowUtils.parseConfig(clone(rawConfig));
rawConfig[2].wires = [["4"]];
var newConfig = flowUtils.parseConfig(rawConfig);
var diff = flowUtils.diffConfigs(config,newConfig);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
flow.start();
var activeNodes = flow.getActiveNodes();
Object.keys(activeNodes).should.have.length(6);
var sfInstanceId = Object.keys(activeNodes)[4];
var sfInstanceId2 = Object.keys(activeNodes)[5];
currentNodes["1"].should.have.a.property("handled",0);
currentNodes["3"].should.have.a.property("handled",0);
currentNodes["4"].should.have.a.property("handled",0);
currentNodes["1"].receive({payload:"test"});
currentNodes["1"].should.have.a.property("handled",1);
currentNodes[sfInstanceId].should.have.a.property("handled",1);
currentNodes[sfInstanceId2].should.have.a.property("handled",1);
currentNodes["3"].should.have.a.property("handled",1);
currentNodes["4"].should.have.a.property("handled",0);
flow.update(newConfig,newConfig.flows["t1"]);
flow.start(diff)
currentNodes["1"].receive({payload:"test2"});
currentNodes["1"].should.have.a.property("handled",2);
currentNodes[sfInstanceId].should.have.a.property("handled",2);
currentNodes[sfInstanceId2].should.have.a.property("handled",2);
currentNodes["3"].should.have.a.property("handled",1);
currentNodes["4"].should.have.a.property("handled",1);
flow.stop().then(function() {
done();
});
});
it("instantiates a node with environment variable property values",function(done) {
after(function() {
delete process.env.NODE_RED_TEST_VALUE;
})
process.env.NODE_RED_TEST_VALUE = "a-value";
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE)",wires:[]},
{id:"2",x:10,y:10,z:"t1",type:"test",foo:{a:"$(NODE_RED_TEST_VALUE)"},wires:[]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:" $(NODE_RED_TEST_VALUE)",wires:[]},
{id:"4",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE) ",wires:[]},
{id:"5",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE_NONE)",wires:[]},
{id:"6",x:10,y:10,z:"t1",type:"test",foo:["$(NODE_RED_TEST_VALUE)"],wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
flow.start();
var activeNodes = flow.getActiveNodes();
activeNodes["1"].foo.should.equal("a-value");
activeNodes["2"].foo.a.should.equal("a-value");
activeNodes["3"].foo.should.equal(" $(NODE_RED_TEST_VALUE)");
activeNodes["4"].foo.should.equal("$(NODE_RED_TEST_VALUE) ");
activeNodes["5"].foo.should.equal("$(NODE_RED_TEST_VALUE_NONE)");
activeNodes["6"].foo[0].should.equal("a-value");
flow.stop().then(function() {
done();
});
});
});
describe('#stop', function() {
it("stops all nodes",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"asyncTest",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
flow.start();
currentNodes.should.have.a.property("1");
currentNodes.should.have.a.property("2");
currentNodes.should.have.a.property("3");
flow.stop().then(function() {
currentNodes.should.not.have.a.property("1");
currentNodes.should.not.have.a.property("2");
currentNodes.should.not.have.a.property("3");
stoppedNodes.should.have.a.property("1");
stoppedNodes.should.have.a.property("2");
stoppedNodes.should.have.a.property("3");
done();
});
});
it("stops specified nodes",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
flow.start();
currentNodes.should.have.a.property("1");
currentNodes.should.have.a.property("2");
currentNodes.should.have.a.property("3");
flow.stop(["2"]).then(function() {
currentNodes.should.have.a.property("1");
currentNodes.should.not.have.a.property("2");
currentNodes.should.have.a.property("3");
stoppedNodes.should.not.have.a.property("1");
stoppedNodes.should.have.a.property("2");
stoppedNodes.should.not.have.a.property("3");
done();
2015-01-15 18:10:32 +01:00
});
});
it("stops subflow instance nodes",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]},
{id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[[]]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
flow.start();
var activeNodes = flow.getActiveNodes();
Object.keys(activeNodes).should.have.length(4);
var sfInstanceId = Object.keys(activeNodes)[3];
flow.stop(["2"]).then(function() {
currentNodes.should.not.have.a.property(sfInstanceId);
stoppedNodes.should.have.a.property(sfInstanceId);
done();
});
});
});
describe("#handleStatus",function() {
it("passes a status event to the adjacent status node",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]},
{id:"sn2",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
flow.start();
var activeNodes = flow.getActiveNodes();
Object.keys(activeNodes).should.have.length(5);
flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"});
2015-01-16 16:43:47 +01:00
currentNodes["sn"].should.have.a.property("handled",1);
var statusMessage = currentNodes["sn"].messages[0];
statusMessage.should.have.a.property("status");
statusMessage.status.should.have.a.property("text","my-status");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id","1");
statusMessage.status.source.should.have.a.property("type","test");
statusMessage.status.source.should.have.a.property("name","a");
currentNodes["sn2"].should.have.a.property("handled",1);
statusMessage = currentNodes["sn2"].messages[0];
statusMessage.should.have.a.property("status");
statusMessage.status.should.have.a.property("text","my-status");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id","1");
statusMessage.status.source.should.have.a.property("type","test");
statusMessage.status.source.should.have.a.property("name","a");
flow.stop().then(function() {
done();
2015-01-16 16:43:47 +01:00
});
});
it("passes a status event to the adjacent scoped status node ",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sn",x:10,y:10,z:"t1",type:"status",scope:["2"],foo:"a",wires:[]},
{id:"sn2",x:10,y:10,z:"t1",type:"status",scope:["1"],foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
2015-01-16 16:43:47 +01:00
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
2015-01-16 16:43:47 +01:00
});
flow.start();
var activeNodes = flow.getActiveNodes();
Object.keys(activeNodes).should.have.length(5);
flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"});
currentNodes["sn"].should.have.a.property("handled",0);
currentNodes["sn2"].should.have.a.property("handled",1);
var statusMessage = currentNodes["sn2"].messages[0];
statusMessage.should.have.a.property("status");
statusMessage.status.should.have.a.property("text","my-status");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id","1");
statusMessage.status.source.should.have.a.property("type","test");
statusMessage.status.source.should.have.a.property("name","a");
flow.stop().then(function() {
done();
});
2015-01-16 16:43:47 +01:00
});
it("passes a status event to the adjacent status node in subflow",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]},
{id:"sf1-1",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]},
{id:"sf1-sn",x:10,y:10,z:"sf1",type:"status",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
2015-01-16 16:43:47 +01:00
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
flow.start();
var activeNodes = flow.getActiveNodes();
var sfInstanceId = Object.keys(activeNodes)[3];
var statusInstanceId = Object.keys(activeNodes)[4];
flow.handleStatus(activeNodes[sfInstanceId],{text:"my-status"});
currentNodes[statusInstanceId].should.have.a.property("handled",1);
var statusMessage = currentNodes[statusInstanceId].messages[0];
statusMessage.should.have.a.property("status");
statusMessage.status.should.have.a.property("text","my-status");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id",sfInstanceId);
statusMessage.status.source.should.have.a.property("type","test2");
statusMessage.status.source.should.have.a.property("name",undefined);
flow.stop().then(function() {
done();
});
2015-01-16 16:43:47 +01:00
});
it("passes a status event to the multiple adjacent status nodes in subflow",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"4",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]},
{id:"sf1-1",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]},
{id:"sf1-sn",x:10,y:10,z:"sf1",type:"status",foo:"a",wires:[]},
{id:"sf1-sn2",x:10,y:10,z:"sf1",type:"status",scope:["none"],wires:[]},
{id:"sf1-sn3",x:10,y:10,z:"sf1",type:"status",scope:["sf1-1"],wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
2015-01-16 16:43:47 +01:00
flow.start();
var activeNodes = flow.getActiveNodes();
var sfInstanceId = Object.keys(activeNodes)[4];
var statusInstanceId = Object.keys(activeNodes)[5];
var statusInstanceId2 = Object.keys(activeNodes)[6];
var statusInstanceId3 = Object.keys(activeNodes)[7];
flow.handleStatus(activeNodes[sfInstanceId],{text:"my-status"});
currentNodes[statusInstanceId].should.have.a.property("handled",1);
var statusMessage = currentNodes[statusInstanceId].messages[0];
statusMessage.should.have.a.property("status");
statusMessage.status.should.have.a.property("text","my-status");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id",sfInstanceId);
statusMessage.status.source.should.have.a.property("type","test2");
statusMessage.status.source.should.have.a.property("name",undefined);
activeNodes["4"].should.have.a.property("handled",0);
currentNodes[statusInstanceId2].should.have.a.property("handled",0);
currentNodes[statusInstanceId3].should.have.a.property("handled",1);
statusMessage = currentNodes[statusInstanceId3].messages[0];
statusMessage.should.have.a.property("status");
statusMessage.status.should.have.a.property("text","my-status");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id",sfInstanceId);
statusMessage.status.source.should.have.a.property("type","test2");
statusMessage.status.source.should.have.a.property("name",undefined);
flow.stop().then(function() {
2015-01-16 16:43:47 +01:00
done();
});
});
it("passes a status event to the subflow's parent tab status node",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]},
{id:"sf1-1",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]},
{id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
2015-01-16 16:43:47 +01:00
flow.start();
var activeNodes = flow.getActiveNodes();
var sfInstanceId = Object.keys(activeNodes)[3];
2015-01-16 16:43:47 +01:00
flow.handleStatus(activeNodes[sfInstanceId],{text:"my-status"});
2015-01-16 16:43:47 +01:00
currentNodes["sn"].should.have.a.property("handled",1);
var statusMessage = currentNodes["sn"].messages[0];
statusMessage.should.have.a.property("status");
statusMessage.status.should.have.a.property("text","my-status");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id",sfInstanceId);
statusMessage.status.source.should.have.a.property("type","test2");
statusMessage.status.source.should.have.a.property("name",undefined);
flow.stop().then(function() {
2015-01-16 16:43:47 +01:00
done();
});
});
});
2015-01-16 16:43:47 +01:00
describe("#handleError",function() {
it("passes an error event to the adjacent catch node",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]},
{id:"sn2",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
2015-01-16 16:43:47 +01:00
flow.start();
var activeNodes = flow.getActiveNodes();
Object.keys(activeNodes).should.have.length(5);
flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"});
currentNodes["sn"].should.have.a.property("handled",1);
var statusMessage = currentNodes["sn"].messages[0];
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id","1");
statusMessage.error.source.should.have.a.property("type","test");
statusMessage.error.source.should.have.a.property("name","a");
currentNodes["sn2"].should.have.a.property("handled",1);
statusMessage = currentNodes["sn2"].messages[0];
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id","1");
statusMessage.error.source.should.have.a.property("type","test");
statusMessage.error.source.should.have.a.property("name","a");
flow.stop().then(function() {
2015-01-16 16:43:47 +01:00
done();
});
});
it("passes an error event to the adjacent scoped catch node ",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sn",x:10,y:10,z:"t1",type:"catch",scope:["2"],foo:"a",wires:[]},
{id:"sn2",x:10,y:10,z:"t1",type:"catch",scope:["1"],foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
2015-01-16 16:43:47 +01:00
flow.start();
var activeNodes = flow.getActiveNodes();
Object.keys(activeNodes).should.have.length(5);
flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"});
currentNodes["sn"].should.have.a.property("handled",0);
currentNodes["sn2"].should.have.a.property("handled",1);
var statusMessage = currentNodes["sn2"].messages[0];
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id","1");
statusMessage.error.source.should.have.a.property("type","test");
statusMessage.error.source.should.have.a.property("name","a");
flow.stop().then(function() {
2015-01-16 16:43:47 +01:00
done();
});
});
it("passes an error event to the adjacent catch node in subflow",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]},
{id:"sf1-1",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]},
{id:"sf1-sn",x:10,y:10,z:"sf1",type:"catch",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
2015-01-16 16:43:47 +01:00
flow.start();
var activeNodes = flow.getActiveNodes();
var sfInstanceId = Object.keys(activeNodes)[3];
var catchInstanceId = Object.keys(activeNodes)[4];
flow.handleError(activeNodes[sfInstanceId],"my-error",{a:"foo"});
currentNodes[catchInstanceId].should.have.a.property("handled",1);
var statusMessage = currentNodes[catchInstanceId].messages[0];
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id",sfInstanceId);
statusMessage.error.source.should.have.a.property("type","test2");
statusMessage.error.source.should.have.a.property("name",undefined);
flow.stop().then(function() {
2015-01-16 16:43:47 +01:00
done();
});
});
it("passes an error event to the multiple adjacent catch nodes in subflow",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]},
{id:"sf1-1",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]},
{id:"sf1-sn",x:10,y:10,z:"sf1",type:"catch",foo:"a",wires:[]},
{id:"sf1-sn2",x:10,y:10,z:"sf1",type:"catch",scope:["none"],wires:[]},
{id:"sf1-sn3",x:10,y:10,z:"sf1",type:"catch",scope:["sf1-1"],wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
flow.start();
var activeNodes = flow.getActiveNodes();
var sfInstanceId = Object.keys(activeNodes)[3];
var catchInstanceId = Object.keys(activeNodes)[4];
var catchInstanceId2 = Object.keys(activeNodes)[5];
var catchInstanceId3 = Object.keys(activeNodes)[6];
flow.handleError(activeNodes[sfInstanceId],"my-error",{a:"foo"});
currentNodes[catchInstanceId].should.have.a.property("handled",1);
var statusMessage = currentNodes[catchInstanceId].messages[0];
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id",sfInstanceId);
statusMessage.error.source.should.have.a.property("type","test2");
statusMessage.error.source.should.have.a.property("name",undefined);
currentNodes[catchInstanceId2].should.have.a.property("handled",0);
currentNodes[catchInstanceId3].should.have.a.property("handled",1);
statusMessage = currentNodes[catchInstanceId3].messages[0];
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id",sfInstanceId);
statusMessage.error.source.should.have.a.property("type","test2");
statusMessage.error.source.should.have.a.property("name",undefined);
flow.stop().then(function() {
done();
2015-02-20 20:54:19 +01:00
});
});
it("passes an error event to the subflow's parent tab catch node",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sf1",type:"subflow","name":"Subflow 2","info":"",
"in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]},
{id:"sf1-1",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]},
{id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
2015-02-20 20:54:19 +01:00
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
2015-02-20 20:54:19 +01:00
});
flow.start();
var activeNodes = flow.getActiveNodes();
var sfInstanceId = Object.keys(activeNodes)[3];
flow.handleError(activeNodes[sfInstanceId],"my-error",{a:"foo"});
currentNodes["sn"].should.have.a.property("handled",1);
var statusMessage = currentNodes["sn"].messages[0];
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id",sfInstanceId);
statusMessage.error.source.should.have.a.property("type","test2");
statusMessage.error.source.should.have.a.property("name",undefined);
flow.stop().then(function() {
done();
});
});
it("moves any existing error object sideways",function(done){
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]}
]);
var flow = Flow.create(config,config.flows["t1"]);
getNode.restore();
getNode = sinon.stub(flows,"get",function(id) {
return flow.getNode(id);
});
2015-02-23 00:15:55 +01:00
flow.start();
var activeNodes = flow.getActiveNodes();
flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo",error:"existing"});
currentNodes["sn"].should.have.a.property("handled",1);
var statusMessage = currentNodes["sn"].messages[0];
statusMessage.should.have.a.property("_error","existing");
statusMessage.should.have.a.property("error");
statusMessage.error.should.have.a.property("message","my-error");
statusMessage.error.should.have.a.property("source");
statusMessage.error.source.should.have.a.property("id","1");
statusMessage.error.source.should.have.a.property("type","test");
statusMessage.error.source.should.have.a.property("name","a");
flow.stop().then(function() {
done();
});
2015-02-23 00:15:55 +01:00
});
it.skip("prevents an error looping more than 10 times",function(){});
2015-01-15 18:10:32 +01:00
});
});