Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mblackstock committed Feb 12, 2018
0 parents commit 0198254
Show file tree
Hide file tree
Showing 16 changed files with 2,853 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
node_modules
.vscode/launch.json
119 changes: 119 additions & 0 deletions README.md
@@ -0,0 +1,119 @@
# Node Test Helper

This project pulls out the node helper module from the Node-RED core so that it can used for node contributors.

For examples on how to use this helper, see the Node-RED core node test code and some node .js files supplied in the `test/examples` folder.

## Adding to node project

To add to your node project test dependencies:

npm install node-red-test-helper --save-dev

Inside your node test code:

```javascript
var helper = require('node-red-test-helper');
```

## Testing the helper

npm run test

This runs tests on a snapshot of some of the core nodes' Javascript files to ensure the helper works.

## Example test

This is an example test for testing the lower-case node in the [Node-RED documentation](https://nodered.org/docs/creating-nodes/first-node).

```javascript
var should = require("should");
var helper = require("node-red-contrib-lower-case");
var lowerNode = require("../lower-case.js");

describe('lower-case Node', function () {

afterEach(function () {
helper.unload();
});

it('should be loaded', function (done) {
var flow = [{ id: "n1", type: "lower-case", name: "lower-case" }];
helper.load(lowerNode, flow, function () {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'lower-case');
done();
});
});

it('should make payload lower case', function (done) {
var flow = [
{ id: "n1", type: "lower-case", name: "lower-case",wires:[["n2"]] },
{ id: "n2", type: "helper" }
];
helper.load(lowerNode, flow, function () {
var n2 = helper.getNode("n2");
var n1 = helper.getNode("n1");
n2.on("input", function (msg) {
msg.should.have.property('payload', 'uppercase');
done();
});
n1.receive({ payload: "UpperCase" });
});
});
});
```

## API

### load(testNode, testFlows, testCredentials, cb)

Load the test node, flows and credentials.

### unload()

Return promise to stop all flows, clean up test runtime and log spy.

### getNode(id)

Get the node from the runtime.

### credentials

TODO

### clearFlows()

Calls RED.flows.stopFlows() to stop all flows.

### request: function()

Create http (supertest) request to the editor/admin url.

Example:

```javascript
helper.request().post('/inject/invalid').expect(404).end(done);
```

### startServer(done)

Start a Node-RED test server; `done()` when complete.

### stopServer(done)

Stop server. Generally called after unload() complete

### url()

Return the URL of the helper server.

### log()

Return a spy on the logs to look for events from the node under test. For example:

```javascript
var logEvents = helper.log().args.filter(function(evt {
return evt[0].type == "batch";
});
```
156 changes: 156 additions & 0 deletions index.js
@@ -0,0 +1,156 @@
/**
* 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.
**/

var should = require("should");
var sinon = require("sinon");
var when = require("when");
var request = require('supertest');
var express = require("express");
var http = require('http');

var RED = require("node-red");
var redNodes = require("node-red/red/runtime/nodes");
var flows = require("node-red/red/runtime/nodes/flows");
var credentials = require("node-red/red/runtime/nodes/credentials");
var comms = require("node-red/red/api/editor/comms.js");
var log = require("node-red/red/runtime/log.js");
var context = require("node-red/red/runtime/nodes/context.js");
var events = require('node-red/red/runtime/events');

var app = express();

var address = '127.0.0.1';
var listenPort = 0; // use ephemeral port
var port;
var url;
var logSpy;
var server;

function helperNode(n) {
RED.nodes.createNode(this, n);
}

module.exports = {
load: function(testNode, testFlows, testCredentials, cb) {
var i;

logSpy = sinon.spy(log,"log");
logSpy.FATAL = log.FATAL;
logSpy.ERROR = log.ERROR;
logSpy.WARN = log.WARN;
logSpy.INFO = log.INFO;
logSpy.DEBUG = log.DEBUG;
logSpy.TRACE = log.TRACE;
logSpy.METRIC = log.METRIC;

if (typeof testCredentials === 'function') {
cb = testCredentials;
testCredentials = {};
}

var storage = {
getFlows: function() {
return when.resolve({flows:testFlows,credentials:testCredentials});
}
};

var settings = {
available: function() { return false; }
};

var red = {};
for (i in RED) {
if (RED.hasOwnProperty(i) && !/^(init|start|stop)$/.test(i)) {
var propDescriptor = Object.getOwnPropertyDescriptor(RED,i);
Object.defineProperty(red,i,propDescriptor);
}
}

red["_"] = function(messageId) {
return messageId;
};

redNodes.init({events:events,settings:settings, storage:storage,log:log,});
RED.nodes.registerType("helper", helperNode);
if (Array.isArray(testNode)) {
for (i = 0; i < testNode.length; i++) {
testNode[i](red);
}
} else {
testNode(red);
}
flows.load().then(function() {
flows.startFlows();
should.deepEqual(testFlows, flows.getFlows().flows);
cb();
});
},

unload: function() {
// TODO: any other state to remove between tests?
redNodes.clearRegistry();
logSpy.restore();
context.clean({allNodes:[]});
return flows.stopFlows();
},

getNode: function(id) {
return flows.get(id);
},

credentials: credentials,

clearFlows: function() {
return flows.stopFlows();
},

request: function() {
return request(RED.httpAdmin);
},

startServer: function(done) {
server = http.createServer(function(req,res) { app(req,res); });
RED.init(server, {
SKIP_BUILD_CHECK: true,
logging:{console:{level:'off'}}
});
server.listen(listenPort, address);
server.on('listening', function() {
port = server.address().port;
url = 'http://' + address + ':' + port;
comms.start();
done();
});
},

//TODO consider saving TCP handshake/server reinit on start/stop/start sequences
stopServer: function(done) {
if (server) {
try {
server.on('close', function() {
comms.stop();
});
server.close(done);
} catch(e) {
done();
}
}
},

url: function() { return url; },

log: function() { return logSpy;}
};
21 changes: 21 additions & 0 deletions package.json
@@ -0,0 +1,21 @@
{
"name": "node-red-contrib-test-helper",
"version": "0.1.0",
"description": "Helper module for testing nodes",
"main": "index.js",
"scripts": {
"test": "mocha 'test/**/*_spec.js'"
},
"license": "Apache-2.0",
"dependencies": {
"express": "4.16.2",
"node-red": "^0.18.2",
"mocha": "~3.4.2",
"should": "^8.4.0",
"sinon": "1.17.7",
"supertest": "3.0.0"
},
"devDependencies": {
"hash-sum": "1.0.2"
}
}
5 changes: 5 additions & 0 deletions test/_spec.js
@@ -0,0 +1,5 @@
var helper = require('../index.js');

describe('_spec.js', function() {
console.log('todo');
});
36 changes: 36 additions & 0 deletions test/comment_spec.js
@@ -0,0 +1,36 @@
/**
* 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.
**/

var should = require("should");
var helper = require("../index.js");
var commentNode = require("./examples/90-comment.js");

describe('comment Node', function() {

afterEach(function() {
helper.unload();
});

it('should be loaded', function(done) {
var flow = [{id:"n1", type:"comment", name: "comment" }];
helper.load(commentNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'comment');
done();
});
});

});

0 comments on commit 0198254

Please sign in to comment.