diff --git a/README.md b/README.md index 1483595..03dbf63 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Using the test-helper, your tests can start the Node-RED runtime, load a flow an ## Adding to your node project dependencies -To add unit tests your node project test dependencies, add this test helper as follows: +To add unit tests to your node project test dependencies, add this test helper as follows: npm install node-red-node-test-helper --save-dev @@ -22,6 +22,32 @@ This will add the helper module to your `package.json` file as a development dep Both [Mocha](https://mochajs.org/) and [Should](https://shouldjs.github.io/) will be pulled in with the test helper. Mocha is a unit test framework for Javascript; Should is an assertion library. For more information on these frameworks, see their associated documentation. +## Alternate linking of node project dependencies + +Instead of installing the unit test node project test dependencies, which can pull in a very large number of packages, you can install the unit test packages globally and link them to your node project. This is a better option if you plan on developing more than one node project. + +Install the unit test packages globally as follows: + + npm install -g node-red + npm install -g node-red-node-test-helper + npm install -g should + npm install -g mocha + npm install -g sinon + npm install -g supertest + npm install -g express + +In your node project development directory, link the unit test packages as follows: + + npm link node-red + npm link node-red-node-test-helper + npm link should + npm link mocha + npm link sinon + npm link supertest + npm link express + +Depending on the nodes in your test flow, you may also need to link in other packages as required. If a test indicates that a package cannot be found, install the package globally and then link it to your node project the same way as the packages above. + ## Adding test script to `package.json` To run your tests you can add a test script to your `package.json` file in the `scripts` section. To run all of the files with the `_spec.js` prefix in the test directory for example: @@ -88,6 +114,14 @@ In this example, we require `should` for assertions, this helper module, as well We then have a set of mocha unit tests. These tests check that the node loads correctly, and ensures it makes the payload string lower case as expected. +## Creating test flows in the Node Red editor + +The Node Red editor can be used to generate test flow configurations. Create a flow in the editor with the node you wish to test and configure them using the node configuration editor. Add `debug` nodes to receive output messages from your test node. `catch` and `status` nodes can also be used to catch errors and status changes from your test node. It is not necessary to include `inject` nodes as this helper module will allow you to inject test messages. + +Highlight the nodes in the test flow and select `Export` then `Clipboard` to copy your test flow configuration, and then paste the JSON string into the test script. Repeat this process to create different variations of your test flow if required. + +When the flow is run in this helper module, `debug` nodes are converted to `helper` nodes. + ## Getting nodes in the runtime The asynchronous `helper.load()` method calls the supplied callback function once the Node-RED server and runtime is ready. We can then call the `helper.getNode(id)` method to get a reference to nodes in the runtime. For more information on these methods see the API section below. @@ -128,7 +162,7 @@ For additional test examples, see the `.js` files supplied in the `test/examples 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. -* testFlows: (array of objects) Flow data to test a node. If you want to use the flow data exported from Node-RED editor, need to covert it to JavaScript object using JSON.parse(). +* testFlows: (array of objects|JSON string)) Flow configuration to test a node. The flow configuration can be exported from Node-RED editor and pasted as a JSON string. * testCredentials: (object) Optional node credentials. * cb: (function) Function to call back when testFlows has been started. @@ -194,4 +228,4 @@ var logEvents = helper.log().args.filter(function(evt { npm run test -This runs tests on snaphots of some of the core nodes' Javascript files to ensure the helper is working as expected. \ No newline at end of file +This runs tests on snaphots of some of the core nodes' Javascript files to ensure the helper is working as expected. diff --git a/index.js b/index.js index df71e7c..f8ba031 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,8 @@ var events = require('node-red/red/runtime/events'); var app = express(); +var util = require('util'); + var address = '127.0.0.1'; var listenPort = 0; // use ephemeral port var port; @@ -41,10 +43,14 @@ var server; function helperNode(n) { RED.nodes.createNode(this, n); + + this.error = function(logMessage,msg) { + console.log(logMessage); + } } module.exports = { - load: function(testNode, testFlows, testCredentials, cb) { + load: function(testNode, testFlow, testCredentials, cb) { var i; logSpy = sinon.spy(log,"log"); @@ -61,9 +67,24 @@ module.exports = { testCredentials = {}; } + if (typeof testFlow === "string") { + testFlow = JSON.parse(testFlow); + } + + var flowId = testFlow[0].z || "f1"; + + testFlow.forEach(function(node) { + node.z = flowId; + if (node.type == "debug") { + node.type ="helper"; + } + }); + + testFlow.unshift({id: flowId, type:"tab", label:"Test flow"}); + var storage = { getFlows: function() { - return when.resolve({flows:testFlows,credentials:testCredentials}); + return when.resolve({flows:testFlow,credentials:testCredentials}); } }; @@ -94,7 +115,7 @@ module.exports = { } flows.load().then(function() { flows.startFlows(); - should.deepEqual(testFlows, flows.getFlows().flows); + should.deepEqual(testFlow, flows.getFlows().flows); cb(); }); }, diff --git a/package.json b/package.json index 4452edb..8b51f50 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,9 @@ }, { "name": "Mike Blackstock" + }, + { + "name": "Dean Cording" } ], "keywords": [ diff --git a/test/function_spec.js b/test/function_spec.js index 854d62c..29d8387 100644 --- a/test/function_spec.js +++ b/test/function_spec.js @@ -29,6 +29,10 @@ describe('function node', function() { helper.unload(); }); + after(function(done) { + helper.stopServer(done); + }); + it('should be loaded', function(done) { var flow = [{id:"n1", type:"function", name: "function" }]; helper.load(functionNode, flow, function() { diff --git a/test/httprequest_spec.js b/test/httprequest_spec.js index 92b9e7c..5a9f796 100644 --- a/test/httprequest_spec.js +++ b/test/httprequest_spec.js @@ -64,6 +64,7 @@ describe('HTTP Request Node', function() { after(function() { testServer.close(); + helper.stopServer(); }); afterEach(function() { helper.unload(); diff --git a/test/lower-case_spec.js b/test/lower-case_spec.js index 2d29e19..c9a1dec 100644 --- a/test/lower-case_spec.js +++ b/test/lower-case_spec.js @@ -9,22 +9,29 @@ describe('lower-case Node', function () { }); it('should be loaded', function (done) { - var flow = [{ id: "n1", type: "lower-case", name: "lower-case" }]; + + // Exported flow pasted as JSON string + var flow = '[{"id":"3912a37a.c3818c","type":"lower-case","z":"e316ac4b.c85a2","name":"lower-case","x":240,"y":320,"wires":[[]]}]'; + helper.load(lowerNode, flow, function () { - var n1 = helper.getNode("n1"); + var n1 = helper.getNode("3912a37a.c3818c"); n1.should.have.property('name', 'lower-case'); done(); }); }); it('should make payload lower case', function (done) { - var flow = [ - { id: "n1", type: "lower-case", name: "test name",wires:[["n2"]] }, - { id: "n2", type: "helper" } - ]; + + // Exported flow pasted as Javascript + var flow = [{"id":"3912a37a.c3818c","type":"lower-case","z":"e316ac4b.c85a2", + "name":"lower-case","x":240,"y":320,"wires":[["7b57d83e.378fd8"]]}, + {"id":"7b57d83e.378fd8","type":"debug","z":"e316ac4b.c85a2","name":"", + "active":true,"tosidebar":true,"console":false,"tostatus":false, + "complete":"true","x":400,"y":340,"wires":[]}]; + helper.load(lowerNode, flow, function () { - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); + var n2 = helper.getNode("7b57d83e.378fd8"); + var n1 = helper.getNode("3912a37a.c3818c"); n2.on("input", function (msg) { msg.should.have.property('payload', 'uppercase'); done(); diff --git a/test/websocket_spec.js b/test/websocket_spec.js index 78a3cb3..52773c5 100644 --- a/test/websocket_spec.js +++ b/test/websocket_spec.js @@ -66,6 +66,10 @@ describe('websocket Node', function() { helper.unload(); }); + after(function(done) { + helper.stopServer(done); + }); + describe('websocket-listener', function() { it('should load', function(done) { var flow = [{ id: "n1", type: "websocket-listener", path: "/ws" }];