Compare commits

..

2 Commits

Author SHA1 Message Date
Dave Conway-Jones
63cf6dd6df WIP re quotes 2022-10-29 16:53:14 +01:00
Dave Conway-Jones
501c78666d Fix CSV node to handle headers with quotes and spaces
This is a breaking change so needs thinking about
Includes updated tests
2022-10-19 09:19:54 +01:00
82 changed files with 135 additions and 340 deletions

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

@@ -1965,7 +1965,7 @@ RED.nodes = (function() {
}
}
} else {
const keepNodesCurrentZ = reimport && n.z && (RED.workspaces.contains(n.z) || RED.nodes.subflow(n.z))
const keepNodesCurrentZ = reimport && n.z && RED.workspaces.contains(n.z)
if (!keepNodesCurrentZ && n.z && !workspace_map[n.z] && !subflow_map[n.z]) {
n.z = activeWorkspace;
}
@@ -2067,7 +2067,7 @@ RED.nodes = (function() {
node.id = getID();
} else {
node.id = n.id;
const keepNodesCurrentZ = reimport && node.z && (RED.workspaces.contains(node.z) || RED.nodes.subflow(node.z))
const keepNodesCurrentZ = reimport && node.z && RED.workspaces.contains(node.z)
if (!keepNodesCurrentZ && (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z]))) {
if (createMissingWorkspace) {
if (missingWorkspace === null) {
@@ -2740,7 +2740,6 @@ RED.nodes = (function() {
}
});
const nodeGroupMap = {}
var replaceNodeIds = Object.keys(replaceNodes);
if (replaceNodeIds.length > 0) {
var reimportList = [];
@@ -2751,12 +2750,6 @@ RED.nodes = (function() {
} else {
allNodes.removeNode(n);
}
if (n.g) {
// reimporting a node *without* including its group object
// will cause the g property to be cleared. Cache it
// here so we can restore it
nodeGroupMap[n.id] = n.g
}
reimportList.push(convertNode(n));
RED.events.emit('nodes:remove',n);
});
@@ -2778,18 +2771,6 @@ RED.nodes = (function() {
var newNodeMap = {};
result.nodes.forEach(function(n) {
newNodeMap[n.id] = n;
if (nodeGroupMap[n.id]) {
// This node is in a group - need to substitute the
// node reference inside the group
n.g = nodeGroupMap[n.id]
const group = RED.nodes.group(n.g)
if (group) {
var index = group.nodes.findIndex(gn => gn.id === n.id)
if (index > -1) {
group.nodes[index] = n
}
}
}
});
RED.nodes.eachLink(function(l) {
if (newNodeMap.hasOwnProperty(l.source.id)) {

View File

@@ -238,7 +238,6 @@ RED.editor = (function() {
var valid = validateNodeProperty(node, defaults, property,value);
if (((typeof valid) === "string") || !valid) {
input.addClass("input-error");
input.next(".red-ui-typedInput-container").addClass("input-error");
if ((typeof valid) === "string") {
var tooltip = input.data("tooltip");
if (tooltip) {
@@ -251,7 +250,6 @@ RED.editor = (function() {
}
} else {
input.removeClass("input-error");
input.next(".red-ui-typedInput-container").removeClass("input-error");
var tooltip = input.data("tooltip");
if (tooltip) {
input.data("tooltip", null);

0
packages/node_modules/@node-red/editor-client/src/js/ui/library.js vendored Normal file → Executable file
View File

2
packages/node_modules/@node-red/editor-client/src/js/ui/palette.js vendored Normal file → Executable file
View File

@@ -432,7 +432,6 @@ RED.palette = (function() {
categoryNode.find(".red-ui-palette-content").slideToggle();
categoryNode.find("i").toggleClass("expanded");
}
categoryNode.hide();
}
}
@@ -511,7 +510,6 @@ RED.palette = (function() {
currentCategoryNode.find(".red-ui-palette-content").slideToggle();
currentCategoryNode.find("i").toggleClass("expanded");
}
currentCategoryNode.hide();
}
}

View File

3
packages/node_modules/@node-red/editor-client/src/js/ui/view.js vendored Normal file → Executable file
View File

@@ -3369,9 +3369,6 @@ RED.view = (function() {
}
if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < dblClickInterval) {
mouse_mode = RED.state.DEFAULT;
// Avoid dbl click causing text selection.
d3.event.preventDefault()
document.getSelection().removeAllRanges()
if (d.type != "subflow") {
if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) {
RED.workspaces.show(d.type.substring(8));

View File

@@ -4,7 +4,7 @@
<label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label>
<select id="node-input-scope-select">
<option value="all" data-i18n="catch.scope.all"></option>
<option value="target" data-i18n="catch.scope.selected"></option>
<option value="target" data-i18n="catch.scope.selected"></options>
</select>
</div>
<div class="form-row node-input-uncaught-row">

View File

@@ -4,7 +4,7 @@
<label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label>
<select id="node-input-scope-select">
<option value="all" data-i18n="status.scope.all"></option>
<option value="target" data-i18n="status.scope.selected"></option>
<option value="target" data-i18n="status.scope.selected"></options>
</select>
</div>
<div class="form-row node-input-target-row">

View File

@@ -21,7 +21,6 @@
<option value="javascript">JavaScript</option>
<option value="css">CSS</option>
<option value="markdown">Markdown</option>
<option value="php">PHP</option>
<option value="python">Python</option>
<option value="sql">SQL</option>
<option value="yaml">YAML</option>

View File

@@ -366,16 +366,6 @@ module.exports = function(RED) {
}
}
function updateStatus(node, allNodes) {
let setStatus = setStatusDisconnected
if(node.connecting) {
setStatus = setStatusConnecting
} else if(node.connected) {
setStatus = setStatusConnected
}
setStatus(node, allNodes)
}
function setStatusDisconnected(node, allNodes) {
if(allNodes) {
for (var id in node.users) {
@@ -707,17 +697,13 @@ module.exports = function(RED) {
if (Object.keys(node.users).length === 1) {
if(node.autoConnect) {
node.connect();
//update nodes status
setTimeout(function() {
updateStatus(node, true)
}, 1)
}
}
};
node.deregister = function(mqttNode, done, autoDisconnect) {
node.deregister = function(mqttNode,done) {
delete node.users[mqttNode.id];
if (autoDisconnect && !node.closing && node.connected && Object.keys(node.users).length === 0) {
if (!node.closing && node.connected && Object.keys(node.users).length === 0) {
node.disconnect();
}
done();
@@ -1234,7 +1220,7 @@ module.exports = function(RED) {
} else {
node.brokerConn.unsubscribe(node.topic,node.id, removed);
}
node.brokerConn.deregister(node, done, removed);
node.brokerConn.deregister(node, done);
node.brokerConn = null;
} else {
done();
@@ -1297,9 +1283,9 @@ module.exports = function(RED) {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
}
node.brokerConn.register(node);
node.on('close', function(removed, done) {
node.on('close', function(done) {
if (node.brokerConn) {
node.brokerConn.deregister(node, done, removed)
node.brokerConn.deregister(node,done);
node.brokerConn = null;
} else {
done();

View File

@@ -46,7 +46,7 @@ module.exports = function(RED) {
isText = true;
} else if (parsedType.type !== "application") {
isText = false;
} else if ((parsedType.subtype !== "octet-stream")
} else if ((parsedType.subtype !== "octet-stream")
&& (parsedType.subtype !== "cbor")
&& (parsedType.subtype !== "x-protobuf")) {
checkUTF = true;
@@ -200,15 +200,6 @@ module.exports = function(RED) {
this.callback = function(req,res) {
var msgid = RED.util.generateId();
res._msgid = msgid;
// Since Node 15, req.headers are lazily computed and the property
// marked as non-enumerable.
// That means it doesn't show up in the Debug sidebar.
// This redefines the property causing it to be evaluated *and*
// marked as enumerable again.
Object.defineProperty(req, 'headers', {
value: req.headers,
enumerable: true
})
if (node.method.match(/^(post|delete|put|options|patch)$/)) {
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body});
} else if (node.method == "get") {

View File

@@ -435,10 +435,6 @@ in your Node-RED user directory (${RED.settings.userDir}).
formData.append(opt, val);
} else if (typeof val === 'object' && val.hasOwnProperty('value')) {
formData.append(opt,val.value,val.options || {});
} else if (Array.isArray(val)) {
for (var i=0; i<val.length; i++) {
formData.append(opt, val[i])
}
} else {
formData.append(opt,JSON.stringify(val));
}

View File

@@ -38,16 +38,38 @@ module.exports = function(RED) {
if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true;
var node = this;
var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
// var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
// pass in an array of column names to be trimmed, de-quoted and retrimmed
var clean = function(col,sep) {
if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); }
col = col.trim().split(re) || [""];
col = col.map(x => x.replace(/"/g,'').trim());
if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
var ff = true; // flag to indicate if inside or outside a pair of quotes true = outside.
var jj = 0; // pointer into array of template items
var kk = [""]; // array of data for each of the template items
for (var ii = 0; ii < col.length; ii++) {
//console.log("II",ii,col[ii]);
if (col[ii] === node.quo) { // if it's a quote toggle inside or outside
if (ii === 0 || col[ii-1] === sep) { ff = !ff; }
else if (col[ii] === node.quo && col[ii+1] === node.quo) { kk[jj] += '\\'; } // do nothing, "" = " in CSV world
else if (!ff && kk[jj][0] !== node.quo) { ff = !ff; }
else { kk[jj] += col[ii]; }
}
else if ((col[ii] === sep) && ff) { // if it is the end of the group then finish
jj += 1;
ff = true;
kk[jj] = col.length - 1 === ii ? null : "";
}
else if (col[ii] === " " && ff && (ii == 0 | col[ii-1] == sep | col[ii+1] == sep)) {
// skip
}
else {
kk[jj] += col[ii];
}
}
if ((kk.length === 1) && (kk[0] === "")) { node.goodtmpl = false; }
else { node.goodtmpl = true; }
return col;
console.log("KK",kk)
return kk;
}
var template = clean(node.template,',');
var notemplate = template.length === 1 && template[0] === '';
@@ -61,7 +83,7 @@ module.exports = function(RED) {
if (typeof msg.payload == "object") { // convert object to CSV string
try {
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
template = clean(node.template);
template = clean(node.template,",");
}
var ou = "";
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
@@ -74,7 +96,18 @@ module.exports = function(RED) {
template = Object.keys(msg.payload[0]);
}
}
ou += template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret;
var t = RED.util.cloneMessage(template);
template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v)
for (var i=0; i < t.length; i++) {
var v = t[i];
if (v.indexOf('"') !== -1 ) { v = v.replaceAll('"','""'); }
if (v.indexOf(node.sep)!==-1 || v.indexOf('"')!==-1 ) { v = '"'+v+'"'; }
console.log("V3",v)
t[i]=v;
}
console.log("T",t)
console.log("Te",template)
ou += t.join(node.sep) + node.ret;
if (node.hdrout === "once") { node.hdrSent = true; }
}
for (var s = 0; s < msg.payload.length; s++) {
@@ -136,7 +169,10 @@ module.exports = function(RED) {
}
else {
var tt = template[t];
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
if (template[t].indexOf('"') >=0 ) {
tt = tt.replaceAll("'","\\'");
tt = "'"+tt+"'";
}
else { tt = '"'+tt+'"'; }
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
/* istanbul ignore else */
@@ -199,7 +235,7 @@ module.exports = function(RED) {
if ((node.hdrin === true) && first) { // if the template is in the first line
if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break
if (line.length - i === 1) { tmp += line[i]; }
template = clean(tmp,node.sep);
template = clean(tmp.trimEnd(),node.sep);
first = false;
}
else { tmp += line[i]; }

View File

@@ -251,9 +251,7 @@ module.exports = function(RED) {
}
else {
node.buffer = buff.slice(p,buff.length);
if (node.buffer.length > 0) {
node.pendingDones.push(done);
}
node.pendingDones.push(done);
}
if (node.buffer.length == 0) {
done();

0
packages/node_modules/@node-red/nodes/core/storage/10-file.html vendored Normal file → Executable file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

0
packages/node_modules/@node-red/nodes/locales/de/messages.json vendored Normal file → Executable file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

0
packages/node_modules/@node-red/nodes/locales/ko/messages.json vendored Normal file → Executable file
View File

0
packages/node_modules/@node-red/nodes/locales/ru/messages.json vendored Normal file → Executable file
View File

View File

@@ -43,40 +43,37 @@ function load(disableNodePathScan) {
return loadModuleFiles(modules);
}
function splitPath(p) {
return path.posix.normalize((p || '').replace(/\\/g, path.sep)).split(path.sep)
}
function loadModuleTypeFiles(module, type) {
const things = module[type];
let first = true;
const promises = [];
for (let thingName in things) {
var first = true;
var promises = [];
for (var thingName in things) {
/* istanbul ignore else */
if (things.hasOwnProperty(thingName)) {
if (module.name != "node-red" && first) {
// Check the module directory exists
first = false;
let moduleFn = module.path
const fn = things[thingName].file
const parts = splitPath(fn)
const nmi = parts.indexOf('node_modules')
if(nmi > -1) {
moduleFn = parts.slice(0,nmi+2).join(path.sep);
}
if (!moduleFn) {
// shortcut - skip calling statSync on empty string
break; // Module not found, don't attempt to load its nodes
var fn = things[thingName].file;
var parts = fn.split("/");
var i = parts.length-1;
for (;i>=0;i--) {
if (parts[i] == "node_modules") {
break;
}
}
var moduleFn = parts.slice(0,i+2).join("/");
try {
const stat = fs.statSync(moduleFn);
var stat = fs.statSync(moduleFn);
} catch(err) {
break; // Module not found, don't attempt to load its nodes
// Module not found, don't attempt to load its nodes
break;
}
}
try {
let promise;
var promise;
if (type === "nodes") {
promise = loadNodeConfig(things[thingName]);
} else if (type === "plugins") {
@@ -85,7 +82,8 @@ function loadModuleTypeFiles(module, type) {
promises.push(
promise.then(
(function() {
const n = thingName;
var m = module.name;
var n = thingName;
return function(nodeSet) {
things[n] = nodeSet;
return nodeSet;
@@ -95,6 +93,7 @@ function loadModuleTypeFiles(module, type) {
);
} catch(err) {
console.log(err)
//
}
}
}

View File

@@ -156,16 +156,6 @@ function scanDirForNodesModules(dir,moduleName,package) {
}
}
}
// if we have found a package.json, this IS a node_module, lets see if it is a node-red node
if (!isNodeRedModule && files.indexOf('package.json') > -1) {
package = getPackageDetails(dir) // get package details
if(package && package.isNodeRedModule) {
isNodeRedModule = true
files = ['package.json'] // shortcut the file scan
}
}
for (let i=0;i<files.length;i++) {
let fn = files[i];
if (!isNodeRedModule && /^@/.test(fn)) {

View File

@@ -185,17 +185,10 @@ function loadNodeConfigs() {
function addModule(module) {
moduleNodes[module.name] = [];
moduleConfigs[module.name] = module;
for (const setName in module.nodes) {
// console.log("registry.js.addModule",module.name,"user?",module.user,"usedBy",module.usedBy,"dependencies",module.dependencies)
for (var setName in module.nodes) {
if (module.nodes.hasOwnProperty(setName)) {
const set = module.nodes[setName];
if (!set.types) {
const err = new Error("Set has no types")
err.code = "set_has_no_types"
err.details = {
...set
}
set.err = err
}
var set = module.nodes[setName];
moduleNodes[module.name].push(set.name);
nodeList.push(set.id);
if (!set.err) {

View File

@@ -161,8 +161,6 @@ function start() {
for (i=0;i<nodeErrors.length;i+=1) {
if (nodeErrors[i].err.code === "type_already_registered") {
log.warn("["+nodeErrors[i].id+"] "+log._("server.type-already-registered",{type:nodeErrors[i].err.details.type,module: nodeErrors[i].err.details.moduleA}));
} else if (nodeErrors[i].err.code === "set_has_no_types") {
log.warn("["+nodeErrors[i].id+"] "+log._("server.set-has-no-types", nodeErrors[i].err.details));
} else {
log.warn("["+nodeErrors[i].id+"] "+nodeErrors[i].err);
}

View File

@@ -373,11 +373,6 @@ Node.prototype.send = function(msg) {
if (msg === null || typeof msg === "undefined") {
return;
} else if (!util.isArray(msg)) {
// A single message has been passed in
if (typeof msg !== 'object') {
this.error(Log._("nodes.flow.non-message-returned", { type: typeof msg }));
return
}
if (this._wire) {
// A single message and a single wire on output 0
// TODO: pre-load flows.get calls - cannot do in constructor
@@ -430,31 +425,27 @@ Node.prototype.send = function(msg) {
for (k = 0; k < msgs.length; k++) {
var m = msgs[k];
if (m !== null && m !== undefined) {
if (typeof m !== 'object') {
this.error(Log._("nodes.flow.non-message-returned", { type: typeof m }));
} else {
if (!m._msgid) {
hasMissingIds = true;
}
/* istanbul ignore else */
if (!sentMessageId) {
sentMessageId = m._msgid;
}
sendEvents.push({
msg: m,
source: {
id: this.id,
node: this,
port: i
},
destination: {
id: wires[j],
node: undefined
},
cloneMessage: msgSent
});
msgSent = true;
if (!m._msgid) {
hasMissingIds = true;
}
/* istanbul ignore else */
if (!sentMessageId) {
sentMessageId = m._msgid;
}
sendEvents.push({
msg: m,
source: {
id: this.id,
node: this,
port: i
},
destination: {
id: wires[j],
node: undefined
},
cloneMessage: msgSent
});
msgSent = true;
}
}
}

View File

@@ -71,8 +71,6 @@ function runGitCommand(args,cwd,env,emit) {
err.code = "git_missing_user";
} else if (/name consists only of disallowed characters/i.test(stderr)) {
err.code = "git_missing_user";
} else if (/nothing (add )?to commit/i.test(stdout)) {
return stdout;
}
throw err;
})
@@ -108,7 +106,7 @@ function runGitCommandWithSSHCommand(args,cwd,auth,emit) {
commandEnv.GIT_SSH = path.join(__dirname,"node-red-ssh.sh");
commandEnv.NODE_RED_KEY_FILE=auth.key_path;
// GIT_SSH_COMMAND - added in git 2.3.0
commandEnv.GIT_SSH_COMMAND = "ssh -i \"" + auth.key_path + "\" -F /dev/null";
commandEnv.GIT_SSH_COMMAND = "ssh -i " + auth.key_path + " -F /dev/null";
// console.log('commandEnv:', commandEnv);
return runGitCommand(args,cwd,commandEnv,emit).then( result => {
rs.close();

0
packages/node_modules/@node-red/runtime/locales/de/runtime.json vendored Normal file → Executable file
View File

View File

@@ -20,7 +20,6 @@
"errors-help": "Run with -v for details",
"missing-modules": "Missing node modules:",
"node-version-mismatch": "Node module cannot be loaded on this version. Requires: __version__ ",
"set-has-no-types": "Set does not have any types. name: '__name__', module: '__module__', file: '__file__'",
"type-already-registered": "'__type__' already registered by module __module__",
"removing-modules": "Removing modules from config",
"added-types": "Added node types:",
@@ -135,8 +134,7 @@
"flow": {
"unknown-type": "Unknown type: __type__",
"missing-types": "missing types",
"error-loop": "Message exceeded maximum number of catches",
"non-message-returned": "Node tried to send a message of type __type__"
"error-loop": "Message exceeded maximum number of catches"
},
"index": {
"unrecognised-id": "Unrecognised id: __id__",

0
packages/node_modules/@node-red/runtime/locales/ko/runtime.json vendored Normal file → Executable file
View File

View File

@@ -1529,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
@@ -2281,7 +2281,7 @@ describe('HTTP Request Node', function() {
let port = testPort++
let server;
before(function() {
server = net.createServer(function (socket) {
socket.write("HTTP/1.0 200\nContent-Type: text/plain\n\nHelloWorld")
@@ -2291,7 +2291,7 @@ describe('HTTP Request Node', function() {
server.listen(port,'127.0.0.1', function(err) {
})
});
after(function() {
server.close()
});
@@ -2322,50 +2322,16 @@ describe('HTTP Request Node', function() {
var n2 = helper.getNode("n2");
n2.on('input', function(msg) {
try{
msg.payload.should.match(/RequestError: Parse Error/)
msg.payload.should.equal(`RequestError: Parse Error: Missing expected CR after header value : http://localhost:${port}/`)
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' ]
}
});
})
});
})
});

View File

@@ -138,21 +138,22 @@ describe('CSV node', function() {
});
});
it('should remove quotes and whitespace from template', function(done) {
var flow = [ { id:"n1", type:"csv", temp:'"a", "b" , " c "," d " ', wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(csvNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 });
check_parts(msg, 0, 1);
done();
});
var testString = "1,2,3,4"+String.fromCharCode(10);
n1.emit("input", {payload:testString});
});
});
// it('should remove quotes and whitespace from template', function(done) {
// var flow = [ { id:"n1", type:"csv", temp:'"a", "b" , " c "," d " ', wires:[["n2"]] },
// {id:"n2", type:"helper"} ];
// helper.load(csvNode, flow, function() {
// var n1 = helper.getNode("n1");
// var n2 = helper.getNode("n2");
// n2.on("input", function(msg) {
// console.log("GOT",msg.payload)
// msg.should.have.property('payload', { a: 1, b: 2, " c ": 3, " d ": 4 });
// check_parts(msg, 0, 1);
// done();
// });
// var testString = "1,2,3,4"+String.fromCharCode(10);
// n1.emit("input", {payload:testString});
// });
// });
it('should create column names if no template provided', function(done) {
var flow = [ { id:"n1", type:"csv", temp:'', wires:[["n2"]] },
@@ -195,8 +196,8 @@ describe('CSV node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, "d, d": 4 });
msg.should.have.property('columns', 'a,b b,"c,c","d, d"');
msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, " d, d ": 4 });
msg.should.have.property('columns', 'a,b b,"c,c"," d, d "');
check_parts(msg, 0, 1);
done();
});
@@ -212,8 +213,8 @@ describe('CSV node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, "d, d": 4 });
msg.should.have.property('columns', 'a,b b,"c,c","d, d"');
msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, " d, d ": 4 });
msg.should.have.property('columns', 'a,b b,"c,c"," d, d "');
check_parts(msg, 0, 1);
done();
});
@@ -229,8 +230,8 @@ describe('CSV node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, "b b":2, "c;c":3, "d, d": 4 });
msg.should.have.property('columns', 'a,b b,c;c,"d, d"');
msg.should.have.property('payload', { a: 1, "b b":2, "c;c":3, " d, d ": 4 });
msg.should.have.property('columns', 'a,b b,c;c," d, d "');
check_parts(msg, 0, 1);
done();
});
@@ -246,8 +247,8 @@ describe('CSV node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, "b b":2, "c/c":3, "d, d": 4 });
msg.should.have.property('columns', 'a,b b,c/c,"d, d"');
msg.should.have.property('payload', { a: 1, "b b":2, "c/c":3, " d, d ": 4 });
msg.should.have.property('columns', 'a,b b,c/c," d, d "');
check_parts(msg, 0, 1);
done();
});
@@ -263,8 +264,8 @@ describe('CSV node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload', { a: 1, "b b":2, "c\\c":3, "d, d": 4 });
msg.should.have.property('columns', 'a,b b,c\\c,"d, d"');
msg.should.have.property('payload', { a: 1, "b b":2, "c\\c":3, " d, d ": 4 });
msg.should.have.property('columns', 'a,b b,c\\c," d, d "');
check_parts(msg, 0, 1);
done();
});
@@ -699,7 +700,6 @@ 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', '4,foo,true,,0,"Hello\nWorld",,,undefined,null,null\n');
done();
@@ -718,7 +718,6 @@ 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",,undefined,null\n');
done();
@@ -774,7 +773,7 @@ describe('CSV node', function() {
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');
msg.should.have.property('payload', '"a""a",b\'b\nA1,B1\nA2,B2\n');
done();
}
catch(e) { done(e); }

View File

@@ -15,14 +15,11 @@
**/
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);
@@ -92,10 +89,7 @@ describe('watch Node', function() {
msg.should.have.property('payload', result.payload);
msg.should.have.property('type', result.type);
if('size' in result) {
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);
}
msg.should.have.property('size', result.size);
}
count++;
if(count === len) {

View File

@@ -329,36 +329,17 @@ 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');

View File

@@ -1,26 +0,0 @@
<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>

View File

@@ -1,11 +0,0 @@
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);
}

View File

@@ -1,9 +0,0 @@
{
"name" : "@lowercase/lower-case2",
"node-red" : {
"nodes": {
"lower-case": "lower-case2/lower-case.js"
}
},
"version": "2.0.0"
}

View File

@@ -1,26 +0,0 @@
<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>

View File

@@ -1,11 +0,0 @@
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);
}

View File

@@ -1,9 +0,0 @@
{
"name" : "lower-case",
"node-red" : {
"nodes": {
"lower-case": "lower-case/lower-case.js"
}
},
"version": "1.0.0"
}