From 0a4672780c269d01813116d78609710a5975db60 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sat, 21 Aug 2021 13:35:15 +0900 Subject: [PATCH 1/9] initial prototype of flow testing plugin --- nodes/flow-test-config.html | 61 +++ nodes/flow-test-config.js | 27 ++ nodes/package.json | 21 + src/flow-tester.html | 902 +++++++++++++++++++++++++++++++++++- src/flow-tester.js | 131 ++++++ src/style.css | 18 +- 6 files changed, 1155 insertions(+), 5 deletions(-) create mode 100644 nodes/flow-test-config.html create mode 100644 nodes/flow-test-config.js create mode 100644 nodes/package.json diff --git a/nodes/flow-test-config.html b/nodes/flow-test-config.html new file mode 100644 index 0000000..4324532 --- /dev/null +++ b/nodes/flow-test-config.html @@ -0,0 +1,61 @@ + + + + + + + diff --git a/nodes/flow-test-config.js b/nodes/flow-test-config.js new file mode 100644 index 0000000..3207cfd --- /dev/null +++ b/nodes/flow-test-config.js @@ -0,0 +1,27 @@ +/** + * 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. + **/ + +module.exports = function(RED) { + "use strict"; + + function FlowTestConfig(n) { + RED.nodes.createNode(this, n); + const node = this; + node.name = n.name; + } + + RED.nodes.registerType("flow-test-config", FlowTestConfig); +}; diff --git a/nodes/package.json b/nodes/package.json new file mode 100644 index 0000000..5578f7a --- /dev/null +++ b/nodes/package.json @@ -0,0 +1,21 @@ +{ + "name": "node-red-flow-test", + "version": "0.0.1", + "description": "Node-RED node for Flow Test", + "node-red": { + "nodes": { + "flow-test-config": "flow-test-config.js" + } + }, + "license": "Apache 2.0", + "keywords": [ + "node-red" + ], + "author": { + "name": "Hiroyasu Nishiyama", + "email": "hiroyasu.nishiyama.uq@hitachi.com" + }, + "dependencies": {}, + "devDependencies": { + } +} diff --git a/src/flow-tester.html b/src/flow-tester.html index 34150b7..4a139a0 100644 --- a/src/flow-tester.html +++ b/src/flow-tester.html @@ -1,17 +1,897 @@ + + diff --git a/src/flow-tester.js b/src/flow-tester.js index fc1c93d..916d10c 100644 --- a/src/flow-tester.js +++ b/src/flow-tester.js @@ -1,7 +1,138 @@ +/** + * 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. + **/ + module.exports = (RED) => { + + let checks = []; + let checkSend = {}; + + function message(msg) { + RED.comms.publish("flow-test:notify", msg); + } + + function init() { + checks = []; + checkSend = {}; + + RED.hooks.add("onSend.flow-test", (events, done) => { + events.forEach((event) => { + const src = event.source; + const msg = event.msg; + const sendCB = checkSend[src.id]; + if (sendCB) { + sendCB(msg); + } + }); + done(); + }); + return Promise.resolve(true); + } + + function start() { + Promise.all(checks).then((results) => { + if (results.find(v => (v !== true)) === undefined) { + message("test success"); + } + else { + message("test failed"); + } + }).catch((e) => { + message("test failed"); + }); + return Promise.resolve(true); + } + + function cleanup() { + RED.hooks.remove("onSend.flow-test"); + return Promise.resolve(true); + } + + function executeSend(target, value) { + const node = RED.nodes.getNode(target); + node.receive({ + payload: value + }); + return Promise.resolve(true); + } + + function initMatch(nid, value) { + let resolve = null; + let reject = null; + const promise = new Promise((resolve_, reject_) => { + resolve = resolve_; + reject = reject_; + }); + checkSend[nid] = (msg) => { + if (msg && (msg.payload === value)) { + resolve(true); + } + else { + resolve(false); + } + }; + checks.push(promise); + return Promise.resolve(true); + } + RED.plugins.registerPlugin('node-red-flow-tester', { settings: { "*": { exportable: true } + }, + onadd: () => { + const routeAuthHandler = RED.auth.needsPermission("flow-tester.write"); + RED.httpAdmin.post( + "/flow-tester/executeAction/:action", + routeAuthHandler, + (req, res) => { + const kind = req.params.action; + const opt = req.body; + let promise = Promise.resolve(null); + switch (kind) { + case "init": + promise = init(); + break; + case "start": + promise = start(); + break; + case "cleanup": + promise = cleanup(); + break; + case "send": + promise = executeSend(opt.target, opt.value); + break; + case "matchInit": + promise = initMatch(opt.node, opt.value); + break; + case "press": + case "match": + case "recv": + case "set": + case "wait": + case "function": + default: + console.log("unexpected action kind: ", kind); + break; + } + promise.then((result) => { + res.json(result); + }).catch((err) => { + res.sendStatus(400); + }); + }); } }); + }; diff --git a/src/style.css b/src/style.css index a4c0fb7..160bedc 100644 --- a/src/style.css +++ b/src/style.css @@ -3,5 +3,21 @@ height: 100%; display: flex; flex-direction: column; - background: red; } + +.red-ui-suite-label { +} + +.red-ui-suite-label-control { + position: absolute; + top: 0px; + right: 0px; + margin-top: 5px; + margin-right: 5px; + display: none; +} + +.red-ui-suite-label:hover .red-ui-suite-label-control { + display: inline; +} + From bddb096187ca7cd05268ad64075c6f8996c3422b Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Wed, 1 Sep 2021 20:06:30 +0900 Subject: [PATCH 2/9] remove license --- nodes/flow-test-config.html | 16 ---------------- nodes/flow-test-config.js | 16 ---------------- src/flow-tester.html | 16 ---------------- src/flow-tester.js | 17 +---------------- 4 files changed, 1 insertion(+), 64 deletions(-) diff --git a/nodes/flow-test-config.html b/nodes/flow-test-config.html index 4324532..29adc2e 100644 --- a/nodes/flow-test-config.html +++ b/nodes/flow-test-config.html @@ -1,19 +1,3 @@ - - diff --git a/nodes/flow-test-config.js b/nodes/flow-test-config.js index 3207cfd..5eaa6d5 100644 --- a/nodes/flow-test-config.js +++ b/nodes/flow-test-config.js @@ -1,19 +1,3 @@ -/** - * 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. - **/ - module.exports = function(RED) { "use strict"; diff --git a/src/flow-tester.html b/src/flow-tester.html index 4a139a0..f6810fa 100644 --- a/src/flow-tester.html +++ b/src/flow-tester.html @@ -1,19 +1,3 @@ - - diff --git a/examples/node-red-contrib-flow-tester-addon/src/flow-tester-addon.js b/examples/node-red-contrib-flow-tester-addon/src/flow-tester-addon.js new file mode 100644 index 0000000..551e77b --- /dev/null +++ b/examples/node-red-contrib-flow-tester-addon/src/flow-tester-addon.js @@ -0,0 +1,48 @@ +module.exports = (RED) => { + "use strict"; + + console.log("; Initialize flow tester addon"); + RED.plugins.registerPlugin('node-red-flow-tester-addon', { + type: "flow-tester-addon", + actions: function () { + return [ + { + name: "addon:example1", + execute: function(opt) { + // opt object contains: + // - data: optional data for action + // - node: target node + // - msg: message object + return new Promise((resolve, reject) => { + const action = opt.action; + console.log(action.value); + resolve(); + }); + } + }, + { + name: "addon:example2", + execute: function(opt) { + return new Promise((resolve, reject) => { + const msg = opt.msg; + const payload = msg.payload; + if (payload) { + try { + const action = opt.action; + const rex = new RegExp(action.value); + if (rex.test(payload)) { + resolve(); + } + } + catch (e) { + console.log("error:", e); + } + } + reject(); + }); + } + } + ]; + } + }); +}; diff --git a/examples/node-red-contrib-flow-tester-addon/src/locales/en-US/flow-tester-addon.json b/examples/node-red-contrib-flow-tester-addon/src/locales/en-US/flow-tester-addon.json new file mode 100644 index 0000000..3f29600 --- /dev/null +++ b/examples/node-red-contrib-flow-tester-addon/src/locales/en-US/flow-tester-addon.json @@ -0,0 +1,4 @@ +{ + "label": { + } +} diff --git a/examples/node-red-contrib-flow-tester-addon/src/style.css b/examples/node-red-contrib-flow-tester-addon/src/style.css new file mode 100644 index 0000000..e69de29 diff --git a/examples/node-red-contrib-flow-tester-addon/test/test.js b/examples/node-red-contrib-flow-tester-addon/test/test.js new file mode 100644 index 0000000..be2c5fa --- /dev/null +++ b/examples/node-red-contrib-flow-tester-addon/test/test.js @@ -0,0 +1,2 @@ +describe("Flow tester addon tests", function() { +}); diff --git a/src/flow-tester.html b/src/flow-tester.html index e9d0499..807143b 100644 --- a/src/flow-tester.html +++ b/src/flow-tester.html @@ -14,7 +14,7 @@ var suites = []; var id2suite = {}; var id2test = {}; - + var selectedTestID = undefined; var currentTestSuite = null; @@ -30,6 +30,8 @@ var currentExecution = Promise.resolve(); + var addonList = []; + var MAX_LOG = 1000; var logs = []; @@ -108,9 +110,7 @@ for (let node of nodes) { var acts = evMap[node]; for (let act of acts) { - if ((act.kind === "match") || - ((act.kind === "function") && - act.performCheck)) { + if (act.performCheck) { count++; } } @@ -119,6 +119,11 @@ return count; } + function clearMatchResults() { + testResult.success = 0; + testResult.fail = 0; + } + /** * Execute a test case * @param {Object} suiteNode - config node for test suite @@ -139,9 +144,7 @@ currentTestCase = tid; numberOfChecks = countNumberOfChecks(actions); - - testResult.success = 0; - testResult.fail = 0; + clearMatchResults(); addLog("Running test: " +testCase.name); return invokeAction("init", {}); @@ -227,7 +230,7 @@ timeout: TIMEOUT, max_actions: MAX_ACTIONS, actions: { - // test specific events + // common events setup: {}, cleanup: {}, // node specific events @@ -470,6 +473,17 @@ }); } + function findAddonAction(name) { + for (let addon of addonList) { + for (let def of addon.actions()) { + if (def.name === name) { + return def; + } + } + } + return null; + } + /** * Check that specified node belongs to a flow * @param {Object} node - node to be checked @@ -480,13 +494,40 @@ return (!sf); } + function unselectAllAddon(opt) { + for (let addon of addonList) { + for (let def of addon.actions()) { + const onUnselect = def.onUnselect; + if (onUnselect) { + onUnselect(opt); + } + } + } + } + + function prepareAllAddon(opt) { + var actions = []; + for (let addon of addonList) { + for (let def of addon.actions()) { + const onAddItem = def.onAddItem; + if (onAddItem) { + if (onAddItem(opt)) { + actions.push(def); + } + } + } + } + return actions; + } + /** * Create a tab for setting event information * @param {string} id - ID of DOM element * @param {Object} container - enclosing DOM element * @param {Object} node - target node + * @param {string} event - name of event */ - function createEventTab(id, container, node) { + function createEventTab(id, container, node, event) { var div = $("
", { id: id }).css({ @@ -529,17 +570,6 @@ "margin-left": "5px", "margin-bottom": "0px", }).appendTo(row0); - var actions = - (node.type === "flow-test-config") ? [ - "click", "send", "wait", "function" - ] : [ - "click", "send", "set", "match", "wait", "function" - ]; - for (let action of actions) { - $("