diff --git a/packages/node_modules/@node-red/nodes/core/core/25-complete.html b/packages/node_modules/@node-red/nodes/core/core/25-complete.html
new file mode 100644
index 000000000..f82ed02f8
--- /dev/null
+++ b/packages/node_modules/@node-red/nodes/core/core/25-complete.html
@@ -0,0 +1,136 @@
+
+
diff --git a/packages/node_modules/@node-red/nodes/core/core/25-complete.js b/packages/node_modules/@node-red/nodes/core/core/25-complete.js
new file mode 100644
index 000000000..78008f81d
--- /dev/null
+++ b/packages/node_modules/@node-red/nodes/core/core/25-complete.js
@@ -0,0 +1,30 @@
+/**
+ * Copyright JS Foundation and other contributors, http://js.foundation
+ *
+ * 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.
+ **/
+
+module.exports = function(RED) {
+ "use strict";
+
+ function CompleteNode(n) {
+ RED.nodes.createNode(this,n);
+ var node = this;
+ this.scope = n.scope;
+ this.on("input",function(msg) {
+ this.send(msg);
+ });
+ }
+
+ RED.nodes.registerType("complete",CompleteNode);
+}
diff --git a/packages/node_modules/@node-red/nodes/core/core/58-debug.js b/packages/node_modules/@node-red/nodes/core/core/58-debug.js
index 1571605da..badbad25e 100644
--- a/packages/node_modules/@node-red/nodes/core/core/58-debug.js
+++ b/packages/node_modules/@node-red/nodes/core/core/58-debug.js
@@ -81,7 +81,7 @@ module.exports = function(RED) {
}
}
- this.on("input", function(msg) {
+ this.on("input", function(msg, done) {
if (this.complete === "true") {
// debug complete msg object
if (this.console === "true") {
@@ -90,13 +90,14 @@ module.exports = function(RED) {
if (this.active && this.tosidebar) {
sendDebug({id:node.id, name:node.name, topic:msg.topic, msg:msg, _path:msg._path});
}
+ done();
} else {
- prepareValue(msg,function(err,msg) {
+ prepareValue(msg,function(err,debugMsg) {
if (err) {
node.error(err);
return;
}
- var output = msg.msg;
+ var output = debugMsg.msg;
if (node.console === "true") {
if (typeof output === "string") {
node.log((output.indexOf("\n") !== -1 ? "\n" : "") + output);
@@ -114,9 +115,10 @@ module.exports = function(RED) {
}
if (node.active) {
if (node.tosidebar == true) {
- sendDebug(msg);
+ sendDebug(debugMsg);
}
}
+ done();
});
}
})
diff --git a/packages/node_modules/@node-red/nodes/core/logic/15-change.js b/packages/node_modules/@node-red/nodes/core/logic/15-change.js
index 8d100233e..fb25939c0 100644
--- a/packages/node_modules/@node-red/nodes/core/logic/15-change.js
+++ b/packages/node_modules/@node-red/nodes/core/logic/15-change.js
@@ -328,13 +328,14 @@ module.exports = function(RED) {
}
if (valid) {
- this.on('input', function(msg) {
+ this.on('input', function(msg, done) {
applyRules(msg, 0, (err,msg) => {
if (err) {
node.error(err,msg);
} else if (msg) {
node.send(msg);
}
+ done();
})
});
}
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/core/25-complete.html b/packages/node_modules/@node-red/nodes/locales/en-US/core/25-complete.html
new file mode 100644
index 000000000..d73f8e13d
--- /dev/null
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/core/25-complete.html
@@ -0,0 +1,29 @@
+
+
+
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
index 76e1cf2f8..6e8794eb7 100755
--- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
@@ -112,6 +112,9 @@
"selected": "selected nodes"
}
},
+ "complete": {
+ "completeNodes": "complete: __number__"
+ },
"debug": {
"output": "Output",
"none": "None",
diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/Node.js b/packages/node_modules/@node-red/runtime/lib/nodes/Node.js
index b1003d717..f4ecabc86 100644
--- a/packages/node_modules/@node-red/runtime/lib/nodes/Node.js
+++ b/packages/node_modules/@node-red/runtime/lib/nodes/Node.js
@@ -27,6 +27,8 @@ function Node(n) {
this.type = n.type;
this.z = n.z;
this._closeCallbacks = [];
+ this._inputCallback = null;
+ this._inputCallbacks = null;
if (n.name) {
this.name = n.name;
@@ -43,6 +45,10 @@ function Node(n) {
// as part of its constructure - config._flow will overwrite this._flow
// which we can tolerate as they are the same object.
Object.defineProperty(this,'_flow', {value: n._flow, enumerable: false, writable: true })
+ this._asyncDelivery = n._flow.asyncMessageDelivery;
+ }
+ if (this._asyncDelivery === undefined) {
+ this._asyncDelivery = true;
}
this.updateWires(n.wires);
}
@@ -79,17 +85,95 @@ Node.prototype.context = function() {
return this._context;
}
-Node.prototype._on = Node.prototype.on;
+Node.prototype._complete = function(msg,error) {
+ if (error) {
+ // For now, delegate this to this.error
+ // But at some point, the timeout handling will need to know about
+ // this as well.
+ this.error(error,msg);
+ } else {
+ this._flow.handleComplete(this,msg);
+ }
+}
+Node.prototype._on = Node.prototype.on;
Node.prototype.on = function(event, callback) {
var node = this;
if (event == "close") {
this._closeCallbacks.push(callback);
+ } else if (event === "input") {
+ if (this._inputCallback) {
+ this._inputCallbacks = [this._inputCallback, callback];
+ this._inputCallback = null;
+ } else if (this._inputCallbacks) {
+ this._inputCallbacks.push(callback);
+ } else {
+ this._inputCallback = callback;
+ }
} else {
this._on(event, callback);
}
};
+Node.prototype._emit = Node.prototype.emit;
+Node.prototype.emit = function(event,arg) {
+ var node = this;
+ if (event === "input") {
+ // When Pluggable Message Routing arrives, this will be called from
+ // that and will already be sync/async depending on the router.
+ if (this._asyncDelivery) {
+ setImmediate(function() {
+ node._emitInput(arg);
+ });
+ } else {
+ this._emitInput(arg);
+ }
+ } else {
+ this._emit(event,arg);
+ }
+}
+
+Node.prototype._emitInput = function(arg) {
+ var node = this;
+ if (node._inputCallback) {
+ try {
+ node._inputCallback(arg,function(err) { node._complete(arg,err); });
+ } catch(err) {
+ node.error(err,arg);
+ }
+ } else if (node._inputCallbacks) {
+ var c = node._inputCallbacks.length;
+ for (var i=0;i {
+ this.completeNodeMap[id] = this.completeNodeMap[id] || [];
+ this.completeNodeMap[id].push(node);
+ })
+ }
}
}
}
@@ -516,6 +525,20 @@ class Flow {
return handled;
}
+ handleComplete(node,msg) {
+ if (this.completeNodeMap[node.id]) {
+ let toSend = msg;
+ this.completeNodeMap[node.id].forEach((completeNode,index) => {
+ toSend = redUtil.cloneMessage(msg);
+ completeNode.receive(toSend);
+ })
+ }
+ }
+
+ get asyncMessageDelivery() {
+ return asyncMessageDelivery
+ }
+
dump() {
console.log("==================")
console.log(this.TYPE, this.id);
@@ -562,6 +585,7 @@ function stopNode(node,removed) {
module.exports = {
init: function(runtime) {
nodeCloseTimeout = runtime.settings.nodeCloseTimeout || 15000;
+ asyncMessageDelivery = !runtime.settings.runtimeSyncDelivery
Log = runtime.log;
Subflow = require("./Subflow");
Subflow.init(runtime);
diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js
index ef9fbdab2..981ed7173 100644
--- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js
@@ -729,6 +729,10 @@ module.exports = {
updateFlow: updateFlow,
removeFlow: removeFlow,
disableFlow:null,
- enableFlow:null
+ enableFlow:null,
+ isDeliveryModeAsync: function() {
+ // If settings is null, this is likely being run by unit tests
+ return !settings || !settings.runtimeSyncDelivery
+ }
};
diff --git a/test/nodes/core/core/80-function_spec.js b/test/nodes/core/core/80-function_spec.js
index 01e40c464..295c45ee2 100644
--- a/test/nodes/core/core/80-function_spec.js
+++ b/test/nodes/core/core/80-function_spec.js
@@ -268,45 +268,50 @@ describe('function node', function() {
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
n1.receive({payload:"foo",topic: "bar"});
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "function";
- });
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().ERROR);
- msg.should.have.property('id', 'n1');
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'ReferenceError: retunr is not defined (line 2, col 1)');
- done();
- } catch(err) {
- done(err);
- }
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().ERROR);
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'ReferenceError: retunr is not defined (line 2, col 1)');
+ done();
+ } catch(err) {
+ done(err);
+ }
+ },50);
});
});
it('should handle node.on()', function(done) {
- var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.on('close',function(){node.log('closed')});"}];
+ var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.on('close',function(){ node.log('closed')});"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
n1.receive({payload:"foo",topic: "bar"});
- helper.getNode("n1").close();
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "function";
+ setTimeout(function() {
+ n1.close().then(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().INFO);
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'closed');
+ done();
+ } catch(err) {
+ done(err);
+ }
});
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().INFO);
- msg.should.have.property('id', 'n1');
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'closed');
- done();
- } catch(err) {
- done(err);
- }
+ },1500);
});
});
@@ -532,22 +537,24 @@ describe('function node', function() {
});
function checkCallbackError(name, done) {
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function (evt) {
- return evt[0].type == "function";
- });
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().ERROR);
- msg.should.have.property('id', name);
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'Error: Callback must be a function');
- done();
- }
- catch (e) {
- done(e);
- }
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function (evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().ERROR);
+ msg.should.have.property('id', name);
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'Error: Callback must be a function');
+ done();
+ }
+ catch (e) {
+ done(e);
+ }
+ },50);
}
it('should get persistable node context (w/o callback)', function(done) {
@@ -1267,12 +1274,12 @@ describe('function node', function() {
n1.receive({payload:"foo",topic: "bar"});
} else {
msg.should.have.property('payload', "hello");
+ delete process.env._TEST_FOO_;
done();
}
} catch(err) {
- done(err);
- } finally {
delete process.env._TEST_FOO_;
+ done(err);
}
});
n1.receive({payload:"foo",topic: "bar"});
@@ -1285,21 +1292,23 @@ describe('function node', function() {
helper.load(functionNode, flow, function () {
var n1 = helper.getNode("n1");
n1.receive({payload: "foo", topic: "bar"});
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function (evt) {
- return evt[0].type == "function";
- });
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().INFO);
- msg.should.have.property('id', 'n1');
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'test');
- done();
- } catch (err) {
- done(err);
- }
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function (evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().INFO);
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'test');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ },50);
});
});
it('should log a Debug Message', function (done) {
@@ -1307,21 +1316,23 @@ describe('function node', function() {
helper.load(functionNode, flow, function () {
var n1 = helper.getNode("n1");
n1.receive({payload: "foo", topic: "bar"});
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function (evt) {
- return evt[0].type == "function";
- });
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().DEBUG);
- msg.should.have.property('id', 'n1');
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'test');
- done();
- } catch (err) {
- done(err);
- }
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function (evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().DEBUG);
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'test');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ },50);
});
});
it('should log a Trace Message', function (done) {
@@ -1329,21 +1340,23 @@ describe('function node', function() {
helper.load(functionNode, flow, function () {
var n1 = helper.getNode("n1");
n1.receive({payload: "foo", topic: "bar"});
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function (evt) {
- return evt[0].type == "function";
- });
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().TRACE);
- msg.should.have.property('id', 'n1');
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'test');
- done();
- } catch (err) {
- done(err);
- }
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function (evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().TRACE);
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'test');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ },50);
});
});
it('should log a Warning Message', function (done) {
@@ -1351,21 +1364,23 @@ describe('function node', function() {
helper.load(functionNode, flow, function () {
var n1 = helper.getNode("n1");
n1.receive({payload: "foo", topic: "bar"});
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function (evt) {
- return evt[0].type == "function";
- });
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().WARN);
- msg.should.have.property('id', 'n1');
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'test');
- done();
- } catch (err) {
- done(err);
- }
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function (evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().WARN);
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'test');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ },50);
});
});
it('should log an Error Message', function (done) {
@@ -1373,21 +1388,23 @@ describe('function node', function() {
helper.load(functionNode, flow, function () {
var n1 = helper.getNode("n1");
n1.receive({payload: "foo", topic: "bar"});
- try {
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function (evt) {
- return evt[0].type == "function";
- });
- logEvents.should.have.length(1);
- var msg = logEvents[0][0];
- msg.should.have.property('level', helper.log().ERROR);
- msg.should.have.property('id', 'n1');
- msg.should.have.property('type', 'function');
- msg.should.have.property('msg', 'test');
- done();
- } catch (err) {
- done(err);
- }
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function (evt) {
+ return evt[0].type == "function";
+ });
+ logEvents.should.have.length(1);
+ var msg = logEvents[0][0];
+ msg.should.have.property('level', helper.log().ERROR);
+ msg.should.have.property('id', 'n1');
+ msg.should.have.property('type', 'function');
+ msg.should.have.property('msg', 'test');
+ done();
+ } catch (err) {
+ done(err);
+ }
+ },50);
});
});
});
diff --git a/test/nodes/core/logic/18-sort_spec.js b/test/nodes/core/logic/18-sort_spec.js
index ac8d7d11c..960a6d666 100644
--- a/test/nodes/core/logic/18-sort_spec.js
+++ b/test/nodes/core/logic/18-sort_spec.js
@@ -475,20 +475,21 @@ describe('SORT node', function() {
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
- setTimeout(function() {
- var logEvents = helper.log().args.filter(function (evt) {
- return evt[0].type == "sort";
- });
- var evt = logEvents[0][0];
- evt.should.have.property('id', "n1");
- evt.should.have.property('type', "sort");
- evt.should.have.property('msg', "sort.clear");
- done();
- }, 150);
var msg = { payload: 0,
parts: { id: "X", index: 0, count: 2} };
n1.receive(msg);
- n1.close();
+ setTimeout(function() {
+ n1.close().then(function() {
+ var logEvents = helper.log().args.filter(function (evt) {
+ return evt[0].type == "sort";
+ });
+ var evt = logEvents[0][0];
+ evt.should.have.property('id', "n1");
+ evt.should.have.property('type', "sort");
+ evt.should.have.property('msg', "sort.clear");
+ done();
+ });
+ }, 150);
});
});
diff --git a/test/nodes/core/parsers/70-HTML_spec.js b/test/nodes/core/parsers/70-HTML_spec.js
index e7e7ffa8b..3183a0539 100644
--- a/test/nodes/core/parsers/70-HTML_spec.js
+++ b/test/nodes/core/parsers/70-HTML_spec.js
@@ -21,7 +21,7 @@ var fs = require('fs-extra');
var htmlNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-HTML.js");
var helper = require("node-red-node-test-helper");
-describe('html node', function() {
+describe('HTML node', function() {
var resourcesDir = __dirname+ path.sep + ".." + path.sep + ".." + path.sep + ".." + path.sep + "resources" + path.sep;
var file = path.join(resourcesDir, "70-HTML-test-file.html");
@@ -228,16 +228,20 @@ describe('html node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.receive({payload:null,topic: "bar"});
- helper.log().called.should.be.true();
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "html";
- });
- logEvents.should.have.length(1);
- // Each logEvent is the array of args passed to the function.
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ setTimeout(function() {
+ try {
+ helper.log().called.should.be.true();
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "html";
+ });
+ logEvents.should.have.length(1);
+ // Each logEvent is the array of args passed to the function.
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ done();
+ } catch(err) { done(err) }
+ },50);
} catch(err) {
done(err);
}
diff --git a/test/nodes/core/parsers/70-JSON_spec.js b/test/nodes/core/parsers/70-JSON_spec.js
index 7effdfab7..2ec3304bb 100644
--- a/test/nodes/core/parsers/70-JSON_spec.js
+++ b/test/nodes/core/parsers/70-JSON_spec.js
@@ -148,14 +148,18 @@ describe('JSON node', function() {
var jn1 = helper.getNode("jn1");
var jn2 = helper.getNode("jn2");
jn1.receive({payload:'foo',topic: "bar"});
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "json";
- });
- logEvents.should.have.length(1);
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.should.startWith("Unexpected token o");
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ setTimeout(function() {
+ try {
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "json";
+ });
+ logEvents.should.have.length(1);
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].msg.should.startWith("Unexpected token o");
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ done();
+ } catch(err) { done(err) }
+ },20);
} catch(err) {
done(err);
}
@@ -378,14 +382,18 @@ describe('JSON node', function() {
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
var obj = {"number": "foo", "string": 3};
jn1.receive({payload:obj, schema:schema});
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "json";
- });
- logEvents.should.have.length(1);
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ setTimeout(function() {
+ try {
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "json";
+ });
+ logEvents.should.have.length(1);
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ done();
+ } catch(err) { done(err) }
+ },50);
} catch(err) {
done(err);
}
@@ -402,14 +410,18 @@ describe('JSON node', function() {
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
var obj = {"number": "foo", "string": 3};
jn1.receive({payload:obj, schema:schema});
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "json";
- });
- logEvents.should.have.length(1);
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ setTimeout(function() {
+ try {
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "json";
+ });
+ logEvents.should.have.length(1);
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ done();
+ } catch(err) { done(err) }
+ },50);
} catch(err) {
done(err);
}
@@ -426,14 +438,18 @@ describe('JSON node', function() {
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
var jsonString = '{"number":"Hello","string":3}';
jn1.receive({payload:jsonString, schema:schema});
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "json";
- });
- logEvents.should.have.length(1);
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ setTimeout(function() {
+ try {
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "json";
+ });
+ logEvents.should.have.length(1);
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ done();
+ } catch(err) { done(err) }
+ },50);
} catch(err) {
done(err);
}
@@ -450,14 +466,18 @@ describe('JSON node', function() {
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
var jsonString = '{"number":"Hello","string":3}';
jn1.receive({payload:jsonString, schema:schema});
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "json";
- });
- logEvents.should.have.length(1);
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ setTimeout(function() {
+ try {
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "json";
+ });
+ logEvents.should.have.length(1);
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ done();
+ } catch(err) { done(err) }
+ },50);
} catch(err) {
done(err);
}
@@ -474,14 +494,18 @@ describe('JSON node', function() {
var schema = "garbage";
var obj = {"number": "foo", "string": 3};
jn1.receive({payload:obj, schema:schema});
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "json";
- });
- logEvents.should.have.length(1);
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.should.equal("json.errors.schema-error-compile");
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ setTimeout(function() {
+ try {
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "json";
+ });
+ logEvents.should.have.length(1);
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].msg.should.equal("json.errors.schema-error-compile");
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ done();
+ } catch(err) { done(err) }
+ },50);
} catch(err) {
done(err);
}
diff --git a/test/nodes/core/parsers/70-YAML_spec.js b/test/nodes/core/parsers/70-YAML_spec.js
index 925a52922..3441e0946 100644
--- a/test/nodes/core/parsers/70-YAML_spec.js
+++ b/test/nodes/core/parsers/70-YAML_spec.js
@@ -130,14 +130,18 @@ describe('YAML node', function() {
var yn1 = helper.getNode("yn1");
var yn2 = helper.getNode("yn2");
yn1.receive({payload:'employees:\n-firstName: John\n- lastName: Smith\n',topic: "bar"});
- var logEvents = helper.log().args.filter(function(evt) {
- return evt[0].type == "yaml";
- });
- logEvents.should.have.length(1);
- logEvents[0][0].should.have.a.property('msg');
- logEvents[0][0].msg.should.startWith("end of the stream");
- logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
- done();
+ setTimeout(function() {
+ try {
+ var logEvents = helper.log().args.filter(function(evt) {
+ return evt[0].type == "yaml";
+ });
+ logEvents.should.have.length(1);
+ logEvents[0][0].should.have.a.property('msg');
+ logEvents[0][0].msg.should.startWith("end of the stream");
+ logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
+ done();
+ } catch(err) { done(err) }
+ },50);
} catch(err) {
done(err);
}
diff --git a/test/unit/@node-red/runtime/lib/nodes/Node_spec.js b/test/unit/@node-red/runtime/lib/nodes/Node_spec.js
index 7e5aed1b0..8267411fb 100644
--- a/test/unit/@node-red/runtime/lib/nodes/Node_spec.js
+++ b/test/unit/@node-red/runtime/lib/nodes/Node_spec.js
@@ -156,10 +156,50 @@ describe('Node', function() {
throw new Error("test error");
});
n.receive(message);
- n.error.called.should.be.true();
- n.error.firstCall.args[1].should.equal(message);
- done();
+ setTimeout(function() {
+ n.error.called.should.be.true();
+ n.error.firstCall.args[1].should.equal(message);
+ done();
+ },50);
+ });
+ it('calls parent flow handleComplete when callback provided', function(done) {
+ var n = new RedNode({id:'123',type:'abc', _flow: {
+ handleComplete: function(node,msg) {
+ try {
+ msg.should.deepEqual(message);
+ done();
+ } catch(err) {
+ done(err);
+ }
+ }
+ }});
+
+ var message = {payload:"hello world"};
+ n.on('input',function(msg, nodeDone) {
+ nodeDone();
+ });
+ n.receive(message);
+ });
+ it('logs error if callback provides error', function(done) {
+ var n = new RedNode({id:'123',type:'abc'});
+ sinon.stub(n,"error",function(err,msg) {});
+
+ var message = {payload:"hello world"};
+ n.on('input',function(msg, nodeDone) {
+ nodeDone(new Error("test error"));
+ setTimeout(function() {
+ try {
+ n.error.called.should.be.true();
+ n.error.firstCall.args[0].toString().should.equal("Error: test error");
+ n.error.firstCall.args[1].should.equal(message);
+ done();
+ } catch(err) {
+ done(err);
+ }
+ },50);
+ });
+ n.receive(message);
});
});
@@ -172,15 +212,45 @@ describe('Node', function() {
var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]});
var n2 = new RedNode({_flow:flow,id:'n2',type:'abc'});
var message = {payload:"hello world"};
-
+ var messageReceived = false;
n2.on('input',function(msg) {
// msg equals message, and is not a new copy
+ messageReceived = true;
should.deepEqual(msg,message);
should.strictEqual(msg,message);
done();
});
-
n1.send(message);
+ messageReceived.should.be.false();
+ });
+
+ it('emits a single message - synchronous mode', function(done) {
+ var flow = {
+ getNode: (id) => { return {'n1':n1,'n2':n2}[id]},
+ asyncMessageDelivery: false
+ };
+ var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]});
+ var n2 = new RedNode({_flow:flow,id:'n2',type:'abc'});
+ var message = {payload:"hello world"};
+ var messageReceived = false;
+ var notSyncErr;
+ n2.on('input',function(msg) {
+ try {
+ // msg equals message, and is not a new copy
+ messageReceived = true;
+ should.deepEqual(msg,message);
+ should.strictEqual(msg,message);
+ done(notSyncErr);
+ } catch(err) {
+ done(err);
+ }
+ });
+ n1.send(message);
+ try {
+ messageReceived.should.be.true();
+ } catch(err) {
+ notSyncErr = err;
+ }
});
it('emits multiple messages on a single output', function(done) {
@@ -356,12 +426,13 @@ describe('Node', function() {
it("logs the uuid for all messages sent", function(done) {
var logHandler = {
+ msgIds:[],
messagesSent: 0,
emit: function(event, msg) {
if (msg.event == "node.abc.send" && msg.level == Log.METRIC) {
this.messagesSent++;
+ this.msgIds.push(msg.msgid);
(typeof msg.msgid).should.not.be.equal("undefined");
- done();
}
}
};
@@ -375,6 +446,17 @@ describe('Node', function() {
var receiver1 = new RedNode({_flow:flow,id:'n2',type:'abc'});
var receiver2 = new RedNode({_flow:flow,id:'n3',type:'abc'});
sender.send({"some": "message"});
+ setTimeout(function() {
+ try {
+ logHandler.messagesSent.should.equal(1);
+ should.exist(logHandler.msgIds[0])
+ Log.removeHandler(logHandler);
+ done();
+ } catch(err) {
+ Log.removeHandler(logHandler);
+ done(err);
+ }
+ },50)
})
});
diff --git a/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js b/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js
index e7430a6c2..c1f7b0f3e 100644
--- a/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js
+++ b/test/unit/@node-red/runtime/lib/nodes/flows/Flow_spec.js
@@ -122,6 +122,7 @@ describe('Flow', function() {
currentNodes[node.id] = node;
this.on('input',function(msg) {
node.handled++;
+ msg.handled = node.handled;
node.messages.push(msg);
node.send(msg);
});
@@ -136,12 +137,42 @@ describe('Flow', function() {
}
util.inherits(TestAsyncNode,Node);
+ var TestDoneNode = function(n) {
+ Node.call(this,n);
+ var node = this;
+ this.scope = n.scope;
+ this.uncaught = n.uncaught;
+ this.foo = n.foo;
+ this.handled = 0;
+ this.messages = [];
+ this.stopped = false;
+ this.closeDelay = n.closeDelay || 50;
+ currentNodes[node.id] = node;
+ this.on('input',function(msg, done) {
+ node.handled++;
+ node.messages.push(msg);
+ node.send(msg);
+ done();
+ });
+ this.on('close',function(done) {
+ setTimeout(function() {
+ node.stopped = true;
+ stoppedNodes[node.id] = node;
+ delete currentNodes[node.id];
+ done();
+ },node.closeDelay);
+ });
+ }
+ util.inherits(TestDoneNode,Node);
+
before(function() {
getType = sinon.stub(typeRegistry,"get",function(type) {
if (type=="test") {
return TestNode;
} else if (type=="testError"){
return TestErrorNode;
+ } else if (type=="testDone"){
+ return TestDoneNode;
} else {
return TestAsyncNode;
}
@@ -189,28 +220,28 @@ describe('Flow', function() {
currentNodes["2"].should.have.a.property("handled",0);
currentNodes["3"].should.have.a.property("handled",0);
+ currentNodes["3"].on("input", function() {
+ currentNodes["1"].should.have.a.property("handled",1);
+ currentNodes["2"].should.have.a.property("handled",1);
+ currentNodes["3"].should.have.a.property("handled",1);
- 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() {
- try {
- 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();
- } catch(err) {
- done(err);
- }
+ flow.stop().then(function() {
+ try {
+ 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();
+ } catch(err) {
+ done(err);
+ }
+ });
});
+ currentNodes["1"].receive({payload:"test"});
});
it("instantiates config nodes in the right order",function(done) {
@@ -350,25 +381,27 @@ describe('Flow', function() {
currentNodes["1"].receive({payload:"test"});
- currentNodes["1"].should.have.a.property("handled",1);
- // Message doesn't reach 3 as 2 is disabled
- currentNodes["3"].should.have.a.property("handled",0);
+ setTimeout(function() {
+ currentNodes["1"].should.have.a.property("handled",1);
+ // Message doesn't reach 3 as 2 is disabled
+ currentNodes["3"].should.have.a.property("handled",0);
- flow.stop().then(function() {
- try {
- 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.not.have.a.property("2");
- stoppedNodes.should.have.a.property("3");
- stoppedNodes.should.have.a.property("4");
- done();
- } catch(err) {
- done(err);
- }
- });
+ flow.stop().then(function() {
+ try {
+ 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.not.have.a.property("2");
+ stoppedNodes.should.have.a.property("3");
+ stoppedNodes.should.have.a.property("4");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ },50);
});
});
@@ -551,31 +584,34 @@ describe('Flow', function() {
flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status",random:"otherProperty"});
- currentNodes["sn"].should.have.a.property("handled",1);
- var statusMessage = currentNodes["sn"].messages[0];
+ setTimeout(function() {
- 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["sn"].should.have.a.property("handled",1);
+ var statusMessage = currentNodes["sn"].messages[0];
- 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");
- statusMessage.should.have.a.property("status");
- statusMessage.status.should.have.a.property("text","my-status");
- statusMessage.status.should.have.a.property("random","otherProperty");
- 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("random","otherProperty");
+ 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();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50)
});
it("passes a status event to the adjacent scoped status node ",function(done) {
var config = flowUtils.parseConfig([
@@ -596,21 +632,23 @@ describe('Flow', function() {
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];
+ setTimeout(function() {
+ 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");
+ 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();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
});
@@ -636,33 +674,35 @@ describe('Flow', function() {
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];
+ setTimeout(function() {
+ 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");
+ 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];
+ 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");
+ 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");
- // Node sn3 has uncaught:true - so should not get called
- currentNodes["sn3"].should.have.a.property("handled",0);
+ // Node sn3 has uncaught:true - so should not get called
+ currentNodes["sn3"].should.have.a.property("handled",0);
- flow.stop().then(function() {
- done();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
it("passes an error event to the adjacent scoped catch node ",function(done) {
var config = flowUtils.parseConfig([
@@ -684,39 +724,42 @@ describe('Flow', function() {
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];
+ setTimeout(function() {
+ 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");
+ 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");
- // Node sn3/4 have uncaught:true - so should not get called
- currentNodes["sn3"].should.have.a.property("handled",0);
- currentNodes["sn4"].should.have.a.property("handled",0);
+ // Node sn3/4 have uncaught:true - so should not get called
+ currentNodes["sn3"].should.have.a.property("handled",0);
+ currentNodes["sn4"].should.have.a.property("handled",0);
- // Inject error that sn1/2 will ignore - so should get picked up by sn3
- flow.handleError(config.flows["t1"].nodes["3"],"my-error-2",{a:"foo-2"});
+ // Inject error that sn1/2 will ignore - so should get picked up by sn3
+ flow.handleError(config.flows["t1"].nodes["3"],"my-error-2",{a:"foo-2"});
+ setTimeout(function() {
+ currentNodes["sn"].should.have.a.property("handled",0);
+ currentNodes["sn2"].should.have.a.property("handled",1);
+ currentNodes["sn3"].should.have.a.property("handled",1);
+ currentNodes["sn4"].should.have.a.property("handled",1);
- currentNodes["sn"].should.have.a.property("handled",0);
- currentNodes["sn2"].should.have.a.property("handled",1);
- currentNodes["sn3"].should.have.a.property("handled",1);
- currentNodes["sn4"].should.have.a.property("handled",1);
+ statusMessage = currentNodes["sn3"].messages[0];
+ statusMessage.should.have.a.property("error");
+ statusMessage.error.should.have.a.property("message","my-error-2");
+ statusMessage.error.should.have.a.property("source");
+ statusMessage.error.source.should.have.a.property("id","3");
+ statusMessage.error.source.should.have.a.property("type","test");
- statusMessage = currentNodes["sn3"].messages[0];
- statusMessage.should.have.a.property("error");
- statusMessage.error.should.have.a.property("message","my-error-2");
- statusMessage.error.should.have.a.property("source");
- statusMessage.error.source.should.have.a.property("id","3");
- statusMessage.error.source.should.have.a.property("type","test");
-
- flow.stop().then(function() {
- done();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
+ },50);
});
it("moves any existing error object sideways",function(done){
var config = flowUtils.parseConfig([
@@ -733,22 +776,54 @@ describe('Flow', function() {
var activeNodes = flow.getActiveNodes();
flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo",error:"existing"});
+ setTimeout(function() {
+ currentNodes["sn"].should.have.a.property("handled",1);
+ var statusMessage = currentNodes["sn"].messages[0];
- 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");
- 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();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
it("prevents an error looping more than 10 times",function(){});
});
+
+ describe("#handleComplete",function() {
+ it("passes a complete event to the adjacent Complete node",function(done) {
+ var config = flowUtils.parseConfig([
+ {id:"t1",type:"tab"},
+ {id:"1",x:10,y:10,z:"t1",type:"testDone",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:"testDone",foo:"a",wires:[]},
+ {id:"cn",x:10,y:10,z:"t1",type:"complete",scope:["1","3"],foo:"a",wires:[]}
+ ]);
+ var flow = Flow.create({},config,config.flows["t1"]);
+
+ flow.start();
+
+ var activeNodes = flow.getActiveNodes();
+ Object.keys(activeNodes).should.have.length(4);
+
+ var msg = {payload: "hello world"}
+ var n1 = currentNodes["1"].receive(msg);
+ setTimeout(function() {
+ currentNodes["cn"].should.have.a.property("handled",2);
+ currentNodes["cn"].messages[0].should.have.a.property("handled",1);
+ currentNodes["cn"].messages[1].should.have.a.property("handled",2);
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
+ });
+ });
+
+
});
diff --git a/test/unit/@node-red/runtime/lib/nodes/flows/Subflow_spec.js b/test/unit/@node-red/runtime/lib/nodes/flows/Subflow_spec.js
index d6cab9732..f92a01328 100644
--- a/test/unit/@node-red/runtime/lib/nodes/flows/Subflow_spec.js
+++ b/test/unit/@node-red/runtime/lib/nodes/flows/Subflow_spec.js
@@ -271,32 +271,34 @@ describe('Subflow', function() {
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);
+ setTimeout(function() {
+ 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() {
- Object.keys(currentNodes).should.have.length(0);
- Object.keys(stoppedNodes).should.have.length(6);
+ flow.stop().then(function() {
+ Object.keys(currentNodes).should.have.length(0);
+ Object.keys(stoppedNodes).should.have.length(6);
- // 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();
- });
+ // 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();
+ });
+ },50);
});
it("instantiates a subflow inside a subflow and stops it",function(done) {
var config = flowUtils.parseConfig([
@@ -322,17 +324,14 @@ describe('Subflow', function() {
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();
- });
+ setTimeout(function() {
+ 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();
+ });
+ },50);
});
it("rewires a subflow node on update/start",function(done){
@@ -369,27 +368,31 @@ describe('Subflow', function() {
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);
+ setTimeout(function() {
+ 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)
+ flow.update(newConfig,newConfig.flows["t1"]);
+ flow.start(diff)
- currentNodes["1"].receive({payload:"test2"});
+ currentNodes["1"].receive({payload:"test2"});
+ setTimeout(function() {
- 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);
+ 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();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
+ },50);
});
});
describe('#stop', function() {
@@ -436,20 +439,20 @@ describe('Subflow', function() {
var activeNodes = flow.getActiveNodes();
activeNodes["1"].receive({payload:"test"});
+ setTimeout(function() {
+ currentNodes["sn"].should.have.a.property("handled",1);
+ var statusMessage = currentNodes["sn"].messages[0];
- 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 status");
+ statusMessage.status.should.have.a.property("source");
+ statusMessage.status.source.should.have.a.property("type","testStatus");
+ statusMessage.status.source.should.have.a.property("name","test-status-node");
- statusMessage.should.have.a.property("status");
- statusMessage.status.should.have.a.property("text","test status");
- statusMessage.status.should.have.a.property("source");
- statusMessage.status.source.should.have.a.property("type","testStatus");
- statusMessage.status.source.should.have.a.property("name","test-status-node");
-
- flow.stop().then(function() {
-
- done();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) {
var config = flowUtils.parseConfig([
@@ -472,21 +475,23 @@ describe('Subflow', function() {
activeNodes["1"].receive({payload:"test"});
- parentFlowStatusCalled.should.be.false();
+ setTimeout(function() {
+ parentFlowStatusCalled.should.be.false();
- currentNodes["sn"].should.have.a.property("handled",1);
- var statusMessage = currentNodes["sn"].messages[0];
+ 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 status");
- statusMessage.status.should.have.a.property("source");
- statusMessage.status.source.should.have.a.property("type","testStatus");
- statusMessage.status.source.should.have.a.property("name","test-status-node");
+ statusMessage.should.have.a.property("status");
+ statusMessage.status.should.have.a.property("text","test status");
+ statusMessage.status.should.have.a.property("source");
+ statusMessage.status.source.should.have.a.property("type","testStatus");
+ statusMessage.status.source.should.have.a.property("name","test-status-node");
- flow.stop().then(function() {
+ flow.stop().then(function() {
- done();
- });
+ done();
+ });
+ },50);
});
});
@@ -517,19 +522,21 @@ describe('Subflow', function() {
activeNodes["1"].receive({payload:"test-payload"});
- currentNodes["sn"].should.have.a.property("handled",1);
- var statusMessage = currentNodes["sn"].messages[0];
+ setTimeout(function() {
+ 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");
+ 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() {
+ flow.stop().then(function() {
- done();
- });
+ done();
+ });
+ },50);
});
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([
@@ -557,19 +564,21 @@ describe('Subflow', function() {
activeNodes["1"].receive({payload:{text:"payload-obj"}});
- currentNodes["sn"].should.have.a.property("handled",1);
- var statusMessage = currentNodes["sn"].messages[0];
+ setTimeout(function() {
+ 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");
+ 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() {
+ flow.stop().then(function() {
- done();
- });
+ done();
+ });
+ },50);
});
it("emits a status event when a message is passed to a subflow-status node - msg.status", function(done) {
var config = flowUtils.parseConfig([
@@ -597,19 +606,21 @@ describe('Subflow', function() {
activeNodes["1"].receive({status:{text:"status-obj"}});
- currentNodes["sn"].should.have.a.property("handled",1);
- var statusMessage = currentNodes["sn"].messages[0];
+ setTimeout(function() {
+ 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");
+ 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() {
+ flow.stop().then(function() {
- done();
- });
+ done();
+ });
+ },50);
});
it("does not emit a regular status event if it contains a subflow-status node", function(done) {
var config = flowUtils.parseConfig([
@@ -666,18 +677,20 @@ describe('Subflow', function() {
activeNodes["1"].receive({payload:"test"});
- currentNodes["sn"].should.have.a.property("handled",1);
- var statusMessage = currentNodes["sn"].messages[0];
+ setTimeout(function() {
+ 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","test error");
- statusMessage.error.should.have.a.property("source");
- statusMessage.error.source.should.have.a.property("type","testError");
- statusMessage.error.source.should.have.a.property("name","test-error-node");
+ statusMessage.should.have.a.property("error");
+ statusMessage.error.should.have.a.property("message","test error");
+ statusMessage.error.should.have.a.property("source");
+ statusMessage.error.source.should.have.a.property("type","testError");
+ statusMessage.error.source.should.have.a.property("name","test-error-node");
- flow.stop().then(function() {
- done();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) {
var config = flowUtils.parseConfig([
@@ -699,20 +712,22 @@ describe('Subflow', function() {
activeNodes["1"].receive({payload:"test"});
- parentFlowErrorCalled.should.be.false();
+ setTimeout(function() {
+ parentFlowErrorCalled.should.be.false();
- currentNodes["sn"].should.have.a.property("handled",1);
- var statusMessage = currentNodes["sn"].messages[0];
+ 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","test error");
- statusMessage.error.should.have.a.property("source");
- statusMessage.error.source.should.have.a.property("type","testError");
- statusMessage.error.source.should.have.a.property("name","test-error-node");
+ statusMessage.should.have.a.property("error");
+ statusMessage.error.should.have.a.property("message","test error");
+ statusMessage.error.should.have.a.property("source");
+ statusMessage.error.source.should.have.a.property("type","testError");
+ statusMessage.error.source.should.have.a.property("name","test-error-node");
- flow.stop().then(function() {
- done();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
});
@@ -756,11 +771,13 @@ describe('Subflow', function() {
process.env["__KEY__"] = "__VAL__";
currentNodes["1"].receive({payload: "test"});
- currentNodes["3"].should.have.a.property("received", "__VAL__");
-
- flow.stop().then(function() {
- done();
- });
+ setTimeout(function() {
+ currentNodes["3"].should.have.a.property("received", "__VAL__");
+
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
it("can access subflow env var", function(done) {
@@ -792,13 +809,15 @@ describe('Subflow', function() {
}
process.env["__KEY__"] = "__VAL0__";
setEnv(testenv_node, "__KEY__", "__VAL1__");
-
- currentNodes["1"].receive({payload: "test"});
- currentNodes["3"].should.have.a.property("received", "__VAL1__");
- flow.stop().then(function() {
- done();
- });
+ currentNodes["1"].receive({payload: "test"});
+ setTimeout(function() {
+ currentNodes["3"].should.have.a.property("received", "__VAL1__");
+
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
});
it("can access nested subflow env var", function(done) {
@@ -840,21 +859,27 @@ describe('Subflow', function() {
process.env["__KEY__"] = "__VAL0__";
currentNodes["1"].receive({payload: "test"});
- currentNodes["3"].should.have.a.property("received", "__VAL0__");
+ setTimeout(function() {
+ currentNodes["3"].should.have.a.property("received", "__VAL0__");
- setEnv(node_sf1_1, "__KEY__", "__VAL1__");
- currentNodes["1"].receive({payload: "test"});
- currentNodes["3"].should.have.a.property("received", "__VAL1__");
+ setEnv(node_sf1_1, "__KEY__", "__VAL1__");
+ currentNodes["1"].receive({payload: "test"});
+ setTimeout(function() {
+ currentNodes["3"].should.have.a.property("received", "__VAL1__");
- setEnv(node_sf2_1, "__KEY__", "__VAL2__");
- currentNodes["1"].receive({payload: "test"});
- currentNodes["3"].should.have.a.property("received", "__VAL2__");
+ setEnv(node_sf2_1, "__KEY__", "__VAL2__");
+ currentNodes["1"].receive({payload: "test"});
+ setTimeout(function() {
+ currentNodes["3"].should.have.a.property("received", "__VAL2__");
- flow.stop().then(function() {
- done();
- });
+ flow.stop().then(function() {
+ done();
+ });
+ },50);
+ },50);
+ },50);
});
-
+
});
-
+
});