Merge branch '0.18' into projects

This commit is contained in:
Nick O'Leary
2018-01-16 11:21:54 +00:00
146 changed files with 6584 additions and 1498 deletions

View File

@@ -0,0 +1,88 @@
/**
* 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 RED = require("../../red/red.js");
var when = require("when");
var http = require('http');
var express = require("express");
var fs = require('fs-extra');
var app = express();
var server;
var homeDir = './test/resources/home';
var address = '127.0.0.1';
var listenPort = 0; // use ephemeral port
var url;
function cleanup() {
var flowsFile = homeDir + '/flows_'+require('os').hostname()+'.json';
try {
fs.statSync(flowsFile);
fs.unlinkSync(flowsFile);
} catch (err) {
}
};
module.exports = {
startServer: function(done) {
cleanup();
app.use("/",express.static("public"));
server = http.createServer(app);
var settings = {
httpAdminRoot: "/",
httpNodeRoot: "/api",
userDir: homeDir,
functionGlobalContext: { }, // enables global context
SKIP_BUILD_CHECK: true,
logging: {console: {level:'off'}}
};
RED.init(server, settings);
app.use(settings.httpAdminRoot,RED.httpAdmin);
app.use(settings.httpNodeRoot,RED.httpNode);
server.listen(listenPort, address);
server.on('listening', function() {
var port = server.address().port;
url = 'http://' + address + ':' + port;
});
RED.start().then(function() {
done();
});
},
stopServer: function(done) {
if (server) {
try {
RED.stop().then(function() {
server.close(done);
cleanup();
done();
});
} catch(e) {
cleanup();
done();
}
}
},
url: function() {
return url;
},
red: function() {
return RED;
},
};

View File

@@ -0,0 +1,35 @@
/**
* 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.
**/
function open() {
browser.click('#red-ui-tab-debug');
}
function getMessage() {
var debugMessagePath = '//div[@class="debug-content debug-content-list"]//span[contains(@class, "debug-message-type")]';
browser.waitForExist(debugMessagePath);
return browser.getText(debugMessagePath);
}
function clearMessage() {
browser.click('//a[@id="debug-tab-clear"]');
}
module.exports = {
open: open,
getMessage: getMessage,
clearMessage: clearMessage,
};

View File

@@ -0,0 +1,24 @@
/**
* 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.
**/
function clickOk() {
browser.click('#node-dialog-ok');
browser.pause(300);
}
module.exports = {
clickOk: clickOk,
};

View File

@@ -0,0 +1,52 @@
/**
* 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 icons = {
// input
"inject": "icons/node-red/inject.png",
// output
"debug": "icons/node-red/debug.png",
// function
"change": "icons/node-red/swap.png",
};
function getIdWithIcon(icon) {
//*[name()="image" and @*="icons/node-red/inject.png"]/../..
var id = browser.getAttribute('//*[name()="image" and @*="' + icon + '"]/../..', 'id');
return id;
}
function Node(type) {
this.id = '//*[@id="' + getIdWithIcon(icons[type]) + '"]';
}
Node.prototype.edit = function() {
browser.click(this.id);
browser.click(this.id);
browser.pause(500); // Necessary for headless mode.
}
Node.prototype.connect = function(targetNode) {
var outputPort = this.id + '/*[@class="port_output"]';
var inputPort = targetNode.id + '/*[@class="port_input"]';
browser.dragAndDrop(outputPort, inputPort)
}
Node.prototype.clickLeftButton = function() {
browser.click(this.id + '/*[@class="node_button node_left_button"]');
}
module.exports = Node;

View File

@@ -0,0 +1,62 @@
/**
* 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 when = require('when');
var events = require("../../../../red/runtime/events.js");
var node = require('./node_page');
var palette = {
"inject": "#palette_node_inject",
"debug": "#palette_node_debug",
"change": "#palette_node_change",
};
function addNode(type, x, y) {
var offsetX = x ? x : 0;
var offsetY = y ? y : 0;
browser.moveToObject(palette[type]);
browser.buttonDown();
browser.moveToObject("#palette-search", offsetX + 300, offsetY + 100); // adjust to the top-left corner of workspace.
browser.buttonUp();
return new node(type);
}
function deleteAllNodes() {
browser.keys(['Control', 'a', 'a', 'Control']); // call twice to release the keys.
browser.keys(['Delete']);
}
function deploy() {
browser.call(function () {
return when.promise(function(resolve, reject) {
events.on("runtime-event", function(event) {
if (event.id === 'runtime-deploy') {
resolve();
}
});
browser.click('#btn-deploy');
});
});
browser.pause(500); // Necessary for headless mode.
}
module.exports = {
addNode: addNode,
deleteAllNodes: deleteAllNodes,
deploy: deploy
};

View File

@@ -0,0 +1,92 @@
/**
* 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 when = require('when');
var should = require("should");
var fs = require('fs-extra');
var helper = require("../editor_helper");
var editWindow = require('../pageobjects/workspace/editWindow_page');
var debugTab = require('../pageobjects/workspace/debugTab_page');
var workspace = require('../pageobjects/workspace/workspace_page');
var nodeWidth = 200;
describe('Node-RED main page', function() {
beforeEach(function() {
workspace.deleteAllNodes();
});
before(function() {
browser.windowHandleMaximize();
browser.call(function () {
return when.promise(function(resolve, reject) {
helper.startServer(function() {
resolve();
});
});
});
browser.url(helper.url());
browser.waitForExist('#palette_node_inject');
});
after(function() {
browser.call(function () {
return when.promise(function(resolve, reject) {
helper.stopServer(function() {
resolve();
});
});
});
});
it('should have a right title', function () {
browser.getTitle().should.equal('Node-RED');
});
it('should output a timestamp', function() {
var injectNode = workspace.addNode("inject");
var debugNode = workspace.addNode("debug", nodeWidth);
injectNode.connect(debugNode);
workspace.deploy();
debugTab.open();
debugTab.clearMessage();
injectNode.clickLeftButton();
debugTab.getMessage().should.within(1500000000000, 3000000000000);
});
it('should set a message property to a fixed value', function() {
var injectNode = workspace.addNode("inject");
var changeNode = workspace.addNode("change", nodeWidth);
var debugNode = workspace.addNode("debug", nodeWidth * 2);
changeNode.edit();
browser.setValue('.node-input-rule-property-value', 'Hello World!');
editWindow.clickOk();
injectNode.connect(changeNode);
changeNode.connect(debugNode);
workspace.deploy();
debugTab.open();
debugTab.clearMessage();
injectNode.clickLeftButton();
debugTab.getMessage().should.be.equal('"Hello World!"');
});
});

271
test/editor/wdio.conf.js Normal file
View File

@@ -0,0 +1,271 @@
/**
* 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.
**/
exports.config = {
//
// ==================
// Specify Test Files
// ==================
// Define which test specs should run. The pattern is relative to the directory
// from which `wdio` was called. Notice that, if you are calling `wdio` from an
// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
// directory is where your package.json resides, so `wdio` will be called from there.
//
specs: [
'./test/editor/**/*_uispec.js'
],
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
],
//
// ============
// Capabilities
// ============
// Define your capabilities here. WebdriverIO can run multiple capabilities at the same
// time. Depending on the number of capabilities, WebdriverIO launches several test
// sessions. Within your capabilities you can overwrite the spec and exclude options in
// order to group specific specs to a specific capability.
//
// First, you can define how many instances should be started at the same time. Let's
// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
// set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
// files and you set maxInstances to 10, all spec files will get tested at the same time
// and 30 processes will get spawned. The property handles how many capabilities
// from the same test should run tests.
//
maxInstances: 10,
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:
// https://docs.saucelabs.com/reference/platforms-configurator
//
capabilities: [{
// maxInstances can get overwritten per capability. So if you have an in-house Selenium
// grid with only 5 firefox instances available you can make sure that not more than
// 5 instances get started at a time.
maxInstances: 5,
//
browserName: 'chrome',
chromeOptions: {
// Runs tests without opening a broser.
args: ['--headless', '--disable-gpu', 'window-size=1920,1080'],
// Runs tests with opening a broser.
// args: ['--disable-gpu'],
},
}],
//
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
//
// By default WebdriverIO commands are executed in a synchronous way using
// the wdio-sync package. If you still want to run your tests in an async way
// e.g. using promises you can set the sync option to false.
sync: true,
//
// Level of logging verbosity: silent | verbose | command | data | result | error
logLevel: 'silent',
//
// Enables colors for log output.
coloredLogs: true,
//
// Warns when a deprecated command is used
deprecationWarnings: false,
//
// If you only want to run your tests until a specific amount of tests have failed use
// bail (default is 0 - don't bail, run all tests).
bail: 0,
//
// Saves a screenshot to a given path if a command fails.
screenshotPath: './test/errorShots/',
//
// Set a base URL in order to shorten url command calls. If your `url` parameter starts
// with `/`, the base url gets prepended, not including the path portion of your baseUrl.
// If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url
// gets prepended directly.
baseUrl: 'http://localhost',
//
// Default timeout for all waitFor* commands.
waitforTimeout: 10000,
//
// Default timeout in milliseconds for request
// if Selenium Grid doesn't send response
connectionRetryTimeout: 90000,
//
// Default request retries count
connectionRetryCount: 3,
//
// Initialize the browser instance with a WebdriverIO plugin. The object should have the
// plugin name as key and the desired plugin options as properties. Make sure you have
// the plugin installed before running any tests. The following plugins are currently
// available:
// WebdriverCSS: https://github.com/webdriverio/webdrivercss
// WebdriverRTC: https://github.com/webdriverio/webdriverrtc
// Browserevent: https://github.com/webdriverio/browserevent
// plugins: {
// webdrivercss: {
// screenshotRoot: 'my-shots',
// failedComparisonsRoot: 'diffs',
// misMatchTolerance: 0.05,
// screenWidth: [320,480,640,1024]
// },
// webdriverrtc: {},
// browserevent: {}
// },
//
// Test runner services
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
port: '9515',
path: '/',
services: ['chromedriver'],
//
// Framework you want to run your specs with.
// The following are supported: Mocha, Jasmine, and Cucumber
// see also: http://webdriver.io/guide/testrunner/frameworks.html
//
// Make sure you have the wdio adapter package for the specific framework installed
// before running any tests.
framework: 'mocha',
//
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: http://webdriver.io/guide/reporters/dot.html
reporters: ['spec'],
//
// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
mochaOpts: {
timeout: 20000,
ui: 'bdd'
},
//
// =====
// Hooks
// =====
// WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
// it and to build services around it. You can either apply a single function or an array of
// methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
// resolved to continue.
/**
* Gets executed once before all workers get launched.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
*/
// onPrepare: function (config, capabilities) {
// },
/**
* Gets executed just before initialising the webdriver session and test framework. It allows you
* to manipulate configurations depending on the capability or spec.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
// beforeSession: function (config, capabilities, specs) {
// },
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
// before: function (capabilities, specs) {
// },
/**
* Runs before a WebdriverIO command gets executed.
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
*/
// beforeCommand: function (commandName, args) {
// },
/**
* Hook that gets executed before the suite starts
* @param {Object} suite suite details
*/
// beforeSuite: function (suite) {
// },
/**
* Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
* @param {Object} test test details
*/
// beforeTest: function (test) {
// },
/**
* Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
* beforeEach in Mocha)
*/
// beforeHook: function () {
// },
/**
* Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
* afterEach in Mocha)
*/
// afterHook: function () {
// },
/**
* Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
* @param {Object} test test details
*/
// afterTest: function (test) {
// },
/**
* Hook that gets executed after the suite has ended
* @param {Object} suite suite details
*/
// afterSuite: function (suite) {
// },
/**
* Runs after a WebdriverIO command gets executed
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
* @param {Number} result 0 - command success, 1 - command error
* @param {Object} error error object if any
*/
// afterCommand: function (commandName, args, result, error) {
// },
/**
* Gets executed after all tests are done. You still have access to all global variables from
* the test.
* @param {Number} result 0 - test pass, 1 - test fail
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// after: function (result, capabilities, specs) {
// },
/**
* Gets executed right after terminating the webdriver session.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// afterSession: function (config, capabilities, specs) {
// },
/**
* Gets executed after all workers got shut down and the process is about to exit.
* @param {Object} exitCode 0 - success, 1 - fail
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
*/
// onComplete: function(exitCode, config, capabilities) {
// }
}

View File

@@ -28,22 +28,73 @@ describe('inject node', function() {
helper.unload();
});
it('should inject once', function(done) {
it('should inject once with default delay property', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n1 = helper.getNode("n1");
n1.should.have.property('onceDelay', 100);
done();
});
});
helper.load(injectNode, [{id:"n1", type:"inject",
payload:"payload", topic: "t1",
it('should inject once with default delay', function(done) {
var timestamp = new Date();
timestamp.setSeconds(timestamp.getSeconds() + 1);
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 't1');
msg.should.have.property('payload', 'payload');
done();
try {
msg.should.have.property('topic', 't1');
msg.should.have.property('payload');
should(msg.payload).be.lessThan(timestamp.getTime());
done();
} catch(err) {
done(err);
}
});
});
});
it('should inject once with 500 msec. delay', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, onceDelay: 0.5, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n1 = helper.getNode("n1");
n1.should.have.property('onceDelay', 500);
done();
});
});
it('should inject once with delay of two seconds', function(done) {
this.timeout(2700); // have to wait for the inject with delay of two seconds
var timestamp = new Date();
timestamp.setSeconds(timestamp.getSeconds() + 1);
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date",
once: true, onceDelay: 2, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 't1');
should(msg.payload).be.greaterThan(timestamp.getTime());
done();
});
});
});
it('should inject repeatedly', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject",
@@ -66,6 +117,30 @@ describe('inject node', function() {
});
});
it('should inject once with delay of two seconds and repeatedly', function(done) {
var timestamp = new Date();
timestamp.setSeconds(timestamp.getSeconds() + 1);
helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1",
payload:"",payloadType:"date", repeat: 0.2,
once: true, onceDelay: 1.2, wires:[["n2"]] },
{id:"n2", type:"helper"}],
function() {
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function(msg) {
msg.should.have.property('topic', 't1');
should(msg.payload).be.greaterThan(timestamp.getTime());
count += 1;
if (count > 2) {
helper.clearFlows().then(function() {
done();
});
}
});
});
});
it('should inject with cron', function(done) {
helper.load(injectNode, [{id:"n1", type:"inject",
payloadType:"date", topic: "t3",

View File

@@ -25,13 +25,19 @@ describe('debug node', function() {
helper.startServer(done);
});
beforeEach(function (done) {
setTimeout(function() {
done();
}, 55);
});
afterEach(function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"debug", name: "Debug", complete:"false" }];
var flow = [{id:"n1", type:"debug", name:"Debug", complete:"false" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'Debug');
@@ -41,31 +47,31 @@ describe('debug node', function() {
});
it('should publish on input', function(done) {
var flow = [{id:"n1", type:"debug", name: "Debug" }];
var flow = [{id:"n1", type:"debug", name:"Debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",name:"Debug",msg:"test",
format:"string[4]",property:"payload"}
});
}]);
}, done);
});
});
it('should publish to console', function(done) {
var flow = [{id:"n1", type:"debug", console: "true"}];
var flow = [{id:"n1", type:"debug", console:"true"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
var count = 0;
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"test",property:"payload",format:"string[4]"}
});
}]);
count++;
}, function() {
try {
@@ -86,44 +92,72 @@ describe('debug node', function() {
});
it('should publish complete message', function(done) {
var flow = [{id:"n1", type:"debug", complete: "true" }];
var flow = [{id:"n1", type:"debug", complete:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{\n "payload": "test"\n}',format:"Object"}
});
}]);
}, done);
});
});
it('should publish complete message to console', function(done) {
var flow = [{id:"n1", type:"debug", complete:"true", console:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{\n "payload": "test"\n}',format:"Object"}
}]);
}, function() {
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "debug";
});
logEvents.should.have.length(1);
var tstmp = logEvents[0][0].timestamp;
logEvents[0][0].should.eql({level:helper.log().INFO, id:"n1",type:"debug",msg:'\n{ payload: \'test\' }',timestamp:tstmp});
done();
} catch(err) {
done(err);
}
});
});
});
it('should publish other property', function(done) {
var flow = [{id:"n1", type:"debug", complete: "foo" }];
var flow = [{id:"n1", type:"debug", complete:"foo" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test", foo:"bar"});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"bar",property:"foo",format:"string[3]"}
});
}]);
}, done);
});
});
it('should publish multi-level properties', function(done) {
var flow = [{id:"n1", type:"debug", complete: "foo.bar" }];
var flow = [{id:"n1", type:"debug", complete:"foo.bar" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test", foo: {bar: "bar"}});
n1.emit("input", {payload:"test", foo: {bar:"bar"}});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"bar",property:"foo.bar",format:"string[3]"}
});
}]);
}, done);
});
});
@@ -135,9 +169,9 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: new Error("oops")});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'{"name":"Error","message":"oops"}',property:"payload",format:"error"}
});
}]);
}, done);
});
});
@@ -149,9 +183,37 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: true});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg: 'true',property:"payload",format:"boolean"}
});
}]);
}, done);
});
});
it('should publish a number', function(done) {
var flow = [{id:"n1", type:"debug", console:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: 7});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"7",property:"payload",format:"number"}
}]);
}, done);
});
});
it('should publish a NaN', function(done) {
var flow = [{id:"n1", type:"debug", console:"true" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Number.NaN});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"NaN",property:"payload",format:"number"}
}]);
}, done);
});
});
@@ -163,9 +225,23 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {});
}, function(msg) {
JSON.parse(msg).should.eql({
topic:"debug",data:{id:"n1",msg: '(undefined)',property:"payload",format:"undefined"}
});
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"undefined"}
}]);
}, done);
});
});
it('should publish a null', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:null});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"null"}
}]);
}, done);
});
});
@@ -177,10 +253,10 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: {type:'foo'}});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg:'{\n "type": "foo"\n}',property:"payload",format:"Object"}
});
}]);
}, done);
});
});
@@ -192,11 +268,11 @@ describe('debug node', function() {
websocket_test(function() {
n1.emit("input", {payload: [0,1,2,3]});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{id:"n1",msg: '[\n 0,\n 1,\n 2,\n 3\n]',format:"array[4]",
property:"payload"}
});
}]);
}, done);
});
});
@@ -210,27 +286,81 @@ describe('debug node', function() {
o.o = o;
n1.emit("input", {payload: o});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{
id:"n1",
msg:'{\n "name": "bar",\n "o": "[Circular ~]"\n}',
property:"payload",format:"Object"
}
});
}]);
}, done);
});
});
it('should publish an object to console', function(done) {
var flow = [{id:"n1", type:"debug", console:"true"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {type:'foo'}});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:'{\n "type": "foo"\n}',property:"payload",format:"Object"}
}]);
}, function() {
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "debug";
});
logEvents.should.have.length(1);
var tstmp = logEvents[0][0].timestamp;
logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:'\n{ type: \'foo\' }',timestamp:tstmp});
done();
} catch(err) {
done(err);
}
});
});
});
it('should publish a string after a newline to console if the string contains \\n', function(done) {
var flow = [{id:"n1", type:"debug", console:"true"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload:"test\ntest"});
}, function(msg) {
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"test\ntest",property:"payload",format:"string[9]"}
}]);
}, function() {
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "debug";
});
logEvents.should.have.length(1);
var tstmp = logEvents[0][0].timestamp;
logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:"\ntest\ntest",timestamp:tstmp});
done();
} catch(err) {
done(err);
}
});
});
});
it('should truncate a long message', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Array(1002).join("X")});
n1.emit("input", {payload:Array(1002).join("X")});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql({
a.should.eql([{
topic:"debug",
data:{
id:"n1",
@@ -238,6 +368,130 @@ describe('debug node', function() {
property:"payload",
format:"string[1001]"
}
}]);
}, done);
});
});
it('should truncate a long string in the object', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {foo: Array(1002).join("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql([{
topic:"debug",
data:{
id:"n1",
msg:'{\n "foo": "'+Array(1001).join("X")+'..."\n}',
property:"payload",
format:"Object"
}
}]);
}, done);
});
});
it('should truncate a large array', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Array(1001).fill("X")});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql([{
topic:"debug",
data:{
id:"n1",
msg:JSON.stringify({
__encoded__: true,
type: "array",
data: Array(1000).fill("X"),
length: 1001
},null," "),
property:"payload",
format:"array[1001]"
}
}]);
}, done);
});
});
it('should truncate a large array in the object', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {foo: Array(1001).fill("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a.should.eql([{
topic:"debug",
data:{
id:"n1",
msg:JSON.stringify({
foo:{
__encoded__: true,
type: "array",
data: Array(1000).fill("X"),
length: 1001
}
},null," "),
property:"payload",
format:"Object"
}
}]);
}, done);
});
});
it('should truncate a large buffer', function(done) {
var flow = [{id:"n1", type:"debug" }];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: Buffer(501).fill("\"")});
}, function(msg) {
var a = JSON.parse(msg);
a[0].should.eql({
topic:"debug",
data:{
id:"n1",
msg: Array(1001).join("2"),
property:"payload",
format:"buffer[501]"
}
});
}, done);
});
});
it('should truncate a large buffer in the object', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: {foo: Buffer(1001).fill("X")}});
}, function(msg) {
var a = JSON.parse(msg);
a[0].should.eql({
topic:"debug",
data:{
id:"n1",
msg:JSON.stringify({
foo:{
type: "Buffer",
data: Array(1000).fill(88),
__encoded__: true,
length: 1001
}
},null," "),
property:"payload",
format:"Object"
}
});
}, done);
});
@@ -248,17 +502,17 @@ describe('debug node', function() {
helper.load(debugNode, flow, function() {
var n1 = helper.getNode("n1");
websocket_test(function() {
n1.emit("input", {payload: new Buffer('HELLO', 'utf8')});
n1.emit("input", {payload: new Buffer.from('HELLO', 'utf8')});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",
data:{
id:"n1",
msg: '48454c4c4f',
msg:'48454c4c4f',
property:"payload",
format: "buffer[5]"
format:"buffer[5]"
}
});
}]);
}, done);
});
});
@@ -276,9 +530,9 @@ describe('debug node', function() {
n1.emit("input", {payload:"message 2"});
});
}, function(msg) {
JSON.parse(msg).should.eql({
JSON.parse(msg).should.eql([{
topic:"debug",data:{id:"n1",msg:"message 2",property:"payload",format:"string[9]"}
});
}]);
}, done);
});
});
@@ -324,6 +578,18 @@ describe('debug node', function() {
});
});
describe('get', function() {
it('should return the view.html', function(done) {
var flow = [{id:"n1", type:"debug"}];
helper.load(debugNode, flow, function() {
helper.request()
.get('/debug/view/view.html')
.expect(200)
.end(done);
});
});
});
});
function websocket_test(open_callback, message_callback, done_callback) {
@@ -331,8 +597,12 @@ function websocket_test(open_callback, message_callback, done_callback) {
var close_callback = function() { ws.close(); };
ws.on('open', function() { open_callback(close_callback); });
ws.on('message', function(msg) {
message_callback(msg, close_callback);
ws.close();
done_callback();
try {
message_callback(msg, close_callback);
ws.close();
done_callback();
} catch(err) {
done_callback(err);
}
});
}

View File

@@ -0,0 +1,118 @@
/**
* 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 linkNode = require("../../../../nodes/core/core/60-link.js");
var helper = require("../../helper.js");
describe('link Node', function() {
before(function(done) {
helper.startServer(done);
});
afterEach(function() {
helper.unload();
});
it('should be loaded (link in)', function(done) {
var flow = [{id:"n1", type:"link in", name: "link-in" }];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'link-in');
done();
});
});
it('should be loaded (link out)', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out" }];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'link-out');
done();
});
});
it('should be linked', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2"]},
{id:"n2", type:"link in", name: "link-in", wires:[["n3"]]},
{id:"n3", type:"helper"}];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
var n3 = helper.getNode("n3");
n3.on("input", function(msg) {
try {
msg.should.have.property('payload', 'hello');
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"hello"});
});
});
it('should be linked to multiple nodes', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2", "n3"]},
{id:"n2", type:"link in", name: "link-in0", wires:[["n4"]]},
{id:"n3", type:"link in", name: "link-in1", wires:[["n4"]]},
{id:"n4", type:"helper"} ];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
var n4 = helper.getNode("n4");
var count = 0;
n4.on("input", function (msg) {
try {
msg.should.have.property('payload', 'hello');
count++;
if(count == 2) {
done();
}
} catch(err) {
done(err);
}
});
n1.receive({payload:"hello"});
});
});
it('should be linked from multiple nodes', function(done) {
var flow = [{id:"n1", type:"link out", name: "link-out0", links:["n3"]},
{id:"n2", type:"link out", name: "link-out1", links:["n3"]},
{id:"n3", type:"link in", name: "link-in", wires:[["n4"]]},
{id:"n4", type:"helper"} ];
helper.load(linkNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var n4 = helper.getNode("n4");
var count = 0;
n4.on("input", function(msg) {
try {
msg.should.have.property('payload', 'hello');
count++;
if(count == 2) {
done();
}
} catch(err) {
done(err);
}
});
n1.receive({payload:"hello"});
n2.receive({payload:"hello"});
});
});
});

View File

@@ -76,11 +76,15 @@ describe('exec node', function() {
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal("echo");
msg.should.have.property("rc");
msg.rc.should.have.property("code",0);
msg = messages[1];
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal("ECHO");
msg.should.have.property("rc");
msg.rc.should.have.property("code",0);
msg = messages[2];
msg.should.have.property("payload");
@@ -88,7 +92,8 @@ describe('exec node', function() {
child_process.exec.restore();
done();
} catch(err) {
}
catch(err) {
child_process.exec.restore();
done(err);
}
@@ -136,20 +141,23 @@ describe('exec node', function() {
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal("echo and more");
msg.should.have.property("rc");
msg.rc.should.have.property("code",0);
msg = messages[1];
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal("ECHO AND MORE");
msg.should.have.property("rc");
msg.rc.should.have.property("code",0);
child_process.exec.restore();
done();
} catch(err) {
}
catch(err) {
child_process.exec.restore();
done(err);
}
};
n2.on("input", function(msg) {
messages[0] = msg;
completeTest();
@@ -169,7 +177,7 @@ describe('exec node', function() {
function(arg1, arg2, arg3, arg4) {
//console.log(arg1);
// arg3(error,stdout,stderr);
arg3("error",new Buffer([0x01,0x02,0x03,0x88]));
arg3("error",new Buffer([0x01,0x02,0x03,0x88]),new Buffer([0x01,0x02,0x03,0x88]));
});
helper.load(execNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -199,33 +207,11 @@ describe('exec node', function() {
// Although Windows timeout command is equivalent to sleep, this cannot be used because it promptly outputs a message.
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"0.3", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
} else {
}
else {
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"0.3", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
}
helper.load(execNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.have.property("signal","SIGTERM");
done();
});
n1.receive({});
});
});
it('should be able to kill a long running command', function(done) {
var flow;
if (osType === "Windows_NT") {
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
} else {
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
}
helper.load(execNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
@@ -236,9 +222,42 @@ describe('exec node', function() {
msg.should.have.property("payload");
msg.payload.should.have.property("signal","SIGTERM");
done();
} catch(err) {
done(err);
}
catch(err) { done(err); }
});
n1.receive({});
});
});
it('should be able to kill a long running command', function(done) {
var flow;
if (osType === "Windows_NT") {
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
}
else {
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
}
helper.load(execNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n2.on("input", function(msg) {
try {
msg.should.have.property("rc");
msg.rc.should.have.property("code",null);
msg.rc.should.have.property("signal","SIGTERM");
} catch(err) { done(err); }
});
n4.on("input", function(msg) {
try {
msg.should.have.property("payload");
msg.payload.should.have.property("signal","SIGTERM");
done();
}
catch(err) { done(err); }
});
setTimeout(function() {
n1.receive({kill:""});
@@ -262,14 +281,19 @@ describe('exec node', function() {
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n2.on("input", function(msg) {
try {
msg.should.have.property("rc");
msg.rc.should.have.property("code",null);
msg.rc.should.have.property("signal","SIGINT");
} catch(err) { done(err); }
});
n4.on("input", function(msg) {
try {
msg.should.have.property("payload");
msg.payload.should.have.property("signal",sig);
done();
} catch(err) {
done(err);
}
} catch(err) { done(err); }
});
setTimeout(function() {
n1.receive({kill:"SIGINT"});
@@ -278,7 +302,6 @@ describe('exec node', function() {
});
});
it('should return the rc for a failing command', function(done) {
var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"error", addpay:false, append:"", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
@@ -305,6 +328,9 @@ describe('exec node', function() {
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal("error");
msg.should.have.property("rc");
msg.rc.should.have.property("code",1);
msg.rc.should.have.property("message",undefined);
msg = messages[1];
msg.should.have.property("payload");
@@ -317,7 +343,8 @@ describe('exec node', function() {
child_process.exec.restore();
done();
} catch(err) {
}
catch(err) {
child_process.exec.restore();
done(err);
}
@@ -369,9 +396,8 @@ describe('exec node', function() {
msg.payload.should.be.a.String();
msg.payload.should.equal(expected);
done();
} catch(err) {
done(err);
}
catch(err) { done(err); }
});
n1.receive({payload:"hello world"});
});
@@ -397,10 +423,13 @@ describe('exec node', function() {
var n4 = helper.getNode("n4");
n2.on("input", function(msg) {
//console.log(msg);
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal(expected);
done();
try {
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal(expected);
done();
}
catch(err) { done(err); }
});
n1.receive({payload:12345});
});
@@ -431,9 +460,8 @@ describe('exec node', function() {
msg.payload.length.should.equal(7);
}
done();
} catch(err) {
done(err);
}
catch(err) { done(err); }
});
n1.receive({payload:new Buffer([0x01,0x02,0x03,0x88])});
});
@@ -474,12 +502,12 @@ describe('exec node', function() {
should.exist(msg.payload);
msg.payload.should.have.property("code",0);
done();
} catch(err) {
}
catch(err) {
done(err);
}
};
n2.on("input", function(msg) {
messages[0] = msg;
completeTest();
@@ -501,10 +529,13 @@ describe('exec node', function() {
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.have.property("code");
msg.payload.code.should.be.below(0);
done();
try {
msg.should.have.property("payload");
msg.payload.should.have.property("code");
msg.payload.code.should.be.below(0);
done();
}
catch(err) { done(err); }
});
n1.receive({payload:null});
});
@@ -513,6 +544,7 @@ describe('exec node', function() {
it('should return an error for a failing command', function(done) {
var flow;
var expected;
var expectedFound = false;
if (osType === "Windows_NT") {
// Cannot use mkdir because Windows mkdir command automatically creates non-existent directories.
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"},
@@ -521,7 +553,7 @@ describe('exec node', function() {
} else {
flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"mkdir /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
expected = "mkdir: /foo/bar/doo: No such file or directory\n";
expected = ' directory';
}
helper.load(execNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -529,14 +561,26 @@ describe('exec node', function() {
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n3.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.be.a.String();
msg.payload.should.equal(expected);
try {
msg.should.have.property("payload");
msg.payload.should.be.a.String();
if (msg.payload.indexOf(expected) >= 0) {
// The error text on the stderr stream might get sent in more than one piece.
// We only need to know that it occurred before the return code is sent,
// as checked below in node n4.
expectedFound = true;
}
}
catch(err) { done(err); }
});
n4.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.have.property("code",1);
done();
try {
expectedFound.should.be.true;
msg.should.have.property("payload");
msg.payload.should.have.property("code",1);
done();
}
catch(err) { done(err); }
});
n1.receive({payload:null});
});
@@ -557,10 +601,13 @@ describe('exec node', function() {
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.have.property("code",null);
msg.payload.should.have.property("signal","SIGTERM");
done();
try {
msg.should.have.property("payload");
msg.payload.should.have.property("code",null);
msg.payload.should.have.property("signal","SIGTERM");
done();
}
catch(err) { done(err); }
});
n1.receive({});
});
@@ -581,9 +628,12 @@ describe('exec node', function() {
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.have.property("signal","SIGTERM");
done();
try {
msg.should.have.property("payload");
msg.payload.should.have.property("signal","SIGTERM");
done();
}
catch(err) { done(err); }
});
setTimeout(function() {
n1.receive({kill:""});
@@ -608,9 +658,12 @@ describe('exec node', function() {
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.have.property("signal",sig);
done();
try {
msg.should.have.property("payload");
msg.payload.should.have.property("signal",sig);
done();
}
catch(err) { done(err); }
});
setTimeout(function() {
n1.receive({kill:"SIGINT"});

View File

@@ -52,6 +52,21 @@ describe('function node', function() {
});
});
it('should send returned message using send()', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg);"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should pass through _topic', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return msg;"},
{id:"n2", type:"helper"}];
@@ -160,6 +175,22 @@ describe('function node', function() {
});
});
it('should get keys in global context', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.keys();return msg;"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().global.set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', ['count']);
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
function testNonObjectMessage(functionText,done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:functionText},
{id:"n2", type:"helper"}];
@@ -228,6 +259,251 @@ describe('function node', function() {
}
});
});
it('should handle node.on()', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.on('close',function(){node.log('closed')});"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
n1.receive({payload:"foo",topic: "bar"});
helper.getNode("n1").close();
try {
helper.log().called.should.be.true();
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "function";
});
logEvents.should.have.length(1);
var msg = logEvents[0][0];
msg.should.have.property('level', helper.log().INFO);
msg.should.have.property('id', 'n1');
msg.should.have.property('type', 'function');
msg.should.have.property('msg', 'closed');
done();
} catch(err) {
done(err);
}
});
});
it('should set node context', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0');return msg;"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
n1.context().get("count").should.equal("0");
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should get node context', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.get('count');return msg;"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', '0');
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should get keys in node context', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.keys();return msg;"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', ['count']);
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should set flow context', function(done) {
var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0');return msg;"},
{id:"n2", type:"helper",z:"flowA"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
n2.context().flow.get("count").should.equal("0");
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should get flow context', function(done) {
var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.get('count');return msg;"},
{id:"n2", type:"helper",z:"flowA"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().flow.set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', '0');
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should get flow context', function(done) {
var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.flow.get('count');return msg;"},
{id:"n2", type:"helper",z:"flowA"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().flow.set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', '0');
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should get keys in flow context', function(done) {
var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.keys();return msg;"},
{id:"n2", type:"helper",z:"flowA"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().flow.set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', ['count']);
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should set global context', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.set('count','0');return msg;"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
n2.context().global.get("count").should.equal("0");
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should get global context', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('count');return msg;"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().global.set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', '0');
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should get global context', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.global.get('count');return msg;"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n1.context().global.set("count","0");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', '0');
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should handle setTimeout()', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"setTimeout(function(){node.send(msg);},1000);"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
var endTime = process.hrtime(startTime);
var nanoTime = endTime[0] * 1000000000 + endTime[1];
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
if (900000000 < nanoTime && nanoTime < 1100000000) {
done();
} else {
try {
should.fail(null, null, "Delayed time was not between 900 and 1100 ms");
} catch (err) {
done(err);
}
}
});
var startTime = process.hrtime();
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should handle setInterval()', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"setInterval(function(){node.send(msg);},100);"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
count++;
if (count > 2) {
done();
}
});
n1.receive({payload:"foo",topic: "bar"});
});
});
it('should handle clearInterval()', function(done) {
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"var id=setInterval(null,100);setTimeout(function(){clearInterval(id);node.send(msg);},1000);"},
{id:"n2", type:"helper"}];
helper.load(functionNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
done();
});
n1.receive({payload:"foo",topic: "bar"});
});
});
describe('Logger', function () {
it('should log an Info Message', function (done) {
var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "node.log('test');"}];

View File

@@ -37,9 +37,40 @@ describe('template node', function() {
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'payload=foo');
msg.should.have.property('template', '{{payload}}');
done();
});
n1.receive({payload:"foo",topic: "bar"});
n1.receive({payload:"foo",topic: "bar", template: "{{payload}}"});
});
});
it('should modify template from msg.template', function(done) {
var flow = [{id:"n1", type:"template", field:"template", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}];
helper.load(templateNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
msg.should.have.property('template', 'payload=foo');
done();
});
n1.receive({payload:"foo", topic: "bar", template: "payload={{payload}}"});
});
});
it('should modify payload from msg.template', function(done) {
var flow = [{id:"n1", type:"template", field:"payload", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}];
helper.load(templateNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'topic=bar');
msg.should.have.property('template', 'topic={{topic}}');
done();
});
n1.receive({payload:"foo", topic: "bar", template: "topic={{topic}}"});
});
});
@@ -88,6 +119,18 @@ describe('template node', function() {
});
});
it('should handle escape characters in Mustache format and JSON output mode', function(done) {
var flow = [{id:"n1", type:"template", field:"payload", syntax:"mustache", template:"{\"data\":\"{{payload}}\"}", output:"json", wires:[["n2"]]},{id:"n2",type:"helper"}];
helper.load(templateNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.payload.should.have.property('data', 'line\t1\nline\\2\r\nline\b3\f');
done();
});
n1.receive({payload:"line\t1\nline\\2\r\nline\b3\f"});
});
});
it('should modify payload in plain text mode', function(done) {
var flow = [{id:"n1", type:"template", field:"payload", syntax:"plain", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}];

View File

@@ -347,7 +347,7 @@ describe('delay Node', function() {
* @param delay - the variable delay: milliseconds
*/
function variableDelayTest(aTimeoutFrom, aTimeoutTo, aTimeoutUnit, delay, done) {
var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delayv","timeout":5,"timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":aTimeoutFrom,"randomLast":aTimeoutTo,"randomUnits":aTimeoutUnit,"drop":false,"wires":[["helperNode1"]]},
var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delayv","timeout":0.5,"timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":aTimeoutFrom,"randomLast":aTimeoutTo,"randomUnits":aTimeoutUnit,"drop":false,"wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(delayNode, flow, function() {
var delayNode1 = helper.getNode("delayNode1");
@@ -400,12 +400,16 @@ describe('delay Node', function() {
variableDelayTest("200", "300", "milliseconds", 250, done);
});
it('variable delay is zero if msg.delay not specified', function(done) {
variableDelayTest("0", "50", "milliseconds", null, done);
it('variable delay is the default if msg.delay not specified', function(done) {
variableDelayTest("450", "550", "milliseconds", null, done);
});
it('variable delay is zero if msg.delay is zero', function(done) {
variableDelayTest("0", "20", "milliseconds", 0, done);
});
it('variable delay is zero if msg.delay is negative', function(done) {
variableDelayTest("0", "50", "milliseconds", -250, done);
variableDelayTest("0", "20", "milliseconds", -250, done);
});
/**

View File

@@ -15,8 +15,10 @@
**/
var should = require("should");
var sinon = require("sinon");
var helper = require("../../helper.js");
var triggerNode = require("../../../../nodes/core/core/89-trigger.js");
var RED = require("../../../../red/red.js");
describe('trigger node', function() {
@@ -81,14 +83,17 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", '1');
c+=1;
}
else {
msg.should.have.a.property("payload", '0');
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", '1');
c+=1;
}
else {
msg.should.have.a.property("payload", '0');
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:null});
});
@@ -161,6 +166,155 @@ describe('trigger node', function() {
});
});
it('should handle multiple topics as one if not asked to handle', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"all", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
try {
c += 1;
if (c === 1) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "A");
}
else if (c === 2) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "A");
done();
}
} catch(err) {
done(err);
}
});
n1.emit("input", {payload:1,topic:"A"});
n1.emit("input", {payload:2,topic:"B"});
n1.emit("input", {payload:3,topic:"C"});
});
});
it('should handle multiple topics individually if asked to do so', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
try {
c += 1;
if (c === 1) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "A");
}
else if (c === 2) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "B");
}
else if (c === 3) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "C");
}
else if (c === 4) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "A");
}
else if (c === 5) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "B");
}
else if (c === 6) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "C");
done();
}
} catch(err) {
done(err);
}
});
n1.emit("input", {payload:1,topic:"A"});
n1.emit("input", {payload:2,topic:"B"});
n1.emit("input", {payload:3,topic:"C"});
});
});
it('should handle multiple topics individually, and extend one, if asked to do so', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", extend:"true", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
try {
c += 1;
if (c === 1) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "A");
}
else if (c === 2) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "B");
}
else if (c === 3) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "C");
}
else if (c === 4) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "A");
}
else if (c === 5) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "C");
}
else if (c === 6) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "B");
done();
}
} catch(err) {
done(err);
}
});
n1.emit("input", {payload:1,topic:"A"});
n1.emit("input", {payload:2,topic:"B"});
n1.emit("input", {payload:3,topic:"C"});
setTimeout( function() { n1.emit("input", {payload:2,topic:"B"})}, 20 );
});
});
it('should be able to return things from flow and global context variables', function(done) {
var spy = sinon.stub(RED.util, 'evaluateNodeProperty',
function(arg1, arg2, arg3, arg4) { return arg1; }
);
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"flow", op2:"bar", op2type:"global", duration:"20", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
try {
if (c === 0) {
msg.should.have.a.property("payload", "foo");
c+=1;
}
else {
msg.should.have.a.property("payload", "bar");
spy.restore();
done();
}
}
catch(err) { spy.restore(); done(err); }
});
n1.emit("input", {payload:null});
});
});
it('should be able to not output anything on first trigger', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"nul", op1:"true",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
@@ -187,8 +341,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
msg.should.have.a.property("payload", true);
c += 1;
try {
msg.should.have.a.property("payload", true);
c += 1;
}
catch(err) { done(err); }
});
setTimeout( function() {
c.should.equal(1); // should only have had one output.
@@ -199,23 +356,30 @@ describe('trigger node', function() {
});
it('should be able to extend the delay', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op1:"false", op2:"true", duration:"100", wires:[["n2"]] },
var spy = sinon.stub(RED.util, 'evaluateNodeProperty',
function(arg1, arg2, arg3, arg4) { return arg1; }
);
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"flow", op1:"foo", op2:"bar", op2type:"global", duration:"100", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "true");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(149);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "foo");
c += 1;
}
else {
msg.should.have.a.property("payload", "bar");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(149);
spy.restore();
done();
}
}
catch(err) { spy.restore(); done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -233,16 +397,19 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -263,15 +430,18 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -292,15 +462,18 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -321,14 +494,17 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"Hello",topic:"World"});
});
@@ -342,19 +518,46 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", null);
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", null);
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"World"});
});
});
it('should handle string null as null on op2', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"null", op2:"null", duration:"40", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
try {
if (c === 0) {
msg.should.have.a.property("payload", null);
c+=1;
}
else {
msg.should.have.a.property("payload", null);
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"null"});
});
});
it('should be able to set infinite timeout, and clear timeout', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"0", extend: false, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
@@ -363,8 +566,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", 1);
try {
c += 1;
msg.should.have.a.property("payload", "1");
}
catch(err) { done(err); }
});
setTimeout( function() {
if (c === 2) { done(); }
@@ -388,8 +594,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", 1);
try {
c += 1;
msg.should.have.a.property("payload", "1");
}
catch(err) { done(err); }
});
setTimeout( function() {
if (c === 2) { done(); }
@@ -406,7 +615,7 @@ describe('trigger node', function() {
});
it('should be able to set a repeat, and clear loop by reset', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:-25, wires:[["n2"]] },
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", op1:"", op1type:"pay", duration:-25, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -414,9 +623,14 @@ describe('trigger node', function() {
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", "foo");
try {
msg.should.have.property('payload','foo');
msg.payload = "bar"; // try to provoke pass by reference error
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"foo"}); // trigger
n1.emit("input", {payload:"foo"}); // trigger
setTimeout( function() {
n1.emit("input", {reset:true}); // reset
},90);

View File

@@ -101,6 +101,8 @@ describe('websocket Node', function() {
helper.load(websocketNode, flow, function() {
createClient("n1").then(function(sock) {
done();
}).catch(function(err) {
done(err);
});
});
});
@@ -113,6 +115,8 @@ describe('websocket Node', function() {
done();
});
helper.clearFlows();
}).catch(function(err) {
done(err);
});
});
});
@@ -129,6 +133,8 @@ describe('websocket Node', function() {
done();
});
sock.send("hello");
}).catch(function(err) {
done(err);
});
});
});
@@ -145,6 +151,8 @@ describe('websocket Node', function() {
msg.should.have.property("text", "hello");
done();
});
}).catch(function(err) {
done(err);
});
});
});
@@ -161,6 +169,8 @@ describe('websocket Node', function() {
msg.should.have.property("payload", "hello");
done();
});
}).catch(function(err) {
done(err);
});
});
});
@@ -179,6 +189,8 @@ describe('websocket Node', function() {
helper.getNode("n2").send({
payload: "hello"
});
}).catch(function(err) {
done(err);
});
});
});
@@ -197,6 +209,8 @@ describe('websocket Node', function() {
helper.getNode("n3").send({
text: "hello"
});
}).catch(function(err) {
done(err);
});
});
});
@@ -216,6 +230,8 @@ describe('websocket Node', function() {
done();
},100);
helper.getNode("n2").send({topic: "hello"});
}).catch(function(err) {
done(err);
});
});
});
@@ -232,6 +248,8 @@ describe('websocket Node', function() {
done();
});
sock.send("hello");
}).catch(function(err) {
done(err);
});
});
});
@@ -248,6 +266,8 @@ describe('websocket Node', function() {
done();
});
sock.send('{"text":"hello"}');
}).catch(function(err) {
done(err);
});
});
});
@@ -272,10 +292,11 @@ describe('websocket Node', function() {
helper.getNode("n3").send({
payload: "hello"
});
});
when.all([def1.promise, def2.promise]).then(function() {
done();
return when.all([def1.promise, def2.promise]).then(function() {
done();
});
}).catch(function(err) {
done(err);
});
});
});

View File

@@ -0,0 +1,217 @@
/**
* 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 fs = require("fs-extra");
var path = require("path");
var should = require("should");
var helper = require("../../helper.js");
var watchNode = require("../../../../nodes/core/io/23-watch.js");
describe('watch Node', function() {
this.timeout(5000);
var resourcesDir = path.join(__dirname,"..","..","..","resources");
var baseDir = path.join(resourcesDir, "23-watch-test-dir");
var count = 0;
function prepareDir() {
var dirToWatch = path.join(baseDir, "base"+count);
fs.mkdirSync(dirToWatch);
count++;
return {
dirToWatch:dirToWatch,
file0ToWatch:path.join(dirToWatch, "file0.txt"),
file1ToWatch:path.join(dirToWatch, "file1.txt"),
subDirToWatch:path.join(dirToWatch, "subdir"),
file2ToWatch:path.join(dirToWatch, "subdir", "file2.txt")
}
}
function wait(msec, func) {
setTimeout(func, msec);
}
before(function(done) {
fs.ensureDirSync(baseDir);
done();
});
after(function(done) {
fs.removeSync(baseDir);
done();
});
afterEach(function(done) {
helper.unload();
done();
});
function testWatch(flow, change_func, results, done) {
var processed = {};
helper.load(watchNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
var len = Object.keys(results).length;
n2.on("input", function(msg) {
try {
// console.log(msg);
msg.should.have.property('file');
var file = msg.file;
if (file in processed) {
// multiple messages come in rare case
return;
}
processed[file] = true;
if (file === 'subdir') {
// On OSX, we get a change event on subdir when a file inside changes.
// On Travis, we don't. *sigh*
return;
}
(file in results).should.be.true();
var result = results[file];
msg.should.have.property('payload', result.payload);
msg.should.have.property('type', result.type);
if('size' in result) {
msg.should.have.property('size', result.size);
}
count++;
if(count === len) {
n1.close();
// wait for close
wait(100, done);
}
}catch(err) {
done(err);
}
});
// wait for preparation
wait(500, change_func);
});
}
it('should watch a file to be changed', function(done) {
var files = prepareDir();
fs.writeFileSync(files.file0ToWatch, '');
var flow = [{id:"n1", type:"watch", name: "watch",
files: files.file0ToWatch, recursive: false,
wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = {
'file0.txt' : {
'payload' : files.file0ToWatch,
'topic': files.file0ToWatch,
'type': 'file',
'size': 5
}
};
testWatch(flow, function() {
fs.appendFileSync(files.file0ToWatch, "ABCDE");
}, results, done);
});
it('should watch multiple files to be changed', function(done) {
var files = prepareDir();
fs.writeFileSync(files.file0ToWatch, '');
fs.writeFileSync(files.file1ToWatch, '');
var flow = [{id:"n1", type:"watch", name: "watch",
files: files.file0ToWatch +","+files.file1ToWatch, recursive: false,
wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = {
'file0.txt' : {
'payload' : files.file0ToWatch,
'topic': files.file0ToWatch,
'type': 'file'//,
// 'size': 5
},
'file1.txt' : {
'payload' : files.file1ToWatch,
'topic': files.file1ToWatch,
'type': 'file'//,
// 'size': 3
}
};
testWatch(flow, function() {
fs.appendFileSync(files.file0ToWatch, "ABCDE");
fs.appendFileSync(files.file1ToWatch, "123");
}, results, done);
});
it('should watch attribute of a file to be changed', function(done) {
var files = prepareDir();
fs.writeFileSync(files.file0ToWatch, '');
fs.chmodSync(files.file0ToWatch, 0o444);
var flow = [{id:"n1", type:"watch", name: "watch",
files: files.file0ToWatch, recursive: false,
wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = {
'file0.txt' : {
'payload' : files.file0ToWatch,
'topic': files.file0ToWatch,
'type': 'file'//,
// 'size': 0
}
};
testWatch(flow, function() {
fs.chmodSync(files.file0ToWatch, 0o777);
}, results, done);
});
it('should watch a file in a directory to be changed', function(done) {
var files = prepareDir();
fs.writeFileSync(files.file0ToWatch, '');
var flow = [{id:"n1", type:"watch", name: "watch",
files: files.dirToWatch, recursive: true,
wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = {
'file0.txt' : {
'payload' : files.file0ToWatch,
'topic': files.file0ToWatch,
'type': 'file'//,
// 'size': 5
}
};
testWatch(flow, function() {
fs.appendFileSync(files.file0ToWatch, "ABCDE");
}, results, done);
});
it('should watch a sub directory in a directory to be changed', function(done) {
var files = prepareDir();
fs.mkdirSync(files.subDirToWatch);
var flow = [{id:"n1", type:"watch", name: "watch",
files: files.dirToWatch, recursive: true,
wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = {
'file2.txt': {
payload: files.file2ToWatch,
type: 'file'//,
// size: 5
}
};
testWatch(flow, function() {
fs.appendFileSync(files.file2ToWatch, "ABCDE");
}, results, done);
});
});

View File

@@ -0,0 +1,229 @@
/**
* 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 net = require("net");
var should = require("should");
var helper = require("../../helper.js");
var tcpinNode = require("../../../../nodes/core/io/31-tcpin.js");
describe('TCP in Node', function() {
var port = 9200;
var server = undefined;
var server_port = 9300;
var reply_data = undefined;
before(function(done) {
done();
});
after(function() {
});
beforeEach(function(done) {
startServer(done);
});
afterEach(function() {
stopServer();
helper.unload();
});
function sendArray(sock, array) {
if(array.length > 0) {
sock.write(array[0], function() {
sendArray(sock, array.slice(1));
});
}
else {
sock.end();
}
}
function startServer(done) {
server_port += 1;
server = net.createServer(function(c) {
sendArray(c, reply_data);
}).listen(server_port, "localhost", function(err) {
done(err);
});
}
function stopServer() {
server.close();
}
function send(wdata) {
var opt = {port:port, host:"localhost"};
var client = net.createConnection(opt, function() {
client.write(wdata[0], function() {
client.end();
if(wdata.length > 1) {
send(wdata.slice(1));
}
});
});
}
function eql(v0, v1) {
return((v0 === v1) || ((typeof v0) === 'object' && v0.equals(v1)));
}
function testTCP(flow, wdata, rdata, is_server, done) {
if(is_server) {
reply_data = wdata;
}
helper.load(tcpinNode, flow, function() {
var n2 = helper.getNode("n2");
var rcount = 0;
n2.on("input", function(msg) {
if(eql(msg.payload, rdata[rcount])) {
rcount++;
}
else {
should.fail();
}
if(rcount === rdata.length) {
done();
}
});
if(!is_server) {
send(wdata);
}
});
}
function testTCP0(flow, wdata, rdata, done) {
testTCP(flow, wdata, rdata, false, done);
}
function testTCP1(flow, wdata, rdata, done) {
testTCP(flow, wdata, rdata, true, done);
}
it('should recv data (Stream/Buffer)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo"], [Buffer("foo")], done);
});
it('should recv data (Stream/String/Delimiter:\\n)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo\nbar"], ["foo", "bar"], done);
});
it('should recv data (Stream/String/No delimiter)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo\nbar"], ["foo\nbar"], done);
});
it('should recv data (Stream/Base64)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo"], [Buffer("foo").toString('base64')], done);
});
it('should recv data (Single/Buffer)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo"], [Buffer("foo")], done);
});
it('should recv data (Single/String)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo\nbar\nbaz"], ["foo\nbar\nbaz"], done);
});
it('should recv data (Stream/Base64)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo"], [Buffer("foo").toString('base64')], done);
});
it('should recv multiple data (Stream/Buffer)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo", "bar"], [Buffer("foo"), Buffer("bar")], done);
});
it('should recv multiple data (Stream/String/Delimiter:\\n)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo", "bar\nbaz"], ["foo", "bar", "baz"], done);
});
it('should recv multiple data (Stream/String/No delimiter)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP0(flow, ["foo", "bar\nbaz"], ["foo", "bar\nbaz"], done);
});
it('should recv multiple data (Stream/Base64)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
var wdata = ["foo", "bar"];
var rdata = wdata.map(function(x) {
return Buffer(x).toString('base64');
});
testTCP0(flow, wdata, rdata, done);
});
it('should connect & recv data (Stream/Buffer)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP1(flow, ["foo"], [Buffer("foo")], done);
});
it('should connect & recv data (Stream/String/Delimiter:\\n)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP1(flow, ["foo\nbar"], ["foo", "bar"], done);
});
it('should connect & recv data (Stream/String/No delimiter)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP1(flow, ["foo\nbar"], ["foo\nbar"], done);
});
it('should connect & recv data (Stream/Base64)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP1(flow, ["foo"], [Buffer("foo").toString('base64')], done);
});
it('should connect & recv data (Single/Buffer)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP1(flow, ["foo"], [Buffer("foo")], done);
});
it('should connect & recv data (Single/String)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP1(flow, ["foo\nbar\nbaz"], ["foo\nbar\nbaz"], done);
});
it('should connect & recv data (Stream/Base64)', function(done) {
var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP1(flow, ["foo"], [Buffer("foo").toString('base64')], done);
});
});

View File

@@ -0,0 +1,110 @@
/**
* 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 net = require("net");
var should = require("should");
var helper = require("../../helper.js");
var tcpinNode = require("../../../../nodes/core/io/31-tcpin.js");
describe('TCP Request Node', function() {
var server = undefined;
var port = 9000;
function startServer(done) {
port += 1;
server = net.createServer(function(c) {
c.on('data', function(data) {
var rdata = "ACK:"+data.toString();
c.write(rdata);
});
c.on('error', function(err) {
startServer(done);
});
}).listen(port, "127.0.0.1", function(err) {
done();
});
}
before(function(done) {
startServer(done);
});
after(function() {
server.close();
});
afterEach(function() {
helper.unload();
});
function testTCP(flow, val0, val1, done) {
helper.load(tcpinNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', Buffer(val1));
done();
} catch(err) {
done(err);
}
});
if((typeof val0) === 'object') {
n1.receive(val0);
} else {
n1.receive({payload:val0});
}
});
}
it('should send & recv data', function(done) {
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP(flow, "foo", "ACK:foo", done)
});
it('should send & recv data when specified character received', function(done) {
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP(flow, "foo0bar0", "ACK:foo0", done);
});
it('should send & recv data after fixed number of chars received', function(done) {
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP(flow, "foo bar", "ACK:foo", done);
});
it('should send & receive, then keep connection', function(done) {
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP(flow, "foo", "ACK:foo", done);
});
it('should send & close', function(done) {
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP(flow, "foo", "ACK:foo", done);
});
it('should send & recv data to/from server:port from msg', function(done) {
var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] },
{id:"n2", type:"helper"}];
testTCP(flow, {payload:"foo", host:"localhost", port:port}, "ACK:foo", done)
});
});

View File

@@ -0,0 +1,93 @@
/**
* 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 dgram = require("dgram");
var should = require("should");
var helper = require("../../helper.js");
var udpNode = require("../../../../nodes/core/io/32-udp.js");
describe('UDP in Node', function() {
var port = 9100;
before(function(done) {
helper.startServer(done);
});
after(function() {
});
afterEach(function() {
helper.unload();
});
function sendIPv4(msg) {
var sock = dgram.createSocket('udp4');
sock.send(msg, 0, msg.length, port, "127.0.0.1", function(msg) {
sock.close();
});
}
function checkRecv(dt, proto, val0, val1, done) {
var flow = [{id:"n1", type:"udp in",
group: "", multicast:false,
port:port, ipv:proto,
datatype: dt, iface: "",
wires:[["n2"]] },
{id:"n2", type:"helper"}];
helper.load(udpNode, flow, function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
var ip = ((proto === 'udp6') ? '::ffff:':'') +'127.0.0.1';
msg.should.have.property('ip', ip);
msg.should.have.property('port');
msg.should.have.property('payload');
msg.payload.should.deepEqual(val1);
done();
} catch(err) {
done(err);
}
});
sendIPv4(val0);
});
}
it('should recv IPv4 data (Buffer)', function(done) {
checkRecv('buffer', 'udp4', 'hello', Buffer('hello'), done);
});
it('should recv IPv4 data (String)', function(done) {
checkRecv('utf8', 'udp4', 'hello', 'hello', done);
});
it('should recv IPv4 data (base64)', function(done) {
checkRecv('base64', 'udp4', 'hello', Buffer('hello').toString('base64'), done);
});
it('should recv IPv6 data (Buffer)', function(done) {
checkRecv('buffer', 'udp6', 'hello', Buffer('hello'), done);
});
it('should recv IPv6 data (String)', function(done) {
checkRecv('utf8', 'udp6', 'hello', 'hello', done);
});
it('should recv IPv6 data (base64)', function(done) {
checkRecv('base64', 'udp6', 'hello', Buffer('hello').toString('base64'), done);
});
});

View File

@@ -0,0 +1,85 @@
/**
* 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 dgram = require("dgram");
var should = require("should");
var helper = require("../../helper.js");
var udpNode = require("../../../../nodes/core/io/32-udp.js");
describe('UDP out Node', function() {
var port = 9200;
before(function(done) {
helper.startServer(done);
});
after(function() {
});
afterEach(function() {
helper.unload();
});
function recvData(data, done) {
var sock = dgram.createSocket('udp4');
sock.on('message', function(msg, rinfo) {
msg.should.deepEqual(data);
done();
});
sock.bind(port, '127.0.0.1');
port++;
}
function checkSend(proto, val0, val1, decode, dest_in_msg, done) {
var dst_ip = dest_in_msg ? undefined : "127.0.0.1";
var dst_port = dest_in_msg ? undefined : port;
var flow = [{id:"n1", type:"udp out",
addr:dst_ip, port:dst_port, iface: "",
ipv:proto, outport: "random",
base64:decode, multicast:false,
wires:[] }];
helper.load(udpNode, flow, function() {
var n1 = helper.getNode("n1");
var msg = {};
if(decode) {
msg.payload = Buffer("hello").toString('base64');
}
else {
msg.payload = "hello";
}
if(dest_in_msg) {
msg.ip = "127.0.0.1";
msg.port = port;
}
recvData(val1, done);
n1.receive(msg);
});
}
it('should send IPv4 data', function(done) {
checkSend('udp4', 'hello', Buffer('hello'), false, false, done);
});
it('should send IPv4 data (base64)', function(done) {
checkSend('udp4', 'hello', Buffer('hello'), true, false, done);
});
it('should send IPv4 data with dest from msg', function(done) {
checkSend('udp4', 'hello', Buffer('hello'), false, true, done);
});
});

View File

@@ -187,6 +187,10 @@ describe('switch Node', function() {
twoFieldSwitchTest("btwn", "3", "5", true, true, 4, done);
});
it('should check if payload is between given string values', function(done) {
twoFieldSwitchTest("btwn", "c", "e", true, true, "d", done);
});
it('should check if payload is not between given values', function(done) {
twoFieldSwitchTest("btwn", 3, 5, true, false, 12, done);
});
@@ -489,4 +493,9 @@ describe('switch Node', function() {
});
});
it('should handle JSONata expression', function(done) {
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$abs(payload)",propertyType:"jsonata",rules:[{"t":"btwn","v":"$sqrt(16)","vt":"jsonata","v2":"$sqrt(36)","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
customFlowSwitchTest(flow, true, -5, done);
});
});

View File

@@ -64,7 +64,6 @@ describe('change Node', function() {
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
var rule = helper.getNode("changeNode1").rules[0];
helperNode1.on("input", function(msg) {
try {
msg.payload.should.equal("changed");
@@ -77,13 +76,31 @@ describe('change Node', function() {
});
});
it('sets the value of global context property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t":"set","p":"globalValue","pt":"global","to":"changed","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
changeNode1.context().global.get("globalValue").should.equal("changed");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().global.set("globalValue","changeMe");
changeNode1.receive({payload:""});
});
});
it('sets the value and type of the message property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "set", "p": "payload", "pt": "msg", "to": "12345", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
var rule = helper.getNode("changeNode1").rules[0];
helperNode1.on("input", function(msg) {
try {
msg.payload.should.equal(12345);
@@ -263,6 +280,44 @@ describe('change Node', function() {
});
});
it('changes the value to flow context property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"flowValue","tot":"flow"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.should.eql("Hello World!");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("flowValue","Hello World!");
changeNode1.receive({payload:""});
});
});
it('changes the value to global context property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"globalValue","tot":"global"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.should.eql("Hello World!");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().global.set("globalValue","Hello World!");
changeNode1.receive({payload:""});
});
});
it('changes the value to a number', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"123","tot":"num"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
@@ -281,6 +336,24 @@ describe('change Node', function() {
});
});
it('changes the value to a boolean value', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"true","tot":"bool"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.should.eql(true);
done();
} catch(err) {
done(err);
}
});
changeNode1.receive({payload:""});
});
});
it('changes the value to a js object', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":'{"a":123}',"tot":"json"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
@@ -299,13 +372,31 @@ describe('change Node', function() {
});
});
it('changes the value to a buffer object', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"[72,101,108,108,111,32,87,111,114,108,100]","tot":"bin"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
var buff = Buffer.from("Hello World");
msg.payload.should.eql(buff);
done();
} catch(err) {
done(err);
}
});
changeNode1.receive({payload:""});
});
});
it('sets the value of the message property to the current timestamp', function(done) {
var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"set","p":"ts","pt":"msg","to":"","tot":"date"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
var rule = helper.getNode("changeNode1").rules[0];
helperNode1.on("input", function(msg) {
try {
(Date.now() - msg.ts).should.be.approximately(0,50);
@@ -318,6 +409,24 @@ describe('change Node', function() {
});
});
it('changes the value using jsonata', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$length(payload)","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.should.eql(12);
done();
} catch(err) {
done(err);
}
});
changeNode1.receive({payload:"Hello World!"});
});
});
});
describe('#change', function() {
it('changes the value of the message property', function(done) {
@@ -344,7 +453,6 @@ describe('change Node', function() {
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
var rule = helper.getNode("changeNode1").rules[0];
helperNode1.on("input", function(msg) {
try {
msg.payload.should.equal("Change456Me");
@@ -365,7 +473,6 @@ describe('change Node', function() {
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
var rule = helper.getNode("changeNode1").rules[0];
helperNode1.on("input", function(msg) {
try {
msg.payload.should.equal(456);
@@ -541,6 +648,63 @@ describe('change Node', function() {
});
});
it('changes the value using flow context property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"flow","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.should.equal("abc123abc");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("topic","ABC");
changeNode1.receive({payload:"abcABCabc"});
});
});
it('changes the value using global context property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.should.equal("abc123abc");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().global.set("topic","ABC");
changeNode1.receive({payload:"abcABCabc"});
});
});
it('changes the number using global context property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"ABC","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.should.equal("ABC");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().global.set("topic",123);
changeNode1.receive({payload:123});
});
});
it('changes the value using number - string payload', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"123","to":"456","fromt":"num","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
@@ -612,6 +776,103 @@ describe('change Node', function() {
changeNode1.receive({payload:true});
});
});
it('changes the value of the global context', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "global", "from": "Hello", "fromt": "str", "to": "Goodbye", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"},
{id:"helperNode1", type:"helper", wires:[],"z":"flow"}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
helperNode1.context().global.get("payload").should.equal("Goodbye World!");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().global.set("payload","Hello World!");
changeNode1.receive({payload:""});
});
});
it('changes the value and doesnt change type of the flow context for partial match', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"},
{id:"helperNode1", type:"helper", wires:[],"z":"flow"}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
helperNode1.context().flow.get("payload").should.equal("Change456Me");
helperNode1.context().flow.get("payload").should.be.a.String();
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("payload","Change123Me");
changeNode1.receive({payload:""});
});
});
it('changes the value and type of the flow context if a complete match', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"},
{id:"helperNode1", type:"helper", wires:[],"z":"flow"}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
helperNode1.context().flow.get("payload").should.equal(456);
helperNode1.context().flow.get("payload").should.be.a.Number();
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("payload","123");
changeNode1.receive({payload:""});
});
});
it('changes the value using number - number flow context', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "num", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"},
{id:"helperNode1", type:"helper", wires:[],"z":"flow"}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
helperNode1.context().flow.get("payload").should.equal("abc");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("payload",123);
changeNode1.receive({payload:""});
});
});
it('changes the value using boolean - boolean flow context', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "true", "fromt": "bool", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"},
{id:"helperNode1", type:"helper", wires:[],"z":"flow"}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
helperNode1.context().flow.get("payload").should.equal("abc");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("payload",true);
changeNode1.receive({payload:""});
});
});
});
describe("#delete", function() {
@@ -633,6 +894,25 @@ describe('change Node', function() {
});
});
it('deletes the value of global context property', function(done) {
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "delete", "p": "globalValue", "pt": "global"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
helper.load(changeNode, flow, function() {
var changeNode1 = helper.getNode("changeNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
changeNode1.context().global.should.not.have.property("globalValue");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().global.set("globalValue","Hello World!");
changeNode1.receive({payload:""});
});
});
it('deletes the value of a multi-level message property', function(done) {
var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"foo.bar","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];

View File

@@ -323,6 +323,7 @@ describe('JOIN node', function() {
n1.receive({payload:"D", parts:{id:1, type:"string", ch:",", index:3, count:4}});
});
});
it('should join bits of buffer back together automatically', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:",", build:"buffer", mode:"auto"},
{id:"n2", type:"helper"}];
@@ -425,6 +426,34 @@ describe('JOIN node', function() {
});
});
it('should merge full msg objects', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:6, build:"merged", mode:"custom", propertyType:"full", property:""},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.payload.should.have.property("payload",7);
msg.payload.should.have.property("aha",'c');
msg.payload.should.have.property("bar",'b');
msg.payload.should.have.property("bingo",'e');
msg.payload.should.have.property("foo",'d');
msg.payload.should.have.property("topic",'a');
done();
}
catch(e) { done(e)}
});
n1.receive({payload:1, topic:"f"});
n1.receive({payload:2, topic:"a"});
n1.receive({payload:3, foo:"b"});
n1.receive({payload:4, bar:"b"});
n1.receive({payload:5, aha:"c"});
n1.receive({payload:6, foo:"d"});
n1.receive({payload:7, bingo:"e"});
});
});
it('should accumulate a merged object', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true, count:1},
{id:"n2", type:"helper"}];
@@ -559,6 +588,31 @@ describe('JOIN node', function() {
});
});
it('should join complete message objects into an array after a count', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"array", timeout:0, count:3, propertyType:"full",mode:"custom"},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.payload.should.be.an.Array();
msg.payload[0].should.be.an.Object();
msg.payload[0].should.have.property("payload","a");
msg.payload[1].should.be.an.Object();
msg.payload[1].should.have.property("payload","b");
msg.payload[2].should.be.an.Object();
msg.payload[2].should.have.property("payload","c");
done();
}
catch(e) { done(e); }
});
n1.receive({payload:"a"});
n1.receive({payload:"b"});
n1.receive({payload:"c"});
});
});
it('should join split things back into an array', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]]},
{id:"n2", type:"helper"}];

View File

@@ -0,0 +1,232 @@
/**
* 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 sortNode = require("../../../../nodes/core/logic/18-sort.js");
var helper = require("../../helper.js");
var RED = require("../../../../red/red.js");
describe('SORT node', function() {
before(function(done) {
helper.startServer(done);
});
afterEach(function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", name: "SortNode", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'SortNode');
done();
});
});
function check_sort0(flow, data_in, data_out, done) {
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function(msg) {
msg.should.have.property("payload");
msg.should.have.property("parts");
msg.parts.should.have.property("count", data_out.length);
var index = data_out.indexOf(msg.payload);
msg.parts.should.have.property("index", index);
count++;
if (count === data_out.length) {
done();
}
});
var len = data_in.length;
for(var i = 0; i < len; i++) {
var parts = { id: "X", index: i, count: len };
n1.receive({payload:data_in[i], parts: parts});
}
});
}
function check_sort1(flow, data_in, data_out, done) {
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.length.should.equal(data_out.length);
for(var i = 0; i < data_out.length; i++) {
msg.payload[i].should.equal(data_out[i]);
}
done();
});
n1.receive({payload:data_in});
});
}
(function() {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]},
{id:"n2", type:"helper"}];
var data_in = [ "200", "4", "30", "1000" ];
var data_out = [ "1000", "200", "30", "4" ];
it('should sort message group (payload, not number, ascending)', function(done) {
check_sort0(flow, data_in, data_out, done);
});
it('should sort payload (payload, not number, ascending)', function(done) {
check_sort1(flow, data_in, data_out, done);
});
})();
(function() {
var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"payload", wires:[["n2"]]},
{id:"n2", type:"helper"}];
var data_in = [ "200", "4", "30", "1000" ];
var data_out = [ "4", "30", "200", "1000" ];
it('should sort message group (payload, not number, descending)', function(done) {
check_sort0(flow, data_in, data_out, done);
});
it('should sort payload (payload, not number, descending)', function(done) {
check_sort1(flow, data_in, data_out, done);
});
})();
(function() {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, keyType:"payload", wires:[["n2"]]},
{id:"n2", type:"helper"}];
var data_in = [ "200", "4", "30", "1000" ];
var data_out = [ "4", "30", "200", "1000" ];
it('should sort message group (payload, number, ascending)', function(done) {
check_sort0(flow, data_in, data_out, done);
});
it('should sort payload (payload, number, ascending)', function(done) {
check_sort1(flow, data_in, data_out, done);
});
})();
(function() {
var flow = [{id:"n1", type:"sort", order:"descending", as_num:true, keyType:"payload", wires:[["n2"]]},
{id:"n2", type:"helper"}];
var data_in = [ "200", "4", "30", "1000" ];
var data_out = [ "1000", "200", "30", "4" ];
it('should sort message group (payload, number, descending)', function(done) {
check_sort0(flow, data_in, data_out, done);
});
it('should sort payload (payload, number, descending)', function(done) {
check_sort1(flow, data_in, data_out, done);
});
})();
(function() {
var data_in = [ "C200", "A4", "B30", "D1000" ];
var data_out = [ "D1000", "C200", "B30", "A4" ];
it('should sort message group (exp, not number, ascending)', function(done) {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$substring(payload, 1)", wires:[["n2"]]},
{id:"n2", type:"helper"}];
check_sort0(flow, data_in, data_out, done);
});
it('should sort payload (exp, not number, ascending)', function(done) {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$substring($, 1)", wires:[["n2"]]},
{id:"n2", type:"helper"}];
check_sort1(flow, data_in, data_out, done);
});
})();
(function() {
var data_in = [ "C200", "A4", "B30", "D1000" ];
var data_out = [ "A4", "B30", "C200", "D1000" ];
it('should sort message group (exp, not number, descending)', function(done) {
var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"exp", key:"$substring(payload, 1)", wires:[["n2"]]},
{id:"n2", type:"helper"}];
check_sort0(flow, data_in, data_out, done);
});
it('should sort payload (exp, not number, descending)', function(done) {
var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"exp", key:"$substring($, 1)", wires:[["n2"]]},
{id:"n2", type:"helper"}];
check_sort1(flow, data_in, data_out, done);
});
})();
it('should handle JSONata script error', function(done) {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$unknown()", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
setTimeout(function() {
var logEvents = helper.log().args.filter(function (evt) {
return evt[0].type == "sort";
});
var evt = logEvents[0][0];
evt.should.have.property('id', "n1");
evt.should.have.property('type', "sort");
evt.should.have.property('msg', "sort.invalid-exp");
done();
}, 150);
var msg0 = { payload: "A", parts: { id: "X", index: 0, count: 2} };
var msg1 = { payload: "B", parts: { id: "X", index: 1, count: 2} };
n1.receive(msg0);
n1.receive(msg1);
});
});
it('should handle too many pending messages', function(done) {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
RED.settings.sortMaxKeptMsgsCount = 2;
setTimeout(function() {
var logEvents = helper.log().args.filter(function (evt) {
return evt[0].type == "sort";
});
var evt = logEvents[0][0];
evt.should.have.property('id', "n1");
evt.should.have.property('type', "sort");
evt.should.have.property('msg', "sort.too-many");
done();
}, 150);
for(var i = 0; i < 4; i++) {
var msg = { payload: "V"+i,
parts: { id: "X", index: i, count: 4} };
n1.receive(msg);
}
});
});
it('should clear pending messages on close', function(done) {
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
setTimeout(function() {
var logEvents = helper.log().args.filter(function (evt) {
return evt[0].type == "sort";
});
var evt = logEvents[0][0];
evt.should.have.property('id', "n1");
evt.should.have.property('type', "sort");
evt.should.have.property('msg', "sort.clear");
done();
}, 150);
var msg = { payload: 0,
parts: { id: "X", index: 0, count: 2} };
n1.receive(msg);
n1.close();
});
});
});

View File

@@ -46,6 +46,23 @@ describe('CSV node', function() {
});
describe('csv to json', function() {
var parts_id = undefined;
afterEach(function() {
parts_id = undefined;
});
function check_parts(msg, index, count) {
msg.should.have.property('parts');
if(parts_id === undefined) {
parts_id = msg.parts.id;
}
else {
msg.parts.should.have.property('id', parts_id);
}
msg.parts.should.have.property('index', index);
msg.parts.should.have.property('count', count);
}
it('should convert a simple csv string to a javascript object', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] },
@@ -55,6 +72,7 @@ describe('CSV node', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 });
check_parts(msg, 0, 1);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10);
@@ -70,6 +88,7 @@ describe('CSV node', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 });
check_parts(msg, 0, 1);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10);
@@ -85,6 +104,7 @@ describe('CSV node', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { col1: 1, col2: 2, col3: 3, col4: 4 });
check_parts(msg, 0, 1);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10);
@@ -100,6 +120,7 @@ describe('CSV node', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, d: 4 });
check_parts(msg, 0, 1);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10);
@@ -117,6 +138,7 @@ describe('CSV node', function() {
n2.on("input", function(msg) {
//console.log(msg);
msg.should.have.property('payload', { a: 1, b: -2, c: '+3', d: 4, e: -5, f: 'ab"cd', g: 'with,a,comma' });
check_parts(msg, 0, 1);
done();
});
var testString = '"1","-2","+3","04","-05","ab""cd","with,a,comma"'+String.fromCharCode(10);
@@ -134,6 +156,7 @@ describe('CSV node', function() {
//console.log(msg);
msg.should.have.property('payload', { a: "with,an", b: "odd,number", c: "ofquotes" });
//msg.should.have.property('payload', { a: 1, b: -2, c: '+3', d: 4, e: -5, f: 'ab"cd', g: 'with,a,comma' });
check_parts(msg, 0, 1);
done();
});
var testString = '"with,a"n,odd","num"ber","of"qu"ot"es"'+String.fromCharCode(10);
@@ -152,10 +175,12 @@ describe('CSV node', function() {
//console.log(msg);
if (c === 0) {
msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 });
check_parts(msg, 0, 2);
c += 1;
}
else {
msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 });
check_parts(msg, 1, 2);
done();
}
});
@@ -172,6 +197,7 @@ describe('CSV node', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', [ { a: 1, b: 2, c: 3, d: 4 },{ a: 5, b: -6, c: 7, d: '+8' },{ a: 9, b: 0, c: 'a', d: 'b' },{ a: 'c', b: 'd', c: 'e', d: 'f' } ]);
msg.should.not.have.property('parts');
done();
});
var testString = "1,2,3,4\n5,-6,07,+8\n9,0,a,b\nc,d,e,f";
@@ -187,30 +213,200 @@ describe('CSV node', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: "a", b: "127.0.0.1", c: 56.7, d: -32.8, e: "+76.22C" });
check_parts(msg, 0, 1);
done();
});
var testString = "a,127.0.0.1,56.7,-32.8,+76.22C";
n1.emit("input", {payload:testString});
});
});
});
describe('json object to csv', function() {
it('should convert a simple object back to a csv', function(done) {
it('should preserve parts property', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 });
check_parts(msg, 3, 4);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10);
n1.emit("input", {payload:testString, parts: {id:"X", index:3, count:4} });
});
});
it('should be able to use the first of multiple parts as a template if parts are present', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 });
check_parts(msg, 0, 2);
c += 1;
}
else {
msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 });
check_parts(msg, 1, 2);
done();
}
});
var testString1 = "w,x,y,z\n";
var testString2 = "1,2,3,4\n";
var testString3 = "5,6,7,8\n";
n1.emit("input", {payload:testString1, parts:{id:"X", index:0, count:3}});
n1.emit("input", {payload:testString2, parts:{id:"X", index:1, count:3}});
n1.emit("input", {payload:testString3, parts:{id:"X", index:2, count:3}});
});
});
it('should skip several lines from start if requested', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 9, b: 0, c: "A", d: "B" });
check_parts(msg, 0, 1);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10);
n1.emit("input", {payload:testString});
});
});
it('should skip several lines from start then use next line as a tempate', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrin:true, skip: 2, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { "9": "C", "0": "D", "A": "E", "B": "F" });
check_parts(msg, 0, 1);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10)+"C,D,E,F"+String.fromCharCode(10);
n1.emit("input", {payload:testString});
});
});
it('should skip several lines from start and correct parts', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c===0) {
msg.should.have.property('payload', { a: 9, b: 0, c: "A", d: "B" });
check_parts(msg, 0, 2);
c = c+1;
}
else {
msg.should.have.property('payload', { a: "C", b: "D", c: "E", d: "F" });
check_parts(msg, 1, 2);
done();
}
});
var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10)+"C,D,E,F"+String.fromCharCode(10);
n1.emit("input", {payload:testString});
});
});
it('should be able to skip and then use the first of multiple parts as a template if parts are present', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, skip:2, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 });
check_parts(msg, 0, 2);
c += 1;
}
else {
msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 });
check_parts(msg, 1, 2);
done();
}
});
var testStringA = "foo\n";
var testStringB = "bar\n";
var testString1 = "w,x,y,z\n";
var testString2 = "1,2,3,4\n";
var testString3 = "5,6,7,8\n";
n1.emit("input", {payload:testStringA, parts:{id:"X", index:0, count:5}});
n1.emit("input", {payload:testStringB, parts:{id:"X", index:1, count:5}});
n1.emit("input", {payload:testString1, parts:{id:"X", index:2, count:5}});
n1.emit("input", {payload:testString2, parts:{id:"X", index:3, count:5}});
n1.emit("input", {payload:testString3, parts:{id:"X", index:4, count:5}});
});
});
});
describe('json object to csv', function() {
it('should convert a simple object back to a csv', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', '4,3,2,1\n');
msg.should.have.property('payload', '4,foo,true,,0\n');
done();
}
catch(e) { done(e); }
});
var testJson = { d: 1, b: 3, c: 2, a: 4 };
var testJson = { e:0, d:1, b:"foo", c:true, a:4 };
n1.emit("input", {payload:testJson});
});
});
it('should convert a simple object back to a csv with no template', function(done) {
var flow = [ { id:"n1", type:"csv", temp:" ", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', '1,foo,"ba""r","di,ng"\n');
done();
}
catch(e) { done(e); }
});
var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" };
n1.emit("input", {payload:testJson});
});
});
it('should handle a template with spaces in the property names', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,b o,c p,,e", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', '4,foo,true,,0\n');
done();
}
catch(e) { done(e); }
});
var testJson = { e:0, d:1, "b o":"foo", "c p":true, a:4 };
n1.emit("input", {payload:testJson});
});
});
@@ -241,12 +437,12 @@ describe('CSV node', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload', '0,1,2,3,4\n');
msg.should.have.property('payload', ',0,1,foo,"ba""r","di,ng"\n');
done();
}
catch(e) { done(e); }
});
var testJson = [0,1,2,3,4];
var testJson = ["",0,1,"foo",'ba"r','di,ng'];
n1.emit("input", {payload:testJson});
});
});

View File

@@ -207,12 +207,29 @@ describe('html node', function() {
describe('multiple messages', function(){
var cnt = 0;
var parts_id = undefined;
afterEach(function() {
cnt.should.be.exactly(2);
cnt = 0;
parts_id = undefined;
});
function check_parts(msg, index, count) {
msg.should.have.property('parts');
msg.parts.should.have.property('id');
if(parts_id === undefined) {
parts_id = msg.parts.id;
}
else {
msg.parts.should.have.property('id', parts_id);
}
msg.parts.should.have.property('index', index);
msg.parts.should.have.property('count', count);
msg.parts.should.have.property('type', 'string');
msg.parts.should.have.property('ch', '');
}
it('should retrieve list contents as html as default with output as multiple msgs ', function(done) {
fs.readFile(file, 'utf8', function(err, data) {
var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",as:"multi"},
@@ -224,6 +241,7 @@ describe('html node', function() {
n2.on("input", function(msg) {
cnt++;
msg.should.have.property('topic', 'bar');
check_parts(msg, cnt -1, 2);
if (cnt !== 1 && cnt !== 2) {
return false;
}
@@ -252,6 +270,7 @@ describe('html node', function() {
n2.on("input", function(msg) {
cnt++;
msg.should.have.property('topic', 'bar');
check_parts(msg, cnt -1, 2);
if (cnt !== 1 && cnt !== 2) {
return false;
}
@@ -281,6 +300,7 @@ describe('html node', function() {
msg.should.have.property('payload');
msg.payload.should.have.property('src','foo.png');
msg.should.have.property('topic', 'bar');
check_parts(msg, 0, 1);
cnt = 2; // frig the answer as only one img tag
done();
});

View File

@@ -28,17 +28,8 @@ describe('JSON node', function() {
helper.unload();
});
it('should be loaded', function(done) {
var flow = [{id:"jsonNode1", type:"json", name: "jsonNode" }];
helper.load(jsonNode, flow, function() {
var jsonNode1 = helper.getNode("jsonNode1");
jsonNode1.should.have.property('name', 'jsonNode');
done();
});
});
it('should convert a valid json string to a javascript object', function(done) {
var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
@@ -56,7 +47,7 @@ describe('JSON node', function() {
});
it('should convert a javascript object to a json string', function(done) {
var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
@@ -71,7 +62,7 @@ describe('JSON node', function() {
});
it('should convert a array to a json string', function(done) {
var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
@@ -86,7 +77,7 @@ describe('JSON node', function() {
});
it('should log an error if asked to parse an invalid json string', function(done) {
var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
try {
@@ -108,7 +99,7 @@ describe('JSON node', function() {
});
it('should log an error if asked to parse something thats not json or js', function(done) {
var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
@@ -137,7 +128,7 @@ describe('JSON node', function() {
});
it('should pass straight through if no payload set', function(done) {
var flow = [{id:"jn1",type:"json",wires:[["jn2"]],func:"return msg;"},
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
@@ -151,4 +142,105 @@ describe('JSON node', function() {
});
});
it('should ensure the result is a json string', function(done) {
var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
var jn2 = helper.getNode("jn2");
var count = 0;
jn2.on("input", function(msg) {
try {
should.equal(msg.payload, '{"employees":[{"firstName":"John","lastName":"Smith"}]}');
count++;
if (count === 2) {
done();
}
} catch(err) {
done(err);
}
});
var obj = {employees:[{firstName:"John", lastName:"Smith"}]};
jn1.receive({payload:obj,topic: "bar"});
jn1.receive({payload:JSON.stringify(obj),topic: "bar"});
});
});
it('should ensure the result is a JS Object', function(done) {
var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
var jn2 = helper.getNode("jn2");
var count = 0;
jn2.on("input", function(msg) {
try {
msg.should.have.property('topic', 'bar');
msg.payload.should.have.property('employees');
msg.payload.employees[0].should.have.property('firstName', 'John');
msg.payload.employees[0].should.have.property('lastName', 'Smith');
count++;
if (count === 2) {
done();
}
} catch(err) {
done(err);
}
});
var obj = {employees:[{firstName:"John", lastName:"Smith"}]};
jn1.receive({payload:obj,topic: "bar"});
jn1.receive({payload:JSON.stringify(obj),topic: "bar"});
});
});
it('should handle any msg property - receive existing string', function(done) {
var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
var jn2 = helper.getNode("jn2");
jn2.on("input", function(msg) {
try {
msg.should.have.property('topic', 'bar');
msg.should.have.property('one');
msg.one.should.have.property('two');
msg.one.two.should.have.property('employees');
msg.one.two.employees[0].should.have.property('firstName', 'John');
msg.one.two.employees[0].should.have.property('lastName', 'Smith');
done();
} catch(err) {
done(err);
}
});
var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}';
jn1.receive({payload:"",one:{two:jsonString},topic: "bar"});
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "json";
});
});
});
it('should handle any msg property - receive existing obj', function(done) {
var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]},
{id:"jn2", type:"helper"}];
helper.load(jsonNode, flow, function() {
var jn1 = helper.getNode("jn1");
var jn2 = helper.getNode("jn2");
jn2.on("input", function(msg) {
try {
should.equal(msg.one.two, '{"employees":[{"firstName":"John","lastName":"Smith"}]}');
done();
} catch(err) {
done(err);
}
});
var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}';
jn1.receive({payload:"",one:{two:JSON.parse(jsonString)},topic: "bar"});
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "json";
});
});
});
});

View File

@@ -36,6 +36,7 @@ describe('file Nodes', function() {
});
afterEach(function(done) {
fs.removeSync(path.join(resourcesDir,"file-out-node"));
helper.unload().then(function() {
//fs.unlinkSync(fileToTest);
helper.stopServer(done);
@@ -154,32 +155,43 @@ describe('file Nodes', function() {
var f = fs.readFileSync(fileToTest).toString();
f.should.equal("onetwo");
// Delete the file
fs.unlinkSync(fileToTest);
// Recreate it
fs.writeFileSync(fileToTest,"");
// Send two more messages to the file
n1.emit("input", {payload:"three"});
n1.emit("input", {payload:"four"});
setTimeout(function() {
// Check the file was updated
try {
var f = fs.readFileSync(fileToTest).toString();
f.should.equal("threefour");
fs.unlinkSync(fileToTest);
done();
} catch(err) {
done(err);
}
},wait);
if (os.type() === "Windows_NT") {
var dummyFile = path.join(resourcesDir,"50-file-test-dummy.txt");
fs.rename(fileToTest, dummyFile, function() {
recreateTest(n1, dummyFile);
});
} else {
recreateTest(n1, fileToTest);
}
} catch(err) {
done(err);
}
},wait);
});
function recreateTest(n1, fileToDelete) {
// Delete the file
fs.unlinkSync(fileToDelete);
// Recreate it
fs.writeFileSync(fileToTest,"");
// Send two more messages to the file
n1.emit("input", {payload:"three"});
n1.emit("input", {payload:"four"});
setTimeout(function() {
// Check the file was updated
try {
var f = fs.readFileSync(fileToTest).toString();
f.should.equal("threefour");
fs.unlinkSync(fileToTest);
done();
} catch(err) {
done(err);
}
},wait);
}
});
@@ -357,7 +369,7 @@ describe('file Nodes', function() {
it('should fail to create a new directory if not asked to do so (append)', function(done) {
// Stub file write so we can make writes fail
var fileToTest2 = path.join(resourcesDir,"a","50-file-test-file.txt");
var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt");
//var spy = sinon.stub(fs, 'appendFile', function(arg,arg2,arg3,arg4){ arg4(new Error("Stub error message")); });
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":false}];
@@ -383,7 +395,7 @@ describe('file Nodes', function() {
it('should try to create a new directory if asked to do so (append)', function(done) {
// Stub file write so we can make writes fail
var fileToTest2 = path.join(resourcesDir,"a","50-file-test-file.txt");
var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt");
var spy = sinon.stub(fs, "ensureDir", function(arg1,arg2,arg3,arg4) { arg2(null); });
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":false, "createDir":true}];
helper.load(fileNode, flow, function() {
@@ -394,9 +406,7 @@ describe('file Nodes', function() {
return evt[0].type == "file";
});
//console.log(logEvents);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("file.errors.appendfail");
logEvents.should.have.length(0);
done();
}
catch(e) { done(e); }
@@ -408,7 +418,7 @@ describe('file Nodes', function() {
it('should fail to create a new directory if not asked to do so (overwrite)', function(done) {
// Stub file write so we can make writes fail
var fileToTest2 = path.join(resourcesDir,"a","50-file-test-file.txt");
var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt");
//var spy = sinon.stub(fs, 'appendFile', function(arg,arg2,arg3,arg4){ arg4(new Error("Stub error message")); });
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":false, "overwriteFile":true}];
@@ -434,7 +444,7 @@ describe('file Nodes', function() {
it('should try to create a new directory if asked to do so (overwrite)', function(done) {
// Stub file write so we can make writes fail
var fileToTest2 = path.join(resourcesDir,"a","50-file-test-file.txt");
var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt");
var spy = sinon.stub(fs, "ensureDir", function(arg1,arg2,arg3,arg4) { arg2(null); });
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":true, "createDir":true}];
@@ -446,9 +456,7 @@ describe('file Nodes', function() {
return evt[0].type == "file";
});
//console.log(logEvents);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("file.errors.writefail");
logEvents.should.have.length(0);
done();
}
catch(e) { done(e); }

View File

@@ -50,6 +50,7 @@ describe("api/admin/nodes", function() {
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
app.get("/getIcons",nodes.getIcons);
app.delete("/nodes/:id",nodes.delete);
sinon.stub(apiUtil,"determineLangFromHeaders", function() {
return "en-US";
@@ -810,5 +811,29 @@ describe("api/admin/nodes", function() {
});
});
describe('get icons', function() {
it('returns icon list', function(done) {
debugger;
initNodes({
nodes:{
getNodeIcons: function() {
return {"module":["1.png","2.png","3.png"]};
}
}
});
request(app)
.get('/getIcons')
.expect(200)
.end(function(err,res) {
if (err) {
throw err;
}
console.log(res.body);
res.body.should.have.property("module");
res.body.module.should.be.an.Array();
res.body.module.should.have.lengthOf(3);
done();
});
});
});
});

View File

@@ -31,6 +31,13 @@ var address = '127.0.0.1';
var listenPort = 0; // use ephemeral port
describe("api/editor/comms", function() {
beforeEach(function (done) {
setTimeout(function() {
done();
}, 55);
});
describe("with default keepalive", function() {
var server;
var url;
@@ -72,7 +79,7 @@ describe("api/editor/comms", function() {
comms.publish('topic1', 'foo');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic1","data":"foo"}');
msg.should.equal('[{"topic":"topic1","data":"foo"}]');
ws.close();
done();
});
@@ -85,7 +92,8 @@ describe("api/editor/comms", function() {
ws.send('{"subscribe":"topic2"}');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic2","data":"bar"}');
console.log(msg);
msg.should.equal('[{"topic":"topic2","data":"bar"}]');
ws.close();
done();
});
@@ -100,7 +108,8 @@ describe("api/editor/comms", function() {
comms.publish('topic3', 'new');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic3","data":"new"}');
console.log(msg);
msg.should.equal('[{"topic":"topic3","data":"new"}]');
ws.close();
done();
});
@@ -115,7 +124,8 @@ describe("api/editor/comms", function() {
comms.publish('topic3', 'correct');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic3","data":"correct"}');
console.log(msg);
msg.should.equal('[{"topic":"topic3","data":"correct"}]');
ws.close();
done();
});
@@ -133,7 +143,8 @@ describe("api/editor/comms", function() {
comms.publish('topic4', 'bar');
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"topic4","data":"bar"}');
console.log(msg);
msg.should.equal('[{"topic":"topic4","data":"bar"}]');
ws.close();
done();
});
@@ -325,7 +336,7 @@ describe("api/editor/comms", function() {
var ws = new WebSocket(url);
var count = 0;
ws.on('message', function(data) {
var msg = JSON.parse(data);
var msg = JSON.parse(data)[0];
msg.should.have.property('topic','hb');
msg.should.have.property('data').be.a.Number();
count++;
@@ -346,7 +357,7 @@ describe("api/editor/comms", function() {
}, 50);
});
ws.on('message', function(data) {
var msg = JSON.parse(data);
var msg = JSON.parse(data)[0];
// It is possible a heartbeat message may arrive - so ignore them
if (msg.topic != "hb") {
msg.should.have.property('topic', 'foo');
@@ -435,7 +446,7 @@ describe("api/editor/comms", function() {
ws.send('{"subscribe":"foo"}');
comms.publish('foo', 'correct');
} else {
msg.should.equal('{"topic":"foo","data":"correct"}');
msg.should.equal('[{"topic":"foo","data":"correct"}]');
ws.close();
}
});
@@ -509,7 +520,7 @@ describe("api/editor/comms", function() {
},200);
});
ws.on('message', function(msg) {
msg.should.equal('{"topic":"foo","data":"correct"}');
msg.should.equal('[{"topic":"foo","data":"correct"}]');
count++;
ws.close();
});

View File

@@ -26,11 +26,11 @@ var registry = require("../../../../red/runtime/nodes/registry");
describe("red/nodes/index", function() {
before(function() {
sinon.stub(flows,"startFlows");
sinon.stub(index,"startFlows");
process.env.NODE_RED_HOME = path.resolve(path.join(__dirname,"..","..","..",".."))
});
after(function() {
flows.startFlows.restore();
index.startFlows.restore();
delete process.env.NODE_RED_HOME;
});

View File

@@ -150,7 +150,46 @@ describe("red/nodes/registry/localfilesystem",function() {
});
it.skip("finds locales directory");
it.skip("finds icon path directory");
it("scans icon files in the resources tree",function(done) {
var count = 0;
localfilesystem.init({
i18n:{registerMessageCatalog:function(){}},
events:{emit:function(eventName,dir){
if (count === 0) {
eventName.should.equal("node-icon-dir");
dir.name.should.equal("node-red");
dir.icons.should.be.an.Array();
count = 1;
} else if (count === 1) {
done();
}
}},
settings:{coreNodesDir:resourcesDir}
});
localfilesystem.getNodeFiles(true);
});
it("scans icons dir in library",function(done) {
var count = 0;
localfilesystem.init({
i18n:{registerMessageCatalog:function(){}},
events:{emit:function(eventName,dir){
eventName.should.equal("node-icon-dir");
if (count === 0) {
dir.name.should.equal("node-red");
dir.icons.should.be.an.Array();
count = 1;
} else if (count === 1) {
dir.name.should.equal("Library");
dir.icons.should.be.an.Array();
dir.icons.length.should.equal(1);
dir.icons[0].should.be.equal("test_icon.png");
done();
}
}},
settings:{userDir:userDir}
});
localfilesystem.getNodeFiles(true);
});
});
describe("#getModuleFiles",function() {
it("gets a nodes module files",function(done) {
@@ -196,5 +235,27 @@ describe("red/nodes/registry/localfilesystem",function() {
});
it.skip("finds locales directory");
it.skip("finds icon path directory");
it("scans icon files with a module file",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join",function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
localfilesystem.init({
i18n:{registerMessageCatalog:function(){}},
events:{emit:function(eventName,dir){
eventName.should.equal("node-icon-dir");
dir.name.should.equal("TestNodeModule");
dir.icons.should.be.an.Array();
done();
}},
settings:{coreNodesDir:moduleDir}
});
var nodeModule = localfilesystem.getModuleFiles('TestNodeModule');
});
});
});

View File

@@ -72,7 +72,15 @@ describe("red/nodes/registry/registry",function() {
config: "configC",
types: [ "test-c","test-d"]
};
var testNodeSet3 = {
id: "test-module-2/test-name-3",
module: "test-module-2",
name: "test-name-3",
enabled: true,
loaded: false,
config: "configB",
types: [ "test-a","test-e"]
};
@@ -198,7 +206,39 @@ describe("red/nodes/registry/registry",function() {
typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2WithError, "0.0.1");
should.not.exist(typeRegistry.getTypeId("test-c"));
});
});
it('doesnt add node set if type already exists', function() {
typeRegistry.init(settings);
typeRegistry.getNodeList().should.have.lengthOf(0);
typeRegistry.getModuleList().should.eql({});
should.not.exist(typeRegistry.getTypeId("test-e"));
typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1");
typeRegistry.getNodeList().should.have.lengthOf(1);
should.exist(typeRegistry.getTypeId("test-a"));
typeRegistry.addNodeSet(testNodeSet3.id,testNodeSet3, "0.0.1");
typeRegistry.getNodeList().should.have.lengthOf(2);
// testNodeSet3 registers a duplicate test-a and unique test-e
// as test-a is a duplicate, test-e should not get registered
should.not.exist(typeRegistry.getTypeId("test-e"));
var testNodeSet3Result = typeRegistry.getNodeList()[1];
should.exist(testNodeSet3Result.err);
testNodeSet3Result.err.code.should.equal("type_already_registered");
testNodeSet3Result.err.details.type.should.equal("test-a");
testNodeSet3Result.err.details.moduleA.should.equal("test-module");
testNodeSet3Result.err.details.moduleB.should.equal("test-module-2");
//
// typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2WithError, "0.0.1");
//
// should.not.exist(typeRegistry.getTypeId("test-c"));
});
});
describe("#enableNodeSet", function() {
@@ -334,7 +374,7 @@ describe("red/nodes/registry/registry",function() {
enabled: true,
loaded: false,
config: "configB",
types: [ "test-a","test-b"]
types: [ "test-c","test-d"]
}, "0.0.1");
typeRegistry.getNodeConfig("test-module/test-name-2").should.eql('configBHEtest-name-2LP');
typeRegistry.getAllNodeConfigs().should.eql('configAHEtest-nameLPconfigBHEtest-name-2LP');
@@ -493,7 +533,7 @@ describe("red/nodes/registry/registry",function() {
it('returns a registered icon' , function() {
var testIcon = path.resolve(__dirname+'/../../../../resources/icons/test_icon.png');
events.emit("node-icon-dir",{name:"test-module", path: path.resolve(__dirname+'/../../../../resources/icons')});
events.emit("node-icon-dir",{name:"test-module", path: path.resolve(__dirname+'/../../../../resources/icons'), icons:[]});
var iconPath = typeRegistry.getNodeIconPath('test-module','test_icon.png');
iconPath.should.eql(testIcon);
});
@@ -505,4 +545,24 @@ describe("red/nodes/registry/registry",function() {
});
});
describe('#getNodeIcons', function() {
it('returns empty icon list when no modules are registered', function() {
var iconList = typeRegistry.getNodeIcons();
iconList.should.eql({});
});
it('returns an icon list of registered node module', function() {
typeRegistry.addNodeSet("test-module/test-name",testNodeSet1,"0.0.1");
events.emit("node-icon-dir",{name:"test-module", path:"",icons:["test_icon1.png"]});
var iconList = typeRegistry.getNodeIcons();
iconList.should.eql({"test-module":["test_icon1.png"]});
});
it('returns an icon list of unregistered node module', function() {
events.emit("node-icon-dir",{name:"test-module", path:"", icons:["test_icon1.png", "test_icon2.png"]});
var iconList = typeRegistry.getNodeIcons();
iconList.should.eql({"test-module":["test_icon1.png","test_icon2.png"]});
});
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

View File

@@ -17,8 +17,10 @@
var should = require("should");
var fs = require('fs-extra');
var path = require('path');
var sinon = require('sinon');
var localfilesystem = require("../../../../../red/runtime/storage/localfilesystem");
var log = require("../../../../../red/runtime/log");
describe('storage/localfilesystem', function() {
var mockRuntime = {
@@ -285,6 +287,45 @@ describe('storage/localfilesystem', function() {
});
});
it('should fsync the flows file',function(done) {
var flowFile = 'test.json';
var flowFilePath = path.join(userDir,flowFile);
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}).then(function() {
sinon.spy(fs,"fsync");
localfilesystem.saveFlows(testFlow).then(function() {
fs.fsync.callCount.should.eql(1);
fs.fsync.restore();
done();
}).otherwise(function(err) {
done(err);
});
}).otherwise(function(err) {
done(err);
});
});
it('should log fsync errors and continue',function(done) {
var flowFile = 'test.json';
var flowFilePath = path.join(userDir,flowFile);
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}).then(function() {
sinon.stub(fs,"fsync", function(fd, cb) {
cb(new Error());
});
sinon.spy(log,"warn");
localfilesystem.saveFlows(testFlow).then(function() {
log.warn.callCount.should.eql(1);
log.warn.restore();
fs.fsync.callCount.should.eql(1);
fs.fsync.restore();
done();
}).otherwise(function(err) {
done(err);
});
}).otherwise(function(err) {
done(err);
});
});
it('should backup the flows file', function(done) {
var defaultFlowFile = 'flows_'+require('os').hostname()+'.json';
var defaultFlowFilePath = path.join(userDir,defaultFlowFile);