update from upstream

This commit is contained in:
andrew.greene
2022-07-21 16:36:51 -06:00
333 changed files with 31708 additions and 23101 deletions

View File

@@ -0,0 +1,119 @@
const should = require("should");
const request = require('supertest');
const express = require('express');
const bodyParser = require("body-parser");
const sinon = require('sinon');
let app;
const NR_TEST_UTILS = require("nr-test-utils");
const diagnostics = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/diagnostics");
describe("api/editor/diagnostics", function() {
before(function() {
app = express();
app.use(bodyParser.json());
app.get("/diagnostics",diagnostics.getReport);
});
it('returns the diagnostics report when explicitly enabled', function(done) {
const settings = { diagnostics: { ui: true, enabled: true } }
const runtimeAPI = {
diagnostics: {
get: async function (opts) {
return new Promise(function (resolve, reject) {
opts = opts || {}
try {
resolve({ opts: opts, a:1, b:2});
} catch (error) {
error.status = 500;
reject(error);
}
})
}
}
}
diagnostics.init(settings, runtimeAPI);
request(app)
.get("/diagnostics")
.expect(200)
.end(function(err,res) {
if (err || typeof res.error === "object") {
return done(err || res.error);
}
res.should.have.property("statusCode",200);
res.body.should.have.property("a",1);
res.body.should.have.property("b",2);
done();
});
});
it('returns the diagnostics report when not explicitly enabled (implicitly enabled)', function(done) {
const settings = { diagnostics: { enabled: undefined } }
const runtimeAPI = {
diagnostics: {
get: async function (opts) {
return new Promise(function (resolve, reject) {
opts = opts || {}
try {
resolve({ opts: opts, a:3, b:4});
} catch (error) {
error.status = 500;
reject(error);
}
})
}
}
}
diagnostics.init(settings, runtimeAPI);
request(app)
.get("/diagnostics")
.expect(200)
.end(function(err,res) {
if (err || typeof res.error === "object") {
return done(err || res.error);
}
res.should.have.property("statusCode",200);
res.body.should.have.property("a",3);
res.body.should.have.property("b",4);
done();
});
});
it('should error when setting is disabled', function(done) {
const settings = { diagnostics: { ui: true, enabled: false } }
const runtimeAPI = {
diagnostics: {
get: async function (opts) {
return new Promise(function (resolve, reject) {
opts = opts || {}
try {
resolve({ opts: opts});
} catch (error) {
error.status = 500;
reject(error);
}
})
}
}
}
diagnostics.init(settings, runtimeAPI);
request(app)
.get("/diagnostics")
.expect(403)
.end(function(err,res) {
if (!err && typeof res.error !== "object") {
return done(new Error("accessing diagnostics endpoint while disabled should raise error"));
}
res.should.have.property("statusCode",403);
res.body.should.have.property("message","diagnostics are disabled");
res.body.should.have.property("code","diagnostics.disabled");
done();
});
});
});

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M10.004 14.499h20M10.004 46.503h20M10.004 22.5h20M10.004 30.501h20M10.004 38.502h20" stroke="#fff" stroke-width="2.9997000000000003"/></svg>

After

Width:  |  Height:  |  Size: 236 B

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from loose1.html")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from loose1.js")
})()

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M7 38.98v3.983h11v12l13-23H19l-.463.017c-1.28 4.048-5.066 6.983-9.537 6.983zm12-11.017h12l-13-23v12H7V20.9l2 .064c4.467 0 8.25 2.93 9.534 6.972zM6.95 24.22a6 6 0 1 1-.083 11.456" fill="#fff" style="isolation:auto;mix-blend-mode:normal"/></svg>

After

Width:  |  Height:  |  Size: 339 B

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from loose2.html")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from loose2.js")
})()

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from regular module main.js")
})()

View File

@@ -0,0 +1,19 @@
{
"name": "node-red-node-testnode",
"version": "1.0.0",
"description": "A node-red node that does nothing other than exist",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from regular module main.js")
})()

View File

@@ -0,0 +1,14 @@
{
"name": "regular_node",
"version": "1.0.0",
"description": "A regular node that does nothing other than exist",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"test"
],
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from @test/testnode index.html")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from @test/testnode index.js")
})()

View File

@@ -0,0 +1,20 @@
{
"name": "@test/testnode",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from testnode2 index.js")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from testnode2 index.js")
})()

View File

@@ -0,0 +1,20 @@
{
"name": "testnode2",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode2": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,3 @@
(function() {
console.log("Hi from test plugin client side")
})()

View File

@@ -0,0 +1,14 @@
module.exports = function (RED) {
RED.plugins.registerPlugin('test-theme', {
type: 'node-red-theme',
scripts: [
'files/clientside.js'
],
css: [
'files/theme.css',
],
monacoOptions: {
theme: "vs"
}
})
}

View File

@@ -0,0 +1 @@
:root{--red-ui-primary-background: #f2f3fb;}

View File

@@ -0,0 +1,24 @@
{
"name": "test-theme2",
"version": "0.0.1",
"description": "test theme for Node-RED",
"keywords": [
"node-red",
"plugin",
"theme"
],
"author": {
"name": "testy-McTesterson"
},
"license": "MIT",
"node-red": {
"version": ">=2.2.0",
"plugins": {
"test-theme2": "files/plugin.js"
}
},
"engines": {
"node": ">=12.x"
}
}

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from @test/testnode index.html")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from @test/testnode index.js")
})()

View File

@@ -0,0 +1,20 @@
{
"name": "@test/testnode",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,3 @@
(function() {
console.log("Hi from test plugin client side")
})()

View File

@@ -0,0 +1,14 @@
module.exports = function (RED) {
RED.plugins.registerPlugin('test-theme', {
type: 'node-red-theme',
scripts: [
'files/clientside.js'
],
css: [
'files/theme.css',
],
monacoOptions: {
theme: "vs"
}
})
}

View File

@@ -0,0 +1 @@
:root{--red-ui-primary-background: #f2f3fb;}

View File

@@ -0,0 +1,24 @@
{
"name": "@test/test-theme3",
"version": "0.0.1",
"description": "test theme for Node-RED",
"keywords": [
"node-red",
"plugin",
"theme"
],
"author": {
"name": "testy-McTesterson"
},
"license": "MIT",
"node-red": {
"version": ">=2.2.0",
"plugins": {
"test-theme3": "files/plugin.js"
}
},
"engines": {
"node": ">=12.x"
}
}

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from testnode3 index.js")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from testnode3 index.js")
})()

View File

@@ -0,0 +1,20 @@
{
"name": "testnode3",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode3": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,139 @@
var should = require("should");
var sinon = require("sinon");
var NR_TEST_UTILS = require("nr-test-utils");
var diagnostics = NR_TEST_UTILS.require("@node-red/runtime/lib/api/diagnostics")
var 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/diagnostics", function() {
describe("get", function() {
before(function() {
diagnostics.init({
isStarted: () => true,
nodes: {
getNodeList: () => [{module:"node-red", version:"9.9.9"},{module:"node-red-node-inject", version:"8.8.8"}]
},
settings: {
version: "7.7.7",
available: () => true,
//apiMaxLength: xxx, deliberately left blank. Should arrive in report as "UNSET"
debugMaxLength: 1111,
disableEditor: false,
flowFile: "flows.json",
mqttReconnectTime: 321,
serialReconnectTime: 432,
socketReconnectTime: 2222,
socketTimeout: 3333,
tcpMsgQueueSize: 4444,
inboundWebSocketTimeout: 5555,
runtimeState: {enabled: true, ui: false},
adminAuth: {},//should be sanitised to "SET"
httpAdminRoot: "/admin/root/",
httpAdminCors: {},//should be sanitised to "SET"
httpNodeAuth: {},//should be sanitised to "SET"
httpNodeRoot: "/node/root/",
httpNodeCors: {},//should be sanitised to "SET"
httpStatic: "/var/static/",//should be sanitised to "SET"
httpStaticRoot: "/static/root/",
httpStaticCors: {},//should be sanitised to "SET"
uiHost: "something.secret.com",//should be sanitised to "SET"
uiPort: 1337,//should be sanitised to "SET"
userDir: "/var/super/secret/",//should be sanitised to "SET",
nodesDir: "/var/super/secret/",//should be sanitised to "SET",
contextStorage: {
default : { module: "memory" },
file: { module: "localfilesystem" },
secured: { module: "secure_store", user: "fred", pass: "super-duper-secret" },
},
editorTheme: {}
},
log: mockLog()
});
})
it("returns basic user settings", function() {
return diagnostics.get({scope:"fake_scope"}).then(result => {
should(result).be.type("object");
//result.xxxxx
Object.keys(result)
const reportPropCount = Object.keys(result).length;
reportPropCount.should.eql(7);//ensure no more than 7 keys are present in the report (avoid leakage of extra info)
result.should.have.property("report","diagnostics");
result.should.have.property("scope","fake_scope");
result.should.have.property("time").type("object");
result.should.have.property("intl").type("object");
result.should.have.property("nodejs").type("object");
result.should.have.property("os").type("object");
result.should.have.property("runtime").type("object");
//result.runtime.xxxxx
const runtimeCount = Object.keys(result.runtime).length;
runtimeCount.should.eql(5);//ensure 5 keys are present in runtime
result.runtime.should.have.property('isStarted',true)
result.runtime.should.have.property('flows')
result.runtime.should.have.property('modules').type("object");
result.runtime.should.have.property('settings').type("object");
result.runtime.should.have.property('version','7.7.7');
//result.runtime.modules.xxxxx
const moduleCount = Object.keys(result.runtime.modules).length;
moduleCount.should.eql(2);//ensure no more than the 2 modules specified are present
result.runtime.modules.should.have.property('node-red','9.9.9');
result.runtime.modules.should.have.property('node-red-node-inject','8.8.8');
//result.runtime.settings.xxxxx
const settingsCount = Object.keys(result.runtime.settings).length;
settingsCount.should.eql(27);//ensure no more than the 21 settings listed below are present in the settings object
result.runtime.settings.should.have.property('available',true);
result.runtime.settings.should.have.property('apiMaxLength', "UNSET");//deliberately disabled to ensure UNSET is returned
result.runtime.settings.should.have.property('debugMaxLength', 1111);
result.runtime.settings.should.have.property('disableEditor', false);
result.runtime.settings.should.have.property('editorTheme', {});
result.runtime.settings.should.have.property('flowFile', "flows.json");
result.runtime.settings.should.have.property('mqttReconnectTime', 321);
result.runtime.settings.should.have.property('serialReconnectTime', 432);
result.runtime.settings.should.have.property('socketReconnectTime', 2222);
result.runtime.settings.should.have.property('socketTimeout', 3333);
result.runtime.settings.should.have.property('tcpMsgQueueSize', 4444);
result.runtime.settings.should.have.property('inboundWebSocketTimeout', 5555);
result.runtime.settings.should.have.property('runtimeState', {enabled: true, ui: false});
result.runtime.settings.should.have.property("adminAuth", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property("httpAdminCors", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property('httpAdminRoot', "/admin/root/");
result.runtime.settings.should.have.property("httpNodeAuth", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property("httpNodeCors", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property('httpNodeRoot', "/node/root/");
result.runtime.settings.should.have.property("httpStatic", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property('httpStaticRoot', "/static/root/");
result.runtime.settings.should.have.property("httpStaticCors", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property("uiHost", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property("uiPort", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property("userDir", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property('contextStorage').type("object");
result.runtime.settings.should.have.property('nodesDir', "SET")
//result.runtime.settings.contextStorage.xxxxx
const contextCount = Object.keys(result.runtime.settings.contextStorage).length;
contextCount.should.eql(3);//ensure no more than the 3 settings listed below are present in the contextStorage object
result.runtime.settings.contextStorage.should.have.property('default', {module:"memory"});
result.runtime.settings.contextStorage.should.have.property('file', {module:"localfilesystem"});
result.runtime.settings.contextStorage.should.have.property('secured', {module:"secure_store"}); //only module should be present, other fields are dropped for security
})
})
});
});