Merge branch 'dev' into function-modules

This commit is contained in:
Nick O'Leary
2021-02-13 00:21:27 +00:00
122 changed files with 3732 additions and 545 deletions

View File

@@ -53,6 +53,49 @@ 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 do something with the catch node', function(done) {
var flow = [{"id":"funcNode","type":"function","wires":[["goodNode"]],"func":"node.error('This is an error', msg);"},{"id":"goodNode","type":"helper"},{"id":"badNode","type":"helper"},{"id":"catchNode","type":"catch","scope":null,"uncaught":false,"wires":[["badNode"]]}];
var catchNodeModule = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js")
helper.load([catchNodeModule, functionNode], flow, function() {
var funcNode = helper.getNode("funcNode");
var catchNode = helper.getNode("catchNode");
var goodNode = helper.getNode("goodNode");
var badNode = helper.getNode("badNode");
badNode.on("input", function(msg) {
msg.should.have.property('topic', 'bar');
msg.should.have.property('payload', 'foo');
msg.should.have.property('error');
msg.error.should.have.property('message',"This is an error");
msg.error.should.have.property('source');
msg.error.source.should.have.property('id', "funcNode");
done();
});
funcNode.receive({payload:"foo",topic: "bar"});
});
});
/*
it('should be loaded', function(done) {
var flow = [{id:"n1", type:"function", name: "function" }];
helper.load(functionNode, flow, function() {
@@ -1560,4 +1603,5 @@ describe('function node', function() {
});
})
*/
});

View File

@@ -119,13 +119,17 @@ describe('switch Node', function() {
* @param done - callback when done
*/
function customFlowSwitchTest(flow, shouldReceive, sendPayload, done) {
customFlowMessageSwitchTest(flow,shouldReceive,{payload: sendPayload}, done);
}
function customFlowMessageSwitchTest(flow, shouldReceive, message, done) {
helper.load(switchNode, flow, function() {
var switchNode1 = helper.getNode("switchNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
if (shouldReceive === true) {
should.equal(msg.payload,sendPayload);
should.equal(msg,message);
done();
} else {
should.fail(null, null, "We should never get an input!");
@@ -134,7 +138,7 @@ describe('switch Node', function() {
done(err);
}
});
switchNode1.receive({payload:sendPayload});
switchNode1.receive(message);
if (shouldReceive === false) {
setTimeout(function() {
done();
@@ -425,6 +429,29 @@ describe('switch Node', function() {
});
});
it('should use a nested message property to compare value - matches', function(done) {
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"bar"}],checkall:true,outputs:1,wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
customFlowMessageSwitchTest(flow, true, {topic:"foo",payload:{"foo":"bar"}}, done);
})
it('should use a nested message property to compare value - no match', function(done) {
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"bar"}],checkall:true,outputs:1,wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
customFlowMessageSwitchTest(flow, false, {topic:"foo",payload:{"foo":"none"}}, done);
})
it('should use a nested message property to compare nested message property - matches', function(done) {
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"payload[msg.topic2]",vt:"msg"}],checkall:true,outputs:1,wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
customFlowMessageSwitchTest(flow, true, {topic:"foo",topic2:"foo2",payload:{"foo":"bar","foo2":"bar"}}, done);
})
it('should use a nested message property to compare nested message property - no match', function(done) {
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"payload[msg.topic2]",vt:"msg"}],checkall:true,outputs:1,wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
customFlowMessageSwitchTest(flow, false, {topic:"foo",topic2:"foo2",payload:{"foo":"bar","foo2":"none"}}, done);
})
it('should match regex with ignore-case flag set true', function(done) {
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree","case":true}],checkall:true,outputs:1,wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];

View File

@@ -98,7 +98,7 @@ describe('change Node', function() {
});
describe('#set' , function() {
it('sets the value of the message property', function(done) {
var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"changed","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
@@ -672,6 +672,111 @@ describe('change Node', function() {
});
});
it('sets the value of a message property using a nested property', function(done) {
var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"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(2);
done();
} catch(err) {
done(err);
}
});
changeNode1.receive({payload:"",lookup:{a:1,b:2},topic:"b"});
});
});
it('sets the value of a nested message property using a message property', function(done) {
var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"lookup[msg.topic]","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"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.lookup.b.should.equal("newValue");
done();
} catch(err) {
done(err);
}
});
var msg = {
payload: "newValue",
lookup:{a:1,b:2},
topic:"b"
}
changeNode1.receive(msg);
});
});
it('sets the value of a message property using a nested property in flow context', function(done) {
var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"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(2);
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("lookup",{a:1, b:2});
changeNode1.receive({payload: "", topic: "b"});
});
})
it('sets the value of a message property using a nested property in flow context', function(done) {
var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"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(2);
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("lookup",{a:1, b:2});
changeNode1.receive({payload: "", topic: "b"});
});
})
it('sets the value of a nested flow context property using a message property', function(done) {
var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"lookup[msg.topic]","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"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("newValue");
changeNode1.context().flow.get("lookup.b").should.eql("newValue");
done();
} catch(err) {
done(err);
}
});
changeNode1.context().flow.set("lookup",{a:1, b:2});
changeNode1.receive({payload: "newValue", topic: "b"});
});
})
});
describe('#change', function() {
it('changes the value of the message property', function(done) {

View File

@@ -1759,13 +1759,13 @@ describe('JOIN node', function() {
{ msg: {seq:0, payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:3}}, delay:0, avr:500, var:100},
{ msg: {seq:1, payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:3}}, delay:200, avr:500, var:100},
{ msg: {seq:2, payload:"dummy", reset: true, parts:{id:1}}, delay:500, avr:500, var:100}
]);
]);
});
it('should call done() when timed out', function (done) {
mapiDoneJoinTestHelper(done, {mode:"custom", joiner:",", build:"string", timeout:0.5}, [
{ msg: {seq:0, payload:"A"}, delay:0, avr:500, var:100},
{ msg: {seq:1, payload:"B"}, delay:200, avr:500, var:100},
]);
]);
});
it('should call done() when all messages are reduced', function (done) {
mapiDoneJoinTestHelper(done, {mode:"reduce", reduceRight:false, reduceExp:"$A+payload", reduceInit:"0",
@@ -1785,4 +1785,40 @@ describe('JOIN node', function() {
]);
});
});
it('should handle msg.parts even if messages are out of order in auto mode if exactly one message has count set', function (done) {
var flow = [{ id: "n1", type: "join", wires: [["n2"]], mode: "auto" },
{ id: "n2", type: "helper" }];
helper.load(joinNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
msg.payload.length.should.be.eql(5);
msg.payload.should.be.eql([0,1,2,3,4]);
done();
});
var msg = {};
msg.parts = {
id: RED.util.generateId()
};
for(var elem = 1; elem < 5; ++elem) {
var _msg = RED.util.cloneMessage(msg);
_msg.parts.index = elem;
if(elem == 4) {
_msg.parts.count = 5;
}
_msg.payload = elem;
n1.receive(_msg);
}
var _msg = RED.util.cloneMessage(msg);
delete _msg.parts.count;
_msg.parts.index = 0;
_msg.payload = 0;
n1.receive(_msg);
});
})
});

View File

@@ -0,0 +1,3 @@
{
"plugin": "winning"
}

View File

@@ -0,0 +1,12 @@
{
"name": "test-plugin",
"version": "1.0.0",
"description": "",
"node-red": {
"plugins": {
"test": "test.js",
"test-editor-plugin": "test-editor-plugin.html",
"test-runtime-plugin": "test-runtime-plugin.js"
}
}
}

View File

@@ -0,0 +1,9 @@
<script type="text/javascript">
console.log("Loaded test-plugin/test-editor-plugin")
RED.plugins.registerPlugin("my-test-editor-only-plugin", {
type: "bar",
onadd: function() {
console.log("my-test-editor-only-plugin.onadd called")
}
})
</script>

View File

@@ -0,0 +1,10 @@
module.exports = function(RED) {
console.log("Loaded test-plugin/test-runtime-plugin")
RED.plugins.registerPlugin("my-test-runtime-only-plugin", {
type: "bar",
onadd: function() {
console.log("my-test-runtime-only-plugin.onadd called")
}
})
}

View File

@@ -0,0 +1,4 @@
<script type="text/javascript">
console.log("Loaded test-plugin/test")
// RED.plugins.registerPlugin("")
</script>

View File

@@ -0,0 +1,13 @@
module.exports = function(RED) {
console.log("Loaded test-plugin/test")
RED.plugins.registerPlugin("my-test-plugin", {
type: "foo",
onadd: function() {
console.log("my-test-plugin.onadd called")
RED.events.on("registry:plugin-added", function(id) {
console.log(`my-test-plugin: plugin-added event "${id}"`)
});
}
})
}

View File

@@ -0,0 +1,111 @@
const should = require("should");
const request = require('supertest');
const express = require('express');
const bodyParser = require("body-parser");
var app;
var NR_TEST_UTILS = require("nr-test-utils");
var plugins = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/plugins");
describe("api/editor/plugins", function() {
const pluginList = [
{
"id": "test-module/test-set",
"enabled": true,
"local": false,
"plugins": [
{
"type": "foo",
"id": "a-plugin",
"module": "test-module"
},
{
"type": "bar",
"id": "a-plugin2",
"module": "test-module"
},
{
"type": "foo",
"id": "a-plugin3",
"module": "test-module"
}
]
},
{
"id": "test-module/test-disabled-set",
"enabled": false,
"local": false,
"plugins": []
}
];
const pluginConfigs = `
<!-- --- [red-plugin:test-module/test-set] --- -->
test-module-config`;
const pluginCatalogs = { "test-module": {"foo": "bar"}};
before(function() {
app = express();
app.use(bodyParser.json());
app.get("/plugins",plugins.getAll);
app.get("/plugins/messages",plugins.getCatalogs);
plugins.init({
plugins: {
getPluginList: async function() { return pluginList },
getPluginConfigs: async function() { return pluginConfigs },
getPluginCatalogs: async function() { return pluginCatalogs }
}
})
});
it('returns the list of plugins', function(done) {
request(app)
.get("/plugins")
.set('Accept', 'application/json')
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
JSON.stringify(res.body).should.eql(JSON.stringify(pluginList));
done();
} catch(err) {
done(err)
}
});
});
it('returns the plugin configs', function(done) {
request(app)
.get("/plugins")
.set('Accept', 'text/html')
.expect(200)
.expect(pluginConfigs)
.end(function(err,res) {
if (err) {
return done(err);
}
done();
});
});
it('returns the plugin catalogs', function(done) {
request(app)
.get("/plugins/messages")
.set('Accept', 'application/json')
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
JSON.stringify(res.body).should.eql(JSON.stringify(pluginCatalogs));
done();
} catch(err) {
done(err)
}
});
});
});

View File

@@ -33,11 +33,11 @@ describe("api/editor/theme", function () {
theme.init({settings: {}});
fs.statSync.restore();
});
it("applies the default theme", function () {
it("applies the default theme", async function () {
var result = theme.init({});
should.not.exist(result);
var context = theme.context();
var context = await theme.context();
context.should.have.a.property("page");
context.page.should.have.a.property("title", "Node-RED");
context.page.should.have.a.property("favicon", "favicon.ico");
@@ -52,7 +52,7 @@ describe("api/editor/theme", function () {
should.not.exist(theme.settings());
});
it("picks up custom theme", function () {
it("picks up custom theme", async function () {
theme.init({
editorTheme: {
page: {
@@ -104,7 +104,7 @@ describe("api/editor/theme", function () {
theme.app();
var context = theme.context();
var context = await theme.context();
context.should.have.a.property("page");
context.page.should.have.a.property("title", "Test Page Title");
context.page.should.have.a.property("favicon", "theme/favicon/favicon");

View File

@@ -0,0 +1,155 @@
const should = require("should");
const sinon = require("sinon");
const path = require("path");
const NR_TEST_UTILS = require("nr-test-utils");
const plugins = NR_TEST_UTILS.require("@node-red/registry/lib/plugins");
const registry = NR_TEST_UTILS.require("@node-red/registry/lib/registry");
const { events } = NR_TEST_UTILS.require("@node-red/util");
describe("red/nodes/registry/plugins",function() {
let receivedEvents = [];
let modules;
function handleEvent(evnt) {
receivedEvents.push(evnt);
}
beforeEach(function() {
plugins.init({});
receivedEvents = [];
modules = {
"test-module": {
plugins: {
"test-set": {
id: "test-module/test-set",
enabled: true,
config: "test-module-config",
plugins: []
},
"test-disabled-set": {
id: "test-module/test-disabled-set",
enabled: false,
config: "disabled-plugin-config",
plugins: []
}
}
}
}
events.on("registry:plugin-added",handleEvent);
sinon.stub(registry,"getModule", moduleId => modules[moduleId]);
sinon.stub(registry,"getModuleList", () => modules)
});
afterEach(function() {
events.removeListener("registry:plugin-added",handleEvent);
registry.getModule.restore();
registry.getModuleList.restore();
})
describe("registerPlugin", function() {
it("registers a plugin", function() {
let pluginDef = {}
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
receivedEvents.length.should.eql(1);
receivedEvents[0].should.eql("a-plugin");
should.exist(modules['test-module'].plugins['test-set'].plugins[0])
modules['test-module'].plugins['test-set'].plugins[0].should.equal(pluginDef)
})
it("calls a plugins onadd function", function() {
let pluginDef = { onadd: sinon.stub() }
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
pluginDef.onadd.called.should.be.true();
})
})
describe("getPlugin", function() {
it("returns a registered plugin", function() {
let pluginDef = {}
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
pluginDef.should.equal(plugins.getPlugin("a-plugin"));
})
})
describe("getPluginsByType", function() {
it("returns a plugins of a given type", function() {
let pluginDef = {type: "foo"}
let pluginDef2 = {type: "bar"}
let pluginDef3 = {type: "foo"}
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2);
plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3);
let fooPlugins = plugins.getPluginsByType("foo");
let barPlugins = plugins.getPluginsByType("bar");
let noPlugins = plugins.getPluginsByType("none");
noPlugins.should.be.of.length(0);
fooPlugins.should.be.of.length(2);
fooPlugins.should.containEql(pluginDef);
fooPlugins.should.containEql(pluginDef3);
barPlugins.should.be.of.length(1);
barPlugins.should.containEql(pluginDef2);
})
})
describe("getPluginConfigs", function() {
it("gets all plugin configs", function() {
let configs = plugins.getPluginConfigs("en-US");
configs.should.eql(`
<!-- --- [red-plugin:test-module/test-set] --- -->
test-module-config`)
})
})
describe("getPluginList", function() {
it("returns a plugins of a given type", function() {
let pluginDef = {type: "foo"}
let pluginDef2 = {type: "bar"}
let pluginDef3 = {type: "foo"}
plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef);
plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2);
plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3);
let pluginList = plugins.getPluginList();
JSON.stringify(pluginList).should.eql(JSON.stringify(
[
{
"id": "test-module/test-set",
"enabled": true,
"local": false,
"user": false,
"plugins": [
{
"type": "foo",
"id": "a-plugin",
"module": "test-module"
},
{
"type": "bar",
"id": "a-plugin2",
"module": "test-module"
},
{
"type": "foo",
"id": "a-plugin3",
"module": "test-module"
}
]
},
{
"id": "test-module/test-disabled-set",
"enabled": false,
"local": false,
"user": false,
"plugins": []
}
]
))
})
})
});

View File

@@ -0,0 +1,68 @@
const should = require("should");
const sinon = require("sinon");
const NR_TEST_UTILS = require("nr-test-utils");
const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/api/plugins")
const mockLog = () => ({
log: sinon.stub(),
debug: sinon.stub(),
trace: sinon.stub(),
warn: sinon.stub(),
info: sinon.stub(),
metric: sinon.stub(),
audit: sinon.stub(),
_: function() { return "abc"}
})
describe("runtime-api/plugins", function() {
const pluginList = [{id:"one",module:'test-module'},{id:"two",module:"node-red"}];
const pluginConfigs = "123";
describe("getPluginList", function() {
it("gets the plugin list", function() {
plugins.init({
log: mockLog(),
plugins: {
getPluginList: function() { return pluginList}
}
});
return plugins.getPluginList({}).then(function(result) {
result.should.eql(pluginList);
})
});
});
describe("getPluginConfigs", function() {
it("gets the plugin configs", function() {
plugins.init({
log: mockLog(),
plugins: {
getPluginConfigs: function() { return pluginConfigs}
}
});
return plugins.getPluginConfigs({}).then(function(result) {
result.should.eql(pluginConfigs);
})
});
});
describe("getPluginCatalogs", function() {
it("gets the plugin catalogs", function() {
plugins.init({
log: mockLog(),
plugins: {
getPluginList: function() { return pluginList}
},
i18n: {
i: {
changeLanguage: function(lang,done) { done && done() },
getResourceBundle: function(lang, id) { return {lang,id}}
}
}
});
return plugins.getPluginCatalogs({lang: "en-US"}).then(function(result) {
JSON.stringify(result).should.eql(JSON.stringify({ one: { lang: "en-US", id: "one" } }))
})
});
});
});

View File

@@ -0,0 +1,13 @@
const should = require("should");
const sinon = require("sinon");
const NR_TEST_UTILS = require("nr-test-utils");
const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/plugins");
describe("runtime/plugins",function() {
it.skip("delegates all functions to registry module", function() {
// There's no easy way to test this as we can't stub the registry functions
// before the plugin module gets a reference to them
})
});

View File

@@ -164,6 +164,13 @@ describe("@node-red/util/util", function() {
var v2 = util.getMessageProperty({a:"foo"},"a");
v2.should.eql("foo");
});
it('retrieves a nested property', function() {
var v = util.getMessageProperty({a:"foo",b:{foo:1,bar:2}},"msg.b[msg.a]");
v.should.eql(1);
var v2 = util.getMessageProperty({a:"bar",b:{foo:1,bar:2}},"b[msg.a]");
v2.should.eql(2);
});
it('should return undefined if property does not exist', function() {
var v = util.getMessageProperty({a:"foo"},"msg.b");
should.not.exist(v);
@@ -331,7 +338,18 @@ describe("@node-red/util/util", function() {
msg.a[0].should.eql(1);
msg.a[1].should.eql(3);
})
it('handles nested message property references', function() {
var obj = {a:"foo",b:{}};
var result = util.setObjectProperty(obj,"b[msg.a]","bar");
result.should.be.true();
obj.b.should.have.property("foo","bar");
});
it('handles nested message property references', function() {
var obj = {a:"foo",b:{"foo":[0,0,0]}};
var result = util.setObjectProperty(obj,"b[msg.a][2]","bar");
result.should.be.true();
obj.b.foo.should.eql([0,0,"bar"])
});
});
describe('evaluateNodeProperty', function() {
@@ -459,13 +477,24 @@ describe("@node-red/util/util", function() {
// console.log(result);
result.should.eql(expected);
}
function testInvalid(input) {
function testABCWithMessage(input,msg,expected) {
var result = util.normalisePropertyExpression(input,msg);
// console.log("+",input);
// console.log(result);
result.should.eql(expected);
}
function testInvalid(input,msg) {
/*jshint immed: false */
(function() {
util.normalisePropertyExpression(input);
util.normalisePropertyExpression(input,msg);
}).should.throw();
}
function testToString(input,msg,expected) {
var result = util.normalisePropertyExpression(input,msg,true);
console.log("+",input);
console.log(result);
result.should.eql(expected);
}
it('pass a.b.c',function() { testABC('a.b.c',['a','b','c']); })
it('pass a["b"]["c"]',function() { testABC('a["b"]["c"]',['a','b','c']); })
it('pass a["b"].c',function() { testABC('a["b"].c',['a','b','c']); })
@@ -479,12 +508,25 @@ describe("@node-red/util/util", function() {
it("pass 'a.b'[1]",function() { testABC("'a.b'[1]",['a.b',1]); })
it("pass 'a.b'.c",function() { testABC("'a.b'.c",['a.b','c']); })
it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); })
it("pass a[msg[msg.b]]",function() { testABC("a[msg[msg.b]]",["a",["msg",["msg","b"]]]); })
it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); })
it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); })
it("pass a[msg['b]\"[']]",function() { testABC("a[msg['b]\"[']]",["a",["msg","b]\"["]]); })
it("pass a[msg['b][']]",function() { testABC("a[msg['b][']]",["a",["msg","b]["]]); })
it("pass b[msg.a][2]",function() { testABC("b[msg.a][2]",["b",["msg","a"],2])})
it("pass b[msg.a][2] (with message)",function() { testABCWithMessage("b[msg.a][2]",{a: "foo"},["b","foo",2])})
it('pass a.$b.c',function() { testABC('a.$b.c',['a','$b','c']); })
it('pass a["$b"].c',function() { testABC('a["$b"].c',['a','$b','c']); })
it('pass a._b.c',function() { testABC('a._b.c',['a','_b','c']); })
it('pass a["_b"].c',function() { testABC('a["_b"].c',['a','_b','c']); })
it("pass a['a.b[0]'].c",function() { testToString("a['a.b[0]'].c",null,'a["a.b[0]"]["c"]'); })
it("pass a.b.c",function() { testToString("a.b.c",null,'a["b"]["c"]'); })
it('pass a[msg.c][0]["fred"]',function() { testToString('a[msg.c][0]["fred"]',{c:"123"},'a["123"][0]["fred"]'); })
it("fail a'b'.c",function() { testInvalid("a'b'.c"); })
it("fail a['b'.c",function() { testInvalid("a['b'.c"); })
it("fail a[]",function() { testInvalid("a[]"); })
@@ -505,6 +547,12 @@ describe("@node-red/util/util", function() {
it("fail a['']",function() { testInvalid("a['']"); })
it("fail 'a.b'c",function() { testInvalid("'a.b'c"); })
it("fail <blank>",function() { testInvalid("");})
it("fail a[b]",function() { testInvalid("a[b]"); })
it("fail a[msg.]",function() { testInvalid("a[msg.]"); })
it("fail a[msg[]",function() { testInvalid("a[msg[]"); })
it("fail a[msg.[]]",function() { testInvalid("a[msg.[]]"); })
it("fail a[msg['af]]",function() { testInvalid("a[msg['af]]"); })
it("fail b[msg.undefined][2] (with message)",function() { testInvalid("b[msg.undefined][2]",{})})
});
@@ -983,4 +1031,5 @@ describe("@node-red/util/util", function() {
});
});
});