Skip to content

Commit

Permalink
Add customisable userSettings
Browse files Browse the repository at this point in the history
userSettings can now be passed in on initialization via
`helper.init(runtimepath, userSettings)` as well as during the runtime
using `helper.settings(userSettings)`.

This makes it easier for unit tests to emulate their production
environment.

Fixes #21
  • Loading branch information
kwakwaversal committed Jun 6, 2019
1 parent d6695c2 commit 5966ddd
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 2 deletions.
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');
});
});

0 comments on commit 5966ddd

Please sign in to comment.