Add Status Node to Subflow to allow subflow-specific status

Closes #597
This commit is contained in:
Nick O'Leary 2019-02-01 23:44:50 +00:00
parent efe8fbbd11
commit 3bcff91328
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
9 changed files with 500 additions and 41 deletions

View File

@ -273,6 +273,7 @@
"editSubflowProperties": "edit properties",
"input": "inputs:",
"output": "outputs:",
"status": "status node",
"deleteSubflow": "delete subflow",
"info": "Description",
"category": "Category",

View File

@ -125,14 +125,20 @@ RED.history = (function() {
});
}
}
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
ev.subflow.instances.forEach(function(n) {
var node = RED.nodes.node(n.id);
if (node) {
node.changed = n.changed;
node.dirty = true;
}
});
if (ev.subflow) {
if (ev.subflow.hasOwnProperty('instances')) {
ev.subflow.instances.forEach(function(n) {
var node = RED.nodes.node(n.id);
if (node) {
node.changed = n.changed;
node.dirty = true;
}
});
}
if (ev.subflow.hasOwnProperty('status')) {
subflow = RED.nodes.subflow(ev.subflow.id);
subflow.status = ev.subflow.status;
}
}
if (subflow) {
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
@ -232,6 +238,11 @@ RED.history = (function() {
}
});
}
if (ev.subflow.hasOwnProperty('status')) {
if (ev.subflow.status) {
delete ev.node.status;
}
}
RED.editor.validateNode(ev.node);
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
n.inputs = ev.node.in.length;
@ -290,6 +301,7 @@ RED.history = (function() {
RED.workspaces.order(ev.order);
}
}
Object.keys(modifiedTabs).forEach(function(id) {
var subflow = RED.nodes.subflow(id);
if (subflow) {
@ -303,6 +315,7 @@ RED.history = (function() {
RED.palette.refresh();
RED.workspaces.refresh();
RED.sidebar.config.refresh();
RED.subflow.refresh();
}
}

View File

@ -571,6 +571,18 @@ RED.nodes = (function() {
node.icon = n.icon;
}
}
if (n.status) {
node.status = {x: n.status.x, y: n.status.y, wires:[]};
links.forEach(function(d) {
if (d.target === n.status) {
if (d.source.type != "subflow") {
node.status.wires.push({id:d.source.id, port:d.sourcePort})
} else {
node.status.wires.push({id:n.id, port:0})
}
}
});
}
return node;
}
@ -851,6 +863,12 @@ RED.nodes = (function() {
output.i = i;
output.id = getID();
});
if (n.status) {
n.status.type = "subflow";
n.status.direction = "status";
n.status.z = n.id;
n.status.id = getID();
}
new_subflows.push(n);
addSubflow(n,createNewIds);
}
@ -1189,6 +1207,19 @@ RED.nodes = (function() {
});
delete output.wires;
});
if (n.status) {
n.status.wires.forEach(function(wire) {
var link;
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
link = {source:n.in[wire.port], sourcePort:wire.port,target:n.status};
} else {
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:n.status};
}
addLink(link);
new_links.push(link);
});
delete n.status.wires;
}
}
RED.workspaces.refresh();

View File

@ -16,7 +16,6 @@
RED.subflow = (function() {
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow"><div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div></script>';
var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+
'<div class="form-row"><i class="fa fa-tag"></i> <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name"></div>'+
@ -24,26 +23,22 @@ RED.subflow = (function() {
'<div class="form-row form-tips" id="subflow-dialog-user-count"></div>'+
'</script>';
function getSubflow() {
return RED.nodes.subflow(RED.workspaces.active());
}
function findAvailableSubflowIOPosition(subflow,isInput) {
var pos = {x:50,y:30};
if (!isInput) {
pos.x += 110;
}
for (var i=0;i<subflow.out.length+subflow.in.length;i++) {
var port;
if (i < subflow.out.length) {
port = subflow.out[i];
} else {
port = subflow.in[i-subflow.out.length];
}
var ports = [].concat(subflow.out).concat(subflow.in);
if (subflow.status) {
ports.push(subflow.status);
}
ports.sort(function(A,B) {
return A.x-B.x;
});
for (var i=0; i<ports.length; i++) {
var port = ports[i];
if (port.x == pos.x && port.y == pos.y) {
pos.x += 55;
i=0;
}
}
return pos;
@ -191,6 +186,61 @@ RED.subflow = (function() {
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
}
function addSubflowStatus() {
var subflow = RED.nodes.subflow(RED.workspaces.active());
if (subflow.status) {
return;
}
var position = findAvailableSubflowIOPosition(subflow,false);
var statusNode = {
type:"subflow",
direction:"status",
z:subflow.id,
x:position.x,
y:position.y,
id:RED.nodes.id()
};
subflow.status = statusNode;
subflow.dirty = true;
var wasDirty = RED.nodes.dirty();
var wasChanged = subflow.changed;
subflow.changed = true;
var result = refresh(true);
var historyEvent = {
t:'edit',
node:subflow,
dirty:wasDirty,
changed:wasChanged,
subflow: { status: true }
};
RED.history.push(historyEvent);
RED.view.select();
RED.nodes.dirty(true);
RED.view.redraw();
$("#workspace-subflow-status").prop("checked",!!subflow.status);
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
}
function removeSubflowStatus() {
var subflow = RED.nodes.subflow(RED.workspaces.active());
if (!subflow.status) {
return;
}
var subflowRemovedLinks = [];
RED.nodes.eachLink(function(l) {
if (l.target.type == "subflow" && l.target.z == subflow.id && l.target.direction == "status") {
subflowRemovedLinks.push(l);
}
});
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
delete subflow.status;
$("#workspace-subflow-status").prop("checked",!!subflow.status);
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
return { links: subflowRemovedLinks }
}
function refresh(markChange) {
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
refreshToolbar(activeSubflow);
@ -219,12 +269,17 @@ RED.subflow = (function() {
}
}
}
function refreshToolbar(activeSubflow) {
if (activeSubflow) {
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
$("#workspace-subflow-output .spinner-value").text(activeSubflow.out.length);
$("#workspace-subflow-status").prop("checked",!!activeSubflow.status);
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!activeSubflow.status);
}
}
@ -232,22 +287,32 @@ RED.subflow = (function() {
var toolbar = $("#workspace-toolbar");
toolbar.empty();
// Edit properties
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
// Inputs
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
'<div style="display: inline-block;" class="button-group">'+
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
'</div>').appendTo(toolbar);
// Outputs
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
'<div class="spinner-value">3</div>'+
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
'</div>').appendTo(toolbar);
// Status
$('<span class="button-group"><span class="button" style="padding:0"><label for="workspace-subflow-status"><input id="workspace-subflow-status" type="checkbox"> <span data-i18n="subflow.status"></span></label></span></span>').appendTo(toolbar);
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
// Delete
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
toolbar.i18n();
@ -274,6 +339,7 @@ RED.subflow = (function() {
RED.view.redraw(true);
}
});
$("#workspace-subflow-output-add").click(function(event) {
event.preventDefault();
addSubflowOutput();
@ -283,6 +349,7 @@ RED.subflow = (function() {
event.preventDefault();
addSubflowInput();
});
$("#workspace-subflow-input-remove").click(function(event) {
event.preventDefault();
var wasDirty = RED.nodes.dirty();
@ -307,6 +374,33 @@ RED.subflow = (function() {
}
});
$("#workspace-subflow-status").change(function(evt) {
if (this.checked) {
addSubflowStatus();
} else {
var currentStatus = activeSubflow.status;
var wasChanged = activeSubflow.changed;
var result = removeSubflowStatus();
if (result) {
activeSubflow.changed = true;
var wasDirty = RED.nodes.dirty();
RED.history.push({
t:'delete',
links:result.links,
changed: wasChanged,
dirty:wasDirty,
subflow: {
id: activeSubflow.id,
status: currentStatus
}
});
RED.view.select();
RED.nodes.dirty(true);
RED.view.redraw();
}
}
})
$("#workspace-subflow-edit").click(function(event) {
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
event.preventDefault();
@ -328,6 +422,7 @@ RED.subflow = (function() {
$("#chart").css({"margin-top": "40px"});
$("#workspace-toolbar").show();
}
function hideWorkspaceToolbar() {
$("#workspace-toolbar").hide().empty();
$("#chart").css({"margin-top": "0"});
@ -373,6 +468,7 @@ RED.subflow = (function() {
subflows: [activeSubflow]
}
}
function init() {
RED.events.on("workspace:change",function(event) {
var activeSubflow = RED.nodes.subflow(event.workspace);
@ -436,6 +532,7 @@ RED.subflow = (function() {
}
return x;
}
function convertToSubflow() {
var selection = RED.view.selection();
if (!selection.nodes) {
@ -451,7 +548,6 @@ RED.subflow = (function() {
var candidateOutputs = [];
var candidateInputNodes = {};
var boundingBox = [selection.nodes[0].x,
selection.nodes[0].y,
selection.nodes[0].x,
@ -467,8 +563,8 @@ RED.subflow = (function() {
Math.max(boundingBox[3],n.y)
]
}
var offsetX = snapToGrid(boundingBox[0] - 180);
var offsetY = snapToGrid(boundingBox[1] - 60);
var offsetX = snapToGrid(boundingBox[0] - 200);
var offsetY = snapToGrid(boundingBox[1] - 80);
var center = [
@ -643,8 +739,6 @@ RED.subflow = (function() {
RED.view.redraw(true);
}
return {
init: init,
createSubflow: createSubflow,
@ -652,6 +746,7 @@ RED.subflow = (function() {
removeSubflow: removeSubflow,
refresh: refresh,
removeInput: removeSubflowInput,
removeOutput: removeSubflowOutput
removeOutput: removeSubflowOutput,
removeStatus: removeSubflowStatus
}
})();

View File

@ -1261,6 +1261,13 @@ RED.view = (function() {
moving_set.push({n:n});
}
});
if (activeSubflow.status) {
activeSubflow.status.selected = (activeSubflow.status.x > x && activeSubflow.status.x < x2 && activeSubflow.status.y > y && activeSubflow.status.y < y2);
if (activeSubflow.status.selected) {
activeSubflow.status.dirty = true;
moving_set.push({n:activeSubflow.status});
}
}
}
updateSelection();
lasso.remove();
@ -1367,6 +1374,13 @@ RED.view = (function() {
moving_set.push({n:n});
}
});
if (activeSubflow.status) {
if (!activeSubflow.status.selected) {
activeSubflow.status.selected = true;
activeSubflow.status.dirty = true;
moving_set.push({n:activeSubflow.status});
}
}
}
selected_link = null;
@ -1552,6 +1566,7 @@ RED.view = (function() {
var removedLinks = [];
var removedSubflowOutputs = [];
var removedSubflowInputs = [];
var removedSubflowStatus = undefined;
var subflowInstances = [];
var startDirty = RED.nodes.dirty();
@ -1573,6 +1588,8 @@ RED.view = (function() {
removedSubflowOutputs.push(node);
} else if (node.direction === "in") {
removedSubflowInputs.push(node);
} else if (node.direction === "status") {
removedSubflowStatus = node;
}
node.dirty = true;
}
@ -1590,12 +1607,19 @@ RED.view = (function() {
removedLinks = removedLinks.concat(result.links);
}
}
if (removedSubflowStatus) {
result = RED.subflow.removeStatus();
if (result) {
removedLinks = removedLinks.concat(result.links);
}
}
var instances = RED.subflow.refresh(true);
if (instances) {
subflowInstances = instances.instances;
}
moving_set = [];
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0) {
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus) {
RED.nodes.dirty(true);
}
}
@ -1651,10 +1675,14 @@ RED.view = (function() {
subflowOutputs:removedSubflowOutputs,
subflowInputs:removedSubflowInputs,
subflow: {
id: activeSubflow?activeSubflow.id:undefined,
instances: subflowInstances
},
dirty:startDirty
};
if (removedSubflowStatus) {
historyEvent.subflow.status = removedSubflowStatus;
}
}
RED.history.push(historyEvent);
@ -2420,6 +2448,49 @@ RED.view = (function() {
inGroup.append("svg:text").attr("class","port_label").attr("x",18).attr("y",20).style("font-size","10px").text("input");
var subflowStatus = nodeLayer.selectAll(".subflowstatus").data(activeSubflow.status?[activeSubflow.status]:[],function(d,i){ return d.id;});
subflowStatus.exit().remove();
var statusGroup = subflowStatus.enter().insert("svg:g").attr("class","node subflowstatus").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"});
statusGroup.each(function(d,i) {
d.w=40;
d.h=40;
});
statusGroup.append("rect").attr("class","subflowport").attr("rx",8).attr("ry",8).attr("width",40).attr("height",40)
// TODO: This is exactly the same set of handlers used for regular nodes - DRY
.on("mouseup",nodeMouseUp)
.on("mousedown",nodeMouseDown)
.on("touchstart",function(d) {
var obj = d3.select(this);
var touch0 = d3.event.touches.item(0);
var pos = [touch0.pageX,touch0.pageY];
startTouchCenter = [touch0.pageX,touch0.pageY];
startTouchDistance = 0;
touchStartTime = setTimeout(function() {
showTouchMenu(obj,pos);
},touchLongPressTimeout);
nodeMouseDown.call(this,d)
})
.on("touchend", function(d) {
clearTimeout(touchStartTime);
touchStartTime = null;
if (RED.touch.radialMenu.active()) {
d3.event.stopPropagation();
return;
}
nodeMouseUp.call(this,d);
});
statusGroup.append("g").attr('transform','translate(-5,15)').append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
.on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
.on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
.on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);})
.on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);} )
.on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);})
.on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);});
statusGroup.append("svg:text").attr("class","port_label").attr("x",22).attr("y",20).style("font-size","10px").text("status");
subflowOutputs.each(function(d,i) {
if (d.dirty) {
var output = d3.select(this);
@ -2439,9 +2510,22 @@ RED.view = (function() {
d.dirty = false;
}
});
subflowStatus.each(function(d,i) {
if (d.dirty) {
var output = d3.select(this);
output.selectAll(".subflowport").classed("node_selected",function(d) { return d.selected; })
output.selectAll(".port_index").text(function(d){ return d.i+1});
output.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
dirtyNodes[d.id] = d;
d.dirty = false;
}
});
} else {
nodeLayer.selectAll(".subflowoutput").remove();
nodeLayer.selectAll(".subflowinput").remove();
nodeLayer.selectAll(".subflowstatus").remove();
}
var node = nodeLayer.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});

View File

@ -33,6 +33,15 @@
transition: right 0.2s ease;
overflow: hidden;
label {
padding: 1px 8px;
margin: 0;
font-size: 12px;
}
input[type="checkbox"] {
margin: 0 3px 0 0 ;
padding: 0;
}
.button {
@include workspace-button;
margin-right: 10px;

View File

@ -265,7 +265,6 @@ class Flow {
return Promise.all(promises);
}
/**
* Update the flow definition. This doesn't change anything that is running.
* This should be called after `stop` and before `start`.

View File

@ -17,6 +17,8 @@
const clone = require("clone");
const Flow = require('./Flow').Flow;
const util = require("util");
const redUtil = require("@node-red/util").util;
const flowUtil = require("./util");
@ -104,6 +106,40 @@ class Subflow extends Flow {
var self = this;
// Create a subflow node to accept inbound messages and route appropriately
var Node = require("../Node");
if (this.subflowDef.status) {
var subflowStatusConfig = {
id: this.subflowInstance.id+":status",
type: "subflow-status",
z: this.subflowInstance.id,
_flow: this.parent
}
this.statusNode = new Node(subflowStatusConfig);
this.statusNode.on("input", function(msg) {
if (msg.payload !== undefined) {
if (typeof msg.payload === "string") {
// if msg.payload is a String, use it as status text
self.node.status({text:msg.payload})
return;
} else if (Object.prototype.toString.call(msg.payload) === "[object Object]") {
if (msg.payload.hasOwnProperty('text') || msg.payload.hasOwnProperty('fill') || msg.payload.hasOwnProperty('shape') || Object.keys(msg.payload).length === 0) {
// msg.payload is an object that looks like a status object
self.node.status(msg.payload);
return;
}
}
// Anything else - inspect it and use as status text
var text = util.inspect(msg.payload);
if (text.length > 32) { text = text.substr(0,32) + "..."; }
self.node.status({text:text});
} else if (msg.status !== undefined) {
// if msg.status exists
self.node.status(msg.status)
}
})
}
var subflowInstanceConfig = {
id: this.subflowInstance.id,
type: this.subflowInstance.type,
@ -168,7 +204,6 @@ class Subflow extends Flow {
// Wire the subflow outputs
if (this.subflowDef.out) {
var modifiedNodes = {};
for (var i=0;i<this.subflowDef.out.length;i++) {
// i: the output index
// This is what this Output is wired to
@ -180,7 +215,6 @@ class Subflow extends Flow {
this.node._updateWires(subflowInstanceConfig.wires);
} else {
var node = self.node_map[wires[j].id];
modifiedNodes[node.id] = node;
if (!node._originalWires) {
node._originalWires = clone(node.wires);
}
@ -189,9 +223,47 @@ class Subflow extends Flow {
}
}
}
if (this.subflowDef.status) {
var subflowStatusId = this.statusNode.id;
wires = this.subflowDef.status.wires;
for (var j=0;j<wires.length;j++) {
if (wires[j].id === this.subflowDef.id) {
// A subflow input wired straight to a subflow output
subflowInstanceConfig.wires[wires[j].port].push(subflowStatusId);
this.node._updateWires(subflowInstanceConfig.wires);
} else {
var node = self.node_map[wires[j].id];
if (!node._originalWires) {
node._originalWires = clone(node.wires);
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
node.wires[wires[j].port].push(subflowStatusId);
}
}
}
super.start(diff);
}
/**
* Get a node instance from this subflow.
* If the subflow has a status node, check for that, otherwise use
* the super-class function
* @param {String} id [description]
* @param {Boolean} cancelBubble if true, prevents the flow from passing the request to the parent
* This stops infinite loops when the parent asked this Flow for the
* node to begin with.
* @return {[type]} [description]
*/
getNode(id, cancelBubble) {
if (this.statusNode && this.statusNode.id === id) {
return this.statusNode;
}
return super.getNode(id,cancelBubble);
}
/**
* Handle a status event from a node within this flow.
* @param {Node} node The original node that triggered the event
@ -205,10 +277,14 @@ class Subflow extends Flow {
handleStatus(node,statusMessage,reportingNode,muteStatus) {
let handled = super.handleStatus(node,statusMessage,reportingNode,muteStatus);
if (!handled) {
// No status node on this subflow caught the status message.
// Pass up to the parent with this subflow's instance as the
// reporting node
handled = this.parent.handleStatus(node,statusMessage,this.node,true);
if (!this.statusNode || node === this.node) {
// No status node on this subflow caught the status message.
// AND there is no Subflow Status node - so the user isn't
// wanting to manage status messages themselves
// Pass up to the parent with this subflow's instance as the
// reporting node
handled = this.parent.handleStatus(node,statusMessage,this.node,true);
}
}
return handled;

View File

@ -265,7 +265,6 @@ describe('Subflow', function() {
done();
});
});
it("instantiates a subflow inside a subflow and stops it",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
@ -419,7 +418,6 @@ describe('Subflow', function() {
done();
});
});
it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
@ -457,9 +455,164 @@ describe('Subflow', function() {
done();
});
});
});
describe("status node", function() {
it("emits a status event when a message is passed to a subflow-status node - msg.payload as string", 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}]}],
status:{wires:[{id:"sf1-1", port:0}]}
},
{id:"sf1-1",type:"test",name:"test","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"]);
flow.start();
var activeNodes = flow.getActiveNodes();
activeNodes["1"].receive({payload:"test-payload"});
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","test-payload");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id","2");
statusMessage.status.source.should.have.a.property("type","subflow:sf1");
flow.stop().then(function() {
done();
});
});
it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", 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}]}],
status:{wires:[{id:"sf1-1", port:0}]}
},
{id:"sf1-1",type:"test",name:"test","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"]);
flow.start();
var activeNodes = flow.getActiveNodes();
activeNodes["1"].receive({payload:{text:"payload-obj"}});
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","payload-obj");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id","2");
statusMessage.status.source.should.have.a.property("type","subflow:sf1");
flow.stop().then(function() {
done();
});
});
it("emits a status event when a message is passed to a subflow-status node - msg.status", 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}]}],
status:{wires:[{id:"sf1-1", port:0}]}
},
{id:"sf1-1",type:"test",name:"test","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"]);
flow.start();
var activeNodes = flow.getActiveNodes();
activeNodes["1"].receive({status:{text:"status-obj"}});
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","status-obj");
statusMessage.status.should.have.a.property("source");
statusMessage.status.source.should.have.a.property("id","2");
statusMessage.status.source.should.have.a.property("type","subflow:sf1");
flow.stop().then(function() {
done();
});
});
it("does not emit a regular status event if it contains a subflow-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}]}],
status:{wires:[]}
},
{id:"sf1-1",type:"testStatus",name:"test-status-node","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"]);
flow.start();
var activeNodes = flow.getActiveNodes();
activeNodes["1"].receive({payload:"test-payload"});
currentNodes["sn"].should.have.a.property("handled",0);
flow.stop().then(function() {
done();
});
});
})
describe("#handleError",function() {
it("passes an error event to the subflow's parent tab catch node - all scope",function(done) {
var config = flowUtils.parseConfig([
@ -493,7 +646,6 @@ describe('Subflow', function() {
done();
});
});
it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
@ -530,7 +682,6 @@ describe('Subflow', function() {
});
});
});
});