diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.html b/packages/node_modules/@node-red/nodes/core/common/60-link.html
index 98f0ddc25..80974821e 100644
--- a/packages/node_modules/@node-red/nodes/core/common/60-link.html
+++ b/packages/node_modules/@node-red/nodes/core/common/60-link.html
@@ -32,8 +32,17 @@
-
-
+
+
+
+
+
+
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
index ecc5d905c..ec09e01b1 100755
--- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
@@ -170,6 +170,10 @@
"outMode": "Mode",
"sendToAll": "Send to all connected link nodes",
"returnToCaller": "Return to calling link node",
+ "linkCallType": "Link Type",
+ "staticLinkCall": "Fixed target",
+ "dynamicLinkCall": "Dynamic target (msg.target)",
+ "dynamicLinkLabel": "Dynamic",
"error": {
"missingReturn": "Missing return node information"
}
diff --git a/test/nodes/core/common/60-link_spec.js b/test/nodes/core/common/60-link_spec.js
index 5314eed15..63733455c 100644
--- a/test/nodes/core/common/60-link_spec.js
+++ b/test/nodes/core/common/60-link_spec.js
@@ -120,68 +120,241 @@ describe('link Node', function() {
});
describe("link-call node", function() {
- it('should call 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() {
+ 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) {
+ 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) {
+ 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 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:"hello"});
+ n1.receive({payload:4});
});
})
});
-
- it('should allow nested link-call flows', function(done) {
- 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:["m2"], wires:[["link-call-2"]]},
- {id:"link-call-2", type:"link call", links:["m3"], 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});
- });
- })
});