',{style:"height:100%"}).appendTo("#sidebar-content")
options.wrapper.append(options.content);
options.wrapper.hide();
@@ -82,6 +85,8 @@ RED.sidebar = (function() {
group: "sidebar-tabs"
});
+ options.iconClass = options.iconClass || "fa fa-square-o"
+
knownTabs[options.id] = options;
if (options.visible !== false) {
diff --git a/editor/js/ui/tab-config.js b/editor/js/ui/tab-config.js
index 9a4619b28..00caea6cd 100644
--- a/editor/js/ui/tab-config.js
+++ b/editor/js/ui/tab-config.js
@@ -221,8 +221,7 @@ RED.sidebar.config = (function() {
name: RED._("sidebar.config.name"),
content: content,
toolbar: toolbar,
- closeable: true,
- visible: false,
+ iconClass: "fa fa-cog",
onchange: function() { refreshConfigNodeList(); }
});
RED.actions.add("core:show-config-tab",function() {RED.sidebar.show('config')});
diff --git a/editor/js/ui/tab-info.js b/editor/js/ui/tab-info.js
index b4cf87d46..340a6bd74 100644
--- a/editor/js/ui/tab-info.js
+++ b/editor/js/ui/tab-info.js
@@ -83,7 +83,9 @@ RED.sidebar.info = (function() {
id: "info",
label: RED._("sidebar.info.label"),
name: RED._("sidebar.info.name"),
+ iconClass: "fa fa-info",
content: content,
+ pinned: true,
enableOnEdit: true
});
if (tips.enabled()) {
diff --git a/editor/sass/popover.scss b/editor/sass/popover.scss
index d3c077e6d..691acd5c8 100644
--- a/editor/sass/popover.scss
+++ b/editor/sass/popover.scss
@@ -30,7 +30,6 @@
}
.red-ui-popover:after, .red-ui-popover:before {
- top: 50%;
border: solid transparent;
content: " ";
height: 0;
@@ -39,12 +38,18 @@
pointer-events: none;
}
.red-ui-popover.red-ui-popover-right:after, .red-ui-popover.red-ui-popover-right:before {
+ top: 50%;
right: 100%;
}
.red-ui-popover.red-ui-popover-left:after, .red-ui-popover.red-ui-popover-left:before {
+ top: 50%;
left: 100%;
}
+.red-ui-popover.red-ui-popover-bottom:after, .red-ui-popover.red-ui-popover-bottom:before {
+ bottom: 100%;
+ left: 50%;
+}
.red-ui-popover.red-ui-popover-right:after {
border-color: rgba(136, 183, 213, 0);
@@ -72,6 +77,21 @@
margin-top: -11px;
}
+
+.red-ui-popover.red-ui-popover-bottom:after {
+ border-color: rgba(136, 183, 213, 0);
+ border-bottom-color: #fff;
+ border-width: 10px;
+ margin-left: -10px;
+}
+.red-ui-popover.red-ui-popover-bottom:before {
+ border-color: rgba(194, 225, 245, 0);
+ border-bottom-color: $primary-border-color;
+ border-width: 11px;
+ margin-left: -11px;
+}
+
+
.red-ui-popover-size-small {
font-size: 11px;
padding: 5px;
@@ -93,4 +113,12 @@
border-width: 6px;
margin-top: -6px;
}
+ &.red-ui-popover-bottom:after {
+ border-width: 5px;
+ margin-left: -5px;
+ }
+ &.red-ui-popover-bottom:before {
+ border-width: 6px;
+ margin-left: -6px;
+ }
}
diff --git a/editor/sass/tabs.scss b/editor/sass/tabs.scss
index 4b2dc7f5d..488f4f3c9 100644
--- a/editor/sass/tabs.scss
+++ b/editor/sass/tabs.scss
@@ -93,7 +93,7 @@
color: $workspace-button-color-hover;
}
}
- .red-ui-tab-icon {
+ img.red-ui-tab-icon {
opacity: 0.2;
}
}
@@ -113,6 +113,21 @@
&.red-ui-tabs-add.red-ui-tabs-scrollable {
padding-right: 59px;
}
+ &.red-ui-tabs-collapsible {
+ li:not(.active) {
+ display: none;
+ &.red-ui-tab-pinned {
+ a {
+ padding-left: 0;
+ text-align: center;
+ }
+ span {
+ display: none;
+ }
+ width: 32px;
+ }
+ }
+ }
&.red-ui-tabs-vertical {
box-sizing: border-box;
@@ -157,6 +172,15 @@
}
}
}
+ .red-ui-tabs-select {
+ position: absolute;
+ top:0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ opacity: 0.4;
+ background: red;
+ }
}
.red-ui-tab-button {
position: absolute;
@@ -180,7 +204,33 @@
z-index: 2;
}
}
-
+.red-ui-tab-link-buttons {
+ position: absolute;
+ box-sizing: border-box;
+ top: 0;
+ right: 0;
+ height: 35px;
+ background: #fff;
+ border-bottom: 1px solid $primary-border-color;
+ z-index: 2;
+ a {
+ @include workspace-button;
+ line-height: 26px;
+ height: 28px;
+ width: 28px;
+ margin: 4px 3px 3px;
+ border: 1px solid $primary-border-color;
+ z-index: 2;
+ &.red-ui-tab-link-button {
+ &:not(.active) {
+ background: #eee;
+ }
+ }
+ &.red-ui-tab-link-button-menu {
+ border-color: white;
+ }
+ }
+}
.red-ui-tab-scroll {
width: 21px;
top: 0;
@@ -216,7 +266,7 @@
right: 38px;
}
-.red-ui-tab-icon {
+img.red-ui-tab-icon {
margin-left: -8px;
margin-right: 3px;
margin-top: -2px;
@@ -225,6 +275,11 @@
height: 20px;
vertical-align: middle;
}
+i.red-ui-tab-icon {
+ opacity: 0.7;
+ width: 18px;
+ height: 20px;
+}
.red-ui-tabs-badges {
position: absolute;
diff --git a/editor/vendor/jsonata/formatter.js b/editor/vendor/jsonata/formatter.js
index 9b0f544c8..6ff26accb 100644
--- a/editor/vendor/jsonata/formatter.js
+++ b/editor/vendor/jsonata/formatter.js
@@ -117,6 +117,7 @@
'$contains':{ args:[ 'str', 'pattern' ]},
'$count':{ args:[ 'array' ]},
'$each':{ args:[ 'object', 'function' ]},
+ '$env': { args:[ 'arg' ]},
'$exists':{ args:[ 'arg' ]},
'$filter':{ args:[ 'array', 'function' ]},
'$floor':{ args:[ 'number' ]},
diff --git a/nodes/core/core/20-inject.html b/nodes/core/core/20-inject.html
index 4090fdbbd..6f8ebcf69 100644
--- a/nodes/core/core/20-inject.html
+++ b/nodes/core/core/20-inject.html
@@ -263,7 +263,7 @@ If you want every 20 minutes from now - use the
"interval" option.
$("#node-input-payload").typedInput({
default: 'str',
typeField: $("#node-input-payloadType"),
- types:['flow','global','str','num','bool','json','bin','date']
+ types:['flow','global','str','num','bool','json','bin','date','env']
});
$("#inject-time-type-select").change(function() {
diff --git a/nodes/core/core/58-debug.html b/nodes/core/core/58-debug.html
index 7317d7064..95b55d7f5 100644
--- a/nodes/core/core/58-debug.html
+++ b/nodes/core/core/58-debug.html
@@ -154,7 +154,9 @@
name: this._("debug.sidebar.name"),
content: uiComponents.content,
toolbar: uiComponents.footer,
- enableOnEdit: true
+ enableOnEdit: true,
+ pinned: true,
+ iconClass: "fa fa-wrench"
});
RED.actions.add("core:show-debug-tab",function() { RED.sidebar.show('debug'); });
diff --git a/nodes/core/logic/10-switch.html b/nodes/core/logic/10-switch.html
index ffa5a48e5..f4ffc7dea 100644
--- a/nodes/core/logic/10-switch.html
+++ b/nodes/core/logic/10-switch.html
@@ -230,12 +230,12 @@
selectField.append($("
").val(operators[d].v).text(/^switch/.test(operators[d].t)?node._(operators[d].t):operators[d].t));
}
}
- var valueField = $('
',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num','jsonata',previousValueType]});
- var numValueField = $('
',{class:"node-input-rule-num-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['flow','global','num','jsonata']});
+ var valueField = $('
',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
+ var numValueField = $('
',{class:"node-input-rule-num-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['flow','global','num','jsonata','env']});
var expValueField = $('
',{class:"node-input-rule-exp-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'jsonata',types:['jsonata']});
- var btwnValueField = $('
',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
+ var btwnValueField = $('
',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
var btwnAndLabel = $('
',{class:"node-input-rule-btwn-label"}).text(" "+andLabel+" ").appendTo(row3);
- var btwnValue2Field = $('
',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
+ var btwnValue2Field = $('
',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
var typeValueField = $('
',{class:"node-input-rule-type-value",type:"text",style:"margin-left: 5px;"}).appendTo(row)
.typedInput({default:'string',types:[
{value:"string",label:"string",hasValue:false},
diff --git a/nodes/core/logic/15-change.html b/nodes/core/logic/15-change.html
index 999c0ed71..83bdecf9e 100644
--- a/nodes/core/logic/15-change.html
+++ b/nodes/core/logic/15-change.html
@@ -146,7 +146,7 @@
.appendTo(row2);
var propertyValue = $('
',{class:"node-input-rule-property-value",type:"text"})
.appendTo(row2)
- .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata']});
+ .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
var row3_1 = $('
').appendTo(row3);
$('
',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
@@ -154,7 +154,7 @@
.appendTo(row3_1);
var fromValue = $('
',{class:"node-input-rule-property-search-value",type:"text"})
.appendTo(row3_1)
- .typedInput({default:'str',types:['msg','flow','global','str','re','num','bool']});
+ .typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']});
var row3_2 = $('
',{style:"margin-top:8px;"}).appendTo(row3);
$('
',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
@@ -162,7 +162,7 @@
.appendTo(row3_2);
var toValue = $('
',{class:"node-input-rule-property-replace-value",type:"text"})
.appendTo(row3_2)
- .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin']});
+ .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
$('
',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(to)
diff --git a/nodes/core/logic/15-change.js b/nodes/core/logic/15-change.js
index ae50394ed..c7c05118e 100644
--- a/nodes/core/logic/15-change.js
+++ b/nodes/core/logic/15-change.js
@@ -93,6 +93,8 @@ module.exports = function(RED) {
valid = false;
this.error(RED._("change.errors.invalid-expr",{error:e.message}));
}
+ } else if (rule.tot === 'env') {
+ rule.to = RED.util.evaluateNodeProperty(rule.to,'env');
}
}
diff --git a/red/api/editor/locales/en-US/jsonata.json b/red/api/editor/locales/en-US/jsonata.json
index 14c7f7cd1..c839c759e 100644
--- a/red/api/editor/locales/en-US/jsonata.json
+++ b/red/api/editor/locales/en-US/jsonata.json
@@ -190,11 +190,11 @@
},
"$flowContext": {
"args": "string",
- "desc": "Retrieves a flow context property."
+ "desc": "Retrieves a flow context property.\n\nThis is a Node-RED defined function."
},
"$globalContext": {
"args": "string",
- "desc": "Retrieves a global context property."
+ "desc": "Retrieves a global context property.\n\nThis is a Node-RED defined function."
},
"$pad": {
"args": "string, width [, char]",
@@ -215,5 +215,9 @@
"$toMillis": {
"args": "timestamp",
"desc": "Convert a `timestamp` string in the ISO 8601 format to the number of milliseconds since the Unix Epoch (1 January, 1970 UTC) as a number. An error is thrown if the string is not in the correct format."
+ },
+ "$env": {
+ "args": "arg",
+ "desc": "Returns the value of an environment variable.\n\nThis is a Node-RED defined function."
}
}
diff --git a/red/runtime-registry/loader.js b/red/runtime-registry/loader.js
index f0bd86587..4ef841b48 100644
--- a/red/runtime-registry/loader.js
+++ b/red/runtime-registry/loader.js
@@ -64,6 +64,17 @@ function copyObjectProperties(src,dst,copyList,blockList) {
}
}
}
+function requireModule(name) {
+ var moduleInfo = registry.getModuleInfo(name);
+ if (moduleInfo && moduleInfo.path) {
+ var relPath = path.relative(__dirname, moduleInfo.path);
+ return require(relPath);
+ } else {
+ var err = new Error(`Cannot find module '${name}'`);
+ err.code = "MODULE_NOT_FOUND";
+ throw err;
+ }
+}
function createNodeApi(node) {
var red = {
@@ -73,6 +84,7 @@ function createNodeApi(node) {
events: runtime.events,
util: runtime.util,
version: runtime.version,
+ require: requireModule,
comms: {
publish: function(topic,data,retain) {
runtime.events.emit("comms",{
diff --git a/red/runtime-registry/localfilesystem.js b/red/runtime-registry/localfilesystem.js
index 877491f2f..409b46425 100644
--- a/red/runtime-registry/localfilesystem.js
+++ b/red/runtime-registry/localfilesystem.js
@@ -88,7 +88,7 @@ function getLocalNodeFiles(dir) {
try {
files = fs.readdirSync(dir);
} catch(err) {
- return result;
+ return {files: [], icons: []};
}
files.sort();
files.forEach(function(fn) {
@@ -296,6 +296,7 @@ function getNodeFiles(disableNodePathScan) {
nodeList[moduleFile.package.name] = {
name: moduleFile.package.name,
version: moduleFile.package.version,
+ path: moduleFile.dir,
local: moduleFile.local||false,
nodes: {},
icons: nodeModuleFiles.icons,
diff --git a/red/runtime-registry/registry.js b/red/runtime-registry/registry.js
index fa5fc6ae3..65f3251f1 100644
--- a/red/runtime-registry/registry.js
+++ b/red/runtime-registry/registry.js
@@ -339,6 +339,7 @@ function getModuleInfo(module) {
name: module,
version: moduleConfigs[module].version,
local: moduleConfigs[module].local,
+ path: moduleConfigs[module].path,
nodes: []
};
for (var i = 0; i < nodes.length; ++i) {
@@ -560,30 +561,6 @@ var icon_paths = {
var iconCache = {};
var defaultIcon = path.resolve(__dirname + '/../../public/icons/arrow-in.png');
-function nodeIconDir(dir) {
- icon_paths[dir.name] = icon_paths[dir.name] || [];
- icon_paths[dir.name].push(path.resolve(dir.path));
-
- if (dir.icons) {
- if (!moduleConfigs[dir.name]) {
- moduleConfigs[dir.name] = {
- name: dir.name,
- nodes: {},
- icons: []
- };
- }
- var module = moduleConfigs[dir.name];
- if (module.icons === undefined) {
- module.icons = [];
- }
- dir.icons.forEach(function(icon) {
- if (module.icons.indexOf(icon) === -1) {
- module.icons.push(icon);
- }
- });
- }
-}
-
function getNodeIconPath(module,icon) {
if (/\.\./.test(icon)) {
throw new Error();
@@ -624,7 +601,6 @@ function getNodeIcons() {
}
}
}
-
return iconList;
}
diff --git a/red/runtime/nodes/flows/util.js b/red/runtime/nodes/flows/util.js
index cda79adc6..512eb19a7 100644
--- a/red/runtime/nodes/flows/util.js
+++ b/red/runtime/nodes/flows/util.js
@@ -37,7 +37,8 @@ function diffNodes(oldNode,newNode) {
return false;
}
-var EnvVarPropertyRE = /^\$\((\S+)\)$/;
+var EnvVarPropertyRE_old = /^\$\((\S+)\)$/;
+var EnvVarPropertyRE = /^\${(\S+)}$/;
function mapEnvVarProperties(obj,prop) {
if (Buffer.isBuffer(obj[prop])) {
@@ -47,11 +48,9 @@ function mapEnvVarProperties(obj,prop) {
mapEnvVarProperties(obj[prop],i);
}
} else if (typeof obj[prop] === 'string') {
- var m;
- if ( (m = EnvVarPropertyRE.exec(obj[prop])) !== null) {
- if (process.env.hasOwnProperty(m[1])) {
- obj[prop] = process.env[m[1]];
- }
+ if (obj[prop][0] === "$" && (EnvVarPropertyRE_old.test(obj[prop]) || EnvVarPropertyRE.test(obj[prop])) ) {
+ var envVar = obj[prop].substring(2,obj[prop].length-1);
+ obj[prop] = process.env.hasOwnProperty(envVar)?process.env[envVar]:obj[prop];
}
} else {
for (var p in obj[prop]) {
diff --git a/red/runtime/storage/localfilesystem/index.js b/red/runtime/storage/localfilesystem/index.js
index af9d103fb..510ed261b 100644
--- a/red/runtime/storage/localfilesystem/index.js
+++ b/red/runtime/storage/localfilesystem/index.js
@@ -73,7 +73,8 @@ var localfilesystem = {
var defaultPackage = {
"name": "node-red-project",
"description": "A Node-RED Project",
- "version": "0.0.1"
+ "version": "0.0.1",
+ "private": true
};
return util.writeFile(packageFile,JSON.stringify(defaultPackage,"",4));
}
diff --git a/red/runtime/util.js b/red/runtime/util.js
index 88c06a029..257e87777 100644
--- a/red/runtime/util.js
+++ b/red/runtime/util.js
@@ -303,6 +303,23 @@ function setMessageProperty(msg,prop,value,createMissing) {
}
}
+function evaluteEnvProperty(value) {
+ if (/^\${[^}]+}$/.test(value)) {
+ // ${ENV_VAR}
+ value = value.substring(2,value.length-1);
+ value = process.env.hasOwnProperty(value)?process.env[value]:""
+ } else if (!/\${\S+}/.test(value)) {
+ // ENV_VAR
+ value = process.env.hasOwnProperty(value)?process.env[value]:""
+ } else {
+ // FOO${ENV_VAR}BAR
+ value = value.replace(/\${([^}]+)}/g, function(match, v) {
+ return process.env.hasOwnProperty(v)?process.env[v]:""
+ });
+ }
+ return value;
+}
+
function evaluateNodeProperty(value, type, node, msg) {
if (type === 'str') {
return ""+value;
@@ -328,6 +345,8 @@ function evaluateNodeProperty(value, type, node, msg) {
} else if (type === 'jsonata') {
var expr = prepareJSONataExpression(value,node);
return evaluateJSONataExpression(expr,msg);
+ } else if (type === 'env') {
+ return evaluteEnvProperty(value);
}
return value;
}
@@ -340,6 +359,9 @@ function prepareJSONataExpression(value,node) {
expr.assign('globalContext',function(val) {
return node.context().global.get(val);
});
+ expr.assign('env', function(val) {
+ return process.env[val];
+ })
expr.registerFunction('clone', cloneMessage, '<(oa)-:o>');
expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);
return expr;
diff --git a/test/nodes/core/logic/15-change_spec.js b/test/nodes/core/logic/15-change_spec.js
index f5ca0f3d2..e444b2663 100644
--- a/test/nodes/core/logic/15-change_spec.js
+++ b/test/nodes/core/logic/15-change_spec.js
@@ -390,7 +390,7 @@ describe('change Node', function() {
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:[]}];
@@ -409,6 +409,33 @@ describe('change Node', function() {
});
});
+ describe('env var', function() {
+ before(function() {
+ process.env.NR_TEST_A = 'foo';
+ })
+ after(function() {
+ delete process.env.NR_TEST_A;
+ })
+ it('sets the value using env property', function(done) {
+ var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"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("foo");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"123",topic:"ABC"});
+ });
+ });
+ });
+
+
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:[]}];
@@ -872,6 +899,33 @@ describe('change Node', function() {
changeNode1.receive({payload:""});
});
});
+
+ describe('env var', function() {
+ before(function() {
+ process.env.NR_TEST_A = 'foo';
+ })
+ after(function() {
+ delete process.env.NR_TEST_A;
+ })
+ it('changes the value using env property', function(done) {
+ var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"NR_TEST_A","fromt":"msg","tot":"env"}],"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("abcfooabc");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"abcABCabc",topic:"ABC"});
+ });
+ });
+ });
+
});
describe("#delete", function() {
diff --git a/test/red/runtime/nodes/flows/util_spec.js b/test/red/runtime/nodes/flows/util_spec.js
index dda43ce9a..774e786ad 100644
--- a/test/red/runtime/nodes/flows/util_spec.js
+++ b/test/red/runtime/nodes/flows/util_spec.js
@@ -35,10 +35,17 @@ describe('flows/util', function() {
});
describe('#mapEnvVarProperties',function() {
- it('handles ENV substitutions in an object', function() {
+ before(function() {
process.env.foo1 = "bar1";
process.env.foo2 = "bar2";
process.env.foo3 = "bar3";
+ })
+ after(function() {
+ delete process.env.foo1;
+ delete process.env.foo2;
+ delete process.env.foo3;
+ })
+ it('handles ENV substitutions in an object - $()', function() {
var foo = {a:"$(foo1)",b:"$(foo2)",c:{d:"$(foo3)"}};
for (var p in foo) {
if (foo.hasOwnProperty(p)) {
@@ -47,6 +54,15 @@ describe('flows/util', function() {
}
foo.should.eql({ a: 'bar1', b: 'bar2', c: { d: 'bar3' } } );
});
+ it('handles ENV substitutions in an object - ${}', function() {
+ var foo = {a:"${foo1}",b:"${foo2}",c:{d:"${foo3}"}};
+ for (var p in foo) {
+ if (foo.hasOwnProperty(p)) {
+ flowUtil.mapEnvVarProperties(foo,p);
+ }
+ }
+ foo.should.eql({ a: 'bar1', b: 'bar2', c: { d: 'bar3' } } );
+ });
});
describe('#diffNodes',function() {
diff --git a/test/red/runtime/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png b/test/red/runtime/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png
new file mode 100644
index 000000000..e38f39146
Binary files /dev/null and b/test/red/runtime/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png differ
diff --git a/test/red/runtime/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png b/test/red/runtime/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png
new file mode 100644
index 000000000..59a29af14
--- /dev/null
+++ b/test/red/runtime/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png
@@ -0,0 +1,3 @@
+This file exists just to ensure the 'icons' directory is in the repository.
+TODO: a future test needs to ensure the right icon files are loaded - this
+ directory can be used for that
diff --git a/test/red/runtime/util_spec.js b/test/red/runtime/util_spec.js
index f542228a2..1b7438efd 100644
--- a/test/red/runtime/util_spec.js
+++ b/test/red/runtime/util_spec.js
@@ -307,6 +307,38 @@ describe("red/util", function() {
},{});
result.should.eql("123");
});
+ describe('environment variable', function() {
+ before(function() {
+ process.env.NR_TEST_A = "foo";
+ process.env.NR_TEST_B = "${NR_TEST_A}";
+ })
+ after(function() {
+ delete process.env.NR_TEST_A;
+ delete process.env.NR_TEST_B;
+ })
+
+ it('returns an environment variable - NR_TEST_A', function() {
+ var result = util.evaluateNodeProperty('NR_TEST_A','env');
+ result.should.eql('foo');
+ });
+ it('returns an environment variable - ${NR_TEST_A}', function() {
+ var result = util.evaluateNodeProperty('${NR_TEST_A}','env');
+ result.should.eql('foo');
+ });
+ it('returns an environment variable - ${NR_TEST_A', function() {
+ var result = util.evaluateNodeProperty('${NR_TEST_A','env');
+ result.should.eql('');
+ });
+ it('returns an environment variable - foo${NR_TEST_A}bar', function() {
+ var result = util.evaluateNodeProperty('123${NR_TEST_A}456','env');
+ result.should.eql('123foo456');
+ });
+ it('returns an environment variable - foo${NR_TEST_B}bar', function() {
+ var result = util.evaluateNodeProperty('123${NR_TEST_B}456','env');
+ result.should.eql('123${NR_TEST_A}456');
+ });
+
+ });
});
describe('normalisePropertyExpression', function() {