From 2a82d326b0acf734e3ed2ae6ab2ee20e3f1fbee9 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Mon, 20 Jun 2022 18:05:38 +0100 Subject: [PATCH 1/2] add `setFlows` so that node being tested can modify flows bumps version to 0.3.0 adds test in lower-case_spec.js updates readme with API and example --- README.md | 39 ++++++++++++++++++++++++- examples/lower-case_spec.js | 24 ++++++++++++++++ index.js | 57 +++++++++++++++++++++++++++++++++---- package.json | 19 +++++++------ 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 755fd65..525a25e 100644 --- a/README.md +++ b/README.md @@ -314,7 +314,44 @@ Loads a flow then starts the flow. This function has the following arguments: * testNode: (object|array of objects) Module object of a node to be tested returned by require function. This node will be registered, and can be used in testFlows. * testFlow: (array of objects) Flow data to test a node. If you want to use flow data exported from Node-RED editor, export the flow to the clipboard and paste the content into your test scripts. * testCredentials: (object) Optional node credentials. -* cb: (function) Function to call back when testFlows has been started. +* cb: (function) Function to call back when testFlows has been started (not required when called wih `await`) + +### setFlows(testFlow, type, testCredentials, cb) + +Updates the currently loaded flow. This function has the following arguments: + +* testFlow: (array of objects) Flow data to test a node. If you want to use flow data exported from Node-RED editor, export the flow to the clipboard and paste the content into your test scripts. +* type: (string) Flow data to test a node. If you want to use flow data exported from Node-RED editor, export the flow to the clipboard and paste the content into your test scripts. +* testCredentials: (object) Optional node credentials. +* cb: (function) Function to call back when testFlows has been loaded (not required when called wih `await`) + +#### Example + +```js + it('should modify the flow then lower case of payload', async function () { + const flow = [ + { id: "n2", type: "helper" } + ] + await helper.load(lowerNode, flow) + const newFlow = [...flow] + newFlow.push( { id: "n1", type: "lower-case", name: "lower-case", wires:[['n2']] },) + await helper.setFlows(newFlow, "nodes") //update flows + const n1 = helper.getNode('n1') + n1.should.have.a.property('name', 'lower-case') + await new Promise((resolve, reject) => { + const n2 = helper.getNode('n2') + n2.on('input', function (msg) { + try { + msg.should.have.property('payload', 'hello'); + resolve() + } catch (err) { + reject(err); + } + }); + n1.receive({ payload: 'HELLO' }); + }); + }); +``` ### unload() diff --git a/examples/lower-case_spec.js b/examples/lower-case_spec.js index 5e32a30..bc942cf 100644 --- a/examples/lower-case_spec.js +++ b/examples/lower-case_spec.js @@ -54,4 +54,28 @@ describe('lower-case Node', function () { n1.receive({ payload: "UpperCase" }); }); }); + it('should modify the flow then lower case of payload', async function () { + const flow = [ + { id: "n2", type: "helper" } + ] + await helper.load(lowerNode, flow) + + const newFlow = [...flow] + newFlow.push( { id: "n1", type: "lower-case", name: "lower-case", wires:[['n2']] },) + await helper.setFlows(newFlow) + const n1 = helper.getNode('n1') + n1.should.have.a.property('name', 'lower-case') + await new Promise((resolve, reject) => { + const n2 = helper.getNode('n2') + n2.on('input', function (msg) { + try { + msg.should.have.property('payload', 'hello'); + resolve() + } catch (err) { + reject(err); + } + }); + n1.receive({ payload: 'HELLO' }); + }); + }); }); diff --git a/index.js b/index.js index c956922..fa7d3d7 100644 --- a/index.js +++ b/index.js @@ -199,15 +199,19 @@ class NodeTestHelper extends EventEmitter { }); - if (typeof testCredentials === 'function') { cb = testCredentials; testCredentials = {}; } - - var storage = { + const conf = {flows:testFlow,credentials:testCredentials|| {}} + const storage = { + conf: conf, getFlows: function () { - return Promise.resolve({flows:testFlow,credentials:testCredentials}); + return Promise.resolve(conf); + }, + saveFlows: function(conf) { + storage.conf = conf; + return Promise.resolve(); } }; // this._settings.logging = {console:{level:'off'}}; @@ -281,7 +285,7 @@ class NodeTestHelper extends EventEmitter { unload() { // TODO: any other state to remove between tests? this._redNodes.clearRegistry(); - this._logSpy.restore(); + this._logSpy && this._logSpy.restore(); this._sandbox.restore(); // internal API @@ -302,6 +306,49 @@ class NodeTestHelper extends EventEmitter { return this._redNodes.stopFlows(); } + /** + * Update flows + * @param {object|object[]} testFlow Flow data to test a node + * @param {"full"|"flows"|"nodes"} type The type of deploy mode "full", "flows" or "nodes" (defaults to "full") + * @param {object} [testCredentials] Optional node credentials + * @param {function} [cb] Optional callback (not required when called with await) + * @returns {Promise} + */ + setFlows(testFlow, type, testCredentials, cb) { + const helper = this; + if (typeof testCredentials === 'string' ) { + cb = testCredentials; + testCredentials = {}; + } + if(!type || typeof type != "string") { + type = "full" + } + async function waitStarted() { + return new Promise((resolve, reject) => { + let timeover = setTimeout(() => { + if (timeover) { + timeover = null + reject(Error("timeout waiting event")) + } + }, 300); + function hander() { + clearTimeout(timeover) + helper._events.off('flows:started', hander) + if (timeover) { + timeover = null + resolve() + } + } + helper._events.on('flows:started', hander); // call resolve when its done + }); + } + return this._redNodes.setFlows(testFlow, testCredentials || {}, type) + .then(waitStarted) + .then(() => { + if(cb) cb(); + }); + } + request() { return request(this._httpAdmin); } diff --git a/package.json b/package.json index db9ebbf..8be66d6 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "node-red-node-test-helper", - "version": "0.2.7", + "version": "0.3.0", "description": "A test framework for Node-RED nodes", "main": "index.js", "scripts": { "test": "mocha \"test/**/*_spec.js\"", - "examples": "mocha \"examples/**/*_spec.js\"" + "examples": "mocha \"examples/**/*_spec.js\"", + "examples2": "set NODE_PATH=C:\\Users\\Stephen\\repos\\github\\node-red&& mocha \"examples/**/*_spec.js\"" }, "license": "Apache-2.0", "repository": { @@ -13,18 +14,18 @@ "url": "https://github.com/node-red/node-red-node-test-helper.git" }, "dependencies": { - "express": "4.17.1", - "body-parser": "1.19.0", + "body-parser": "1.20.0", + "express": "4.18.1", "read-pkg-up": "7.0.1", - "semver": "7.3.4", + "semver": "7.3.7", "should": "^13.2.3", "should-sinon": "0.0.6", - "sinon": "9.2.4", + "sinon": "11.1.2", "stoppable": "1.1.0", - "supertest": "4.0.2" + "supertest": "6.2.3" }, "devDependencies": { - "mocha": "~7.1.2" + "mocha": "9.2.2" }, "contributors": [ { @@ -46,6 +47,6 @@ "node-red" ], "engines": { - "node": ">=8" + "node": ">=14" } } From 547980cb8fe772d60323542b487920a7e09f22f3 Mon Sep 17 00:00:00 2001 From: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> Date: Mon, 20 Jun 2022 18:16:06 +0100 Subject: [PATCH 2/2] remove temporary npm script --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 8be66d6..b685032 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,7 @@ "main": "index.js", "scripts": { "test": "mocha \"test/**/*_spec.js\"", - "examples": "mocha \"examples/**/*_spec.js\"", - "examples2": "set NODE_PATH=C:\\Users\\Stephen\\repos\\github\\node-red&& mocha \"examples/**/*_spec.js\"" + "examples": "mocha \"examples/**/*_spec.js\"" }, "license": "Apache-2.0", "repository": {