mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
1199 lines
49 KiB
JavaScript
1199 lines
49 KiB
JavaScript
/**
|
|
* 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.
|
|
**/
|
|
|
|
var should = require("should");
|
|
var sinon = require("sinon");
|
|
var helper = require("node-red-node-test-helper");
|
|
var triggerNode = require("nr-test-utils").require("@node-red/nodes/core/function/89-trigger.js");
|
|
var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context");
|
|
var RED = require("nr-test-utils").require("node-red/lib/red");
|
|
|
|
describe('trigger node', function() {
|
|
|
|
beforeEach(function(done) {
|
|
helper.startServer(done);
|
|
});
|
|
|
|
function initContext(done) {
|
|
Context.init({
|
|
contextStorage: {
|
|
memory0: {
|
|
module: "memory"
|
|
},
|
|
memory1: {
|
|
module: "memory"
|
|
},
|
|
memory2: {
|
|
module: "memory"
|
|
}
|
|
}
|
|
});
|
|
Context.load().then(function () {
|
|
done();
|
|
});
|
|
}
|
|
|
|
afterEach(function(done) {
|
|
helper.unload().then(function () {
|
|
return Context.clean({allNodes: {}});
|
|
}).then(function () {
|
|
return Context.close();
|
|
}).then(function () {
|
|
helper.stopServer(done);
|
|
});
|
|
});
|
|
|
|
it("should be loaded with correct defaults", function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", "wires":[[]]}];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
n1.should.have.property('name', 'triggerNode');
|
|
n1.should.have.property('op1', '1');
|
|
n1.should.have.property('op2', '0');
|
|
n1.should.have.property('op1type', 'str');
|
|
n1.should.have.property('op2type', 'str');
|
|
n1.should.have.property('extend', "false");
|
|
n1.should.have.property('units', 'ms');
|
|
n1.should.have.property('duration', 250);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should be able to set delay in seconds", function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"s", duration:"1", "wires":[[]]}];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
n1.should.have.property('duration', 1000);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should be able to set delay in minutes", function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"min", duration:"1", "wires":[[]]}];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
n1.should.have.property('duration', 60000);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("should be able to set delay in hours", function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"hr", duration:"1", "wires":[[]]}];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
n1.should.have.property('duration', 3600000);
|
|
done();
|
|
});
|
|
});
|
|
|
|
function basicTest(type, val, rval) {
|
|
it('should output 1st value when triggered ('+type+')', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:val, op1type:type, op2:"", op2type:"null", duration:"20", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
process.env[val] = rval;
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (rval) {
|
|
msg.should.have.property("payload");
|
|
should.deepEqual(msg.payload, rval);
|
|
}
|
|
else {
|
|
msg.should.have.property("payload", val);
|
|
}
|
|
delete process.env[val];
|
|
done();
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:null});
|
|
});
|
|
});
|
|
|
|
it('should output 2st value when triggered ('+type+')', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"str", op2:val, op2type:type, duration:"20", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
process.env[val] = rval;
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.property("payload", "foo");
|
|
c++;
|
|
}
|
|
else {
|
|
if (rval) {
|
|
msg.should.have.property("payload");
|
|
should.deepEqual(msg.payload, rval);
|
|
}
|
|
else {
|
|
msg.should.have.property("payload", val);
|
|
}
|
|
delete process.env[val];
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:null});
|
|
});
|
|
});
|
|
}
|
|
|
|
basicTest("num", 10);
|
|
basicTest("str", "10");
|
|
basicTest("bool", true);
|
|
var val_json = '{ "x":"vx", "y":"vy", "z":"vz" }';
|
|
basicTest("json", val_json, JSON.parse(val_json));
|
|
var val_buf = "[1,2,3,4,5]";
|
|
basicTest("bin", val_buf, Buffer.from(JSON.parse(val_buf)));
|
|
basicTest("env", "NR-TEST", "env-val");
|
|
|
|
it('should output 1 then 0 when triggered (default)', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"20", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", '1');
|
|
c+=1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", '0');
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:null});
|
|
});
|
|
});
|
|
|
|
it('should ignore any other inputs while triggered if extend is false', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"50",wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
var errored = false;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", '1');
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", '0');
|
|
}
|
|
c+=1;
|
|
}catch(err) {
|
|
errored = true;
|
|
done(err);
|
|
}
|
|
});
|
|
setTimeout( function() {
|
|
if (!errored) {
|
|
try {
|
|
c.should.equal(2);
|
|
done();
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
}
|
|
},100);
|
|
n1.emit("input", {payload:null});
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:null});
|
|
},10);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:null});
|
|
},30);
|
|
});
|
|
});
|
|
|
|
it('should ignore msg.delay if overrideDelay not set', function(done) {
|
|
var flow = [
|
|
{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"50",wires:[["n2"]] },
|
|
{id:"n2", type:"helper"}
|
|
];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
var firstTime;
|
|
n2.on("input", function(msg) {
|
|
if (c === 0) {
|
|
firstTime = Date.now();
|
|
} else if (c === 1) {
|
|
try {
|
|
var delta = Date.now() - firstTime;
|
|
delta.should.be.greaterThan(30);
|
|
delta.should.be.lessThan(100);
|
|
done();
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
}
|
|
c++;
|
|
});
|
|
n1.emit("input", {payload:null, delay: 300});
|
|
});
|
|
});
|
|
|
|
it('should use msg.delay if overrideDelay is set', function(done) {
|
|
var flow = [
|
|
{"id":"n1", "type":"trigger", "name":"triggerNode", overrideDelay: true, duration:"50",wires:[["n2"]] },
|
|
{id:"n2", type:"helper"}
|
|
];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
var firstTime;
|
|
n2.on("input", function(msg) {
|
|
if (c === 0) {
|
|
firstTime = Date.now();
|
|
} else if (c === 1) {
|
|
try {
|
|
var delta = Date.now() - firstTime;
|
|
delta.should.be.greaterThan(270);
|
|
delta.should.be.lessThan(380);
|
|
done();
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
}
|
|
c++;
|
|
});
|
|
n1.emit("input", {payload:null, delay: 300});
|
|
});
|
|
});
|
|
|
|
|
|
it('should handle true and false as strings and delay of 0', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"true",op1type:"val",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", true);
|
|
c+=1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", false);
|
|
done();
|
|
}
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
});
|
|
n1.emit("input", {payload:null});
|
|
});
|
|
});
|
|
|
|
it('should handle multiple topics as one if not asked to handle', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"all", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
if (c === 1) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("topic", "A");
|
|
}
|
|
else if (c === 2) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("topic", "A");
|
|
done();
|
|
}
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
});
|
|
n1.emit("input", {payload:1,topic:"A"});
|
|
n1.emit("input", {payload:2,topic:"B"});
|
|
n1.emit("input", {payload:3,topic:"C"});
|
|
});
|
|
});
|
|
|
|
it('should handle multiple topics individually if asked to do so', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
if (c === 1) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("topic", "A");
|
|
}
|
|
else if (c === 2) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("topic", "B");
|
|
}
|
|
else if (c === 3) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("topic", "C");
|
|
}
|
|
else if (c === 4) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("topic", "A");
|
|
}
|
|
else if (c === 5) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("topic", "B");
|
|
}
|
|
else if (c === 6) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("topic", "C");
|
|
done();
|
|
}
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
});
|
|
n1.emit("input", {payload:1,topic:"A"});
|
|
n1.emit("input", {payload:2,topic:"B"});
|
|
n1.emit("input", {payload:3,topic:"C"});
|
|
});
|
|
});
|
|
|
|
it('should handle multiple topics individually, and extend one, if asked to do so', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", extend:"true", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
if (c === 1) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("topic", "A");
|
|
}
|
|
else if (c === 2) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("topic", "B");
|
|
}
|
|
else if (c === 3) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("topic", "C");
|
|
}
|
|
else if (c === 4) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("topic", "A");
|
|
}
|
|
else if (c === 5) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("topic", "C");
|
|
}
|
|
else if (c === 6) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("topic", "B");
|
|
done();
|
|
}
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
});
|
|
n1.emit("input", {payload:1,topic:"A"});
|
|
n1.emit("input", {payload:2,topic:"B"});
|
|
n1.emit("input", {payload:3,topic:"C"});
|
|
setTimeout( function() { n1.emit("input", {payload:2,topic:"B"})}, 20 );
|
|
});
|
|
});
|
|
|
|
it('should handle multiple other properties individually if asked to do so', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", topic:"foo", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
if (c === 1) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("foo", "A");
|
|
}
|
|
else if (c === 2) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("foo", "B");
|
|
}
|
|
else if (c === 3) {
|
|
msg.should.have.a.property("payload", 1);
|
|
msg.should.have.a.property("foo", "C");
|
|
}
|
|
else if (c === 4) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("foo", "A");
|
|
}
|
|
else if (c === 5) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("foo", "B");
|
|
}
|
|
else if (c === 6) {
|
|
msg.should.have.a.property("payload", 0);
|
|
msg.should.have.a.property("foo", "C");
|
|
done();
|
|
}
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
});
|
|
n1.emit("input", {payload:1,foo:"A"});
|
|
n1.emit("input", {payload:2,foo:"B"});
|
|
n1.emit("input", {payload:3,foo:"C"});
|
|
});
|
|
});
|
|
|
|
it('should be able to return things from flow and global context variables', function(done) {
|
|
var spy = sinon.stub(RED.util, 'evaluateNodeProperty',
|
|
function(arg1, arg2, arg3, arg4, arg5) { if (arg5) { arg5(null, arg1) } else { return arg1; } }
|
|
);
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"flow", op2:"bar", op2type:"global", duration:"20", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "foo");
|
|
c+=1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "bar");
|
|
RED.util.evaluateNodeProperty.restore();
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { RED.util.evaluateNodeProperty.restore(); done(err); }
|
|
});
|
|
n1.emit("input", {payload:null});
|
|
});
|
|
});
|
|
|
|
it('should be able to return things from persistable flow and global context variables', function (done) {
|
|
var flow = [{"id": "n1", "type": "trigger", "name": "triggerNode", "op1": "#:(memory1)::foo", "op1type": "flow",
|
|
"op2": "#:(memory1)::bar", "op2type": "global", "duration": "20", "wires": [["n2"]], "z": "flow" },
|
|
{"id": "n2", "type": "helper"}];
|
|
helper.load(triggerNode, flow, function () {
|
|
initContext(function () {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function (msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "foo");
|
|
c += 1;
|
|
} else {
|
|
msg.should.have.a.property("payload", "bar");
|
|
done();
|
|
}
|
|
} catch (err) {
|
|
done(err);
|
|
}
|
|
});
|
|
var context = n1.context();
|
|
var flow = context.flow;
|
|
var global = context.global;
|
|
flow.set("foo", "foo", "memory1", function (err) {
|
|
global.set("bar", "bar", "memory1", function (err) {
|
|
n1.emit("input", { payload: null });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should be able to return things from multiple persistable global context variables', function (done) {
|
|
var flow = [{"id": "n1", "z": "flow", "type": "trigger",
|
|
"duration": "20", "wires": [["n2"]],
|
|
"op1": "#:(memory1)::val", "op1type": "global",
|
|
"op2": "#:(memory2)::val", "op2type": "global"
|
|
},
|
|
{"id": "n2", "type": "helper"}];
|
|
helper.load(triggerNode, flow, function () {
|
|
initContext(function () {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var count = 0;
|
|
n2.on("input", function (msg) {
|
|
try {
|
|
if (count === 0) {
|
|
msg.should.have.a.property("payload", "foo");
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "bar");
|
|
}
|
|
count++;
|
|
if (count === 1) {
|
|
done();
|
|
}
|
|
}
|
|
catch (err) {
|
|
done(err);
|
|
}
|
|
});
|
|
var global = n1.context().global;
|
|
global.set("val", "foo", "memory1", function (err) {
|
|
global.set("val", "bar", "memory2", function (err) {
|
|
n1.emit("input", { payload: null });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should be able to return things from multiple persistable flow context variables', function (done) {
|
|
var flow = [{"id": "n1", "z": "flow", "type": "trigger",
|
|
"duration": "20", "wires": [["n2"]],
|
|
"op1": "#:(memory1)::val", "op1type": "flow",
|
|
"op2": "#:(memory2)::val", "op2type": "flow"
|
|
},
|
|
{"id": "n2", "type": "helper"}];
|
|
helper.load(triggerNode, flow, function () {
|
|
initContext(function () {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var count = 0;
|
|
n2.on("input", function (msg) {
|
|
try {
|
|
if (count === 0) {
|
|
msg.should.have.a.property("payload", "foo");
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "bar");
|
|
}
|
|
count++;
|
|
if (count === 1) {
|
|
done();
|
|
}
|
|
}
|
|
catch (err) {
|
|
done(err);
|
|
}
|
|
});
|
|
var flow = n1.context().flow;
|
|
flow.set("val", "foo", "memory1", function (err) {
|
|
flow.set("val", "bar", "memory2", function (err) {
|
|
n1.emit("input", { payload: null });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should be able to return things from multiple persistable flow & global context variables', function (done) {
|
|
var flow = [{"id": "n1", "z": "flow", "type": "trigger",
|
|
"duration": "20", "wires": [["n2"]],
|
|
"op1": "#:(memory1)::val", "op1type": "flow",
|
|
"op2": "#:(memory2)::val", "op2type": "global"
|
|
},
|
|
{"id": "n2", "type": "helper"}];
|
|
helper.load(triggerNode, flow, function () {
|
|
initContext(function () {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var count = 0;
|
|
n2.on("input", function (msg) {
|
|
try {
|
|
if (count === 0) {
|
|
msg.should.have.a.property("payload", "foo");
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "bar");
|
|
}
|
|
count++;
|
|
if (count === 1) {
|
|
done();
|
|
}
|
|
}
|
|
catch (err) {
|
|
done(err);
|
|
}
|
|
});
|
|
var context = n1.context();
|
|
var flow = context.flow;
|
|
var global = context.flow;
|
|
flow.set("val", "foo", "memory1", function (err) {
|
|
global.set("val", "bar", "memory2", function (err) {
|
|
n1.emit("input", { payload: null });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should be able to not output anything on first trigger', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"nul", op1:"true",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
msg.should.have.a.property("payload", false);
|
|
done();
|
|
} catch(err) {
|
|
done(err);
|
|
}
|
|
});
|
|
n1.emit("input", {payload:null});
|
|
});
|
|
});
|
|
|
|
it('should be able to not output anything on second edge', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op2type:"nul", op1:"true",op1type:"val", op2:"false", duration:"30", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
msg.should.have.a.property("payload", true);
|
|
c += 1;
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
setTimeout( function() {
|
|
c.should.equal(1); // should only have had one output.
|
|
done();
|
|
},90);
|
|
n1.emit("input", {payload:null});
|
|
});
|
|
});
|
|
|
|
it('should be able to reset correctly having not output anything on second edge', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op2type:"nul", op1:"true",op1type:"val", op2:"false", duration:"100", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
var errors = [];
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
msg.should.have.a.property("topic", "pass")
|
|
msg.should.have.a.property("payload", true);
|
|
c += 1;
|
|
}
|
|
catch(err) { errors.push(err) }
|
|
});
|
|
setTimeout( function() {
|
|
if (errors.length > 0) {
|
|
done(errors[0])
|
|
} else {
|
|
c.should.equal(2);
|
|
done();
|
|
}
|
|
},350);
|
|
n1.emit("input", {payload:1, topic:"pass"});
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:2, topic:"should-block"});
|
|
},50);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:3, topic:"pass"});
|
|
},200);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:2, topic:"should-block"});
|
|
},250);
|
|
});
|
|
});
|
|
|
|
it('should be able to extend the delay', function(done) {
|
|
this.timeout(5000); // add extra time for flake
|
|
var spy = sinon.stub(RED.util, 'evaluateNodeProperty',
|
|
function(arg1, arg2, arg3, arg4, arg5) { if (arg5) { arg5(null, arg1) } else { return arg1; } }
|
|
);
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"flow", op1:"foo", op2:"bar", op2type:"global", duration:"100", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "foo");
|
|
c += 1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "bar");
|
|
//console.log(Date.now() - ss);
|
|
(Date.now() - ss).should.be.greaterThan(149);
|
|
spy.restore();
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { spy.restore(); done(err); }
|
|
});
|
|
var ss = Date.now();
|
|
n1.emit("input", {payload:"Hello"});
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:null});
|
|
},50);
|
|
});
|
|
});
|
|
|
|
it('should be able to extend the delay (but with no 2nd output)', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op2type:"nul", op1:"false", op2:"true", duration:"200", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "Hello");
|
|
c += 1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "World");
|
|
(Date.now() - ss).should.be.greaterThan(300);
|
|
done();
|
|
}
|
|
} catch(err) {
|
|
console.log(err);
|
|
done(err);
|
|
}
|
|
});
|
|
var ss = Date.now();
|
|
n1.emit("input", {payload:"Hello"});
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"Error"});
|
|
},50);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"Error"});
|
|
},100);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"World"});
|
|
},330);
|
|
});
|
|
});
|
|
|
|
it('should be able to extend the delay and output the most recent payload', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"60", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
msg.should.have.a.property("payload", "World");
|
|
(Date.now() - ss).should.be.greaterThan(120);
|
|
done();
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
var ss = Date.now();
|
|
n1.emit("input", {payload:"Hello"});
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"Goodbye"});
|
|
},40);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"World"});
|
|
},80);
|
|
});
|
|
});
|
|
|
|
it('should be able output the 2nd payload', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"false", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"50", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "Goodbye");
|
|
msg.should.have.a.property("topic", "test2");
|
|
c += 1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "World");
|
|
msg.should.have.a.property("topic", "test3");
|
|
(Date.now() - ss).should.be.greaterThan(70);
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
var ss = Date.now();
|
|
n1.emit("input", {payload:"Hello", topic:"test1"});
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"Goodbye", topic:"test2"});
|
|
},20);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"World", topic:"test3"});
|
|
},80);
|
|
});
|
|
});
|
|
|
|
it('should be able output the 2nd payload and handle multiple topics', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"false", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"80", bytopic:"topic", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "Goodbye1");
|
|
msg.should.have.a.property("topic", "test1");
|
|
c += 1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "Goodbye2");
|
|
msg.should.have.a.property("topic", "test2");
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:"Hello1", topic:"test1"});
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"Hello2", topic:"test2"});
|
|
},20);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"Goodbye2", topic:"test2"});
|
|
},20);
|
|
setTimeout( function() {
|
|
n1.emit("input", {payload:"Goodbye1", topic:"test1"});
|
|
},20);
|
|
});
|
|
});
|
|
|
|
it('should be able to apply mustache templates to payloads', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"{{payload}}", op2:"{{topic}}", duration:"50", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "Hello");
|
|
c+=1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "World");
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:"Hello",topic:"World"});
|
|
});
|
|
});
|
|
|
|
it('should be able to send 2nd message to a 2nd output', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"hello", op2:"world", duration:"50", outputs:2, wires:[["n2"],["n3"]] },
|
|
{id:"n2", type:"helper"}, {id:"n3", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var n3 = helper.getNode("n3");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", "hello");
|
|
msg.should.have.a.property("topic", "test");
|
|
c+=1;
|
|
}
|
|
else { done(err); }
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n3.on("input", function(msg) {
|
|
try {
|
|
if (c === 1) {
|
|
msg.should.have.a.property("payload", "world");
|
|
msg.should.have.a.property("topic", "test");
|
|
done();
|
|
}
|
|
else { done(err); }
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:"go",topic:"test"});
|
|
});
|
|
});
|
|
|
|
it('should handle string null as null', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"pay", op1:"null", op2:"null", duration:"40", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", null);
|
|
c+=1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", "World");
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:"World"});
|
|
});
|
|
});
|
|
|
|
it('should handle string null as null on op2', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"null", op2:"null", duration:"40", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
if (c === 0) {
|
|
msg.should.have.a.property("payload", null);
|
|
c+=1;
|
|
}
|
|
else {
|
|
msg.should.have.a.property("payload", null);
|
|
done();
|
|
}
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:"null"});
|
|
});
|
|
});
|
|
|
|
it('should be able to set infinite timeout, and clear timeout', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"0", extend: false, wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
msg.should.have.a.property("payload", "1");
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
setTimeout( function() {
|
|
if (c === 2) { done(); }
|
|
else {
|
|
done(new Error("Too many messages received"));
|
|
}
|
|
},20);
|
|
n1.emit("input", {payload:null}); // trigger
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {reset:true}); // clear the blockage
|
|
n1.emit("input", {payload:null}); // trigger
|
|
});
|
|
});
|
|
|
|
it('should be able to set infinite timeout, and clear timeout by message', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:"0", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
msg.should.have.a.property("payload", "1");
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:null}); // trigger
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {payload:"foo"}); // don't clear the blockage
|
|
n1.emit("input", {payload:"boo"}); // clear the blockage
|
|
n1.emit("input", {payload:null}); // trigger
|
|
setTimeout( function() {
|
|
if (c === 2) { done(); }
|
|
else {
|
|
done(new Error("Too many messages received"));
|
|
}
|
|
},50);
|
|
});
|
|
});
|
|
|
|
it('should be able to set infinite timeout, and clear timeout by boolean true', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"true", duration:"0", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
msg.should.have.a.property("payload", "1");
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
setTimeout( function() {
|
|
if (c === 2) { done(); }
|
|
else {
|
|
done(new Error("Too many messages received"));
|
|
}
|
|
},20);
|
|
n1.emit("input", {payload:null}); // trigger
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {payload:false}); // don't clear the blockage
|
|
n1.emit("input", {payload:true}); // clear the blockage
|
|
n1.emit("input", {payload:null}); // trigger
|
|
});
|
|
});
|
|
|
|
it('should be able to set infinite timeout, and clear timeout by boolean false', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"false", duration:"0", wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
try {
|
|
c += 1;
|
|
msg.should.have.a.property("payload", "1");
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
setTimeout( function() {
|
|
if (c === 2) { done(); }
|
|
else {
|
|
done(new Error("Too many messages received"));
|
|
}
|
|
},20);
|
|
n1.emit("input", {payload:null}); // trigger
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {payload:null}); // blocked
|
|
n1.emit("input", {payload:"foo"}); // don't clear the blockage
|
|
n1.emit("input", {payload:false}); // clear the blockage
|
|
n1.emit("input", {payload:null}); // trigger
|
|
});
|
|
});
|
|
|
|
it('should be able to set a repeat, and clear loop by reset', function(done) {
|
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", op1:"", op1type:"pay", duration:-25, wires:[["n2"]] },
|
|
{id:"n2", type:"helper"} ];
|
|
helper.load(triggerNode, flow, function() {
|
|
var n1 = helper.getNode("n1");
|
|
var n2 = helper.getNode("n2");
|
|
var c = 0;
|
|
n2.on("input", function(msg) {
|
|
c += 1;
|
|
try {
|
|
msg.should.have.property('payload','foo');
|
|
msg.payload = "bar"; // try to provoke pass by reference error
|
|
}
|
|
catch(err) { done(err); }
|
|
});
|
|
n1.emit("input", {payload:"foo"}); // trigger
|
|
n1.emit("input", {payload:"foo"}); // trigger
|
|
setTimeout( function() {
|
|
n1.emit("input", {reset:true}); // reset
|
|
},90);
|
|
setTimeout( function() {
|
|
c.should.within(2,5); // should send foo between 2 and 5 times.
|
|
done();
|
|
},180);
|
|
});
|
|
});
|
|
describe('messaging API', function () {
|
|
function mapiDoneTriggerTestHelper(done, nodeSetting, msgAndTimings) {
|
|
const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js");
|
|
const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js");
|
|
const flow = [
|
|
{ ...nodeSetting, id: "triggerNode1", type: "trigger", wires: [[]] },
|
|
{ id: "completeNode1", type: "complete", scope: ["triggerNode1"], uncaught: false, wires: [["helperNode1"]] },
|
|
{ id: "catchNode1", type: "catch", scope: ["triggerNode1"], uncaught: false, wires: [["helperNode1"]] },
|
|
{ id: "helperNode1", type: "helper", wires: [[]] }];
|
|
const numMsgs = msgAndTimings.length;
|
|
helper.load([triggerNode, completeNode, catchNode], flow, function () {
|
|
const triggerNode1 = helper.getNode("triggerNode1");
|
|
const helperNode1 = helper.getNode("helperNode1");
|
|
RED.settings.nodeMessageBufferMaxLength = 3;
|
|
const t = Date.now();
|
|
let c = 0;
|
|
helperNode1.on("input", function (msg) {
|
|
msg.should.have.a.property('payload');
|
|
(Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var);
|
|
c += 1;
|
|
if (c === numMsgs) {
|
|
done();
|
|
}
|
|
});
|
|
for (let i = 0; i < numMsgs; i++) {
|
|
setTimeout(function () { triggerNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay);
|
|
}
|
|
});
|
|
}
|
|
it('should call done() when first message has been processed', function (done) {
|
|
// not when second and more messages are emitted.
|
|
mapiDoneTriggerTestHelper(done, { units:"s", duration:"1" }, [
|
|
{ msg: { seq: 0, payload: "A"}, delay: 0, avr: 0, var: 100}
|
|
]);
|
|
});
|
|
it('should call done() when it receives reset message', function (done) {
|
|
mapiDoneTriggerTestHelper(done, {units:"s", duration:"1"}, [
|
|
{msg: { seq: 0, payload: "A", reset:true}, delay: 0, avr: 0, var:100}
|
|
]);
|
|
})
|
|
});
|
|
});
|