This commit is contained in:
gongwenxin
2025-08-28 16:20:24 +08:00
parent 61e533cddf
commit 73e887ebf4
1539 changed files with 360926 additions and 0 deletions
+955
View File
@@ -0,0 +1,955 @@
/**
* 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 injectNode = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js");
var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context");
var helper = require("node-red-node-test-helper");
describe('inject node', function() {
beforeEach(function(done) {
helper.startServer(() => {
done()
});
});
function initContext(done) {
Context.init({
contextStorage: {
memory0: {
module: "memory"
},
memory1: {
module: "memory"
}
}
});
Context.load().then(function () {
done();
});
}
afterEach(async function() {
helper.unload().then(function () {
return Context.clean({allNodes: {}});
}).then(function () {
return Context.close();
}).then(function () {
helper.stopServer(done);
});
});
function basicTest(type, val, rval) {
it('inject value ('+type+')', function (done) {
var flow = [
{id:'flow', type:'tab'},
{id: "n1", type: "inject", topic: "t1", payload: val, payloadType: type, wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper", z:'flow'}
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "t1");
if (rval) {
msg.should.have.property("payload");
should.deepEqual(msg.payload, rval);
}
else {
msg.should.have.property("payload", val);
}
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
}
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)));
it('inject value of environment variable ', function (done) {
var flow = [{id: "n1", type: "inject", topic: "t1", payload: "NR_TEST", payloadType: "env", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
delete process.env.NR_TEST
try {
msg.should.have.property("topic", "t1");
msg.should.have.property("payload", "foo");
done();
} catch (err) {
done(err);
}
});
process.env.NR_TEST = 'foo';
n1.receive({});
});
});
it('inject name of node as environment variable ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_NODE_NAME", payloadType: "env", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "NAME");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject id of node as environment variable ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_NODE_ID", payloadType: "env", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "n1");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject path of node as environment variable ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_NODE_PATH", payloadType: "env", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "flow/n1");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject name of flow as environment variable ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_FLOW_NAME", payloadType: "env", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"},
{id: "flow", type: "tab", label: "FLOW" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "FLOW");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject id of flow as environment variable ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_FLOW_ID", payloadType: "env", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"},
{id: "flow", type: "tab", name: "FLOW" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "flow");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject name of group as environment variable ', function (done) {
var flow = [{id: "flow", type: "tab" },
{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_NAME", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
{id: "n2", type: "helper", z: "flow"},
{id: "g0", type: "group", name: "GROUP", z: "flow" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "GROUP");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject id of group as environment variable ', function (done) {
var flow = [{id: "flow", type: "tab" },
{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "NR_GROUP_ID", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
{id: "n2", type: "helper", z: "flow"},
{id: "g0", type: "group", name: "GROUP", z: "flow" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "g0");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject name of node as environment variable by substitution ', function (done) {
var flow = [{id: "flow", type: "tab" },
{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper", z: "flow"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "NAME");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject id of node as environment variable by substitution ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_ID}", payloadType: "str", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "n1");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject path of node as environment variable by substitution ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_NODE_PATH}", payloadType: "str", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "flow/n1");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject name of flow as environment variable by substitution ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_FLOW_NAME}", payloadType: "str", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"},
{id: "flow", type: "tab", label: "FLOW" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "FLOW");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject id of flow as environment variable ', function (done) {
var flow = [{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_FLOW_ID}", payloadType: "str", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"},
{id: "flow", type: "tab", name: "FLOW" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "flow");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject name of group as environment variable by substitution ', function (done) {
var flow = [{id: "flow", type: "tab" },
{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_NAME}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"},
{id: "n2", type: "helper", z: "flow"},
{id: "g0", type: "group", name: "GROUP", z: "flow" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "GROUP");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('inject id of group as environment variable by substitution ', function (done) {
var flow = [{id: "flow", type: "tab" },
{id: "n1", type: "inject", name: "NAME", topic: "t1", payload: "${NR_GROUP_ID}", payloadType: "str", wires: [["n2"]], z: "flow", g: "g0"},
{id: "n2", type: "helper", z: "flow"},
{id: "g0", type: "group", name: "GROUP", z: "flow" },
];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("payload", "g0");
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('sets the value of flow context property', function (done) {
var flow = [{id: "n1", type: "inject", topic: "t1", payload: "flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "t1");
msg.should.have.property("payload", "changeMe");
done();
} catch (err) {
done(err);
}
});
n1.context().flow.set("flowValue", "changeMe");
n1.receive({});
});
});
it('sets the value of persistable flow context property', function (done) {
var flow = [{id: "n1", type: "inject", topic: "t1", payload: "#:(memory0)::flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
initContext(function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "t1");
msg.should.have.property("payload", "changeMe");
done();
} catch (err) {
done(err);
}
});
n1.context().flow.set("flowValue", "changeMe", "memory0", function (err) {
n1.receive({});
});
});
});
});
it('sets the value of two persistable flow context property', function (done) {
var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]},
{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "flow", wires: [["n2"]]},
{id: "n2", z: "flow", type: "helper"}];
helper.load(injectNode, flow, function () {
initContext(function () {
var n0 = helper.getNode("n0");
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function (msg) {
try {
msg.should.have.property("topic");
if (msg.topic === "t0") {
msg.should.have.property("payload", "foo");
}
else if (msg.topic === "t1") {
msg.should.have.property("payload", "bar");
}
else {
done(new Error("unexpected message"));
}
count++;
if (count === 2) {
done();
}
} catch (err) {
done(err);
}
});
var global = n0.context().flow;
global.set("val", "foo", "memory0", function (err) {
global.set("val", "bar", "memory1", function (err) {
n0.receive({});
n1.receive({});
});
});
});
});
});
it('sets the value of global context property', function (done) {
var flow = [{id: "n1", type: "inject", topic: "t1", payload: "globalValue", payloadType: "global", wires: [["n2"]]},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "t1");
msg.should.have.property("payload", "changeMe");
done();
} catch (err) {
done(err);
}
});
n1.context().global.set("globalValue", "changeMe");
n1.receive({});
});
});
it('sets the value of persistable global context property', function (done) {
var flow = [{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]},
{id: "n2", z: "flow", type: "helper"}];
helper.load(injectNode, flow, function () {
initContext(function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "t1");
msg.should.have.property("payload", "foo");
done();
} catch (err) {
done(err);
}
});
var global = n1.context().global;
global.set("val", "foo", "memory1", function (err) {
n1.receive({});
});
});
});
});
it('sets the value of two persistable global context property', function (done) {
var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "global", wires: [["n2"]]},
{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]},
{id: "n2", z: "flow", type: "helper"}];
helper.load(injectNode, flow, function () {
initContext(function () {
var n0 = helper.getNode("n0");
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function (msg) {
try {
msg.should.have.property("topic");
if (msg.topic === "t0") {
msg.should.have.property("payload", "foo");
}
else if (msg.topic === "t1") {
msg.should.have.property("payload", "bar");
}
else {
done(new Error("unexpected message"));
}
count++;
if (count === 2) {
done();
}
} catch (err) {
done(err);
}
});
var global = n0.context().global;
global.set("val", "foo", "memory0", function (err) {
global.set("val", "bar", "memory1", function (err) {
n0.receive({});
n1.receive({});
});
});
});
});
});
it('sets the value of persistable flow & global context property', function (done) {
var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]},
{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]},
{id: "n2", z: "flow", type: "helper"}];
helper.load(injectNode, flow, function () {
initContext(function () {
var n0 = helper.getNode("n0");
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function (msg) {
try {
msg.should.have.property("topic");
if (msg.topic === "t0") {
msg.should.have.property("payload", "foo");
}
else if (msg.topic === "t1") {
msg.should.have.property("payload", "bar");
}
else {
done(new Error("unexpected message"));
}
count++;
if (count === 2) {
done();
}
} catch (err) {
done(err);
}
});
var context = n0.context();
var flow = context.flow;
var global = context.global;
flow.set("val", "foo", "memory0", function (err) {
global.set("val", "bar", "memory1", function (err) {
n0.receive({});
n1.receive({});
});
});
});
});
});
it('sets the value of two persistable global context property', function (done) {
var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "global", wires: [["n2"]]},
{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]},
{id: "n2", z: "flow", type: "helper"}];
helper.load(injectNode, flow, function () {
initContext(function () {
var n0 = helper.getNode("n0");
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function (msg) {
try {
msg.should.have.property("topic");
if (msg.topic === "t0") {
msg.should.have.property("payload", "foo");
}
else if (msg.topic === "t1") {
msg.should.have.property("payload", "bar");
}
else {
done(new Error("unexpected message"));
}
count++;
if (count === 2) {
done();
}
} catch (err) {
done(err);
}
});
var global = n0.context().global;
global.set("val", "foo", "memory0", function (err) {
global.set("val", "bar", "memory1", function (err) {
n0.receive({});
n1.receive({});
});
});
});
});
});
it('should inject once with default delay property', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n1 = helper.getNode("n1");
n1.should.have.property('onceDelay', 100);
done();
});
});
it('should inject once with default delay', function(done) {
var timestamp = new Date();
timestamp.setSeconds(timestamp.getSeconds() + 1);
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('topic', 't1');
msg.should.have.property('payload');
should(msg.payload).be.lessThan(timestamp.getTime());
done();
} catch(err) {
done(err);
}
});
});
});
it('should inject once with 500 msec. delay', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, onceDelay: 0.5, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n1 = helper.getNode("n1");
n1.should.have.property('onceDelay', 500);
done();
});
});
it('should inject once with delay of two seconds', function(done) {
this.timeout(2700); // have to wait for the inject with delay of two seconds
var timestamp = new Date();
timestamp.setSeconds(timestamp.getSeconds() + 1);
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, onceDelay: 2, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 't1');
should(msg.payload).be.greaterThan(timestamp.getTime());
done();
});
});
});
it('should inject repeatedly', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject",
payload:"payload", topic: "t2",
repeat: 0.2, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function(msg) {
msg.should.have.property('topic', 't2');
msg.should.have.property('payload', 'payload');
count += 1;
if (count > 2) {
helper.clearFlows().then(function() {
done();
});
}
});
});
});
it('should inject once with delay of two seconds and repeatedly', function(done) {
var timestamp = new Date();
timestamp.setSeconds(timestamp.getSeconds() + 1);
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date", repeat: 0.2,
once: true, onceDelay: 1.2, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function(msg) {
msg.should.have.property('topic', 't1');
should(msg.payload).be.greaterThan(timestamp.getTime());
count += 1;
if (count > 2) {
helper.clearFlows().then(function() {
done();
});
}
});
});
});
it('should inject with cron', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject",
payloadType:"date", topic: "t3",
crontab: "* * * * * *", wires:[["n3"]] },
{id:"n3", type:"helper"}],
function() {
var n3 = helper.getNode("n3");
n3.on("input", function(msg) {
msg.should.have.property('topic', 't3');
msg.should.have.property('payload').be.a.Number();
helper.clearFlows().then(function() {
done();
});
});
});
});
it('should inject multiple properties ', function (done) {
var flow = [{id: "n1", type: "inject", props: [{p:"topic", v:"t1", vt:"str"}, {p:"payload", v:"foo", vt:"str"}, {p:"x", v: 10, "vt":"num"}, {p:"y", v: "x+2", "vt":"jsonata"}], wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "t1");
msg.should.have.property("payload", "foo");
msg.should.have.property("x", 10);
msg.should.have.property("y", 12);
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('should inject custom properties in message', function (done) {
//n1: inject node with { topic:"static", payload:"static", bool1:true, str1:"1" }
var flow = [{id: "n1", type: "inject", props: [{p:"payload", v:"static", vt:"str"}, {p:"topic", v:"static", vt:"str"}, {p:"bool1", v:"true", vt:"bool"}, {p:"str1", v:"1", vt:"str"}], wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.not.have.property("payload"); //payload removed
msg.should.have.property("topic", "t_override"); //changed value to t_override
msg.should.have.property("str1", 1);//changed type from str to num
msg.should.have.property("num1", 1);//new prop
msg.should.have.property("bool1", false);//changed value to false
done();
} catch (err) {
done(err);
}
});
n1.receive({ __user_inject_props__: [
{p:"topic", v:"t_override", vt:"str"}, //change value to t_override
{p:"str1", v:"1", vt:"num"}, //change type
{p:"num1", v:"1", vt:"num"}, //new prop
{p:"bool1", v:"false", vt:"bool"}, //change value to false
]});
});
});
it('should inject multiple properties using legacy props if needed', function (done) {
var flow = [{id: "n1", type: "inject", payload:"123", payloadType:"num", topic:"foo", props: [{p:"topic", vt:"str"}, {p:"payload"}], wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "foo");
msg.should.have.property("payload", 123);
done();
} catch (err) {
done(err);
}
});
n1.receive({});
});
});
it('should report invalid JSONata expression', function (done) {
var flow = [{id: "n1", type: "inject", props: [{p:"topic", v:"t1", vt:"str"}, {p:"payload", v:"@", vt:"jsonata"}], wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function (msg) {
try {
msg.should.have.property("topic", "t1");
msg.should.not.have.property("payload");
count++;
if (count == 2) {
done();
}
} catch (err) {
done(err);
}
});
n1.on("call:error", function(err) {
count++;
if (count == 1) {
done();
}
});
n1.receive({});
});
});
describe('post', function() {
it('should inject message', function(done) {
helper.load(injectNode,
[{id:"n1", type:"inject",
payloadType:"str", topic: "t4",payload:"hello",
wires:[["n4"]] },
{ id:"n4", type:"helper"}], function() {
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property('topic', 't4');
msg.should.have.property('payload', 'hello');
helper.clearFlows().then(function() {
done();
});
});
try {
helper.request()
.post('/inject/n1')
.expect(200).end(function(err) {
if (err) {
console.log(err);
return helper.clearFlows()
.then(function () {
done(err);
});
}
});
} catch(err) {
done(err);
}
});
});
it('should inject custom properties in posted message', function(done) {
var flow = [{id:"n1", type:"inject", payloadType:"str", topic: "t4",payload:"hello", wires:[["n4"]] },
{ id:"n4", type:"helper"}];
helper.load(injectNode, flow, function() {
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.not.have.property("payload"); //payload removed
msg.should.have.property("topic", "t_override"); //changed value to t_override
msg.should.have.property("str1", "1"); //injected prop
msg.should.have.property("num1", 1); //injected prop
msg.should.have.property("bool1", true); //injected prop
msg.should.have.property("jsonata1", "AB"); //injected prop
helper.clearFlows().then(function() {
done();
});
});
try {
helper.request()
.post('/inject/n1')
.send({ __user_inject_props__: [
{p:"topic", v:"t_override", vt:"str"}, //change value to t_override
{p:"str1", v:"1", vt:"str"}, //new prop
{p:"num1", v:"1", vt:"num"}, //new prop
{p:"bool1", v:"true", vt:"bool"}, //new prop
{p:"jsonata1", v:'"A" & "B"', vt:"jsonata"}, //new prop
]})
.expect(200).end(function(err) {
if (err) {
console.log(err);
return helper.clearFlows()
.then(function () {
done(err);
});
}
});
} catch(err) {
done(err);
}
});
});
it('should fail for invalid node', function(done) {
helper.request().post('/inject/invalid').expect(404).end(done);
});
});
});
+701
View File
@@ -0,0 +1,701 @@
/**
* 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 debugNode = require("nr-test-utils").require("@node-red/nodes/core/common/21-debug.js");
var helper = require("node-red-node-test-helper");
var WebSocket = require('ws');
describe('debug node', function() {
before(function(done) {
helper.startServer(done);
});
after(function(done) {
helper.stopServer(done);
});
beforeEach(function (done) {
setTimeout(function() {
done();
}, 55);
});
afterEach(function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"debug", name:"Debug", complete:"false" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'Debug');
n1.should.have.property('complete', "payload");
done();
});
});
it('should publish on input', function(done) {
var flow = [{id:"n1", type:"debug", name:"Debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",name:"Debug",msg:"test",path:"global",
format:"string[4]",property:"payload"}
}]);
}, done);
});
});
it('should publish to console', function(done) {
var flow = [{id:"n1", type:"debug", console:"true"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
var count = 0;
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"test",property:"payload",format:"string[4]",path:"global"}
}]);
count++;
}, function() {
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "debug";
});
logEvents.should.have.length(1);
var tstmp = logEvents[0][0].timestamp;
logEvents[0][0].should.eql({level:helper.log().INFO, id:'n1',type:'debug',msg:'test', timestamp:tstmp,path:"global"});
done();
} catch(err) {
done(err);
}
});
});
});
it('should publish complete message', function(done) {
var flow = [{id:"n1", type:"debug", complete:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{"payload":"test"}',format:"Object",path:"global"}
}]);
}, done);
});
});
it('should publish complete message to console', function(done) {
var flow = [{id:"n1", type:"debug", complete:"true", console:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{"payload":"test"}',format:"Object",path:"global"}
}]);
}, function() {
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "debug";
});
logEvents.should.have.length(1);
var tstmp = logEvents[0][0].timestamp;
logEvents[0][0].should.eql({level:helper.log().INFO, id:"n1",type:"debug",msg:'\n{ payload: \'test\' }',timestamp:tstmp,path:"global"});
done();
} catch(err) {
done(err);
}
});
});
});
it('should publish other property', function(done) {
var flow = [{id:"n1", type:"debug", complete:"foo" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test", foo:"bar"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"bar",property:"foo",format:"string[3]",path:"global"}
}]);
}, done);
});
});
it('should publish multi-level properties', function(done) {
var flow = [{id:"n1", type:"debug", complete:"foo.bar" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test", foo: {bar:"bar"}});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"bar",property:"foo.bar",format:"string[3]",path:"global"}
}]);
}, done);
});
});
it('should publish an Error', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: new Error("oops")});
}, function(msg) {
const fullMsg = JSON.parse(msg)
fullMsg[0].should.have.property('topic', 'debug')
fullMsg[0].should.have.property('data')
fullMsg[0].data.should.have.property('id', 'n1')
fullMsg[0].data.should.have.property('property', 'payload')
fullMsg[0].data.should.have.property('format', 'error')
fullMsg[0].data.should.have.property('path', 'global')
fullMsg[0].data.should.have.property('msg')
const msgData = JSON.parse(fullMsg[0].data.msg)
msgData.should.have.property('__enc__', true)
msgData.should.have.property('type', 'error')
msgData.data.should.have.property('name', 'Error')
msgData.data.should.have.property('message', 'oops')
}, done);
});
});
it('should publish a boolean', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: true});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg: 'true',property:"payload",format:"boolean",path:"global"}
}]);
}, done);
});
});
it('should publish a number', function(done) {
var flow = [{id:"n1", type:"debug", console:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: 7});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"7",property:"payload",format:"number",path:"global"}
}]);
}, done);
});
});
it('should publish a NaN', function(done) {
var flow = [{id:"n1", type:"debug", console:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Number.NaN});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"NaN",property:"payload",format:"number",path:"global"}
}]);
}, done);
});
});
it('should publish with no payload', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"undefined",path:"global"}
}]);
}, done);
});
});
it('should publish a null', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:null});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"null",path:"global"}
}]);
}, done);
});
});
it('should publish an object', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {type:'foo'}});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{"type":"foo"}',property:"payload",format:"Object",path:"global"}
}]);
}, done);
});
});
it('should publish an object with no-prototype-builtins', function(done) {
const flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
const n1 = helper.getNode("n1");
const payload = Object.create(null);
payload.type = 'foo';
websocket_test(function() {
n1.emit("input", {payload: payload});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{"type":"foo"}',property:"payload",format:"Object",path:"global"}
}]);
}, done);
});
});
it('should publish an object with overriden hasOwnProperty', function(done) {
const flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
const n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {type:'foo', hasOwnProperty: null}});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{"type":"foo","hasOwnProperty":null}',property:"payload",format:"Object",path:"global"}
}]);
}, done);
});
});
it('should publish an array', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: [0,1,2,3]});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg: '[0,1,2,3]',format:"array[4]",
property:"payload",path:"global"}
}]);
}, done);
});
});
it('should publish an object with circular references', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
var o = { name: 'bar' };
o.o = o;
n1.emit("input", {payload: o});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{
id:"n1",
msg:'{"name":"bar","o":"[Circular ~]"}',
property:"payload",format:"Object",path:"global"
}
}]);
}, done);
});
});
it('should publish an object to console', function(done) {
var flow = [{id:"n1", type:"debug", console:"true"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {type:'foo'}});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'{"type":"foo"}',property:"payload",format:"Object",path:"global"}
}]);
}, function() {
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "debug";
});
logEvents.should.have.length(1);
var tstmp = logEvents[0][0].timestamp;
logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:'\n{ type: \'foo\' }',timestamp:tstmp,path:"global"});
done();
} catch(err) {
done(err);
}
});
});
});
it('should publish a string after a newline to console if the string contains \\n', function(done) {
var flow = [{id:"n1", type:"debug", console:"true"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test\ntest"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"test\ntest",property:"payload",format:"string[9]",path:"global"}
}]);
}, function() {
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "debug";
});
logEvents.should.have.length(1);
var tstmp = logEvents[0][0].timestamp;
logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:"\ntest\ntest",timestamp:tstmp,path:"global"});
done();
} catch(err) {
done(err);
}
});
});
});
it('should publish complete message with edit', function(done) {
var flow = [{id:"n1", type:"debug", name:"Debug", complete: "true",
targetType: "jsonata", complete: '"<" & payload & ">"'}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",name:"Debug",msg:"<test>",
format:"string[6]",path:"global"}
}]);
}, done);
});
});
it('should truncate a long message', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:Array(1002).join("X")});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql([{
topic:"debug",
data:{
id:"n1",
msg: Array(1001).join("X")+'...',
property:"payload",
format:"string[1001]",
path:"global"
}
}]);
}, done);
});
});
it('should truncate a long string in the object', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {foo: Array(1002).join("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql([{
topic:"debug",
data:{
id:"n1",
msg:'{"foo":"'+Array(1001).join("X")+'..."}',
property:"payload",
format:"Object",
path:"global"
}
}]);
}, done);
});
});
it('should truncate a large array', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Array(1001).fill("X")});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql([{
topic:"debug",
data:{
id:"n1",
msg:JSON.stringify({
__enc__: true,
type: "array",
data: Array(1000).fill("X"),
length: 1001
}),
property:"payload",
format:"array[1001]",
path:"global"
}
}]);
}, done);
});
});
it('should truncate a large array in the object', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {foo: Array(1001).fill("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql([{
topic:"debug",
data:{
id:"n1",
msg:JSON.stringify({
foo:{
__enc__: true,
type: "array",
data: Array(1000).fill("X"),
length: 1001
}
}),
property:"payload",
format:"Object",
path:"global"
}
}]);
}, done);
});
});
it('should truncate a large buffer', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Buffer.alloc(501,"\"")});
}, function(msg) {
var a = JSON.parse(msg);
a[0].should.eql({
topic:"debug",
data:{
id:"n1",
msg: Array(1001).join("2"),
property:"payload",
format:"buffer[501]",
path:"global"
}
});
}, done);
});
});
it('should truncate a large buffer in the object', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {foo: Buffer.alloc(1001,"X")}});
}, function(msg) {
var a = JSON.parse(msg);
a[0].should.eql({
topic:"debug",
data:{
id:"n1",
msg:JSON.stringify({
foo:{
type: "Buffer",
data: Array(1000).fill(88),
__enc__: true,
length: 1001
}
}),
property:"payload",
format:"Object",
path:"global"
}
});
}, done);
});
});
it('should convert Buffer to hex', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Buffer.from('HELLO', 'utf8')});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{
id:"n1",
msg:'48454c4c4f',
property:"payload",
format:"buffer[5]",
path:"global"
}
}]);
}, done);
});
});
it('should publish when active', function(done) {
var flow = [{id:"n1", type:"debug", active: false }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"message 1"});
helper.request()
.post('/debug/n1/enable')
.expect(200).end(function(err) {
if (err) { return done(err); }
n1.emit("input", {payload:"message 2"});
});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"message 2",property:"payload",format:"string[9]",path:"global"}
}]);
}, done);
});
});
it('should not publish when inactive', function(done) {
var flow = [{id:"n1", type:"debug", active: true }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function(close) {
helper.request()
.post('/debug/n1/disable')
.expect(201).end(function(err) {
if (err) {
close();
return done(err);
}
n1.emit("input", {payload:"message"});
setTimeout(function() {
close();
done();
}, 200);
});
}, function(msg) {
should.fail(null,null,"unexpected message");
}, function() {});
});
});
describe('post', function() {
it('should return 404 on invalid state', function(done) {
var flow = [{id:"n1", type:"debug", active: true }];
helper.load(debugNode, flow, function() {
helper.request()
.post('/debug/n1/foobar')
.expect(404).end(done);
});
});
it('should return 404 on invalid node', function(done) {
helper.request()
.post('/debug/n99/enable')
.expect(404).end(done);
});
it('should return 400 for invalid bulk disable', function(done) {
var flow = [{id:"n1", type:"debug", active: true }];
helper.load(debugNode, flow, function() {
helper.request()
.post('/debug/disable')
.send({})
.set('Content-type', 'application/json')
.expect(400).end(done);
});
})
it('should return success for bulk disable', function(done) {
var flow = [{id:"n1", type:"debug", active: true }];
helper.load(debugNode, flow, function() {
helper.request()
.post('/debug/disable')
.send({nodes:['n1']})
.set('Content-type', 'application/json')
.expect(201).end(done);
});
})
});
describe('get', function() {
it('should return the view.html', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
helper.request()
.get('/debug/view/view.html')
.expect(200)
.end(done);
});
});
});
});
function websocket_test(open_callback, message_callback, done_callback) {
var ws = new WebSocket(helper.url() + "/comms");
var close_callback = function() { ws.close(); };
ws.on('open', function() { open_callback(close_callback); });
ws.on('message', function(msg) {
try {
message_callback(msg, close_callback);
ws.close();
done_callback();
} catch(err) {
done_callback(err);
}
});
}
@@ -0,0 +1,39 @@
var should = require("should");
var catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js");
var helper = require("node-red-node-test-helper");
describe('complete Node', function() {
afterEach(function() {
helper.unload();
});
it('should output a message when called', function(done) {
var flow = [ { id:"n1", type:"complete", name:"status", wires:[["n2"]], scope:[] },
{id:"n2", type:"helper"} ];
helper.load(catchNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.should.have.property('name', 'status');
n2.on("input", function(msg) {
msg.text.should.equal("Oh dear");
msg.should.have.property('source');
msg.source.should.have.property('id',"12345");
msg.source.should.have.property('type',"testnode");
msg.source.should.have.property('name',"fred");
done();
});
var mst = {
text: "Oh dear",
source: {
id: "12345",
type: "testnode",
name: "fred"
}
}
n1.emit("input", mst);
});
});
});
+44
View File
@@ -0,0 +1,44 @@
/**
* 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 catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js");
var helper = require("node-red-node-test-helper");
describe('catch Node', function() {
afterEach(function() {
helper.unload();
});
it('should output a message when called', function(done) {
var flow = [ { id:"n1", type:"catch", name:"catch", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(catchNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.should.have.property('name', 'catch');
n2.on("input", function(msg) {
msg.should.be.a.Error();
msg.toString().should.equal("Error: big error");
done();
});
var err = new Error("big error");
n1.emit("input", err);
});
});
});
+54
View File
@@ -0,0 +1,54 @@
/**
* 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 catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-status.js");
var helper = require("node-red-node-test-helper");
describe('status Node', function() {
afterEach(function() {
helper.unload();
});
it('should output a message when called', function(done) {
var flow = [ { id:"n1", type:"status", name:"status", wires:[["n2"]], scope:[] },
{id:"n2", type:"helper"} ];
helper.load(catchNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.should.have.property('name', 'status');
n2.on("input", function(msg) {
msg.text.should.equal("Oh dear");
msg.should.have.property('source');
msg.source.should.have.property('id',"12345");
msg.source.should.have.property('type',"testnode");
msg.source.should.have.property('name',"fred");
done();
});
var mst = {
text: "Oh dear",
source: {
id: "12345",
type: "testnode",
name: "fred"
}
}
n1.emit("input", mst);
});
});
});
+403
View File
@@ -0,0 +1,403 @@
/**
* 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 linkNode = require("nr-test-utils").require("@node-red/nodes/core/common/60-link.js");
var helper = require("node-red-node-test-helper");
var clone = require("clone");
describe('link Node', function() {
before(function(done) {
helper.startServer(done);
});
after(function(done) {
helper.stopServer(done);
});
afterEach(function() {
helper.unload();
});
it('should be loaded (link in)', function(done) {
var flow = [{id:"n1", type:"link in", name: "link-in" }];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'link-in');
done();
});
});
it('should be loaded (link out)', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out" }];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'link-out');
done();
});
});
it('should be linked', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2"]},
{id:"n2", type:"link in", name: "link-in", wires:[["n3"]]},
{id:"n3", type:"helper"}];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
var n3 = helper.getNode("n3");
n3.on("input", function(msg) {
try {
msg.should.have.property('payload', 'hello');
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"hello"});
});
});
it('should be linked to multiple nodes', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2", "n3"]},
{id:"n2", type:"link in", name: "link-in0", wires:[["n4"]]},
{id:"n3", type:"link in", name: "link-in1", wires:[["n4"]]},
{id:"n4", type:"helper"} ];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
var n4 = helper.getNode("n4");
var count = 0;
n4.on("input", function (msg) {
try {
msg.should.have.property('payload', 'hello');
count++;
if(count == 2) {
done();
}
} catch(err) {
done(err);
}
});
n1.receive({payload:"hello"});
});
});
it('should be linked from multiple nodes', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out0", links:["n3"]},
{id:"n2", type:"link out", name: "link-out1", links:["n3"]},
{id:"n3", type:"link in", name: "link-in", wires:[["n4"]]},
{id:"n4", type:"helper"} ];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var n4 = helper.getNode("n4");
var count = 0;
n4.on("input", function(msg) {
try {
msg.should.have.property('payload', 'hello');
count++;
if(count == 2) {
done();
}
} catch(err) {
done(err);
}
});
n1.receive({payload:"hello"});
n2.receive({payload:"hello"});
});
});
describe("link-call node", function() {
it('should call static link-in node and get response', function (done) {
var flow = [{ id: "link-in-1", type: "link in", wires: [["func"]] },
{ id: "func", type: "helper", wires: [["link-out-1"]] },
{ id: "link-out-1", type: "link out", mode: "return" },
{ id: "link-call", type: "link call", links: ["link-in-1"], wires: [["n4"]] },
{ id: "n4", type: "helper" }];
helper.load(linkNode, flow, function () {
var func = helper.getNode("func");
func.on("input", function (msg, send, done) {
msg.payload = "123";
send(msg);
done();
})
var n1 = helper.getNode("link-call");
var n4 = helper.getNode("n4");
n4.on("input", function (msg) {
try {
msg.should.have.property('payload', '123');
done();
} catch (err) {
done(err);
}
});
n1.receive({ payload: "hello" });
});
})
it('should call link-in node by name and get response', function (done) {
this.timeout(500);
var payload = Date.now();
var flow = [
{ id: "tab-flow-1", type: "tab", label: "Flow 1" },
{ id: "tab-flow-2", type: "tab", label: "Flow 2" },
{ id: "link-in-1", z: "tab-flow-1", type: "link in", name: "double payload", wires: [["func"]] },
{ id: "link-in-2", z: "tab-flow-2", type: "link in", name: "double payload", wires: [["func"]] },
{ id: "func", z: "tab-flow-1", type: "helper", wires: [["link-out-1"]] },
{ id: "link-out-1", z: "tab-flow-1", type: "link out", mode: "return" },
{ id: "link-call", z: "tab-flow-1", type: "link call", linkType: "dynamic", links: [], wires: [["n4"]] },
{ id: "n4", z: "tab-flow-1", type: "helper" }
];
helper.load(linkNode, flow, function () {
var func = helper.getNode("func");
func.on("input", function (msg, send, done) {
msg.payload += msg.payload;
send(msg);
done();
})
var n1 = helper.getNode("link-call");
var n4 = helper.getNode("n4");
n4.on("input", function (msg) {
try {
msg.should.have.property('payload');
msg.payload.should.eql(payload + payload);
done();
} catch (err) {
done(err);
}
});
n1.receive({ payload: payload, target: "double payload" });
});
})
// //TODO: This test is DISABLED - helper.load() calls callback but none of the nodes are available (issue loading a flow with a subflow)
// it('should call link-in node by name in subflow', function (done) {
// this.timeout(9999500);
// var payload = Date.now();
// var flow = [
// {"id":"sub-flow-template","type":"subflow","name":"Subflow","info":"","category":"","in":[{"wires":[{"id":"link-call-1"}]}],"out":[{"wires":[{"id":"link-call-1","port":0}]}]},
// {"id":"link-call-1","type":"link call","z":"sub-flow-template","name":"","links":[],"linkType":"dynamic","timeout":"5","wires":[[]]},
// {"id":"link-in-1","type":"link in","z":"sub-flow-template","name":"double payload","links":[],"wires":[["func"]]},
// {"id":"func","type":"function","z":"sub-flow-template","name":"payload.a x payload.b","func":"msg.payload += msg.payload\nreturn msg;\n","outputs":1,"wires":[["link-out-1"]]},
// {"id":"link-out-1","type":"link out","z":"sub-flow-template","name":"","mode":"return","links":[],"wires":[]},
// {"id":"sub-flow-1","type":"subflow:sub-flow-template","z":"tab-flow-1","name":"","wires":[["n4"]]},
// { id: "n4", z: "tab-flow-1", type: "helper" }
// ];
// helper.load(linkNode, flow, function () {
// var sf = helper.getNode("sub-flow-1");
// var func = helper.getNode("func");
// var n4 = helper.getNode("n4");
// func.on("input", function (msg, send, done) {
// msg.payload += msg.payload;
// send(msg);
// done();
// })
// n4.on("input", function (msg) {
// try {
// msg.should.have.property('payload');
// msg.payload.should.eql(payload + payload);
// done();
// } catch (err) {
// done(err);
// }
// });
// sf.receive({ payload: payload, target: "double payload" });
// });
// })
it('should timeout waiting for link return', function (done) {
this.timeout(1000);
const flow = [
{ id: "tab-flow-1", type: "tab", label: "Flow 1" },
{ id: "link-in-1", z: "tab-flow-1", type: "link in", name: "double payload", wires: [["func"]] },
{ id: "func", z: "tab-flow-1", type: "helper", wires: [["link-out-1"]] },
{ id: "link-out-1", z: "tab-flow-1", type: "link out", mode: "" }, //not return mode, cause link-call timeout
{ id: "link-call", z: "tab-flow-1", type: "link call", linkType: "static", "timeout": "0.5", links: ["link-in-1"], wires: [["n4"]] },
{ id: "catch-all", z: "tab-flow-1", type: "catch", scope: ["link-call"], uncaught: true, wires: [["n4"]] },
{ id: "n4", z: "tab-flow-1", type: "helper" }
];
helper.load(linkNode, flow, function () {
const funcNode = helper.getNode("func");
const linkCallNode = helper.getNode("link-call");
const helperNode = helper.getNode("n4");
funcNode.on("input", function (msg, send, done) {
msg.payload += msg.payload;
send(msg);
done();
})
helperNode.on("input", function (msg) {
try {
msg.should.have.property("target", "double payload");
msg.should.have.property("error");
msg.error.should.have.property("message", "timeout");
msg.error.should.have.property("source");
done();
} catch (err) {
done(err);
}
});
linkCallNode.receive({ payload: "hello", target: "double payload" });
});
})
it('should raise error due to multiple targets on same tab', function (done) {
this.timeout(55500);
const flow = [
{ id: "tab-flow-1", type: "tab", label: "Flow 1" },
{ id: "link-in-1", z: "tab-flow-1", type: "link in", name: "double payload", wires: [["func"]] },
{ id: "link-in-2", z: "tab-flow-1", type: "link in", name: "double payload", wires: [["func"]] },
{ id: "func", z: "tab-flow-1", type: "helper", wires: [["link-out-1"]] },
{ id: "link-out-1", z: "tab-flow-1", type: "link out", mode: "return" },
{ id: "link-call", z: "tab-flow-1", type: "link call", linkType: "dynamic", links: [], wires: [["n4"]] },
{ id: "catch-all", z: "tab-flow-1", type: "catch", scope: ["link-call"], uncaught: true, wires: [["n4"]] },
{ id: "n4", z: "tab-flow-1", type: "helper" }
];
helper.load(linkNode, flow, function () {
const funcNode = helper.getNode("func");
const linkCall = helper.getNode("link-call");
const helperNode = helper.getNode("n4");
funcNode.on("input", function (msg, send, _done) {
done(new Error("Function should not be called"))
})
helperNode.on("input", function (msg) {
try {
msg.should.have.property("target", "double payload");
msg.should.have.property("error");
msg.error.should.have.property("message");
msg.error.message.should.match(/.*Multiple link-in nodes.*/)
msg.error.should.have.property("source");
done();
} catch (err) {
done(err);
}
});
linkCall.receive({ payload: "hello", target: "double payload" });
});
})
it('should raise error due to multiple targets on different tabs', function (done) {
this.timeout(500);
const flow = [
{ id: "tab-flow-1", type: "tab", label: "Flow 1" },
{ id: "tab-flow-2", type: "tab", label: "Flow 2" },
{ id: "tab-flow-3", type: "tab", label: "Flow 3" },
{ id: "link-in-1", z: "tab-flow-2", type: "link in", name: "double payload", wires: [["func"]] },
{ id: "link-in-2", z: "tab-flow-3", type: "link in", name: "double payload", wires: [["func"]] },
{ id: "func", z: "tab-flow-1", type: "helper", wires: [["link-out-1"]] },
{ id: "link-out-1", z: "tab-flow-1", type: "link out", mode: "return" },
{ id: "link-call", z: "tab-flow-1", type: "link call", linkType: "dynamic", links: [], wires: [["n4"]] },
{ id: "catch-all", z: "tab-flow-1", type: "catch", scope: ["link-call"], uncaught: true, wires: [["n4"]] },
{ id: "n4", z: "tab-flow-1", type: "helper" }
];
helper.load(linkNode, flow, function () {
const funcNode = helper.getNode("func");
const linkCall = helper.getNode("link-call");
const helperNode = helper.getNode("n4");
funcNode.on("input", function (msg, send, _done) {
done(new Error("Function should not be called"))
})
helperNode.on("input", function (msg) {
try {
msg.should.have.property("target", "double payload");
msg.should.have.property("error");
msg.error.should.have.property("message");
msg.error.message.should.match(/.*Multiple link-in nodes.*/)
msg.error.should.have.property("source");
done();
} catch (err) {
done(err);
}
});
linkCall.receive({ payload: "hello", target: "double payload" });
});
})
it('should not raise error after deploying a name change to a duplicate link-in node', async function () {
this.timeout(400);
const flow = [
{ id: "tab-flow-1", type: "tab", label: "Flow 1" },
{ id: "link-in-1", z: "tab-flow-1", type: "link in", name: "duplicate", wires: [["link-out-1"]] },
{ id: "link-in-2", z: "tab-flow-1", type: "link in", name: "duplicate", wires: [["link-out-1"]] }, //duplicate name
{ id: "link-out-1", z: "tab-flow-1", type: "link out", mode: "return" },
{ id: "link-call", z: "tab-flow-1", type: "link call", linkType: "dynamic", links: [], wires: [["n4"]] },
{ id: "n4", z: "tab-flow-1", type: "helper" }
];
await helper.load(linkNode, flow)
const linkIn2before = helper.getNode("link-in-2");
linkIn2before.should.have.property("name", "duplicate") // check link-in-2 has been deployed with the duplicate name
//modify the flow and deploy change
const newConfig = clone(flow);
newConfig[2].name = "add" // change nodes name
await helper.setFlows(newConfig, "nodes") // deploy "nodes" only
const helperNode = helper.getNode("n4");
const linkCall2 = helper.getNode("link-call");
const linkIn2after = helper.getNode("link-in-2");
linkIn2after.should.have.property("name", "add") // check link-in-2 no longer has a duplicate name
//poke { payload: "hello", target: "add" } into the link-call node and
//ensure that a message arrives via the link-in node named "add"
await new Promise((resolve, reject) => {
helperNode.on("input", function (msg) {
try {
msg.should.have.property("target", "add");
msg.should.not.have.property("error");
resolve()
} catch (err) {
reject(err);
}
});
linkCall2.receive({ payload: "hello", target: "add" });
});
})
it('should allow nested link-call flows', function(done) {
this.timeout(500);
var flow = [/** Multiply by 2 link flow **/
{id:"li1", type:"link in", wires: [[ "m2"]]},
{id:"m2", type:"helper", wires: [["lo1"]]},
{id:"lo1", type:"link out", mode: "return"},
/** Multiply by 3 link flow **/
{id:"li2", type:"link in", wires: [[ "m3"]]},
{id:"m3", type:"helper", wires: [["lo2"]]},
{id:"lo2", type:"link out", mode: "return"},
/** Multiply by 6 link flow **/
{id:"li3", type:"link in", wires: [[ "link-call-1"]]},
{id:"link-call-1", type:"link call", links:["li1"], wires:[["link-call-2"]]},
{id:"link-call-2", type:"link call", links:["li2"], wires:[["lo3"]]},
{id:"lo3", type:"link out", mode: "return"},
/** Test Flow Entry **/
{id:"link-call", type:"link call", links:["li3"], wires:[["n4"]]},
{id:"n4", type:"helper"} ];
helper.load(linkNode, flow, function() {
var m2 = helper.getNode("m2");
m2.on("input", function(msg, send, done) { msg.payload *= 2 ; send(msg); done(); })
var m3 = helper.getNode("m3");
m3.on("input", function(msg, send, done) { msg.payload *= 3 ; send(msg); done(); })
var n1 = helper.getNode("link-call");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
try {
msg.should.have.property('payload', 24);
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:4});
});
})
});
});
+36
View File
@@ -0,0 +1,36 @@
/**
* 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 commentNode = require("nr-test-utils").require("@node-red/nodes/core/common/90-comment.js");
var helper = require("node-red-node-test-helper");
describe('comment Node', function() {
afterEach(function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"comment", name: "comment" }];
helper.load(commentNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'comment');
done();
});
});
});
@@ -0,0 +1,73 @@
var should = require("should");
var config = require("nr-test-utils").require("@node-red/nodes/core/common/91-global-config.js");
var inject = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js");
var helper = require("node-red-node-test-helper");
describe('Global Config Node', function() {
afterEach(function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"global-config", name: "XYZ" }];
helper.load(config, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property("name", "XYZ");
done();
});
});
it('should access global environment variable', function(done) {
var flow = [{id:"n1", type:"global-config", name: "XYZ",
env: [ {
name: "X",
type: "string",
value: "foo"
}]
},
{id: "n2", type: "inject", topic: "t1", payload: "X", payloadType: "env", wires: [["n3"]], z: "flow"},
{id: "n3", type: "helper"}
];
helper.load([config, inject], flow, function() {
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
n3.on("input", (msg) => {
try {
msg.should.have.property("payload", "foo");
done();
} catch (err) {
done(err);
}
});
n2.receive({});
});
});
it('should evaluate a global environment variable that is a JSONata value', function (done) {
const flow = [{
id: "n1", type: "global-config", name: "XYZ",
env: [
{ name: "now-var", type: "jsonata", value: "$millis()" }
]
},
{ id: "n2", type: "inject", topic: "t1", payload: "now-var", payloadType: "env", wires: [["n3"]], z: "flow" },
{ id: "n3", type: "helper" }
];
helper.load([config, inject], flow, function () {
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
n3.on("input", (msg) => {
try {
const now = Date.now();
msg.should.have.property("payload").and.be.approximately(now, 1000);
done();
} catch (err) {
done(err);
}
});
n2.receive({});
});
});
});
+36
View File
@@ -0,0 +1,36 @@
/**
* 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 unknown = require("nr-test-utils").require("@node-red/nodes/core/common/98-unknown.js");
var helper = require("node-red-node-test-helper");
describe('unknown Node', function() {
afterEach(function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"unknown", name: "unknown" }];
helper.load(unknown, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'unknown');
done();
});
});
});