From b41c7962c29e131d9a35c3f2d28d5a2b55ad7aaa Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 19 Feb 2021 15:24:56 +0000 Subject: [PATCH] Add tests for pluggable library --- .../@node-red/runtime/lib/library/index.js | 75 ++++++++++--------- .../runtime/lib/api/settings_spec.js | 38 ++++++++++ .../runtime/lib/library/index_spec.js | 68 +++++++++++++++-- 3 files changed, 140 insertions(+), 41 deletions(-) diff --git a/packages/node_modules/@node-red/runtime/lib/library/index.js b/packages/node_modules/@node-red/runtime/lib/library/index.js index 83bac06a2..c08b7b44c 100644 --- a/packages/node_modules/@node-red/runtime/lib/library/index.js +++ b/packages/node_modules/@node-red/runtime/lib/library/index.js @@ -28,43 +28,15 @@ let runtimeLibraries = []; // Libraries defined by the user in the editor. let userLibraries = []; -function init(runtime) { +let runtime; - events.on("registry:plugin-added", function(id) { - const plugin = runtime.plugins.getPlugin(id); - if (plugin.type === "node-red-library-source") { - libraryPlugins[plugin.id] = plugin; - - runtimeLibraries.forEach(library => { - if (library.type === id) { - library.local = false; - - if (!/^[a-z0-9-_]+$/.test(library.id)) { - log.warn(log._("library.failedToInit",{error:log._("library.invalidProperty",{prop:"id",value:library.id})})); - return; - } - try { - libraries[library.id] = new plugin.class(library) - libraryConfigs[library.id] = library; - libraryConfigs[library.id].type = id; - if (libraries[library.id].init) { - libraries[library.id].init().catch(err => { - delete libraries[library.id]; - delete libraryConfigs[library.id]; - log.warn(log._("library.failedToInit",{library:library.id, error:err.toString()})); - }); - } - } catch(err) { - log.warn(log._("library.failedToInit",{library:library.id, error:err.toString()})); - } - } - }) - - - } - }) +function init(_runtime) { + runtime = _runtime; + events.removeListener("registry:plugin-added",onPluginAdded); + events.on("registry:plugin-added",onPluginAdded); knownTypes.flows = 'node-red'; + libraries["local"] = require("./local"); libraries["local"].init(runtime); libraryConfigs["local"] = libraries["local"] @@ -83,6 +55,41 @@ function init(runtime) { } +function onPluginAdded(id) { + const plugin = runtime.plugins.getPlugin(id); + if (plugin.type === "node-red-library-source") { + libraryPlugins[plugin.id] = plugin; + + runtimeLibraries.forEach(library => { + if (library.type === id) { + library.local = false; + + if (!/^[a-z0-9-_]+$/.test(library.id)) { + log.warn(log._("library.failedToInit",{error:log._("library.invalidProperty",{prop:"id",value:library.id})})); + return; + } + try { + libraries[library.id] = new plugin.class(library) + libraryConfigs[library.id] = library; + libraryConfigs[library.id].type = id; + if (libraries[library.id].init) { + libraries[library.id].init().catch(err => { + delete libraries[library.id]; + delete libraryConfigs[library.id]; + log.warn(log._("library.failedToInit",{library:library.id, error:err.toString()})); + }); + } + } catch(err) { + log.warn(log._("library.failedToInit",{library:library.id, error:err.toString()})); + } + } + }) + + + } +} + + function registerType(id,type) { // TODO: would like to enforce this, but currently the tests register the same type multiple // times and have no way to remove themselves. diff --git a/test/unit/@node-red/runtime/lib/api/settings_spec.js b/test/unit/@node-red/runtime/lib/api/settings_spec.js index b5b4878e2..9b3b94229 100644 --- a/test/unit/@node-red/runtime/lib/api/settings_spec.js +++ b/test/unit/@node-red/runtime/lib/api/settings_spec.js @@ -44,6 +44,11 @@ describe("runtime-api/settings", function() { paletteCategories :["red","blue","green"], exportNodeSettings: (obj) => { obj.testNodeSetting = "helloWorld"; + }, + }, + plugins: { + exportPluginSettings: (obj) => { + obj.testPluginSettings = "helloPluginWorld"; } }, nodes: { @@ -51,13 +56,16 @@ describe("runtime-api/settings", function() { installerEnabled: () => false, getCredentialKeyType: () => "test-key-type" }, + library: {getLibraries: () => ["lib1"] }, storage: {} }) return settings.getRuntimeSettings({}).then(result => { result.should.have.property("httpNodeRoot","testHttpNodeRoot"); result.should.have.property("version","testVersion"); result.should.have.property("paletteCategories",["red","blue","green"]); + result.should.have.property("libraries",["lib1"]); result.should.have.property("testNodeSetting","helloWorld"); + result.should.have.property("testPluginSettings","helloPluginWorld"); result.should.not.have.property("foo",123); result.should.have.property("flowEncryptionType","test-key-type"); result.should.not.have.property("user"); @@ -75,6 +83,11 @@ describe("runtime-api/settings", function() { paletteCategories :["red","blue","green"], exportNodeSettings: (obj) => { obj.testNodeSetting = "helloWorld"; + }, + }, + plugins: { + exportPluginSettings: (obj) => { + obj.testPluginSettings = "helloPluginWorld"; } }, nodes: { @@ -82,6 +95,7 @@ describe("runtime-api/settings", function() { installerEnabled: () => false, getCredentialKeyType: () => "test-key-type" }, + library: {getLibraries: () => { ["lib1"]} }, storage: {} }) return settings.getRuntimeSettings({ @@ -111,6 +125,11 @@ describe("runtime-api/settings", function() { paletteCategories :["red","blue","green"], exportNodeSettings: (obj) => { obj.testNodeSetting = "helloWorld"; + }, + }, + plugins: { + exportPluginSettings: (obj) => { + obj.testPluginSettings = "helloPluginWorld"; } }, nodes: { @@ -118,6 +137,7 @@ describe("runtime-api/settings", function() { installerEnabled: () => false, getCredentialKeyType: () => "test-key-type" }, + library: {getLibraries: () => { ["lib1"]} }, storage: { projects: { getActiveProject: () => 'test-active-project', @@ -162,6 +182,11 @@ describe("runtime-api/settings", function() { paletteCategories :["red","blue","green"], exportNodeSettings: (obj) => { obj.testNodeSetting = "helloWorld"; + }, + }, + plugins: { + exportPluginSettings: (obj) => { + obj.testPluginSettings = "helloPluginWorld"; } }, nodes: { @@ -169,6 +194,7 @@ describe("runtime-api/settings", function() { installerEnabled: () => false, getCredentialKeyType: () => "test-key-type" }, + library: {getLibraries: () => { ["lib1"]} }, storage: { projects: { getActiveProject: () => 'test-active-project', @@ -203,6 +229,11 @@ describe("runtime-api/settings", function() { paletteCategories :["red","blue","green"], exportNodeSettings: (obj) => { obj.testNodeSetting = "helloWorld"; + }, + }, + plugins: { + exportPluginSettings: (obj) => { + obj.testPluginSettings = "helloPluginWorld"; } }, nodes: { @@ -210,6 +241,7 @@ describe("runtime-api/settings", function() { installerEnabled: () => false, getCredentialKeyType: () => "test-key-type" }, + library: {getLibraries: () => { ["lib1"]} }, storage: { projects: { flowFileExists: () => true, @@ -248,6 +280,11 @@ describe("runtime-api/settings", function() { paletteCategories :["red","blue","green"], exportNodeSettings: (obj) => { obj.testNodeSetting = "helloWorld"; + }, + }, + plugins: { + exportPluginSettings: (obj) => { + obj.testPluginSettings = "helloPluginWorld"; } }, nodes: { @@ -255,6 +292,7 @@ describe("runtime-api/settings", function() { installerEnabled: () => false, getCredentialKeyType: () => "test-key-type" }, + library: {getLibraries: () => { ["lib1"]} }, storage: { projects: { flowFileExists: () => false, diff --git a/test/unit/@node-red/runtime/lib/library/index_spec.js b/test/unit/@node-red/runtime/lib/library/index_spec.js index ac2608a18..378e61f5c 100644 --- a/test/unit/@node-red/runtime/lib/library/index_spec.js +++ b/test/unit/@node-red/runtime/lib/library/index_spec.js @@ -14,13 +14,14 @@ * limitations under the License. **/ -var should = require("should"); -var sinon = require("sinon"); +const should = require("should"); +const sinon = require("sinon"); -var NR_TEST_UTILS = require("nr-test-utils"); -var library = NR_TEST_UTILS.require("@node-red/runtime/lib/library/index") -var localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local") -var examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples") +const NR_TEST_UTILS = require("nr-test-utils"); +const library = NR_TEST_UTILS.require("@node-red/runtime/lib/library/index") +const localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local") +const examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples") +const events = NR_TEST_UTILS.require("@node-red/util/lib/events") var mockLog = { log: sinon.stub(), @@ -72,6 +73,59 @@ describe("runtime/library", function() { // should(()=>{library.register("unknown","/abc")} ).throw(); // }) }) + + describe("getLibraries", function() { + before(function() { + library.init({}); + }); + it('returns the default and examples libraries', function() { + const libs = library.getLibraries(); + libs.should.have.length(2); + libs[0].should.have.property('id', 'local'); + libs[0].should.have.property('label','editor:library.types.local'); + libs[0].should.have.property("user", false); + libs[0].should.have.property('icon', 'font-awesome/fa-hdd-o'); + + libs[1].should.have.property('id', 'examples'); + libs[1].should.have.property('label','editor:library.types.examples'); + libs[1].should.have.property("user", false); + libs[1].should.have.property('icon', 'font-awesome/fa-life-ring'); + libs[1].should.have.property('readOnly', true); + libs[1].should.have.property('types', ['flows']); + }); + + it('returns the libraries from settings', function() { + library.init({ + plugins: { + getPlugin: id => { return { + id: "test-library-plugin", + type: "node-red-library-source", + class: function() {} + } + } + }, + settings: { + editorTheme: { + library: { + sources: [ + {id: "test-plugin-id", type:"test-library-plugin"} + ] + } + } + } + }); + let libs = library.getLibraries(); + libs.should.have.length(2); + + events.emit("registry:plugin-added","test-library-plugin" ) + + libs = library.getLibraries(); + libs.should.have.length(3); + libs[2].should.have.property('id', 'test-plugin-id'); + libs[2].should.have.property("user", false); + }); + }) + describe("getEntry", function() { before(function() { library.init({}); @@ -102,7 +156,7 @@ describe("runtime/library", function() { }); it ('returns a flow example entry', function(done) { - library.getEntry("_examples_","flows","/test-module/abc").then(function(result) { + library.getEntry("examples","flows","/test-module/abc").then(function(result) { result.should.have.property("library","_examples_") result.should.have.property("path","/test-module/abc") done();