Compare commits

..

14 Commits

Author SHA1 Message Date
Dave Conway-Jones
539ca8b84d keep changes in this branch in sync with other trigger changes 2017-12-29 16:57:37 +00:00
Dave Conway-Jones
813a5d2879 add some istanbul ignore to trigger 2017-11-03 19:20:03 +00:00
Dave Conway-Jones
d2b53ebba0 boost trigger node test coverage 2017-09-20 17:35:24 +01:00
Dave Conway-Jones
32d98a7fa3 trigger test - add missing try/catch to all test with callback 2017-09-20 09:31:11 +01:00
Dave Conway-Jones
69946f0be0 test trigger repeat for pass by ref error 2017-09-20 08:42:23 +01:00
Dave Conway-Jones
6e610c0435 Add some tests for trigger by topic 2017-09-19 23:21:50 +01:00
Dave Conway-Jones
cdf9e2c214 ensure trigger node clones repeating message 2017-09-19 22:40:36 +01:00
Dave Conway-Jones
6b672bd9af Let trigger node support per topic mode 2017-09-18 15:44:37 +01:00
btsimonh
b81940351f Allow port zero for Express (#1363)
* Allow uiPort to be 0 (i.e. distinguish from undefined).  When Express runs up, catch the real port number to settings.serverPort, and use that in getListenPath if set, else use uiPort.
2017-09-17 09:30:39 +01:00
Kazuki Nakanishi
a42e99c4aa Fix the appearance of 'is between' rule on switch node property (#1383) 2017-09-17 08:46:47 +01:00
HirokiUchikawa
ff40b521b7 Fix problem with multi-byte character (#1391) 2017-09-17 08:46:14 +01:00
Nick O'Leary
85392496e7 Allow setTimeout in Function node to be promisified in node 8 2017-09-12 15:13:13 +01:00
Jeston Tigchon
29cae9975e Upgrade JSONata to v1.3.0 (#1386) 2017-09-07 21:58:29 +01:00
Kosuke Akizuki
170d6b28f8 Change font family (#1357)
thanks  @k4zzk
2017-08-24 12:14:55 +01:00
15 changed files with 403 additions and 195 deletions

View File

@@ -15,9 +15,7 @@
**/
RED.text = {};
RED.text.bidi = (function() {
var textDir = "";
var textDirPref = "auto";
var bidiEnabled = false;
var textDir = "";
var LRE = "\u202A",
RLE = "\u202B",
PDF = "\u202C";
@@ -112,8 +110,8 @@ RED.text.bidi = (function() {
}
/**
* Sets the text direction
* @param dir - the actual text direction
* Sets the text direction preference
* @param dir - the text direction preference
*/
function setTextDirection(dir) {
textDir = dir;
@@ -122,44 +120,9 @@ RED.text.bidi = (function() {
RED.palette.refresh();
enforceTextDirectionOnPage();
}
/**
* Gets the bidi enabled preference
*/
function getBidiEnabled() {
return bidiEnabled;
}
/**
* Sets the bidi enabled preference
* @param state - the bidi enabled preference
*/
function setBidiEnabled(state) {
bidiEnabled = state;
setTextDirection((state ? textDirPref : ""));
}
/**
* Gets the text direction preference
*/
function getTextDirPref() {
return textDirPref;
}
/**
* Sets the text direction preference
* @param dirPref - text direction preference
*/
function setTextDirPref(dirPref) {
textDirPref = dirPref;
setTextDirection(textDirPref);
}
return {
setBidiEnabled: setBidiEnabled,
setTextDirPref: setTextDirPref,
getBidiEnabled: getBidiEnabled,
getTextDirPref: getTextDirPref,
setTextDirection: setTextDirection,
enforceTextDirectionWithUCC: enforceTextDirectionWithUCC,
resolveBaseTextDir: resolveBaseTextDir,
prepareInput: prepareInput

View File

@@ -142,34 +142,8 @@ RED.userSettings = (function() {
}
});
})
addBidiPreferences(pane);
return pane;
}
function addBidiPreferences(pane) {
$('<h3></h3>').text(RED._("menu.label.bidi")).appendTo(pane);
var row;
// Bidi enabled toggle
row = $('<div class="user-settings-row"></div>').appendTo(pane);
var input = $('<label for="user-settings-view-bidi-enabled"><input id="user-settings-view-bidi-enabled" type="checkbox"> '+RED._("menu.label.bidiSupport") +'</label>').appendTo(row).find("input");
input.prop('checked',RED.text.bidi.getBidiEnabled());
// Text Direction combo
row = $('<div class="user-settings-row"></div>').appendTo(pane);
$('<label for="user-settings-view-text-direction">'+RED._("menu.label.view.textDir")+'</label>').appendTo(row);
var select = $('<select id="user-settings-view-text-direction"><option value="ltr">' + RED._("menu.label.view.ltr") + '</option><option value="rtl">' + RED._("menu.label.view.rtl") + '</option><option value="auto">' + RED._("menu.label.view.auto") + '</option></select>').appendTo(row);
select.val(RED.text.bidi.getTextDirPref());
select.prop('disabled', !RED.text.bidi.getBidiEnabled());
input.change(function() {
RED.text.bidi.setBidiEnabled(input.prop('checked'));
select.prop('disabled', !RED.text.bidi.getBidiEnabled());
});
select.change(function() {
RED.text.bidi.setTextDirPref(select.val());
});
}
function setSelected(id, value) {
var opt = allSettings[id];

View File

@@ -201,7 +201,7 @@
border-radius:5px;
overflow: hidden;
font-size: 14px !important;
font-family: monospace !important;
font-family: Menlo, Consolas, 'DejaVu Sans Mono', Courier, monospace !important;
}
.editor-button {

View File

@@ -128,6 +128,8 @@
'$map':{ args:[ 'array', 'function' ]},
'$match':{ args:[ 'str', 'pattern', 'limit' ]},
'$max':{ args:[ 'array' ]},
'$merge':{ args:[ 'array' ]},
'$millis':{ args:[ ]},
'$min':{ args:[ 'array' ]},
'$not':{ args:[ 'arg' ]},
'$now':{ args:[ ]},

View File

@@ -187,6 +187,13 @@ module.exports = function(RED) {
}
}
};
if (util.hasOwnProperty('promisify')) {
sandbox.setTimeout[util.promisify.custom] = function(after, value) {
return new Promise(function(resolve, reject) {
sandbox.setTimeout(function(){ resolve(value) }, after);
});
}
}
var context = vm.createContext(sandbox);
try {
this.script = vm.createScript(functionText);

View File

@@ -56,6 +56,13 @@
</ul>
</div>
<br/>
<div class="form-row">
<label data-i18n="trigger.for" for="node-input-bytopic"></label>
<select id="node-input-bytopic">
<option value="all" data-i18n="trigger.alltopics"></option>
<option value="topic" data-i18n="trigger.bytopics"></option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></input>
@@ -85,7 +92,7 @@
<p>If set to a <i>string</i> type, the node supports the mustache template syntax.</p>
<p>If the node receives a message with a <code>reset</code> property, or a <code>payload</code>
that matches that configured in the node, any timeout or repeat currently in
progress will be cleared and no message triggered.</o>
progress will be cleared and no message triggered.</p>
<p>The node can be configured to resend a message at a regular interval until it
is reset by a received message.</p>
</script>
@@ -103,6 +110,7 @@
extend: {value:"false"},
units: {value:"ms"},
reset: {value:""},
bytopic: {value: "all"},
name: {value:""}
},
inputs:1,

View File

@@ -19,6 +19,7 @@ module.exports = function(RED) {
var mustache = require("mustache");
function TriggerNode(n) {
RED.nodes.createNode(this,n);
this.bytopic = n.bytopic || "all";
this.op1 = n.op1 || "1";
this.op2 = n.op2 || "0";
this.op1type = n.op1type || "str";
@@ -47,7 +48,7 @@ module.exports = function(RED) {
this.extend = n.extend || "false";
this.units = n.units || "ms";
this.reset = n.reset || '';
this.duration = parseInt(n.duration);
this.duration = parseFloat(n.duration);
if (isNaN(this.duration)) {
this.duration = 250;
}
@@ -65,29 +66,32 @@ module.exports = function(RED) {
this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1);
if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); }
if ((this.op2type === "num") && (!isNaN(this.op2))) { this.op2 = Number(this.op2); }
if (this.op1 == "null") { this.op1 = null; }
if (this.op2 == "null") { this.op2 = null; }
//if (this.op1 == "null") { this.op1 = null; }
//if (this.op2 == "null") { this.op2 = null; }
//try { this.op1 = JSON.parse(this.op1); }
//catch(e) { this.op1 = this.op1; }
//try { this.op2 = JSON.parse(this.op2); }
//catch(e) { this.op2 = this.op2; }
var node = this;
var tout = null;
var m2;
node.topics = {};
this.on("input", function(msg) {
var topic = msg.topic || "_none";
if (node.bytopic === "all") { topic = "_none"; }
node.topics[topic] = node.topics[topic] || {};
if (msg.hasOwnProperty("reset") || ((node.reset !== '') && (msg.payload == node.reset)) ) {
if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null;
if (node.loop === true) { clearInterval(node.topics[topic].tout); }
else { clearTimeout(node.topics[topic].tout); }
delete node.topics[topic];
node.status({});
}
else {
if (((!tout) && (tout !== 0)) || (node.loop === true)) {
if (node.op2type === "pay" || node.op2type === "payl") { m2 = msg.payload; }
else if (node.op2Templated) { m2 = mustache.render(node.op2,msg); }
if (((!node.topics[topic].tout) && (node.topics[topic].tout !== 0)) || (node.loop === true)) {
if (node.op2type === "pay" || node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
else if (node.op2Templated) { node.topics[topic].m2 = mustache.render(node.op2,msg); }
else if (node.op2type !== "nul") {
m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
node.topics[topic].m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
}
if (node.op1type === "pay") { }
@@ -96,58 +100,67 @@ module.exports = function(RED) {
msg.payload = RED.util.evaluateNodeProperty(node.op1,node.op1type,node,msg);
}
if (node.op1type !== "nul") { node.send(msg); }
if (node.op1type !== "nul") { node.send(RED.util.cloneMessage(msg)); }
if (node.duration === 0) { tout = 0; }
if (node.duration === 0) { node.topics[topic].tout = 0; }
else if (node.loop === true) {
if (tout) { clearInterval(tout); }
/* istanbul ignore else */
if (node.topics[topic].tout) { clearInterval(node.topics[topic].tout); }
/* istanbul ignore else */
if (node.op1type !== "nul") {
var msg2 = RED.util.cloneMessage(msg);
tout = setInterval(function() { node.send(msg2); },node.duration);
node.topics[topic].tout = setInterval(function() { node.send(RED.util.cloneMessage(msg2)); }, node.duration);
}
}
else {
tout = setTimeout(function() {
node.topics[topic].tout = setTimeout(function() {
if (node.op2type !== "nul") {
var msg2 = RED.util.cloneMessage(msg);
if (node.op2type === "flow" || node.op2type === "global") {
m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
node.topics[topic].m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
}
msg2.payload = m2;
msg2.payload = node.topics[topic].m2;
node.send(msg2);
}
tout = null;
delete node.topics[topic];
node.status({});
},node.duration);
}, node.duration);
}
node.status({fill:"blue",shape:"dot",text:" "});
}
else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) {
if (tout) { clearTimeout(tout); }
if (node.op2type === "payl") { m2 = msg.payload; }
tout = setTimeout(function() {
/* istanbul ignore else */
if (node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
/* istanbul ignore else */
if (node.topics[topic].tout) { clearTimeout(node.topics[topic].tout); }
node.topics[topic].tout = setTimeout(function() {
if (node.op2type !== "nul") {
var msg2 = RED.util.cloneMessage(msg);
if (node.op2type === "flow" || node.op2type === "global") {
m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
node.topics[topic].m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
}
if (node.topics[topic] !== undefined) {
msg2.payload = node.topics[topic].m2;
node.send(msg2);
}
msg2.payload = m2;
node.send(msg2);
}
tout = null;
delete node.topics[topic];
node.status({});
},node.duration);
}, node.duration);
}
else {
if (node.op2type === "payl") { m2 = msg.payload; }
if (node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
}
}
});
this.on("close", function() {
if (tout) {
if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null;
for (var t in node.topics) {
/* istanbul ignore else */
if (node.topics[t]) {
if (node.loop === true) { clearInterval(node.topics[t].tout); }
else { clearTimeout(node.topics[t].tout); }
delete node.topics[t];
}
}
node.status({});
});

View File

@@ -270,6 +270,9 @@
"wait-reset": "wait to be reset",
"wait-for": "wait for",
"wait-loop": "resend it every",
"for": "Handling",
"bytopics": "each msg.topic independently",
"alltopics": "all messages",
"duration": {
"ms": "Milliseconds",
"s": "Seconds",

View File

@@ -199,6 +199,7 @@
} else if (type === "btwn") {
row2.hide();
row3.show();
btwnValue2Field.typedInput('show');
} else {
row2.hide();
row3.hide();

View File

@@ -45,7 +45,7 @@
"is-utf8":"0.2.1",
"js-yaml": "3.8.4",
"json-stringify-safe":"5.0.1",
"jsonata":"1.2.6",
"jsonata":"1.3.0",
"media-typer": "0.3.0",
"mqtt": "2.9.0",
"multer": "1.3.0",

19
red.js
View File

@@ -167,7 +167,16 @@ if (settings.httpNodeRoot !== false) {
settings.httpNodeAuth = settings.httpNodeAuth || settings.httpAuth;
}
settings.uiPort = parsedArgs.port||settings.uiPort||1880;
// if we got a port from command line, use it (even if 0)
// replicate (settings.uiPort = parsedArgs.port||settings.uiPort||1880;) but allow zero
if (parsedArgs.port !== undefined){
settings.uiPort = parsedArgs.port;
} else {
if (settings.uiPort === undefined){
settings.uiPort = 1880;
}
}
settings.uiHost = settings.uiHost||"0.0.0.0";
if (flowFile) {
@@ -261,9 +270,14 @@ if (settings.httpStatic) {
}
function getListenPath() {
var port = settings.serverPort;
if (port === undefined){
port = settings.uiPort;
}
var listenPath = 'http'+(settings.https?'s':'')+'://'+
(settings.uiHost == '0.0.0.0'?'127.0.0.1':settings.uiHost)+
':'+settings.uiPort;
':'+port;
if (settings.httpAdminRoot !== false) {
listenPath += settings.httpAdminRoot;
} else if (settings.httpStatic) {
@@ -292,6 +306,7 @@ RED.start().then(function() {
if (settings.httpAdminRoot === false) {
RED.log.info(RED.log._("server.admin-ui-disabled"));
}
settings.serverPort = server.address().port;
process.title = parsedArgs.title || 'node-red';
RED.log.info(RED.log._("server.now-running", {listenpath:getListenPath()}));
});

View File

@@ -67,9 +67,7 @@
"editPalette":"Manage palette",
"other": "Other",
"showTips": "Show tips",
"help": "Node-RED website",
"bidi": "Bidi",
"bidiSupport": "Enable Bidi support"
"help": "Node-RED website"
}
},
"user": {

View File

@@ -95,6 +95,10 @@
"args":"",
"desc":"Returns a pseudo random number greater than or equal to zero and less than one."
},
"$millis": {
"args":"",
"desc":"Returns the number of milliseconds since the Unix Epoch (1 January, 1970 UTC) as a number. All invocations of `$millis()` within an evaluation of an expression will all return the same value."
},
"$sum": {
"args": "array",
"desc": "Returns the arithmetic sum of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
@@ -160,6 +164,10 @@
"args": "object",
"desc": "Splits an object containing key/value pairs into an array of objects, each of which has a single key/value pair from the input object. If the parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "Merges an array of `objects` into a single `object` containing all the key/value pairs from each of the objects in the input array. If any of the input objects contain the same key, then the returned `object` will contain the value of the last one in the array. It is an error if the input array contains an item that is not an object."
},
"$sift": {
"args":"object, function",
"desc":"Returns an object that contains only the key/value pairs from the `object` parameter that satisfy the predicate `function` passed in as the second parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, key [, object]])`"

View File

@@ -45,62 +45,64 @@ function getFileMeta(root,path) {
var meta = {};
var read = 0;
var length = 10;
var remaining = "";
var remaining = Buffer(0);
var buffer = Buffer(length);
var idx = -1;
while(read < size) {
read+=fs.readSync(fd,buffer,0,length);
var data = remaining+buffer.toString();
var parts = data.split("\n");
remaining = parts.splice(-1);
for (var i=0;i<parts.length;i+=1) {
var match = /^\/\/ (\w+): (.*)/.exec(parts[i]);
var data = Buffer.concat([remaining,buffer]);
while((idx = data.indexOf("\n")) != -1){
var part = data.slice(0,idx+1);
var match = /^\/\/ (\w+): (.*)/.exec(part.toString());
if (match) {
meta[match[1]] = match[2];
} else {
read = size;
break;
}
data = data.slice(idx+1);
}
remaining = data;
}
fs.closeSync(fd);
return meta;
}
function getFileBody(root,path) {
var body = "";
var body = Buffer(0);
var fn = fspath.join(root,path);
var fd = fs.openSync(fn,"r");
var size = fs.fstatSync(fd).size;
var scanning = true;
var read = 0;
var length = 50;
var remaining = "";
var remaining = Buffer(0);
var buffer = Buffer(length);
var idx = -1;
while(read < size) {
var thisRead = fs.readSync(fd,buffer,0,length);
read += thisRead;
if (scanning) {
var data = remaining+buffer.slice(0,thisRead).toString();
var parts = data.split("\n");
remaining = parts.splice(-1)[0];
for (var i=0;i<parts.length;i+=1) {
if (! /^\/\/ \w+: /.test(parts[i])) {
var data = Buffer.concat([remaining,buffer.slice(0,thisRead)]);
while((idx = data.indexOf("\n")) != -1){
var part = data.slice(0,idx+1);
if (! /^\/\/ \w+: /.test(part.toString())) {
scanning = false;
body += parts[i]+"\n";
body = Buffer.concat([body,data]);
break;
}
data = data.slice(idx+1);
}
if (! /^\/\/ \w+: /.test(remaining)) {
scanning = false;
}
if (!scanning) {
body += remaining;
remaining = data;
if (scanning && read >= size) {
body = Buffer.concat([body,remaining]);
}
} else {
body += buffer.slice(0,thisRead).toString();
body = Buffer.concat([body,buffer.slice(0,thisRead)]);
}
}
fs.closeSync(fd);
return body;
return body.toString();
}
/**

View File

@@ -15,8 +15,10 @@
**/
var should = require("should");
var sinon = require("sinon");
var helper = require("../../helper.js");
var triggerNode = require("../../../../nodes/core/core/89-trigger.js");
var RED = require("../../../../red/red.js");
describe('trigger node', function() {
@@ -81,14 +83,17 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", '1');
c+=1;
}
else {
msg.should.have.a.property("payload", '0');
done();
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});
});
@@ -161,6 +166,155 @@ describe('trigger node', function() {
});
});
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 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) { 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");
spy.restore();
done();
}
}
catch(err) { spy.restore(); done(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"} ];
@@ -187,8 +341,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
msg.should.have.a.property("payload", true);
c += 1;
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.
@@ -199,23 +356,30 @@ describe('trigger node', function() {
});
it('should be able to extend the delay', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op1:"false", op2:"true", duration:"100", wires:[["n2"]] },
var spy = sinon.stub(RED.util, 'evaluateNodeProperty',
function(arg1, arg2, arg3, arg4) { 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) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "true");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(149);
done();
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"});
@@ -233,16 +397,19 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -263,15 +430,18 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -292,15 +462,18 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -321,14 +494,17 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
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"});
});
@@ -342,19 +518,46 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", null);
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
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"} ];
@@ -363,8 +566,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", 1);
try {
c += 1;
msg.should.have.a.property("payload", "1");
}
catch(err) { done(err); }
});
setTimeout( function() {
if (c === 2) { done(); }
@@ -388,8 +594,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", 1);
try {
c += 1;
msg.should.have.a.property("payload", "1");
}
catch(err) { done(err); }
});
setTimeout( function() {
if (c === 2) { done(); }
@@ -406,7 +615,7 @@ describe('trigger node', function() {
});
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", duration:-25, wires:[["n2"]] },
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");
@@ -414,9 +623,14 @@ describe('trigger node', function() {
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", "foo");
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);