From 7b8ed487e9952e7bad4f5d5853ffa5e0fe9d609f Mon Sep 17 00:00:00 2001 From: Kristian Heljas Date: Thu, 8 Apr 2021 16:52:02 +0300 Subject: [PATCH] Function node: add `node.outputCount` property to sandbox (#2918) * Function node: add `node.outputs` property to sandbox https://discourse.nodered.org/t/expose-configured-output-count-to-function-node-i-can-pr/43848 * style: indetation for function node sanbox code I guess this was unintentionally unindented in https://github.com/node-red/node-red/commit/d51aefa1563005247c23c11973fb97b13a404b1e#diff-24cd715c3b7405ea194bfdc0dc2a350ceb2f5d18696b8163c3e40105b981a666 * Function node: tests for accessing node properties consistently tests that `node.id`, `node.name` and `node.outputs` are available in `init()`, `func()` and `finalize()` methods. * Function node: rename `node.outputs` to `node.outputCount` https://discourse.nodered.org/t/expose-configured-output-count-to-function-node-i-can-pr/43848/9?u=kristian --- .../nodes/core/function/10-function.js | 35 ++++++---- test/nodes/core/function/10-function_spec.js | 68 +++++++++++++++---- 2 files changed, 76 insertions(+), 27 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 1d51677ab..0e8b7f5ef 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -93,6 +93,7 @@ module.exports = function(RED) { var node = this; node.name = n.name; node.func = n.func; + node.outputs = n.outputs; node.ini = n.initialize ? n.initialize.trim() : ""; node.fin = n.finalize ? n.finalize.trim() : ""; node.libs = n.libs || []; @@ -112,21 +113,22 @@ module.exports = function(RED) { var functionText = "var results = null;"+ "results = (async function(msg,__send__,__done__){ "+ - "var __msgid__ = msg._msgid;"+ - "var node = {"+ - "id:__node__.id,"+ - "name:__node__.name,"+ - "log:__node__.log,"+ - "error:__node__.error,"+ - "warn:__node__.warn,"+ - "debug:__node__.debug,"+ - "trace:__node__.trace,"+ - "on:__node__.on,"+ - "status:__node__.status,"+ - "send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+ - "done:__done__"+ - "};\n"+ - node.func+"\n"+ + "var __msgid__ = msg._msgid;"+ + "var node = {"+ + "id:__node__.id,"+ + "name:__node__.name,"+ + "outputCount:__node__.outputCount,"+ + "log:__node__.log,"+ + "error:__node__.error,"+ + "warn:__node__.warn,"+ + "debug:__node__.debug,"+ + "trace:__node__.trace,"+ + "on:__node__.on,"+ + "status:__node__.status,"+ + "send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+ + "done:__done__"+ + "};\n"+ + node.func+"\n"+ "})(msg,__send__,__done__);"; var finScript = null; var finOpt = null; @@ -146,6 +148,7 @@ module.exports = function(RED) { __node__: { id: node.id, name: node.name, + outputCount: node.outputs, log: function() { node.log.apply(node, arguments); }, @@ -330,6 +333,7 @@ module.exports = function(RED) { var node = { id:__node__.id, name:__node__.name, + outputCount:__node__.outputCount, log:__node__.log, error:__node__.error, warn:__node__.warn, @@ -351,6 +355,7 @@ module.exports = function(RED) { var node = { id:__node__.id, name:__node__.name, + outputCount:__node__.outputCount, log:__node__.log, error:__node__.error, warn:__node__.warn, diff --git a/test/nodes/core/function/10-function_spec.js b/test/nodes/core/function/10-function_spec.js index 609fac1d3..4c6f3bab2 100644 --- a/test/nodes/core/function/10-function_spec.js +++ b/test/nodes/core/function/10-function_spec.js @@ -132,6 +132,28 @@ describe('function node', function() { }); }); + it('should allow accessing node.id and node.name and node.outputCount', function(done) { + var flow = [{id:"n1",name:"test-function", outputs: 2, type:"function",wires:[["n2"]],func: "return [{ topic: node.name, payload:node.id, outputCount: node.outputCount }];"}, + {id:"n2", type:"helper"}]; + helper.load(functionNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + // Use this form of assert as `msg` is created inside + // the sandbox and doesn't get all the should.js monkey patching + should.equal(msg.payload, n1.id); + should.equal(msg.topic, n1.name); + should.equal(msg.outputCount, n1.outputs); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:""}); + }); + }); + function testSendCloning(args,done) { var flow = [{id:"n1",type:"function",wires:[["n2"],["n2"]],func:"node.send("+args+"); msg.payload = 'changed';"}, {id:"n2", type:"helper"}]; @@ -1402,17 +1424,38 @@ describe('function node', function() { }); }); - it('should execute finalization', function(done) { - var flow = [{id:"n1",type:"function",wires:[],func:"return msg;",finalize:"global.set('X','bar');"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var ctx = n1.context().global; - helper.unload().then(function () { - ctx.get('X').should.equal("bar"); - done(); + + + describe("finalize function", function() { + + it('should execute', function(done) { + var flow = [{id:"n1",type:"function",wires:[],func:"return msg;",finalize:"global.set('X','bar');"}]; + helper.load(functionNode, flow, function() { + var n1 = helper.getNode("n1"); + var ctx = n1.context().global; + helper.unload().then(function () { + ctx.get('X').should.equal("bar"); + done(); + }); }); }); - }); + + it('should allow accessing node.id and node.name and node.outputCount', function(done) { + var flow = [{id:"n1",name:"test-function", outputs: 2, type:"function",wires:[["n2"]],finalize:"global.set('finalize-data', { topic: node.name, payload:node.id, outputCount: node.outputCount});", func: "return msg;"}]; + helper.load(functionNode, flow, function() { + var n1 = helper.getNode("n1"); + var ctx = n1.context().global; + helper.unload().then(function () { + const finalizeData = ctx.get('finalize-data'); + should.equal(finalizeData.payload, n1.id); + should.equal(finalizeData.topic, n1.name); + should.equal(finalizeData.outputCount, n1.outputs); + done(); + }); + }); + }); + + }) describe('externalModules', function() { afterEach(function() { @@ -1655,8 +1698,8 @@ describe('function node', function() { }); }); - it('should allow accessing node.id and node.name and sending message', function(done) { - var flow = [{id:"n1",name:"test-function", type:"function",wires:[["n2"]],initialize:"setTimeout(function() { node.send({ topic: node.name, payload:node.id})},10)", func: ""}, + it('should allow accessing node.id and node.name and node.outputCount and sending message', function(done) { + var flow = [{id:"n1",name:"test-function", outputs: 1, type:"function",wires:[["n2"]],initialize:"setTimeout(function() { node.send({ topic: node.name, payload:node.id, outputCount: node.outputCount})},10)", func: ""}, {id:"n2", type:"helper"}]; helper.load(functionNode, flow, function() { var n1 = helper.getNode("n1"); @@ -1667,6 +1710,7 @@ describe('function node', function() { // the sandbox and doesn't get all the should.js monkey patching should.equal(msg.payload, n1.id); should.equal(msg.topic, n1.name); + should.equal(msg.outputCount, n1.outputs); done(); } catch(err) { done(err); @@ -1675,5 +1719,5 @@ describe('function node', function() { }); }); - }) + }); });