Merge branch '0.14.0'

This commit is contained in:
Nick O'Leary
2016-06-17 21:30:09 +01:00
167 changed files with 6903 additions and 4341 deletions

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2015 IBM Corp.
* Copyright 2015, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,14 +31,22 @@ describe("Auth permissions", function() {
permissions.hasPermission(["read"],"node.read").should.be.true;
permissions.hasPermission(["read"],"write").should.be.false;
permissions.hasPermission(["read"],"node.write").should.be.false;
permissions.hasPermission(["*.read"],"read").should.be.true;
permissions.hasPermission(["*.read"],"node.read").should.be.true;
permissions.hasPermission(["*.read"],"write").should.be.false;
permissions.hasPermission(["*.read"],"node.write").should.be.false;
});
it('a user with foo permissions',function() {
permissions.hasPermission("foo","foo").should.be.false;
permissions.hasPermission("foo","foo").should.be.true;
});
it('an array of permissions', function() {
permissions.hasPermission(["*"],["foo.read","foo.write"]).should.be.true;
permissions.hasPermission("read",["foo.read","foo.write"]).should.be.false;
permissions.hasPermission("read",["foo.read","bar.read"]).should.be.true;
permissions.hasPermission(["flows.read"],["flows.read"]).should.be.true;
permissions.hasPermission(["flows.read"],["flows.write"]).should.be.false;
permissions.hasPermission(["flows.read","nodes.write"],["flows.write"]).should.be.false;
permissions.hasPermission(["flows.read","nodes.write"],["nodes.write"]).should.be.true;
});
it('permits an empty permission', function() {
permissions.hasPermission("*","").should.be.true;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var should = require("should");
var when = require("when");
var sinon = require("sinon");
@@ -24,16 +24,11 @@ var Tokens = require("../../../../red/api/auth/tokens");
describe("Tokens", function() {
describe("#init",function() {
it('loads sessions', function(done) {
Tokens.init({},{
getSessions:function() {
done();
return when.resolve();
}
});
Tokens.init({}).then(done);
});
});
describe("#get",function() {
it('returns a valid token', function(done) {
Tokens.init({},{
@@ -51,7 +46,7 @@ describe("Tokens", function() {
});
});
});
it('returns null for an invalid token', function(done) {
Tokens.init({},{
getSessions:function() {
@@ -98,7 +93,7 @@ describe("Tokens", function() {
});
});
});
describe("#create",function() {
it('creates a token', function(done) {
var savedSession;
@@ -112,14 +107,14 @@ describe("Tokens", function() {
}
});
var expectedExpiryTime = Date.now()+10000;
Tokens.create("user","client","scope").then(function(token) {
try {
should.exist(savedSession);
var sessionKeys = Object.keys(savedSession);
sessionKeys.should.have.lengthOf(1);
token.should.have.a.property('accessToken',sessionKeys[0]);
savedSession[sessionKeys[0]].should.have.a.property('user','user');
savedSession[sessionKeys[0]].should.have.a.property('client','client');
@@ -133,7 +128,7 @@ describe("Tokens", function() {
});
});
});
describe("#revoke", function() {
it('revokes a token', function(done) {
var savedSession;
@@ -157,5 +152,5 @@ describe("Tokens", function() {
});
});
});
});
});

View File

@@ -50,7 +50,8 @@ describe("flows api", function() {
if (err) {
return done(err);
}
res.body.should.be.an.Array.and.have.lengthOf(3);
res.body.should.be.an.Array;
res.body.should.have.lengthOf(3);
done();
});
});

View File

@@ -15,5 +15,5 @@
**/
describe("locales api", function() {
it.skip("works",function(){});
it.skip("works",function() {});
});

View File

@@ -44,10 +44,10 @@ describe("nodes api", function() {
app.use(bodyParser.json());
app.get("/nodes",nodes.getAll);
app.post("/nodes",nodes.post);
app.get("/nodes/:mod",nodes.getModule);
app.get("/nodes/:mod/:set",nodes.getSet);
app.put("/nodes/:mod",nodes.putModule);
app.put("/nodes/:mod/:set",nodes.putSet);
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule);
app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
app.delete("/nodes/:id",nodes.delete);
sinon.stub(comms,"publish");
sinon.stub(locales,"determineLangFromHeaders", function() {
@@ -76,7 +76,8 @@ describe("nodes api", function() {
if (err) {
throw err;
}
res.body.should.be.an.Array.and.have.lengthOf(3);
res.body.should.be.an.Array;
res.body.should.have.lengthOf(3);
done();
});
});

View File

@@ -108,4 +108,15 @@ describe('context', function() {
context2.global.get("foo").should.eql("test");
});
it('deletes context',function() {
var context = Context.get("1","flowA");
should.not.exist(context.get("foo"));
context.set("foo","abc");
context.get("foo").should.eql("abc");
Context.delete("1","flowA");
context = Context.get("1","flowA");
should.not.exist(context.get("foo"));
})
});

View File

@@ -45,7 +45,7 @@ describe('Flow', function() {
var TestNode = function(n) {
Node.call(this,n);
createCount++;
this._index = createCount++;
this.scope = n.scope;
var node = this;
this.foo = n.foo;
@@ -173,6 +173,61 @@ describe('Flow', function() {
});
it("instantiates config nodes in the right order",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
{id:"4",z:"t1",type:"test",foo:"5"}, // This node depends on #5
{id:"5",z:"t1",type:"test"}
]);
var flow = Flow.create(config,config.flows["t1"]);
flow.start();
Object.keys(flow.getActiveNodes()).should.have.length(5);
currentNodes.should.have.a.property("1");
currentNodes.should.have.a.property("2");
currentNodes.should.have.a.property("3");
currentNodes.should.have.a.property("4");
currentNodes.should.have.a.property("5");
currentNodes["1"].should.have.a.property("_index",2);
currentNodes["2"].should.have.a.property("_index",3);
currentNodes["3"].should.have.a.property("_index",4);
currentNodes["4"].should.have.a.property("_index",1);
currentNodes["5"].should.have.a.property("_index",0);
flow.stop().then(function() {
currentNodes.should.not.have.a.property("1");
currentNodes.should.not.have.a.property("2");
currentNodes.should.not.have.a.property("3");
currentNodes.should.not.have.a.property("4");
currentNodes.should.not.have.a.property("5");
stoppedNodes.should.have.a.property("1");
stoppedNodes.should.have.a.property("2");
stoppedNodes.should.have.a.property("3");
stoppedNodes.should.have.a.property("4");
stoppedNodes.should.have.a.property("5");
done();
});
});
it("detects dependency loops in config nodes",function() {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},
{id:"node1",z:"t1",type:"test",foo:"node2"}, // This node depends on #5
{id:"node2",z:"t1",type:"test",foo:"node1"}
]);
var flow = Flow.create(config,config.flows["t1"]);
/*jshint immed: false */
(function(){
flow.start();
}).should.throw("Circular config node dependency detected: node1");
});
it("instantiates a subflow and stops it",function(done) {
var config = flowUtils.parseConfig([
{id:"t1",type:"tab"},

View File

@@ -38,42 +38,42 @@ describe('flows/util', function() {
describe('#diffNodes',function() {
it('handles a null old node', function() {
flowUtil.diffNodes(null,{}).should.be.true;
flowUtil.diffNodes(null,{}).should.be.true();
});
it('ignores x/y changes', function() {
flowUtil.diffNodes({x:10,y:10},{x:20,y:10}).should.be.false;
flowUtil.diffNodes({x:10,y:10},{x:10,y:20}).should.be.false;
flowUtil.diffNodes({x:10,y:10},{x:20,y:10}).should.be.false();
flowUtil.diffNodes({x:10,y:10},{x:10,y:20}).should.be.false();
});
it('ignores wiring changes', function() {
flowUtil.diffNodes({wires:[]},{wires:[1,2,3]}).should.be.false;
flowUtil.diffNodes({wires:[]},{wires:[1,2,3]}).should.be.false();
});
it('spots existing property change - string', function() {
flowUtil.diffNodes({a:"foo"},{a:"bar"}).should.be.true;
flowUtil.diffNodes({a:"foo"},{a:"bar"}).should.be.true();
});
it('spots existing property change - number', function() {
flowUtil.diffNodes({a:0},{a:1}).should.be.true;
flowUtil.diffNodes({a:0},{a:1}).should.be.true();
});
it('spots existing property change - boolean', function() {
flowUtil.diffNodes({a:true},{a:false}).should.be.true;
flowUtil.diffNodes({a:true},{a:false}).should.be.true();
});
it('spots existing property change - truthy', function() {
flowUtil.diffNodes({a:true},{a:1}).should.be.true;
flowUtil.diffNodes({a:true},{a:1}).should.be.true();
});
it('spots existing property change - falsey', function() {
flowUtil.diffNodes({a:false},{a:0}).should.be.true;
flowUtil.diffNodes({a:false},{a:0}).should.be.true();
});
it('spots existing property change - array', function() {
flowUtil.diffNodes({a:[0,1,2]},{a:[0,2,3]}).should.be.true;
flowUtil.diffNodes({a:[0,1,2]},{a:[0,2,3]}).should.be.true();
});
it('spots existing property change - object', function() {
flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{a:[0,2,3]}}).should.be.true;
flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{b:[0,1,2]}}).should.be.true;
flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{a:[0,2,3]}}).should.be.true();
flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{b:[0,1,2]}}).should.be.true();
});
it('spots added property', function() {
flowUtil.diffNodes({a:"foo"},{a:"foo",b:"bar"}).should.be.true;
flowUtil.diffNodes({a:"foo"},{a:"foo",b:"bar"}).should.be.true();
});
it('spots removed property', function() {
flowUtil.diffNodes({a:"foo",b:"bar"},{a:"foo"}).should.be.true;
flowUtil.diffNodes({a:"foo",b:"bar"},{a:"foo"}).should.be.true();
});
@@ -88,8 +88,7 @@ describe('flows/util', function() {
];
var parsedConfig = flowUtil.parseConfig(originalConfig);
var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"missingTypes":[]};
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
parsedConfig.should.eql(expectedConfig);
});
it('parses a single-tab flow with global config node', function() {
@@ -100,7 +99,7 @@ describe('flows/util', function() {
];
var parsedConfig = flowUtil.parseConfig(originalConfig);
var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"missingTypes":[]};
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
parsedConfig.should.eql(expectedConfig);
});
it('parses a multi-tab flow', function() {
@@ -112,8 +111,7 @@ describe('flows/util', function() {
];
var parsedConfig = flowUtil.parseConfig(originalConfig);
var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t2":{"id":"t2","type":"tab"},"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}},"t2":{"id":"t2","type":"tab","subflows":{},"configs":{},"nodes":{"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}}}},"missingTypes":[]};
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
parsedConfig.should.eql(expectedConfig);
});
it('parses a subflow flow', function() {
@@ -125,8 +123,7 @@ describe('flows/util', function() {
];
var parsedConfig = flowUtil.parseConfig(originalConfig);
var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[]},"sf1":{"id":"sf1","type":"subflow"},"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"subflows":{"sf1":{"id":"sf1","type":"subflow","configs":{},"nodes":{"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"instances":[{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}]}},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}}}},"missingTypes":[]};
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
parsedConfig.should.eql(expectedConfig);
});
it('parses a flow with a missing type', function() {
@@ -138,7 +135,17 @@ describe('flows/util', function() {
var parsedConfig = flowUtil.parseConfig(originalConfig);
parsedConfig.missingTypes.should.eql(['missing']);
var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},"t1-2":{"id":"t1-2","x":10,"y":10,"z":"t1","type":"missing","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},'t1-2': { id: 't1-2', x: 10, y: 10, z: 't1', type: 'missing', wires: [] }}}},"missingTypes":["missing"]};
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true;
redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true();
});
it('parses a flow with a missing flow', function() {
var originalConfig = [
{id:"t1-1",x:10,y:10,z:"t1",type:"test",foo:"cn", wires:[]},
{id:"cn",type:"test"},
];
var parsedConfig = flowUtil.parseConfig(originalConfig);
var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"missingTypes":[]};
parsedConfig.should.eql(expectedConfig);
});
@@ -308,7 +315,7 @@ describe('flows/util', function() {
diffResult.linked.sort().should.eql(["3"]);
});
it('identifies config nodes changes', function() {
it('identifies config nodes changes, node->config', function() {
var config = [
{id:"1",type:"test",foo:"configNode",wires:[["2"]]},
{id:"2",type:"test",bar:"b",wires:[["3"]]},
@@ -329,7 +336,30 @@ describe('flows/util', function() {
diffResult.removed.should.have.length(0);
diffResult.rewired.should.have.length(0);
diffResult.linked.sort().should.eql(["2","3"]);
});
it('identifies config nodes changes, node->config->config', function() {
var config = [
{id:"1",type:"test",foo:"configNode1",wires:[["2"]]},
{id:"2",type:"test",bar:"b",wires:[["3"]]},
{id:"3",type:"test",foo:"a",wires:[]},
{id:"configNode1",foo:"configNode2",type:"testConfig"},
{id:"configNode2",type:"testConfig"}
];
var newConfig = clone(config);
newConfig[4].foo = "bar";
var originalConfig = flowUtil.parseConfig(config);
var changedConfig = flowUtil.parseConfig(newConfig);
originalConfig.missingTypes.should.have.length(0);
var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig);
diffResult.added.should.have.length(0);
diffResult.changed.sort().should.eql(["1","configNode1","configNode2"]);
diffResult.removed.should.have.length(0);
diffResult.rewired.should.have.length(0);
diffResult.linked.sort().should.eql(["2","3"]);
});
it('marks a parent subflow as changed for an internal property change', function() {

View File

@@ -114,7 +114,8 @@ describe("red/nodes/registry/loader",function() {
registry.addNodeSet.lastCall.args[1].should.not.have.a.property('err');
nodes.registerType.calledOnce.should.be.true;
nodes.registerType.lastCall.args[0].should.eql('test-node-1');
nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode1');
nodes.registerType.lastCall.args[1].should.eql('test-node-1');
done();
}).otherwise(function(err) {
@@ -162,8 +163,10 @@ describe("red/nodes/registry/loader",function() {
registry.addNodeSet.lastCall.args[1].should.not.have.a.property('err');
nodes.registerType.calledTwice.should.be.true;
nodes.registerType.firstCall.args[0].should.eql('test-node-multiple-1a');
nodes.registerType.secondCall.args[0].should.eql('test-node-multiple-1b');
nodes.registerType.firstCall.args[0].should.eql('node-red/MultipleNodes1');
nodes.registerType.firstCall.args[1].should.eql('test-node-multiple-1a');
nodes.registerType.secondCall.args[0].should.eql('node-red/MultipleNodes1');
nodes.registerType.secondCall.args[1].should.eql('test-node-multiple-1b');
done();
}).otherwise(function(err) {
@@ -212,7 +215,8 @@ describe("red/nodes/registry/loader",function() {
registry.addNodeSet.lastCall.args[1].should.not.have.a.property('err');
nodes.registerType.calledOnce.should.be.true;
nodes.registerType.lastCall.args[0].should.eql('test-node-2');
nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode2');
nodes.registerType.lastCall.args[1].should.eql('test-node-2');
done();
}).otherwise(function(err) {

View File

@@ -20,8 +20,12 @@ var sinon = require("sinon");
var typeRegistry = require("../../../../../red/runtime/nodes/registry/registry");
var Node = require("../../../../../red/runtime/nodes/Node");
var events = require("../../../../../red/runtime/events");
var inherits = require("util").inherits;
describe("red/nodes/registry/registry",function() {
afterEach(function() {
@@ -428,31 +432,52 @@ describe("red/nodes/registry/registry",function() {
});
describe('#registerNodeConstructor', function() {
function TestNodeConstructor() {
}
var TestNodeConstructor;
beforeEach(function() {
TestNodeConstructor = function TestNodeConstructor() {
};
sinon.stub(events,'emit');
});
afterEach(function() {
events.emit.restore();
});
it('registers a node constructor', function() {
typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor);
typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor);
events.emit.calledOnce.should.be.true;
events.emit.lastCall.args[0].should.eql('type-registered');
events.emit.lastCall.args[1].should.eql('node-type');
})
it('throws error on duplicate node registration', function() {
typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor);
typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor);
events.emit.calledOnce.should.be.true;
events.emit.lastCall.args[0].should.eql('type-registered');
events.emit.lastCall.args[1].should.eql('node-type');
/*jshint immed: false */
(function(){
typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor);
typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor);
}).should.throw("node-type already registered");
events.emit.calledOnce.should.be.true;
})
});
it('extends a constructor with the Node constructor', function() {
TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node);
typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor);
TestNodeConstructor.prototype.should.be.an.instanceOf(Node);
});
it('does not override a constructor\'s prototype', function() {
function Foo(){};
inherits(TestNodeConstructor,Foo);
TestNodeConstructor.prototype.should.be.an.instanceOf(Foo);
TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node);
typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor);
TestNodeConstructor.prototype.should.be.an.instanceOf(Node);
TestNodeConstructor.prototype.should.be.an.instanceOf(Foo);
typeRegistry.registerNodeConstructor('node-set','node-type2',TestNodeConstructor);
TestNodeConstructor.prototype.should.be.an.instanceOf(Node);
TestNodeConstructor.prototype.should.be.an.instanceOf(Foo);
});
});
});

View File

@@ -37,11 +37,13 @@ describe("red/settings", function() {
settings.a.should.equal(123);
settings.b.should.equal("test");
settings.c.should.be.an.Array.with.lengthOf(3);
settings.c.should.be.an.Array;
settings.c.should.have.lengthOf(3);
settings.get("a").should.equal(123);
settings.get("b").should.equal("test");
settings.get("c").should.be.an.Array.with.lengthOf(3);
settings.get("c").should.be.an.Array;
settings.get("c").should.have.lengthOf(3);
/*jshint immed: false */
(function() {
@@ -49,7 +51,8 @@ describe("red/settings", function() {
}).should.throw();
settings.c.push(5);
settings.c.should.be.an.Array.with.lengthOf(4);
settings.c.should.be.an.Array;
settings.c.should.have.lengthOf(4);
/*jshint immed: false */
(function() {
@@ -129,7 +132,8 @@ describe("red/settings", function() {
settings.should.have.property("a",123);
settings.should.have.property("b","test");
settings.c.should.be.an.Array.with.lengthOf(3);
settings.c.should.be.an.Array;
settings.c.should.have.lengthOf(3);
settings.reset();

View File

@@ -58,6 +58,7 @@ describe("red/util", function() {
it('Buffer', function() {
util.compareObjects(new Buffer("hello"),new Buffer("hello")).should.equal(true);
util.compareObjects(new Buffer("hello"),new Buffer("hello ")).should.equal(false);
util.compareObjects(new Buffer("hello"),"hello").should.equal(false);
});
});
@@ -157,7 +158,16 @@ describe("red/util", function() {
(function() {
util.getMessageProperty({a:"foo"},"msg.a.b.c");
}).should.throw();
})
});
it('retrieves a property with array syntax', function() {
var v = util.getMessageProperty({a:["foo","bar"]},"msg.a[0]");
v.should.eql("foo");
var v2 = util.getMessageProperty({a:[null,{b:"foo"}]},"a[1].b");
v2.should.eql("foo");
var v3 = util.getMessageProperty({a:[[["foo"]]]},"a[0][0][0]");
v3.should.eql("foo");
});
});
describe('setMessageProperty', function() {
@@ -190,7 +200,48 @@ describe("red/util", function() {
var msg = {a:{}};
util.setMessageProperty(msg,"msg.a.b.c",undefined);
should.not.exist(msg.a.b);
});
it('sets a property with array syntax', function() {
var msg = {a:{b:["foo",{c:["",""]}]}};
util.setMessageProperty(msg,"msg.a.b[1].c[1]","bar");
msg.a.b[1].c[1].should.eql('bar');
});
it('creates missing array elements - final property', function() {
var msg = {a:[]};
util.setMessageProperty(msg,"msg.a[2]","bar");
msg.a.should.have.length(3);
msg.a[2].should.eql("bar");
});
it('creates missing array elements - mid property', function() {
var msg = {};
util.setMessageProperty(msg,"msg.a[2].b","bar");
msg.a.should.have.length(3);
msg.a[2].b.should.eql("bar");
});
it('creates missing array elements - multi-arrays', function() {
var msg = {};
util.setMessageProperty(msg,"msg.a[2][2]","bar");
msg.a.should.have.length(3);
msg.a.should.be.instanceOf(Array);
msg.a[2].should.have.length(3);
msg.a[2].should.be.instanceOf(Array);
msg.a[2][2].should.eql("bar");
});
it('does not create missing array elements - final property', function() {
var msg = {a:{}};
util.setMessageProperty(msg,"msg.a.b[2]","bar",false);
should.not.exist(msg.a.b);
// check it has not been misinterpreted
msg.a.should.not.have.property("b[2]");
});
it('deletes property inside array if value is undefined', function() {
var msg = {a:[1,2,3]};
util.setMessageProperty(msg,"msg.a[1]",undefined);
msg.a.should.have.length(2);
msg.a[0].should.eql(1);
msg.a[1].should.eql(3);
})
});
describe('evaluateNodeProperty', function() {
@@ -210,6 +261,20 @@ describe("red/util", function() {
var result = util.evaluateNodeProperty('^abc$','re');
result.toString().should.eql("/^abc$/");
});
it('returns boolean',function() {
var result = util.evaluateNodeProperty('true','bool');
result.should.be.true();
result = util.evaluateNodeProperty('TrUe','bool');
result.should.be.true();
result = util.evaluateNodeProperty('false','bool');
result.should.be.false();
result = util.evaluateNodeProperty('','bool');
result.should.be.false();
});
it('returns date',function() {
var result = util.evaluateNodeProperty('','date');
(Date.now() - result).should.be.approximately(0,50);
});
it('returns msg property',function() {
var result = util.evaluateNodeProperty('foo.bar','msg',{},{foo:{bar:"123"}});
result.should.eql("123");
@@ -242,7 +307,48 @@ describe("red/util", function() {
},{});
result.should.eql("123");
});
});
describe('normalisePropertyExpression', function() {
function testABC(input,expected) {
var result = util.normalisePropertyExpression(input);
// console.log("+",input);
// console.log(result);
result.should.eql(expected);
}
})
function testInvalid(input) {
/*jshint immed: false */
(function() {
util.normalisePropertyExpression(input);
}).should.throw();
}
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[0].c",function() { testABC("a[0].c",['a',0,'c']); })
it("pass a.0.c",function() { testABC("a.0.c",['a',0,'c']); })
it("pass a['a.b[0]'].c",function() { testABC("a['a.b[0]'].c",['a','a.b[0]','c']); })
it("pass a[0][0][0]",function() { testABC("a[0][0][0]",['a',0,0,0]); })
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[]"); })
it("fail a]",function() { testInvalid("a]"); })
it("fail a[",function() { testInvalid("a["); })
it("fail a[0d]",function() { testInvalid("a[0d]"); })
it("fail a['",function() { testInvalid("a['"); })
it("fail a[']",function() { testInvalid("a[']"); })
it("fail a[0']",function() { testInvalid("a[0']"); })
it("fail a.[0]",function() { testInvalid("a.[0]"); })
it("fail [0]",function() { testInvalid("[0]"); })
it("fail a[0",function() { testInvalid("a[0"); })
it("fail a.",function() { testInvalid("a."); })
it("fail .a",function() { testInvalid(".a"); })
it("fail a. b",function() { testInvalid("a. b"); })
it("fail a.b",function() { testInvalid(" a.b"); })
it("fail a[0].[1]",function() { testInvalid("a[0].[1]"); })
});
});