mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'master' into dev
This commit is contained in:
@@ -1717,6 +1717,24 @@ describe('change Node', function() {
|
||||
changeNode1.receive({topic:{foo:{bar:1}}, payload:"String"});
|
||||
});
|
||||
});
|
||||
it('moves the value of a message property object to itself', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload","tot":"msg"}],"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.should.have.property('payload');
|
||||
msg.payload.should.equal("bar");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
changeNode1.receive({payload:"bar"});
|
||||
});
|
||||
});
|
||||
it('moves the value of a message property object to a sub-property', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.foo","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
|
@@ -31,6 +31,8 @@ var multer = require("multer");
|
||||
var RED = require("nr-test-utils").require("node-red/lib/red");
|
||||
var fs = require('fs-extra');
|
||||
var auth = require('basic-auth');
|
||||
const { version } = require("os");
|
||||
const net = require('net')
|
||||
|
||||
describe('HTTP Request Node', function() {
|
||||
var testApp;
|
||||
@@ -1527,7 +1529,7 @@ describe('HTTP Request Node', function() {
|
||||
msg.payload.headers.should.have.property('Content-Type').which.startWith('application/json');
|
||||
//msg.dynamicHeaderName should be present in headers with the value of msg.dynamicHeaderValue
|
||||
msg.payload.headers.should.have.property('dyn-header-name').which.startWith('dyn-header-value');
|
||||
//static (custom) header set in Flow UI should be present
|
||||
//static (custom) header set in Flow UI should be present
|
||||
msg.payload.headers.should.have.property('static-header-name').which.startWith('static-header-value');
|
||||
//msg.headers['location'] should be deleted because Flow UI "Location" header has a blank value
|
||||
//ensures headers with matching characters but different case are eliminated
|
||||
@@ -2265,4 +2267,105 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should parse broken headers', function() {
|
||||
|
||||
const versions = process.versions.node.split('.')
|
||||
|
||||
if (( versions[0] == 14 && versions[1] >= 20 ) ||
|
||||
( versions[0] == 16 && versions[1] >= 16 ) ||
|
||||
( versions[0] == 18 && versions[1] >= 5 ) ||
|
||||
( versions[0] > 18)) {
|
||||
// only test if on new enough NodeJS version
|
||||
|
||||
let port = testPort++
|
||||
|
||||
let server;
|
||||
|
||||
before(function() {
|
||||
server = net.createServer(function (socket) {
|
||||
socket.write("HTTP/1.0 200\nContent-Type: text/plain\n\nHelloWorld")
|
||||
socket.end()
|
||||
})
|
||||
|
||||
server.listen(port,'127.0.0.1', function(err) {
|
||||
})
|
||||
});
|
||||
|
||||
after(function() {
|
||||
server.close()
|
||||
});
|
||||
|
||||
it('should accept broken headers', function (done) {
|
||||
var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:`http://localhost:${port}/`, insecureHTTPParser: true},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on('input', function(msg) {
|
||||
try {
|
||||
msg.payload.should.equal('HelloWorld')
|
||||
done()
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
})
|
||||
n1.receive({payload: 'foo'})
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject broken headers', function (done) {
|
||||
var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:`http://localhost:${port}/`},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on('input', function(msg) {
|
||||
try{
|
||||
msg.payload.should.match(/RequestError: Parse Error/)
|
||||
done()
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
})
|
||||
n1.receive({payload: 'foo'})
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('multipart form posts', function() {
|
||||
it('should send arrays as multiple entries', function (done) {
|
||||
const flow = [
|
||||
{
|
||||
id: 'n1', type: 'http request', wires: [['n2']], method: 'POST', ret: 'obj', url: getTestURL('/file-upload'), headers: [
|
||||
]
|
||||
},
|
||||
{ id: "n2", type: "helper" }
|
||||
];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on('input', function(msg){
|
||||
try {
|
||||
msg.payload.body.should.have.property('foo')
|
||||
msg.payload.body.list.should.deepEqual(['a','b','c'])
|
||||
done()
|
||||
} catch (e) {
|
||||
done(e)
|
||||
}
|
||||
});
|
||||
n1.receive({
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data'
|
||||
},
|
||||
payload: {
|
||||
foo: 'bar',
|
||||
list: [ 'a', 'b', 'c' ]
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
|
@@ -27,7 +27,7 @@ describe('MQTT Nodes', function () {
|
||||
} catch (error) { }
|
||||
});
|
||||
|
||||
it('should be loaded and have default values', function (done) {
|
||||
it('should be loaded and have default values (MQTT V4)', function (done) {
|
||||
this.timeout = 2000;
|
||||
const { flow, nodes } = buildBasicMQTTSendRecvFlow({ id: "mqtt.broker", name: "mqtt_broker", autoConnect: false }, { id: "mqtt.in", topic: "in_topic" }, { id: "mqtt.out", topic: "out_topic" });
|
||||
helper.load(mqttNodes, flow, function () {
|
||||
@@ -61,6 +61,52 @@ describe('MQTT Nodes', function () {
|
||||
mqttBroker.options.clientId.should.containEql('nodered_');
|
||||
mqttBroker.options.should.have.property('keepalive').type("number");
|
||||
mqttBroker.options.should.have.property('reconnectPeriod').type("number");
|
||||
//as this is not a v5 connection, ensure v5 properties are not present
|
||||
mqttBroker.options.should.not.have.property('protocolVersion', 5);
|
||||
mqttBroker.options.should.not.have.property('properties');
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error)
|
||||
}
|
||||
});
|
||||
});
|
||||
it('should be loaded and have default values (MQTT V5)', function (done) {
|
||||
this.timeout = 2000;
|
||||
const { flow, nodes } = buildBasicMQTTSendRecvFlow({ id: "mqtt.broker", name: "mqtt_broker", autoConnect: false, cleansession: false, clientid: 'clientid', keepalive: 35, sessionExpiry: '6000', protocolVersion: '5', userProps: {"prop": "val"}}, { id: "mqtt.in", topic: "in_topic" }, { id: "mqtt.out", topic: "out_topic" });
|
||||
helper.load(mqttNodes, flow, function () {
|
||||
try {
|
||||
const mqttIn = helper.getNode("mqtt.in");
|
||||
const mqttOut = helper.getNode("mqtt.out");
|
||||
const mqttBroker = helper.getNode("mqtt.broker");
|
||||
|
||||
should(mqttIn).be.type("object", "mqtt in node should be an object")
|
||||
mqttIn.should.have.property('broker', nodes.mqtt_broker.id); //should be the id of the broker node
|
||||
mqttIn.should.have.property('datatype', 'utf8'); //default: 'utf8'
|
||||
mqttIn.should.have.property('isDynamic', false); //default: false
|
||||
mqttIn.should.have.property('inputs', 0); //default: 0
|
||||
mqttIn.should.have.property('qos', 2); //default: 2
|
||||
mqttIn.should.have.property('topic', "in_topic");
|
||||
mqttIn.should.have.property('wires', [["helper.node"]]);
|
||||
|
||||
should(mqttOut).be.type("object", "mqtt out node should be an object")
|
||||
mqttOut.should.have.property('broker', nodes.mqtt_broker.id); //should be the id of the broker node
|
||||
mqttOut.should.have.property('topic', "out_topic");
|
||||
|
||||
should(mqttBroker).be.type("object", "mqtt broker node should be an object")
|
||||
mqttBroker.should.have.property('broker', BROKER_HOST);
|
||||
mqttBroker.should.have.property('port', BROKER_PORT);
|
||||
mqttBroker.should.have.property('brokerurl');
|
||||
// mqttBroker.should.have.property('autoUnsubscribe', true);//default: true
|
||||
mqttBroker.should.have.property('autoConnect', false);//Set "autoConnect:false" in brokerOptions
|
||||
mqttBroker.should.have.property('options');
|
||||
mqttBroker.options.should.have.property('clean', false);
|
||||
mqttBroker.options.should.have.property('clientId', 'clientid');
|
||||
mqttBroker.options.should.have.property('keepalive').type("number", 35);
|
||||
mqttBroker.options.should.have.property('reconnectPeriod').type("number");
|
||||
//as this IS a v5 connection, ensure v5 properties are not present
|
||||
mqttBroker.options.should.have.property('protocolVersion', 5);
|
||||
mqttBroker.options.should.have.property('properties');
|
||||
mqttBroker.options.properties.should.have.property('sessionExpiryInterval');
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error)
|
||||
@@ -173,7 +219,7 @@ describe('MQTT Nodes', function () {
|
||||
topic: nextTopic(),
|
||||
payload: '{prop:"value3", "num":3}', // send invalid JSON ...
|
||||
}
|
||||
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
|
||||
const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null }
|
||||
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
|
||||
helperNode.on("input", function (msg) {
|
||||
try {
|
||||
@@ -299,7 +345,7 @@ describe('MQTT Nodes', function () {
|
||||
topic: nextTopic(),
|
||||
payload: '{prop:"value3", "num":3}', contentType: "application/json", // send invalid JSON ...
|
||||
}
|
||||
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
|
||||
const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null }
|
||||
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
|
||||
helperNode.on("input", function (msg) {
|
||||
try {
|
||||
@@ -385,7 +431,7 @@ describe('MQTT Nodes', function () {
|
||||
if (skipTests) { return this.skip() }
|
||||
this.timeout = 2000;
|
||||
const options = {}
|
||||
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
|
||||
const hooks = { beforeLoad: null, afterLoad: null, afterConnect: null }
|
||||
hooks.beforeLoad = (flow) => { //add a status node pointed at MQTT Out node (to watch for connection status change)
|
||||
flow.push({ "id": "status.node", "type": "status", "name": "status_node", "scope": ["mqtt.out"], "wires": [["helper.node"]] });//add status node to watch mqtt_out
|
||||
}
|
||||
@@ -416,20 +462,66 @@ describe('MQTT Nodes', function () {
|
||||
this.timeout = 2000;
|
||||
const baseTopic = nextTopic();
|
||||
const brokerOptions = {
|
||||
autoConnect: false,
|
||||
protocolVersion: 4,
|
||||
birthTopic: baseTopic + "/birth",
|
||||
birthPayload: "broker connected",
|
||||
birthPayload: "broker birth",
|
||||
birthQos: 2,
|
||||
}
|
||||
const options = {};
|
||||
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null };
|
||||
options.expectMsg = {
|
||||
const expectMsg = {
|
||||
topic: brokerOptions.birthTopic,
|
||||
payload: brokerOptions.birthPayload,
|
||||
qos: brokerOptions.birthQos
|
||||
};
|
||||
const options = { };
|
||||
const hooks = { };
|
||||
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
|
||||
helperNode.on("input", function (msg) {
|
||||
try {
|
||||
compareMsgToExpected(msg, expectMsg);
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error)
|
||||
}
|
||||
})
|
||||
mqttIn.receive({ "action": "connect" }); //now request connect action
|
||||
return true; //handled
|
||||
}
|
||||
testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
|
||||
});
|
||||
itConditional('should safely discard bad birth topic', function (done) {
|
||||
if (skipTests) { return this.skip() }
|
||||
this.timeout = 2000;
|
||||
const baseTopic = nextTopic();
|
||||
const brokerOptions = {
|
||||
protocolVersion: 4,
|
||||
birthTopic: baseTopic + "#", // a publish topic should never have a wildcard
|
||||
birthPayload: "broker connected",
|
||||
birthQos: 2,
|
||||
}
|
||||
const options = {};
|
||||
const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null };
|
||||
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
|
||||
helperNode.on("input", function (msg) {
|
||||
try {
|
||||
msg.should.have.a.property("error").type("object");
|
||||
msg.error.should.have.a.property("source").type("object");
|
||||
msg.error.source.should.have.a.property("id", mqttIn.id);
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
});
|
||||
return true; //handled
|
||||
}
|
||||
options.expectMsg = null;
|
||||
try {
|
||||
testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
|
||||
done()
|
||||
} catch(err) {
|
||||
done(e)
|
||||
}
|
||||
});
|
||||
itConditional('should publish close message', function (done) {
|
||||
if (skipTests) { return this.skip() }
|
||||
this.timeout = 2000;
|
||||
@@ -587,12 +679,13 @@ function testSendRecv(brokerOptions, inNodeOptions, outNodeOptions, options, hoo
|
||||
const mqttBroker = helper.getNode(brokerOptions.id);
|
||||
const mqttIn = helper.getNode(nodes.mqtt_in.id);
|
||||
const mqttOut = helper.getNode(nodes.mqtt_out.id);
|
||||
let afterLoadHandled = false;
|
||||
let afterLoadHandled = false, finished = false;
|
||||
if (hooks.afterLoad) {
|
||||
afterLoadHandled = hooks.afterLoad(helperNode, mqttBroker, mqttIn, mqttOut)
|
||||
}
|
||||
if (!afterLoadHandled) {
|
||||
helperNode.on("input", function (msg) {
|
||||
finished = true
|
||||
try {
|
||||
compareMsgToExpected(msg, expectMsg);
|
||||
if (hooks.done) { hooks.done(); }
|
||||
@@ -617,10 +710,12 @@ function testSendRecv(brokerOptions, inNodeOptions, outNodeOptions, options, hoo
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if(finished) { return }
|
||||
if (hooks.done) { hooks.done(e); }
|
||||
else { throw e; }
|
||||
});
|
||||
} catch (err) {
|
||||
if(finished) { return }
|
||||
if (hooks.done) { hooks.done(err); }
|
||||
else { throw err; }
|
||||
}
|
||||
@@ -666,6 +761,7 @@ function buildMQTTBrokerNode(id, name, brokerHost, brokerPort, options) {
|
||||
node.cleansession = String(options.cleansession) == "false" ? false : true;
|
||||
node.autoUnsubscribe = String(options.autoUnsubscribe) == "false" ? false : true;
|
||||
node.autoConnect = String(options.autoConnect) == "false" ? false : true;
|
||||
node.sessionExpiry = options.sessionExpiry ? options.sessionExpiry : undefined;
|
||||
|
||||
if (options.birthTopic) {
|
||||
node.birthTopic = options.birthTopic;
|
||||
@@ -760,8 +856,8 @@ function waitBrokerConnect(broker, timeLimit) {
|
||||
let waitConnected = (broker, timeLimit) => {
|
||||
const brokers = Array.isArray(broker) ? broker : [broker];
|
||||
timeLimit = timeLimit || 1000;
|
||||
let timer, resolved = false;
|
||||
return new Promise( (resolve, reject) => {
|
||||
let timer, resolved = false;
|
||||
timer = wait();
|
||||
function wait() {
|
||||
if (brokers.every(e => e.connected == true)) {
|
||||
|
@@ -693,19 +693,20 @@ describe('CSV node', function() {
|
||||
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,f", wires:[["n2"]] },
|
||||
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e,f,g,h,i,j,k", 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) {
|
||||
// console.log("GOT",msg)
|
||||
try {
|
||||
msg.should.have.property('payload', '4,foo,true,,0,"Hello\nWorld"\n');
|
||||
msg.should.have.property('payload', '4,foo,true,,0,"Hello\nWorld",,,undefined,null,null\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = { e:0, d:1, b:"foo", c:true, a:4, f:"Hello\nWorld" };
|
||||
var testJson = { e:0, d:1, b:"foo", c:true, a:4, f:"Hello\nWorld", h:undefined, i:"undefined",j:null,k:"null" };
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -717,13 +718,14 @@ describe('CSV node', function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
// console.log("GOT",msg)
|
||||
try {
|
||||
msg.should.have.property('payload', '1,foo,"ba""r","di,ng"\n');
|
||||
msg.should.have.property('payload', '1,foo,"ba""r","di,ng",,undefined,null\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" };
|
||||
var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng", e:undefined, f:"undefined", g:null,h:"null" };
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -764,6 +766,33 @@ describe('CSV node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle a template with quotes in the property names', function(done) {
|
||||
var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"all", 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', 'a"a,b\'b\nA1,B1\nA2,B2\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = [
|
||||
{
|
||||
"a\"a": "A1",
|
||||
"b'b": "B1"
|
||||
},
|
||||
{
|
||||
"a\"a": "A2",
|
||||
"b'b": "B2"
|
||||
}
|
||||
]
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert an array of objects to a multi-line csv', function(done) {
|
||||
var flow = [ { id:"n1", type:"csv", temp:"a,d,c,b", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"} ];
|
||||
|
@@ -15,11 +15,14 @@
|
||||
**/
|
||||
|
||||
var fs = require("fs-extra");
|
||||
var os = require("os");
|
||||
var path = require("path");
|
||||
var should = require("should");
|
||||
var helper = require("node-red-node-test-helper");
|
||||
var watchNode = require("nr-test-utils").require("@node-red/nodes/core/storage/23-watch.js");
|
||||
|
||||
var arch = os.arch();
|
||||
var platform = os.platform();
|
||||
|
||||
describe('watch Node', function() {
|
||||
this.timeout(5000);
|
||||
@@ -89,7 +92,10 @@ describe('watch Node', function() {
|
||||
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);
|
||||
if (!((arch === "arm64") && (platform === "darwin"))) {
|
||||
// On OSX/ARM, two change events occur and first event do not reflect file size change. So ignore size field in the case.
|
||||
msg.should.have.property('size', result.size);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
if(count === len) {
|
||||
|
@@ -61,7 +61,7 @@ describe("api/index", function() {
|
||||
should.not.exist(api.httpAdmin);
|
||||
done();
|
||||
});
|
||||
describe('initalises admin api without adminAuth', function(done) {
|
||||
describe('initalises admin api without adminAuth', function() {
|
||||
before(function() {
|
||||
beforeEach();
|
||||
api.init({},{},{},{});
|
||||
@@ -78,7 +78,7 @@ describe("api/index", function() {
|
||||
})
|
||||
});
|
||||
|
||||
describe('initalises admin api without editor', function(done) {
|
||||
describe('initalises admin api without editor', function() {
|
||||
before(function() {
|
||||
beforeEach();
|
||||
api.init({ disableEditor: true },{},{},{});
|
||||
@@ -95,7 +95,7 @@ describe("api/index", function() {
|
||||
})
|
||||
});
|
||||
|
||||
describe('initialises api with admin middleware', function(done) {
|
||||
describe('initialises api with admin middleware', function() {
|
||||
it('ignores non-function values',function(done) {
|
||||
api.init({ httpAdminRoot: true, httpAdminMiddleware: undefined },{},{},{});
|
||||
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'testMiddleware')
|
||||
@@ -112,7 +112,7 @@ describe("api/index", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialises api with authentication enabled', function(done) {
|
||||
describe('initialises api with authentication enabled', function() {
|
||||
|
||||
it('enables an oauth/openID based authentication mechanism',function(done) {
|
||||
const stub = sinon.stub(apiAuth, 'genericStrategy').callsFake(function(){});
|
||||
@@ -135,7 +135,7 @@ describe("api/index", function() {
|
||||
|
||||
});
|
||||
|
||||
describe('initialises api with custom cors config', function (done) {
|
||||
describe('initialises api with custom cors config', function () {
|
||||
const httpAdminCors = {
|
||||
origin: "*",
|
||||
methods: "GET,PUT,POST,DELETE"
|
||||
@@ -156,7 +156,7 @@ describe("api/index", function() {
|
||||
})
|
||||
});
|
||||
|
||||
describe('editor start', function (done) {
|
||||
describe('editor start', function () {
|
||||
|
||||
it('cannot be started when editor is disabled', function (done) {
|
||||
const stub = sinon.stub(apiEditor, 'start').callsFake(function () {
|
||||
|
@@ -329,17 +329,36 @@ describe("red/nodes/registry/localfilesystem",function() {
|
||||
localfilesystem.init({nodesDir:[nodesDir2]});
|
||||
const nodeModule = localfilesystem.getModuleFiles();
|
||||
const loaded = Object.keys(nodeModule)
|
||||
loaded.should.have.a.property("length", 3)
|
||||
loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode")
|
||||
loaded.indexOf('lower-case').should.greaterThan(-1, "Should load lower-case")
|
||||
loaded.indexOf('@lowercase/lower-case2').should.greaterThan(-1, "Should load @lowercase/lower-case2")
|
||||
loaded.indexOf('testnode2').should.greaterThan(-1, "Should load testnode2")
|
||||
loaded.indexOf('test-theme2').should.greaterThan(-1, "Should load test-theme2")
|
||||
loaded.should.have.a.property("length", 5)
|
||||
|
||||
// scoped module with nodes in same dir as package.json
|
||||
nodeModule['@test/testnode'].should.have.a.property('name','@test/testnode');
|
||||
nodeModule['@test/testnode'].should.have.a.property('version','1.0.0');
|
||||
nodeModule['@test/testnode'].should.have.a.property('nodes');
|
||||
nodeModule['@test/testnode'].should.have.a.property('path');
|
||||
nodeModule['@test/testnode'].should.have.a.property('user', false);
|
||||
|
||||
// node-red module with nodes in sub dir
|
||||
nodeModule['@lowercase/lower-case2'].should.have.a.property('name','@lowercase/lower-case2');
|
||||
nodeModule['@lowercase/lower-case2'].should.have.a.property('version','2.0.0');
|
||||
nodeModule['@lowercase/lower-case2'].should.have.a.property('nodes');
|
||||
nodeModule['@lowercase/lower-case2'].nodes.should.have.a.property('lower-case');
|
||||
nodeModule['@lowercase/lower-case2'].should.have.a.property('path');
|
||||
nodeModule['@lowercase/lower-case2'].should.have.a.property('user', false);
|
||||
|
||||
// scoped module with nodes in sub dir
|
||||
nodeModule['lower-case'].should.have.a.property('name', 'lower-case');
|
||||
nodeModule['lower-case'].should.have.a.property('version','1.0.0');
|
||||
nodeModule['lower-case'].should.have.a.property('nodes');
|
||||
nodeModule['lower-case'].nodes.should.have.a.property('lower-case');
|
||||
nodeModule['lower-case'].should.have.a.property('path');
|
||||
nodeModule['lower-case'].should.have.a.property('user', false);
|
||||
|
||||
nodeModule['testnode2'].should.have.a.property('name','testnode2');
|
||||
nodeModule['testnode2'].should.have.a.property('version','1.0.0');
|
||||
nodeModule['testnode2'].should.have.a.property('nodes');
|
||||
|
@@ -0,0 +1,26 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('lower-case2',{
|
||||
category: 'function',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "file.png",
|
||||
label: function() {
|
||||
return this.name||"lower-case2";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="lower-case2">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="lower-case2">
|
||||
<p>A simple node that converts the message payloads into all lower-case2 characters</p>
|
||||
</script>
|
@@ -0,0 +1,11 @@
|
||||
module.exports = function(RED) {
|
||||
function LowerCaseNode(config) {
|
||||
RED.nodes.createNode(this,config);
|
||||
var node = this;
|
||||
node.on('input', function(msg) {
|
||||
msg.payload = msg.payload.toLowerCase();
|
||||
node.send(msg);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("lower-case2",LowerCaseNode);
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name" : "@lowercase/lower-case2",
|
||||
"node-red" : {
|
||||
"nodes": {
|
||||
"lower-case": "lower-case2/lower-case.js"
|
||||
}
|
||||
},
|
||||
"version": "2.0.0"
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('lower-case',{
|
||||
category: 'function',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "file.png",
|
||||
label: function() {
|
||||
return this.name||"lower-case";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="lower-case">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="lower-case">
|
||||
<p>A simple node that converts the message payloads into all lower-case characters</p>
|
||||
</script>
|
@@ -0,0 +1,11 @@
|
||||
module.exports = function(RED) {
|
||||
function LowerCaseNode(config) {
|
||||
RED.nodes.createNode(this,config);
|
||||
var node = this;
|
||||
node.on('input', function(msg) {
|
||||
msg.payload = msg.payload.toLowerCase();
|
||||
node.send(msg);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("lower-case",LowerCaseNode);
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name" : "lower-case",
|
||||
"node-red" : {
|
||||
"nodes": {
|
||||
"lower-case": "lower-case/lower-case.js"
|
||||
}
|
||||
},
|
||||
"version": "1.0.0"
|
||||
}
|
@@ -33,6 +33,11 @@ describe("runtime-api/diagnostics", function() {
|
||||
flowFile: "flows.json",
|
||||
mqttReconnectTime: 321,
|
||||
serialReconnectTime: 432,
|
||||
socketReconnectTime: 2222,
|
||||
socketTimeout: 3333,
|
||||
tcpMsgQueueSize: 4444,
|
||||
inboundWebSocketTimeout: 5555,
|
||||
runtimeState: {enabled: true, ui: false},
|
||||
adminAuth: {},//should be sanitised to "SET"
|
||||
httpAdminRoot: "/admin/root/",
|
||||
httpAdminCors: {},//should be sanitised to "SET"
|
||||
@@ -45,6 +50,7 @@ describe("runtime-api/diagnostics", function() {
|
||||
uiHost: "something.secret.com",//should be sanitised to "SET"
|
||||
uiPort: 1337,//should be sanitised to "SET"
|
||||
userDir: "/var/super/secret/",//should be sanitised to "SET",
|
||||
nodesDir: "/var/super/secret/",//should be sanitised to "SET",
|
||||
contextStorage: {
|
||||
default : { module: "memory" },
|
||||
file: { module: "localfilesystem" },
|
||||
@@ -73,8 +79,9 @@ describe("runtime-api/diagnostics", function() {
|
||||
|
||||
//result.runtime.xxxxx
|
||||
const runtimeCount = Object.keys(result.runtime).length;
|
||||
runtimeCount.should.eql(4);//ensure no more than 4 keys are present in runtime
|
||||
runtimeCount.should.eql(5);//ensure 5 keys are present in runtime
|
||||
result.runtime.should.have.property('isStarted',true)
|
||||
result.runtime.should.have.property('flows')
|
||||
result.runtime.should.have.property('modules').type("object");
|
||||
result.runtime.should.have.property('settings').type("object");
|
||||
result.runtime.should.have.property('version','7.7.7');
|
||||
@@ -87,7 +94,7 @@ describe("runtime-api/diagnostics", function() {
|
||||
|
||||
//result.runtime.settings.xxxxx
|
||||
const settingsCount = Object.keys(result.runtime.settings).length;
|
||||
settingsCount.should.eql(21);//ensure no more than the 21 settings listed below are present in the settings object
|
||||
settingsCount.should.eql(27);//ensure no more than the 21 settings listed below are present in the settings object
|
||||
result.runtime.settings.should.have.property('available',true);
|
||||
result.runtime.settings.should.have.property('apiMaxLength', "UNSET");//deliberately disabled to ensure UNSET is returned
|
||||
result.runtime.settings.should.have.property('debugMaxLength', 1111);
|
||||
@@ -96,6 +103,11 @@ describe("runtime-api/diagnostics", function() {
|
||||
result.runtime.settings.should.have.property('flowFile', "flows.json");
|
||||
result.runtime.settings.should.have.property('mqttReconnectTime', 321);
|
||||
result.runtime.settings.should.have.property('serialReconnectTime', 432);
|
||||
result.runtime.settings.should.have.property('socketReconnectTime', 2222);
|
||||
result.runtime.settings.should.have.property('socketTimeout', 3333);
|
||||
result.runtime.settings.should.have.property('tcpMsgQueueSize', 4444);
|
||||
result.runtime.settings.should.have.property('inboundWebSocketTimeout', 5555);
|
||||
result.runtime.settings.should.have.property('runtimeState', {enabled: true, ui: false});
|
||||
result.runtime.settings.should.have.property("adminAuth", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property("httpAdminCors", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property('httpAdminRoot', "/admin/root/");
|
||||
@@ -109,6 +121,7 @@ describe("runtime-api/diagnostics", function() {
|
||||
result.runtime.settings.should.have.property("uiPort", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property("userDir", "SET"); //should be sanitised to "SET"
|
||||
result.runtime.settings.should.have.property('contextStorage').type("object");
|
||||
result.runtime.settings.should.have.property('nodesDir', "SET")
|
||||
|
||||
//result.runtime.settings.contextStorage.xxxxx
|
||||
const contextCount = Object.keys(result.runtime.settings.contextStorage).length;
|
||||
|
Reference in New Issue
Block a user