From ac5f5dbb92cca2b3dcde6b9e89864fefd51981cc Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Thu, 20 Feb 2020 18:06:00 +0100 Subject: [PATCH] TST: some tests for new_module --- src/core/js/new_module.js | 159 ++++++++++++++++---------- src/core/js/webcaosdb.js | 7 ++ test/core/index.html | 1 + test/core/js/modules/new_module.js.js | 55 +++++++-- 4 files changed, 154 insertions(+), 68 deletions(-) diff --git a/src/core/js/new_module.js b/src/core/js/new_module.js index 12a95603..0784b2e8 100644 --- a/src/core/js/new_module.js +++ b/src/core/js/new_module.js @@ -33,30 +33,68 @@ * @requires jQuery * @requires logger */ -var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) { +var new_module = function ($, logger, is_in_view_port, load_config, getEntityPath, connection) { + + + /** + * Check if an entity has a path attribute and one of a set of extensions. + * + * Note: the array of extensions must contain only lower-case strings. + * + * @param {HTMLElement} entity + * @param {string[]} extensions - an array of file extesions, e.g. `jpg`. + * @return {boolean} true iff the entity has a path with one of the + * extensionss. + */ + const _path_has_file_extension = function (entity, extensions) { + var path = getEntityPath(entity); + if (path) { + for (ext in extensions) { + if(path.toLowerCase().endsWith(ext)) { + return true; + } + } + } + return false; + } + + const _create_video_preview = function (entity) { + var path = connection.getFileSystemPath() + getEntityPath(entity); + return $(`<video control="controls"><source src="${path}"/></video>`)[0]; + } + + const _create_picture_preview = function (entity) { + var path = connection.getFileSystemPath() + getEntityPath(entity); + return $(`<img class="entity-image-preview" style="max-width: 200px; max-height=140px;" src="${path}"/>`)[0]; + } + + var fallback_preview = undefined; /** * TODO Please adhere to JSDoc standards: https://jsdoc.app/ */ const _default_creators = [ - { // TODO pictures (remove from entity.xsl) + { // pictures id: "_default_creators.pictures", - is_applicable: (entity) => false, - create: undefined, + is_applicable: (entity) => _path_has_file_extension( + entity, ["jpg", "png", "gif", "svg"]), + create: _create_picture_preview }, - { // TODO videos (remove from entity.xsl) + { // videos id: "_default_creators.videos", - is_applicable: (entity) => false, - create: undefined, + is_applicable: (entity) => _path_has_file_extension( + entity, ["mp4", "mov", "webm"]), + create: _create_video_preview, }, - { // default + { // fallback id: "_default_creators.fallback", is_applicable: (entity) => true, - create: (entity) => "Fall-back preview.", + create: (entity) => fallback_preview, }, ]; + const previewShownEvent = new Event("new_module.preview.shown"); const previewReadyEvent = new Event("new_module.preview.ready"); /** @@ -64,6 +102,7 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) */ const _css_class_preview_container = "caosdb-f-new_module-container"; const _css_class_preview_container_resolvable = "caosdb-f-new_module-container-resolvable"; + const _css_class_preview_container_button = "caosdb-f-new_module-container-button"; /** * TODO Please adhere to JSDoc standards: https://jsdoc.app/ @@ -99,10 +138,16 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) * must have a (deep) child with class `caosdb-f-new_module-container`. */ var set_preview_container = function (entity, element) { - var preview_container = get_preview_container(entity); - if (preview_container) { - $(preview_container).empty(); - $(preview_container).append(element); + var preview_container = $(get_preview_container(entity)); + if (preview_container[0]) { + preview_container.empty(); + var buttons = preview_container.siblings(`.${_css_class_preview_container_button}`); + if (element) { + buttons.css({"visibility": "initial"}); + preview_container.append(element); + } else { + buttons.css({"visibility": "hidden"}); + } } else { logger.error(new Error("Could not find the preview container.")); } @@ -129,7 +174,9 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) set_preview_container(entity, wait); const result = await preview; set_preview_container(entity, result); - entity.dispatchEvent(previewReadyEvent); + if (result) { + entity.dispatchEvent(previewReadyEvent); + } } catch (err) { logger.error(err); const err_msg = "An error occured while loading this preview"; @@ -187,12 +234,14 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) */ var add_preview_container = function (entity) { const button_show = $('<button class="btn btn-xs"><span class="glyphicon glyphicon-menu-down"/> Show Preview</button>') - .css({width: "100%"}); - const button_hide = $('<button class="btn btn-xs"><span class="glyphicon glyphicon-menu-up"/> Show Preview</button>') .css({width: "100%"}) + .addClass(_css_class_preview_container_button); + const button_hide = $('<button class="btn btn-xs"><span class="glyphicon glyphicon-menu-up"/> Hide Preview</button>') + .css({width: "100%"}) + .addClass(_css_class_preview_container_button) .hide(); const style = { padding: "0px 10px" }; - const container = $(`<div class="collapse ${_css_class_preview_container}"/>`) + const container = $(`<div class="collapse"/>`) .addClass(_css_class_preview_container) .addClass(_css_class_preview_container_resolvable) .css(style); @@ -208,6 +257,7 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) } button_show.click(show); button_hide.click(hide); + container.on("shown.bs.collapse", () => { container[0].dispatchEvent(previewShownEvent); }); $(entity).append(container); $(entity).append(button_show); $(entity).append(button_hide); @@ -226,26 +276,24 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) * created. */ var root_preview_handler = async function (entity) { - - - const preview = root_preview_creator(entity); - if (preview) { + var container = $(get_preview_container(entity) || add_preview_container(entity)); + if (container.hasClass(_css_class_preview_container_resolvable)) { + container.removeClass(_css_class_preview_container_resolvable); + const preview = root_preview_creator(entity); set_preview(entity, preview); } } /** - * Trigger the root_preview_handler for all entities when the view port has - * changed with a delay. + * Trigger the root_preview_handler for all entities within the view port + * when the view port. */ var root_preview_handler_trigger = function () { var entities = $(".caosdb-entity-panel,.caosdb-entity-preview"); for (let entity of entities) { - var container = $(get_preview_container(entity) || add_preview_container(entity)); - // TODO viewport + 1000 px - if (container.hasClass(_css_class_preview_container_resolvable) && is_in_view_port(container[0])) { - container.removeClass(_css_class_preview_container_resolvable); + // TODO viewport + 1000 px for earlier loading + if (is_in_view_port(entity)) { root_preview_handler(entity); } } @@ -291,48 +339,35 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) throw new Error("Wrong version in config."); } - _creators = config.creators; - }; - - /** - * TODO Please adhere to JSDoc standards: https://jsdoc.app/ - */ - var load_config = async function () { - var user_config = await retrieve_config("json/new_module.json"); - - var config = { - version: user_config.version, - delay: (user_config.delay || 500), - creators: [], - }; - // append/load creators - for (let c of user_config.creators) { + _creators.splice(0, _creators.length); + for (let c of config.creators) { // TODO check deps - config.creators.push({ + _creators.push({ id: c.id, - is_applicable: eval(c.is_applicable), - create: eval(c.create) + is_applicable: typeof c.is_applicable === "function" ? c.is_applicable : eval(c.is_applicable), + create: typeof c.create === "function" ? c.create : eval(c.create) }); } // append default creators - config.creators = config.creators.concat(_default_creators); - - return config; + for (let c of _default_creators) { + _creators.push(c); + } }; + /** * TODO Please adhere to JSDoc standards: https://jsdoc.app/ */ - var init = async function () { + var init = async function (config) { logger.info("new_module initialized"); try { - const config = await load_config(); - await configure(config); + let _config = config || await load_config("json/new_module.json"); + await configure(_config); - init_watcher(config.delay || 500, root_preview_handler_trigger); + init_watcher(_config.delay || 500, root_preview_handler_trigger); // trigger the whole thing for the first time root_preview_handler_trigger(); @@ -344,22 +379,28 @@ var new_module = function ($, logger, is_in_view_port, retrieve_config, plotly) } return { + previewShownEvent: previewShownEvent, previewReadyEvent: previewReadyEvent, init: init, init_watcher: init_watcher, configure: configure, + add_preview_container: add_preview_container, + _creators: _creators, + _css_class_preview_container, + _css_class_preview_container_button, + _css_class_preview_container_resolvable, } -}($, log.getLogger("new_module"), resolve_references.is_in_viewport_vertically, load_config, Plotly); +}($, log.getLogger("new_module"), resolve_references.is_in_viewport_vertically, load_config, getEntityPath, connection); /** * Helper for plotly */ -var plotly_preview = function (logger, new_module, preview, plotly) { +var plotly_preview = function (logger, new_module, plotly) { var create_plot = function (data) { var div = $('<div/>')[0]; - plotly.newPlot(div, data, { margin: { t: 0}, height: 200}, {responsive: true}); + plotly.newPlot(div, data, { margin: { t: 0}, height: 400, widht: 400 }, {responsive: true}); return div; } @@ -371,10 +412,10 @@ var plotly_preview = function (logger, new_module, preview, plotly) { } var init = function () { - window.addEventListener(preview.showPreviewEvent.type, - resize_plots_event_handler, true); window.addEventListener(new_module.previewReadyEvent.type, resize_plots_event_handler, true); + window.addEventListener(new_module.previewShownEvent.type, + resize_plots_event_handler, true); } return { @@ -382,7 +423,7 @@ var plotly_preview = function (logger, new_module, preview, plotly) { init: init, }; -}(log.getLogger("plotly_preview"), new_module, preview, Plotly); +}(log.getLogger("plotly_preview"), new_module, Plotly); // this will be replaced by require.js in the future. diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js index 56f00429..87b9e744 100644 --- a/src/core/js/webcaosdb.js +++ b/src/core/js/webcaosdb.js @@ -289,6 +289,13 @@ this.connection = new function() { return base; } + /** + * Return the root path of the file system resource. + */ + this.getFileSystemPath = function () { + return connection.getBasePath() + "FileSystem/"; + } + /** * Return a full URI for these entities. * diff --git a/test/core/index.html b/test/core/index.html index 0e49a671..5311de07 100644 --- a/test/core/index.html +++ b/test/core/index.html @@ -38,6 +38,7 @@ <script src="js/bootstrap-select.js"></script> <script src="js/bootstrap-autocomplete.min.js"></script> <script src="js/webcaosdb.js"></script> + <script src="js/plotly.js"></script> <script> caosdb_modules.auto_init = false; log.setLevel("trace"); diff --git a/test/core/js/modules/new_module.js.js b/test/core/js/modules/new_module.js.js index 34e5cdf5..abef0572 100644 --- a/test/core/js/modules/new_module.js.js +++ b/test/core/js/modules/new_module.js.js @@ -24,10 +24,32 @@ var new_module_test_suite = function ($, new_module, QUnit) { + var test_config = { "version": 0.1, + "deps": ["getParents", "getEntityName"], + "creators": [ + { "id": "test.success", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) === 'TestPreviewRecord-success'", + "create": "(entity) => 'SUCCESS'" + }, + { "id": "test.error", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) === 'TestPreviewRecord-error'", + "create": "(entity) => new Promise((res,rej) => {rej('Test Error');})" + }, + { "id": "test.load-forever", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) === 'TestPreviewRecord-load-forever'", + "create": "(entity) => new Promise((res,rej) => {})" + }, + { "id": "test.success-2", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) !== 'TestPreviewRecord-fall-back'", + "create": "(entity) => { return plotly_preview.create_plot([{x: [1,2,3,4,5], y: [1,2,4,8,16]}]); }" + } + ] + }; + QUnit.module("new_module.js", { - before: function (assert) { - // setup before module, e.g.: - new_module.init(); + before: async function (assert) { + // setup before module + await new_module.configure(test_config); }, beforeEach: function (assert) { // setup before each test @@ -40,11 +62,26 @@ var new_module_test_suite = function ($, new_module, QUnit) { } }); - QUnit.test("hello_world", function (assert) { - assert.equal(new_module.hello(), "Hello, world!", "hello returns the correct string"); - new_module.addressee = "you"; - assert.equal(new_module.hello(), "Hello, you!", "changed adressee"); - assert.notOk(new_module._hello, "_hello is private"); + QUnit.test("_creators", function (assert) { + assert.equal(new_module._creators.length, 7, "seven creators"); + }); + + QUnit.test("add_preview_container", function(assert) { + var entity = $("<div/>"); + var container = $(new_module.add_preview_container(entity[0])); + assert.ok(container.hasClass(new_module._css_class_preview_container), `has class ${new_module._css_class_preview_container}`); + assert.ok(container.hasClass(new_module._css_class_preview_container_resolvable), `has class ${new_module._css_class_preview_container_resolvable}`); + }); + + QUnit.test("root_preview_handler", async function(assert) { + for (let name_suffix of ["fall-back", "error", "load-forever", "success"]) { + let name = "TestPreviewRecord-" + name_suffix; + let entity_xml = `<Response><Record name="${name}"><Parent name='TestPreviewRecordType'/></Record></Response>`; + let entity = (await transformation.transformEntities(str2xml(entity_xml)))[0]; + assert.equal(getEntityName(entity), name); + new_module.root_preview_handler(entity[0]); + } + }); -}($, new_module, QUnit); \ No newline at end of file +}($, new_module, QUnit); -- GitLab