Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ We then have a set of mocha unit tests. These tests check that the node loads c

To get started, we need to tell the helper where to find the node-red runtime. this is done by calling `helper.init(require.resolve('node-red'))` as shown.

The helper takes an optional `userSettings` parameter which is merged with the runtime defaults.

```javascript
helper.init(require.resolve('node-red'), {
functionGlobalContext: { os:require('os') }
});
```

## 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.
Expand Down Expand Up @@ -320,6 +328,20 @@ Example:
helper.request().post('/inject/invalid').expect(404).end(done);
```

### settings(userSettings)

Merges any userSettings with the defaults returned by `RED.settings`. Each invocation of this method will overwrite the previous userSettings to prevent unexpected problems in your tests.

This will enable you to replicate your production environment within your tests, for example where you're using the `functionGlobalContext` to enable extra node modules within your functions.

```javascript
// functions can now access os via global.get('os')
helper.settings({ functionGlobalContext: { os:require('os') } });

// reset back to defaults
helper.settings({ });
```

### startServer(done)

Starts a Node-RED server for testing nodes that depend on http or web sockets endpoints like the debug node.
Expand Down Expand Up @@ -361,4 +383,4 @@ var logEvents = helper.log().args.filter(function(evt {

npm run examples

This runs tests on an included lower-case node (as above) as well as snaphots of some of the core nodes' Javascript files to ensure the helper is working as expected.
This runs tests on an included lower-case node (as above) as well as snaphots of some of the core nodes' Javascript files to ensure the helper is working as expected.
22 changes: 22 additions & 0 deletions examples/function_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,28 @@ describe('function node', function() {
});
});

it('should access functionGlobalContext set via herlp settings()', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('foo');return msg;"},
{id:"n2", type:"helper"}];
helper.settings({
functionGlobalContext: {
foo: (function() {
return 'bar';
})(),
},
});
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', 'bar');
done();
});
n1.receive({payload:"replaceme"});
});
helper.settings({});
});

function testNonObjectMessage(functionText,done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:functionText},
{id:"n2", type:"helper"}];
Expand Down
26 changes: 25 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,35 @@ class NodeTestHelper extends EventEmitter {
}
}

init(runtimePath) {
init(runtimePath, userSettings) {
runtimePath = runtimePath || findRuntimePath();
if (runtimePath) {
this._initRuntime(runtimePath);
if (userSettings) {
this.settings(userSettings);
}
}
}

/**
* Merges any userSettings with the defaults returned by `RED.settings`. Each
* invocation of this method will overwrite the previous userSettings to prevent
* unexpected problems in your tests.
*
* This will enable you to replicate your production environment within your tests,
* for example where you're using the `functionGlobalContext` to enable extra node
* modules within your functions.
* @example
* helper.settings({ functionGlobalContext: { os:require('os') } });
* @param {Object} userSettings - an object containing the runtime settings
* @return {Object} custom userSettings merged with default RED.settings
*/
settings(userSettings) {
if (userSettings) {
// to prevent unexpected problems, always merge with the default RED.settings
this._settings = Object.assign({}, this._RED.settings, userSettings);
}
return this._settings;
}

load(testNode, testFlow, testCredentials, cb) {
Expand Down
28 changes: 28 additions & 0 deletions test/settings_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var should = require("should");
var NodeTestHelper = require('../index.js').NodeTestHelper;

var helper;
beforeEach(function() {
// .init() is implicitly called on instantiation so not required
helper = new NodeTestHelper();
});

describe('add custom settings on init', function () {
it('should merge custom settings with RED.settings defaults', function () {
helper._settings.should.not.have.property('functionGlobalContext');
helper.init(null, {functionGlobalContext: {}});
helper._settings.should.have.property('functionGlobalContext');
});
});

describe('helper.settings() usage', function() {
it('should return a settings Object', function() {
var settings = helper.settings();
should.exist(settings);
settings.should.have.property('get');
});
it('should not maintain settings state across multiple invocations', function() {
helper.settings({ foo: true }).should.have.property('foo');
helper.settings({ bar: true }).should.not.have.property('foo');
});
});