diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ddc9b75626ed3cbe4e3a3dd9043b4372228258..60242d138a183f92ba46b1be036839b3100ff354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,10 +18,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed (for now removed features) +* `ext_revisions` module. This module was only a work-around which had been + used for versioning functionality before the native versioning was + implemented. Also, the `BUILD_MODULE_EXT_REVISIONS` is no longer used and can + be removed from the config files in `build.properties.d/` + ### Fixed +* #156 - Edit mode for Safari 11 +* #160 - Entity preview for Safari 11 * Several minor cosmetic flaws -- Fixed edit mode for Safari 11. +* Fixed edit mode for Safari 11. ### Security (in case of vulnerabilities) diff --git a/misc/revision_test_data.py b/misc/revision_test_data.py deleted file mode 100755 index 0f41fa9c1b748be0cbc6c5b917dc6739d3c21d89..0000000000000000000000000000000000000000 --- a/misc/revision_test_data.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Copyright 2020 IndiScale GmbH <info@indiscale.com> -Copyright 2020 Timm Fitschen <t.fitschen@indiscale.com> -""" - -import caosdb -import random -import os - -# data model -c = caosdb.execute_query("FIND Test*") -if len(c) > 0: - print(c) - delete = input("Delete these entities?\nType `yes`:") - if delete == "yes": - c.delete(); - else: - print("You typed `{}`".format(delete)) - print("[Canceled]") - exit(0) - - -print("inserting test data") - -upload_file = open("test.dat", "w") -upload_file.write("hello world\n") -upload_file.close() - -testdata = caosdb.Container() -testdata.extend([ - caosdb.File("TestFile", - path="test.dat", - file="test.dat"), - caosdb.Property("TestRevisionOf", datatype="TestObsolete"), - caosdb.RecordType("TestObsolete"), - caosdb.RecordType("TestRecordType"), - caosdb.Property("TestProperty", datatype=caosdb.TEXT), - caosdb.Record("TestRecord" - ).add_parent("TestRecordType" - ).add_property("TestProperty", "this is a test"), -]) - -testdata.insert() -os.remove("test.dat") diff --git a/src/core/js/caosdb.js b/src/core/js/caosdb.js index 0c63fbe038908903dcf426d1c711d4fad6d3bdd2..cb10201556af4eca6fd3105397eda1a3f5552c0b 100644 --- a/src/core/js/caosdb.js +++ b/src/core/js/caosdb.js @@ -964,23 +964,15 @@ function appendProperty(doc, element, property, append_datatype = false) { /** - * Return a new Document or DocumentFragment, depending on the availability of the latter. + * Return a new Document. * * Helper function. * * @param {string} root - the new root element. - * @returns {(Document|DocumentFragement)} the new document. + * @returns {Document} the new document. */ function _createDocument(root) { - var doc = undefined; - if (window.DocumentFragment) { - doc = new DocumentFragment(); - const rootNode = document.createElementNS(undefined, root); - doc.append(rootNode); - } else { - doc = document.implementation.createDocument(null, root, null); - } - return doc; + return document.implementation.createDocument(null, root, null); } diff --git a/src/core/js/ext_revisions.js b/src/core/js/ext_revisions.js deleted file mode 100644 index 3ee086e60ed34ac659c5bb92de71d78b38f30e2b..0000000000000000000000000000000000000000 --- a/src/core/js/ext_revisions.js +++ /dev/null @@ -1,229 +0,0 @@ -/* - * ** header v3.0 - * This file is a part of the CaosDB Project. - * - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - * - * ** end header - */ - -'use strict'; - -/** - * The ext_revisions module extends the edit_mode update functionality. - * - * The edit_mode.update_entity function is overridden by this module with a - * proxy pattern. That means, that the original function is still called, but a - * proxy (or wrapper) function adds further functionality. - * - * The extended update function creates a back-up version of the updated entity - * and adds a revisionOf property to the updated entity which references - * the back-up. The back-up entity loses all of its original parents and gets - * an "Obsolete" as only parent instead. - * - * Per default, the module assumes two Entities to be present in the - * database. A RecordType named "Obsolete" and a Property named - * "revisionOf". The initialization is aborted if these entities cannot be - * found and the module remains inactive. - * - * @module ext_revisions - * @version 0.1 - * - * @requires jQuery - * @requires log - * @requires edit_mode - * @requires getEntityID - * @requires transaction - * @requires _createDocument - */ -var ext_revisions = function ($, logger, edit_mode, getEntityID, transaction, _createDocument) { - - - /** - * Default names for the two entities which are required by this module. - */ - var _datamodel = { obsolete: "Obsolete", revisionOf: "revisionOf" }; - - /** - * Generate and insert the back-up entity which stores the old state of the - * entity which is to be updated. - * - * The obsolete entity has only one parent named "Obsolete". - * Apart from that, the obsolete entity has all the properties, name, - * description and so on from the original entity before the update. - * - * @param {string} id - the id of the entity which is to be updated. - * @returns {string} the id of the newly created obsolete entity. - */ - var _insert_obsolete = async function (id) { - logger.debug("insert obsolete", id); - - // create new obsolete entity from the original - const obsolete = await transaction.retrieveEntityById(id); - $(obsolete).attr("id", "-1"); - $(obsolete).find("Permissions").remove(); - $(obsolete).find("Parent").remove(); - $(obsolete).append(`<Parent name="${_datamodel.obsolete}"/>`); - - const doc = _createDocument("Request"); - doc.firstElementChild.appendChild(obsolete); - const result = await transaction.insertEntitiesXml(doc); - const obsolete_id = $(result.firstElementChild).find("[id]").first().attr("id"); - logger.trace("leave _insert_obsolete", obsolete_id); - return obsolete_id; - }; - - /** - * Generate a HTML string which represents a new "revisionOf" property - * which references the newly created obsolete entity. The property is - * meant to be appended to the property section of the entity which is to - * be updated. - * - * @param {string} obsolete_id - the id of the newly created obsolete - * entity. - * @returns {string} A HTML represesentation of an entity property. - */ - var _make_revision_of_property = async function (obsolete_id) { - logger.trace("enter _make_revision_of_property", obsolete_id); - const ret = (await transformation.transformProperty(str2xml(`<Response><Property id="${_datamodel._revisionOfId}" name="${_datamodel.revisionOf}" datatype="${_datamodel.obsolete}"></Property></Response>`))).firstElementChild; - - $(ret).find(".caosdb-f-property-value").append(`<div class="caosdb-property-edit-value"><select><option value="${obsolete_id}" selected="selected"></option></select></div>`); - - logger.trace("leave _make_revision_of_property", ret); - return ret; - } - - /** - * Remove all properties from ent_element with the id of the "revisionOf" - * property. - * - * @param {HTMLElement} ent_element - entity in HTML representation. - */ - var _remove_old_revision_of_property = function (ent_element) { - $(ent_element) - .find(".caosdb-f-entity-property") - .filter(function(index, property) { - if(_datamodel._revisionOfId === $(property) - .find(".caosdb-property-id").text()) { - return true; - } - return false; - }).remove(); - } - - /** - * Main functionality of this module is in here. - * - * This method is called by the (overridden) edit_mode.update_entity - * function before the actual update. It inserts a new obsolete entity - * which represesents the old state of the entity, deletes any revisionOf - * properties of the entity (if present) and adds a new revisionOf property - * which references the (newly inserted) obsolete entity. - * - * @param {HTMLElement} ent_element - The entity form which has been - * generated by the edit_mode with the changes by the user. - */ - var _create_revision = async function (ent_element) { - logger.debug("create revision", ent_element); - var id = getEntityID(ent_element); - - var obsolete_id = await _insert_obsolete(id); - - // remove old revision of and add new one - _remove_old_revision_of_property(ent_element); - var revision_of_property = await _make_revision_of_property(obsolete_id); - var properties_section = ent_element.getElementsByClassName("caosdb-properties")[0]; - properties_section.appendChild(revision_of_property); - }; - - /** - * Test whether the necessary entities exist ("revisionOf" and "Obsolete"). - */ - var _check_datamodel = async function() { - var results = Promise.all([ - query(`FIND RecordType ${_datamodel.obsolete}`), - query(`FIND Property ${_datamodel.revisionOf}`) - ]); - - for (let result of (await results)) { - if (result.length !== 1) { - throw new Error("Invalid datamodel"); - } - - var name = getEntityName(result[0]); - if (name && name.toLowerCase() === _datamodel.revisionOf.toLowerCase()) { - _datamodel._revisionOfId = getEntityID(result[0]); - _datamodel.revisionOf = name; - } else if (name && name.toLowerCase() === _datamodel.obsolete.toLowerCase()) { - _datamodel.obsolete = name; - } - } - }; - - /** - * Initialize the ext_revisions module. - * - * Per default, the module assumes two Entities to be present in the - * database. A RecordType named "Obsolete" and a Property named - * "revisionOf". The initialization is aborted if these entities cannot be - * found and the module remains inactive. For testing purposes the names of - * these entities can be set to different values via the respective - * parameters. - * - * @param {string} [obsolete] - The name of the obsolete RecordType. - * @param {string} [revisionOf] - The name of the revisionOf Property. - */ - var init = async function (obsolete, revisionOf) { - if (typeof obsolete === "string") { - _datamodel.obsolete = obsolete; - } - if (typeof revisionOf === "string") { - _datamodel.revisionOf = revisionOf; - } - - try { - await _check_datamodel(); - } catch (err) { - logger.error("could not init ext_revisions", err); - return; - } - - (function(proxied) { - edit_mode.update_entity = async function(ent_element) { - await _create_revision(ent_element); - return await proxied.apply(this, arguments) - }; - })(edit_mode.update_entity); - } - - return { - // public members, part of the API - init: init, - // private members, exposed for testing - _make_revision_of_property: _make_revision_of_property, - _datamodel: _datamodel, - _logger: logger, - } -}($, log.getLogger("ext_revisions"), edit_mode, getEntityID, transaction, _createDocument); - - -// this will be replaced by require.js in the future. -$(document).ready(function () { - if ("${BUILD_MODULE_EXT_REVISIONS}" == "ENABLED") { - caosdb_modules.register(ext_revisions); - } -}); diff --git a/src/core/js/ext_xls_download.js b/src/core/js/ext_xls_download.js index d80bf4efa539f483811ca1d3b7f85b817489a572..f3eac54dde17bb7d385c31e5189806facc1948e2 100644 --- a/src/core/js/ext_xls_download.js +++ b/src/core/js/ext_xls_download.js @@ -71,7 +71,7 @@ var caosdb_table_export = new function () { * @return {string} cleaned up content */ this._clean_cell = function(raw) { - return raw.replaceAll("\t"," ").replaceAll("\n"," ").replaceAll("\r"," ").replaceAll("\x1E"," ").replaceAll("\x15"," ") + return raw.replace(/\t/g," ").replace(/\n/g," ").replace(/\r/g," ").replace(/\x1E/g," ").replace(/\x15/g," ") } /** diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js index b7ad95f4d907104210eb04771b95f03d3f41d5bd..d6ae7825107fb0d39e1b46e925984c3b5da299eb 100644 --- a/src/core/js/webcaosdb.js +++ b/src/core/js/webcaosdb.js @@ -615,7 +615,7 @@ this.transformation = new function () { * @return {XMLDocument} xslt script */ this.retrieveEntityXsl = async function _rEX(root_template) { - const _root = root_template || '<xsl:template match="/"><div class="root"><xsl:apply-templates select="Response/*" mode="entities"/></div></xsl:template>'; + const _root = root_template || '<xsl:template match="/" xmlns="http://www.w3.org/1999/xhtml"><div class="root"><xsl:apply-templates select="Response/*" mode="entities"/></div></xsl:template>'; var entityXsl = await transformation.retrieveXsltScript("entity.xsl"); var commonXsl = await transformation.retrieveXsltScript("common.xsl"); var errorXsl = await transformation.retrieveXsltScript('messages.xsl'); @@ -1661,16 +1661,25 @@ function xslt(xml, xsl, params) { } } } - if (typeof xsltProcessor.transformDocument == 'function') { - // old FFs - var retDoc = document.implementation.createDocument("", "", null); - xsltProcessor.transformDocument(xml, xsl, retDoc, null); - return retDoc.documentElement; - } else { - // modern browsers - xsltProcessor.importStylesheet(xsl); - return xsltProcessor.transformToFragment(xml, document); + var result = null; + try { + if (typeof xsltProcessor.transformDocument == 'function') { + // old FFs + var retDoc = document.implementation.createDocument("", "", null); + xsltProcessor.transformDocument(xml, xsl, retDoc, null); + result = retDoc.documentElement; + } else { + // modern browsers + xsltProcessor.importStylesheet(xsl); + result = xsltProcessor.transformToFragment(xml, document); + } + } catch (error) { + throw new Error(`XSL Transformation terminated with error: ${error.message}`); } + if (!result) { + throw new Error("XSL Transformation did not return any results"); + } + return result; } /** @@ -1681,19 +1690,18 @@ function getXSLScriptClone(source) { } /** - * TODO + * Add a template rule to a XSL style sheet. + * + * The original document is cloned (copy-on-change) before the template rule is + * appended. + * + * @param {XMLDocument} orig_xsl - the original xsl style sheet + * @param {string} templateStr - the new template rule (an xml string) + * @return {XMLDocument} new xsl style sheet with one more rule. */ function injectTemplate(orig_xsl, templateStr) { var xsl = getXSLScriptClone(orig_xsl); - // var entry_t = xsl.createElement("xsl:template"); - // xsl.firstElementChild.appendChild(entry_t); - // entry_t.outerHTML = template; // Does not work in templates in Safari 11 - // Workaround follows, remove after Safari also has the behaviour of Firefox and current WebKit - var temp = xsl.documentElement.cloneNode(false) - temp.innerHTML = templateStr; - var entry_t = temp.firstChild; - xsl.documentElement.appendChild(entry_t); - // End of workaround + xsl.documentElement.insertAdjacentHTML("beforeend", templateStr); return xsl; } diff --git a/src/core/xsl/entity.xsl b/src/core/xsl/entity.xsl index 21b38feedb92de8feebb75e15f502d5181a078fe..e90b841e82eac361a397c53975e9b25cf0e5daf9 100644 --- a/src/core/xsl/entity.xsl +++ b/src/core/xsl/entity.xsl @@ -575,18 +575,18 @@ <div class="modal-body"> <table class="table table-hover"> <thead> - <tr><div class="export-data">Entity ID</div><th/> + <tr><th><div class="export-data">Entity ID</div></th> <th class="export-data">Version ID</th> <th class="export-data">Date</th> <th class="export-data">User</th> - <div class="export-data">URI</div> + <th class="hidden"><div class="export-data">URI</div></th> </tr></thead> <tbody> <xsl:apply-templates mode="entity-version-modal-successor" select="Successor"> <xsl:with-param name="entityId" select="$entityId"/> </xsl:apply-templates> <tr> - <div class="export-data"><xsl:value-of select="$entityId"/></div> + <td class="hidden"><div class="export-data"><xsl:value-of select="$entityId"/></div></td> <td class="caosdb-v-entity-version-hint caosdb-v-entity-version-hint-cur">This Version</td> <td><xsl:apply-templates select="@id" mode="entity-version-id"/> </td><td> @@ -594,7 +594,7 @@ </td><td class="export-data"> <xsl:value-of select="@username"/>@<xsl:value-of select="@realm"/> </td> - <div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div> + <td class="hidden"><div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div></td> </tr> <xsl:apply-templates mode="entity-version-modal-predecessor" select="Predecessor"> <xsl:with-param name="entityId" select="$entityId"/> @@ -647,7 +647,7 @@ <!-- a versions'id (abbreviated) --> <xsl:attribute name="title">Full Version ID: <xsl:value-of select="."/></xsl:attribute> <xsl:value-of select="substring(.,1,8)"/> - <div class="export-data"><xsl:value-of select="."/></div> + <td class="hidden"><div class="export-data"><xsl:value-of select="."/></div></td> </xsl:template> <xsl:template match="@date" mode="entity-version-date"> @@ -656,7 +656,7 @@ <xsl:value-of select="substring(.,0,11)"/> <xsl:value-of select="' '"/> <xsl:value-of select="substring(.,12,8)"/> - <div class="export-data"><xsl:value-of select="."/></div> + <td class="hidden"><div class="export-data"><xsl:value-of select="."/></div></td> </xsl:template> <xsl:template match="Predecessor|Successor" mode="entity-version-modal-single-history-item"> @@ -664,7 +664,7 @@ <xsl:param name="entityId"/> <xsl:param name="hint"/> <tr> - <div class="export-data"><xsl:value-of select="$entityId"/></div> + <td class="hidden"><div class="export-data"><xsl:value-of select="$entityId"/></div></td> <td class="caosdb-v-entity-version-hint"><xsl:value-of select="$hint"/></td> <td> <xsl:apply-templates select="@id" mode="entity-version-link-to-other-version"> @@ -675,7 +675,7 @@ </td><td class="export-data"> <xsl:value-of select="@username"/>@<xsl:value-of select="@realm"/> </td> - <div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div> + <td class="hidden"><div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div></td> </tr> </xsl:template> diff --git a/src/core/xsl/main.xsl b/src/core/xsl/main.xsl index 4d4df12a667c0c119e69e714ce0078b690f6457d..d1c4def54178ea4991a8d0850730f11b297d92ba 100644 --- a/src/core/xsl/main.xsl +++ b/src/core/xsl/main.xsl @@ -265,11 +265,6 @@ <xsl:value-of select="concat($basepath,'webinterface/${BUILD_NUMBER}/js/ext_bottom_line.js')"/> </xsl:attribute> </xsl:element> - <xsl:element name="script"> - <xsl:attribute name="src"> - <xsl:value-of select="concat($basepath,'webinterface/${BUILD_NUMBER}/js/ext_revisions.js')"/> - </xsl:attribute> - </xsl:element> <xsl:element name="script"> <xsl:attribute name="src"> <xsl:value-of select="concat($basepath,'webinterface/${BUILD_NUMBER}/js/ext_sss_markdown.js')"/> diff --git a/test/core/index.html b/test/core/index.html index ea7b63b9e37943f0bdd4e32499c6c1ea9310a618..a725205ea767d38c29931a074a7671a2fbd22bc2 100644 --- a/test/core/index.html +++ b/test/core/index.html @@ -69,7 +69,6 @@ <script src="js/ext_map.js"></script> <script src="js/ext_table_preview.js"></script> <script src="js/ext_bottom_line.js"></script> - <script src="js/ext_revisions.js"></script> <script src="js/ext_autocomplete.js"></script> <script src="js/ext_sss_markdown.js"></script> <script src="js/ext_trigger_crawler_form.js"></script> @@ -91,7 +90,6 @@ <script src="js/modules/ext_references.js.js"></script> <script src="js/modules/ext_map.js.js"></script> <script src="js/modules/ext_bottom_line.js.js"></script> - <script src="js/modules/ext_revisions.js.js"></script> <script src="js/modules/ext_autocomplete.js.js"></script> <script src="js/modules/ext_sss_markdown.js.js"></script> <script src="js/modules/ext_trigger_crawler_form.js.js"></script> diff --git a/test/core/js/modules/caosdb.js.js b/test/core/js/modules/caosdb.js.js index 20dc4b4eee0116fecf57b0a178f622c4385f08b2..8df5e2f9c2b933cd7b678286295961f2c73d7113 100644 --- a/test/core/js/modules/caosdb.js.js +++ b/test/core/js/modules/caosdb.js.js @@ -133,18 +133,7 @@ QUnit.test("available", function(assert) { * Test whether properties are parsed correctly from the document tree. */ QUnit.test("getProperties", function(assert) { - try { - ps = getProperties(); - } - catch (e) { - assert.equal(e.message, "element is undefined"); - } - try { - ps = getProperties(undefined); - } - catch (e) { - assert.equal(e.message, "element is undefined"); - } + assert.throws(getProperties, "undefined element throws"); assert.equal(this.x.length, 4); @@ -332,59 +321,55 @@ QUnit.test("headingAttributes", function(assert) { * @author Alexander Schlemmer * Test replication of entities. */ -QUnit.test("replicationOfEntities", function(assert) { - var done = assert.async(); +QUnit.test("replicationOfEntities", async function(assert) { - var reptest = function(ent, respxml) { + var reptest = async function(k, ent, respxml) { var oldprops = getProperties(ent); var oldpars = getParents(ent); var doc = createResponse( createEntityXML(getEntityRole(ent), getEntityName(ent), getEntityID(ent), getProperties(ent), getParents(ent))); - assert.equal(xml2str(doc), respxml); + assert.equal(xml2str(doc).replace(/\s/g, ""), respxml.replace(/\s/g, "")); doc = createResponse( createEntityXML(getEntityRole(ent), getEntityName(ent), getEntityID(ent), getProperties(ent), getParents(ent), true)); - transformation.transformEntities(doc).then (x => { - ps = getProperties(x[0]); - pars = getParents(x[0]); - - assert.equal(getEntityRole(ent), getEntityRole(x[0])); - assert.equal(getEntityName(ent), getEntityName(x[0])); - assert.equal(getEntityID(ent), getEntityID(x[0])); - assert.equal(ps.length, oldprops.length); - for (var i=0; i<ps.length; i++) { - assert.equal(ps[i].name, oldprops[i].name); - assert.deepEqual(ps[i].value, oldprops[i].value); - assert.equal(ps[i].datatype, oldprops[i].datatype); - assert.equal(ps[i].list, oldprops[i].list); - assert.equal(ps[i].reference, oldprops[i].reference); - } - assert.equal(pars.length, oldpars.length); - for (var i=0; i<pars.length; i++) { - assert.equal(pars[i].name, oldpars[i].name); - assert.equal(pars[i].id, oldpars[i].id); - } - funj += 1; - // console.log(funj, maxfunccall); - if (funj == maxfunccall) { - done(); - } - }); + var k_2 = k; + var doc2 = str2xml(xml2str(doc)); + var x = await transformation.transformEntities(doc); + ps = getProperties(x[0]); + pars = getParents(x[0]); + + assert.equal(getEntityRole(ent), getEntityRole(x[0])); + assert.equal(getEntityName(ent), getEntityName(x[0])); + assert.equal(getEntityID(ent), getEntityID(x[0])); + assert.equal(ps.length, oldprops.length); + for (var i=0; i<ps.length; i++) { + assert.equal(ps[i].name, oldprops[i].name); + assert.deepEqual(ps[i].value, oldprops[i].value); + assert.equal(ps[i].datatype, oldprops[i].datatype); + assert.equal(ps[i].list, oldprops[i].list); + assert.equal(ps[i].reference, oldprops[i].reference); + } + assert.equal(pars.length, oldpars.length); + for (var i=0; i<pars.length; i++) { + assert.equal(pars[i].name, oldpars[i].name); + assert.equal(pars[i].id, oldpars[i].id); + } }; var respxmls = [ '<Response><Record name="nameofrecord"><Parent name="bla"/><Property name="A">245</Property></Record></Response>', '<Response><Record><Parent name="bla"/></Record></Response>', '<Response><Record id="17" name="nameofrec"><Parent id="244" name="bla"/><Parent id="217" name="bla2"/><Property name="B">245</Property><Property name="A">245.0</Property><Property name="A">245</Property></Record></Response>', - '<Response><Record><Parent name="bla"/><Property name="B">245</Property><Property name="A">245</Property><Property name="A"><Value>245</Value></Property><Property name="A"/><Property name="A"><Value>245</Value><Value>245</Value></Property><Property name="A"><Value>245</Value><Value>247</Value><Value>299</Value></Property></Record></Response>']; + `<Response> + <Record> + <Parent name="bla"/> + <Property name="B">245</Property><Property name="A">245</Property><Property name="A"><Value>245</Value></Property><Property name="A"/><Property name="A"><Value>245</Value><Value>245</Value></Property><Property name="A"><Value>245</Value><Value>247</Value><Value>299</Value></Property></Record></Response>`]; - var funj = 0; - var maxfunccall = this.x.length; - for (var i=0; i<this.x.length; i++) { - reptest(this.x[i], respxmls[i]); + for (var i=3; i<this.x.length; i++) { + var _ = await reptest(i, this.x[i], respxmls[i]); } }); diff --git a/test/core/js/modules/common.xsl.js b/test/core/js/modules/common.xsl.js index d77135d6d1e4e1f77c669a937425ec8c9934ad28..16f84cafc3bdb0b48a42cc10a1cdc03445f48541 100644 --- a/test/core/js/modules/common.xsl.js +++ b/test/core/js/modules/common.xsl.js @@ -53,6 +53,17 @@ QUnit.test("trim", function(assert) { assert.equal(trimmed.firstChild.textContent, 'test\n\ttest\n test', "trimmed"); }); +QUnit.test("remove_leading_ws", function(assert) { + var inject = '<xsl:template match="root"><xsl:call-template name="remove_leading_ws"><xsl:with-param name="str" select="text()"/></xsl:call-template></xsl:template>'; + console.log(inject); + var xsl = injectTemplate(this.commonXSL, inject); + var xml_str = '<root> \n \t \n abcd</root>'; + var xml = str2xml(xml_str); + console.log(xml); + var trimmed = xslt(xml, xsl); + console.log(trimmed); + assert.equal(trimmed.firstChild.textContent, 'abcd', "leading white spaces removed"); +}); QUnit.test("reverse", function(assert) { var inject = '<xsl:template match="root"><xsl:call-template name="reverse"><xsl:with-param name="str" select="text()"/></xsl:call-template></xsl:template>'; diff --git a/test/core/js/modules/entity.xsl.js b/test/core/js/modules/entity.xsl.js index 59a2f8a6f9a03cf4990fe11bfd751d437a3ffc3e..9415a10dc6bdbd51f913e9bfe1ce8f48f187d081 100644 --- a/test/core/js/modules/entity.xsl.js +++ b/test/core/js/modules/entity.xsl.js @@ -58,24 +58,9 @@ QUnit.test("Property names are not links anymore", function(assert) { assert.equal(html.firstElementChild.getElementsByClassName("caosdb-property-name")[0].outerHTML, '<strong class=\"caosdb-property-name\">pname</strong>', "link there"); }); -// QUnit.test("parent name is bold link", function(assert) { -// // make this xsl sheet accessible -// let html = applyTemplates(str2xml('<Parent name="TestParent" id="1234" description="DESC"/>'), this.entityXSL, 'entity-body'); -// assert.ok(html); - -// var name_e = html.firstElementChild.getElementsByClassName("caosdb-parent-name")[0]; -// assert.ok(name_e, "element is there"); -// assert.equal(name_e.tagName, "A", "is link"); -// assert.equal(name_e.getAttribute("href"), "/entitypath/1234", "href location"); -// assert.equal(window.getComputedStyle(name_e)["font-weight"], "700", "font is bold"); -// }); - QUnit.test("TestRecordType data type is recognized as a reference", function(assert) { - // inject an entrance rule - var xsl = getXSLScriptClone(this.entityXSL); - var entry_t = xsl.createElement("xsl:template"); - xsl.firstElementChild.appendChild(entry_t); - entry_t.outerHTML = '<xsl:template match="/"><xsl:apply-templates select="Property" mode="property-value"/></xsl:template>'; + var tmpl = '<xsl:template match="/"><xsl:apply-templates select="Property" mode="property-value"/></xsl:template>'; + var xsl = injectTemplate(this.entityXSL, tmpl); var xml_str = '<Property name="TestProperty" id="1234" description="DESC" type="TestRecordType">5678</Property>'; var xml = str2xml(xml_str); @@ -332,14 +317,19 @@ function applyTemplates(xml, xsl, mode, select = "*") { return xslt(xml, modXsl); } -function callTemplate(xsl, template, params) { - let entryRuleStart = '<xsl:template priority="9" match="/"><xsl:call-template name="' + template + '">'; - let entryRuleEnd = '</xsl:call-template></xsl:template>'; +function callTemplate(xsl, template, params, wrap_call) { + let entryRuleStart = '<xsl:call-template name="' + template + '">'; + let entryRuleEnd = '</xsl:call-template>'; var entryRule = entryRuleStart; for (name in params) { entryRule += '<xsl:with-param name="' + name + '"><xsl:value-of select="\'' + params[name] + '\'"/></xsl:with-param>'; } entryRule += entryRuleEnd; + if (typeof wrap_call == "function") { + entryRule = wrap_call(entryRule); + } + entryRule = '<xsl:template xmlns="http://www.w3.org/1999/xhtml" priority="9" match="/">' + + entryRule + '</xsl:template>'; let modXsl = injectTemplate(xsl, entryRule); return xslt(str2xml('<root/>'), modXsl); } diff --git a/test/core/js/modules/ext_revisions.js.js b/test/core/js/modules/ext_revisions.js.js deleted file mode 100644 index e90fd7c97851e5f054690cb0d376be5e14d826e4..0000000000000000000000000000000000000000 --- a/test/core/js/modules/ext_revisions.js.js +++ /dev/null @@ -1,121 +0,0 @@ -/* - * ** header v3.0 - * This file is a part of the CaosDB Project. - * - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - * - * ** end header - */ - -'use strict'; - -var ext_revisions_test_suite = function ($, ext_revisions, QUnit, edit_mode) { - - var datamodel = ext_revisions._datamodel; - - QUnit.module("ext_revisions.js", { - before: function (assert) { - // setup before module - this.original_update_entity = edit_mode.update_entity; - this.original_insert = transaction.insertEntitiesXml; - this.original_retrieve = transaction.retrieveEntityById; - this.original_query = query; - ext_revisions._logger.setLevel("trace"); - }, - beforeEach: function (assert) { - // setup before each test - datamodel.obsolete = "UNITTESTObsolete"; - datamodel.revisionOf = "UNITTESTRevisionOf"; - }, - afterEach: function (assert) { - // teardown after each test - query = this.original_query; - edit_mode.update_entity = this.original_update_entity; - transaction.insertEntitiesXml = this.original_insert; - transaction.retrieveEntityById = this.original_retrieve; - }, - after: function (assert) { - // teardown after module - } - }); - - QUnit.test("_make_revision_of_property", async function(assert) { - var p = await ext_revisions._make_revision_of_property("1234"); - var editfield = $(p).find(".caosdb-property-edit-value"); - var value = $(editfield).find("select").first()[0].selectedOptions[0].value; - assert.ok($(p).hasClass("caosdb-f-entity-property"), "is property"); - assert.equal(value, "1234", "has value 1234"); - assert.equal(getPropertyName(p), datamodel.revisionOf, "has revisionOf name"); - assert.equal(getPropertyDatatype(p), datamodel.obsolete, "has Obsolete datatype"); - }); - - /** - * This is a rather complete test, not a unit test. - */ - QUnit.test("update calls update_entity through proxy", async function (assert) { - var done = assert.async(3); - var done_query = assert.async(2); - var ent_element = $('<div data-entity-id="15"><div class="caosdb-properties"/></div>')[0]; - - // mock server responses to several requests... - var retrieve_fun = async function(id) { - assert.equal(id, "15", "retrieve id 15"); - done(); - return $(`<Record id="15"><Parent name="ORIG_PARENT"/></Record>`)[0]; - } - var insert_fun = async function(xml) { - var rec = xml.firstElementChild.firstElementChild; - assert.equal(rec.id, "-1", "insert with tmp id"); - assert.equal($(rec).find("Parent").attr("name"), datamodel.obsolete, "Obsolete Parent"); - xml.firstElementChild.firstElementChild.id = "2345"; - console.log(xml2str(xml)); - done(); - return xml; - }; - var update_fun = async function(ent_element) { - var prop = edit_mode.getProperties(ent_element)[0]; - assert.equal(prop.name, datamodel.revisionOf, "has revisionOf"); - assert.equal(prop.value, "2345", "revisionOf 2345"); - done(); - }; - var query_fun = async function(query) { - assert.ok(query.startsWith("FIND") && ( query.endsWith(datamodel.obsolete) || query.endsWith(datamodel.revisionOf)), query); - done_query(); // called twice - return [$(`<div data-entity-name="${datamodel.revisionOf}" data-caosdb-id="3456"/>`)[0]]; - } - - // injecting the server mock-up responses. - transaction.retrieveEntityById = retrieve_fun; - transaction.insertEntitiesXml = insert_fun; - edit_mode.update_entity = update_fun; - query = query_fun; - - - // actual tests - assert.equal(update_fun, edit_mode.update_entity, "before init, the edit_mode.update_entity function has not been overridden."); - - // call init which checks the datamodel and overwrites the - // edit_mode.update_entity function. - await ext_revisions.init(); - assert.notEqual(update_fun, edit_mode.update_entity, "after init, the edit_mode.update_entity hab been overriden with a proxy calling the update_fun and the original function."); - - // call edit_mode.update_entity which calls the insert_fun and the - // update_fun - await edit_mode.update_entity(ent_element); - }); - -}($, ext_revisions, QUnit, edit_mode); diff --git a/test/core/js/modules/ext_xls_download.js.js b/test/core/js/modules/ext_xls_download.js.js index 195e9c825d9601dc08f29c8f1b2171ff13eeef47..74cdb244dcf0f5c2238c8d2503a3194580c35d90 100644 --- a/test/core/js/modules/ext_xls_download.js.js +++ b/test/core/js/modules/ext_xls_download.js.js @@ -32,7 +32,7 @@ * @return {HTMLElement} DIV.caosdb-query-response */ transformation.transformSelectTable = async function _tST (xml) { - var root_template = '<xsl:template match="/"><div class="root"><xsl:apply-templates select="Response/Query" mode="query-results"/></div></xsl:template>'; + var root_template = '<xsl:template xmlns="http://www.w3.org/1999/xhtml" match="/"><div class="root"><xsl:apply-templates select="Response/Query" mode="query-results"/></div></xsl:template>'; var queryXsl = await transformation.retrieveXsltScript("query.xsl"); var entityXsl = await transformation.retrieveEntityXsl(root_template); insertParam(entityXsl, "uppercase", 'abcdefghijklmnopqrstuvwxyz'); @@ -115,7 +115,7 @@ QUnit.test("_get_tsv_string", function(assert) { var f = caosdb_table_export._create_tsv_string var tsv_string = f(entities, ["Bag", "Number"], true); var prefix = "data:text/csv;charset=utf-8," - assert.equal(tsv_string, + assert.equal(tsv_string, "ID\tVersion\tBag\tNumber\n242\tabc123\t6366, 6406, 6407, 6408, 6409, 6410, 6411, 6412, 6413\t02 8 4aaa a\n2112\tabc124\t\t1101\n2112\tabc125\t\t1102", "tsv generated"); tsv_string = caosdb_table_export._encode_tsv_string(tsv_string); assert.equal(tsv_string.slice(0,prefix.length), prefix); diff --git a/test/core/js/modules/navbar.xsl.js b/test/core/js/modules/navbar.xsl.js index 0b43cae70cbd4694290885560c69594a5d4a555e..78113ce2bd7c9ada61205ae5fc3b2e098ee92e8f 100644 --- a/test/core/js/modules/navbar.xsl.js +++ b/test/core/js/modules/navbar.xsl.js @@ -57,25 +57,3 @@ QUnit.test("create navbar", function(assert){ assert.ok(html, "html ok"); assert.equal(html.firstChild.tagName, "NAV", "is nav element"); }); - -/* MISC FUNCTIONS */ -function getXSLScriptClone(source){ - return str2xml(xml2str(source)) -} - -function injectTemplate(orig_xsl, template){ - var xsl = getXSLScriptClone(orig_xsl); - var entry_t = xsl.createElement("xsl:template"); - xsl.firstElementChild.appendChild(entry_t); - entry_t.outerHTML = template; - return xsl; -} - -function insertParam(xsl, name, value=null){ - var param = xsl.createElement("xsl:param"); - param.setAttribute("name", name); - if (value != null) { - param.setAttribute("select", "'"+value+"'"); - } - xsl.firstElementChild.append(param); -} diff --git a/test/core/js/modules/query.xsl.js b/test/core/js/modules/query.xsl.js index 6c371281e7d4b01db0050a00111064d1d3c488b0..5c37ad4e39971130912a9169ab64189d20e5a591 100644 --- a/test/core/js/modules/query.xsl.js +++ b/test/core/js/modules/query.xsl.js @@ -172,10 +172,17 @@ QUnit.test("template entity-link", function(assert){ }); QUnit.test("template select-table-row ", function(assert){ - let row = callTemplate(this.queryXSL, "select-table-row", {"entity-id": "sdfg"}, str2xml('<Response>')); - assert.equal(row.firstElementChild.tagName, "TR", "tagName = TR"); - assert.equal(row.firstElementChild.firstElementChild.tagName, "TD", "tagName = TD"); - assert.equal(row.firstElementChild.firstElementChild.firstElementChild.tagName, "A", "tagName = A"); + let row = callTemplate(this.queryXSL, "select-table-row", {"entity-id": "sdfg", "version-id": "dsfg", "ishead": "true"}, (x) => `<table><tbody>${x}</tbody></table>`); + var next = row.firstElementChild; + assert.equal(next.tagName, "TABLE", "tagName = TABLE"); + next = next.firstElementChild; + assert.equal(next.tagName, "TBODY", "tagName = TBODY"); + next = next.firstElementChild; + assert.equal(next.tagName, "TR", "tagName = TR"); + next = next.firstElementChild; + assert.equal(next.tagName, "TD", "tagName = TD"); + next = next.firstElementChild; + assert.equal(next.tagName, "A", "tagName = A"); }); /* MISC FUNCTIONS */ @@ -185,8 +192,6 @@ function getQueryForm(queryXSL) { } function getQueryFormContainer(queryXSL) { - var xsl = injectTemplate(queryXSL, '<xsl:template match="/"><xsl:call-template name="caosdb-query-panel"/></xsl:template>"') - var xml = str2xml("<root/>"); - var html = xslt(xml, xsl); + var html = callTemplate(queryXSL, "caosdb-query-panel", {}); return html.firstElementChild; } diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js index 95c4f62d983077374343a099d4d4bcb2680e25d5..58cbc2e71c6e558652a30caa9445a38a78fad651 100644 --- a/test/core/js/modules/webcaosdb.js.js +++ b/test/core/js/modules/webcaosdb.js.js @@ -39,7 +39,7 @@ QUnit.module("webcaosdb.js", { /* TESTS */ QUnit.test("xslt", function(assert) { let xml_str = '<root/>'; - let xsl_str = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="html" /><xsl:template match="root"><newroot/></xsl:template></xsl:stylesheet>'; + let xsl_str = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="html" /><xsl:template match="root"><newroot>content</newroot></xsl:template></xsl:stylesheet>'; xml = str2xml(xml_str); xsl = str2xml(xsl_str); broken_xsl = str2xml('<blabla/>'); @@ -69,6 +69,28 @@ QUnit.test("xslt", function(assert) { }, "nu ll xsl throws exc."); }); +QUnit.test("markdown.textTohtml", function(assert) { + const str = `\# header\n\#\# another header\nparagraph`; + assert.equal(markdown.textToHtml(str), "<h1 id=\"header\">header</h1>\n<h2 id=\"anotherheader\">another header</h2>\n<p>paragraph</p>"); + +}); + +QUnit.test("injectTemplate", async function(assert) { + const xml_str = '<root/>'; + const xsl_str = '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"><xsl:output method="html" /></xsl:stylesheet>'; + const xml = str2xml(xml_str); + const xsl = str2xml(xsl_str); + + const result_xsl = injectTemplate(xsl, '<xsl:template xmlns="http://www.w3.org/1999/xhtml" match="root"><newroot>content</newroot></xsl:template>'); + + assert.equal(xml2str(result_xsl), "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\"><xsl:output method=\"html\"/><xsl:template xmlns=\"http://www.w3.org/1999/xhtml\" match=\"root\"><newroot>content</newroot></xsl:template></xsl:stylesheet>"); + var result_xml = xslt(xml, result_xsl); + assert.equal(xml2str(result_xml), "<newroot xmlns=\"http://www.w3.org/1999/xhtml\">content</newroot>"); + + result_xml = await asyncXslt(xml, result_xsl); + assert.equal(xml2str(result_xml), "<newroot xmlns=\"http://www.w3.org/1999/xhtml\">content</newroot>"); +}); + QUnit.test("getEntityId", function(assert) { assert.ok(getEntityId, "function available"); let okElem = $('<div><div class="caosdb-id">1234</div></div>')[0]; @@ -110,7 +132,7 @@ QUnit.test("asyncXslt", function(assert) { // broken xsl throws exception asyncXslt(xml, broken_xsl).catch((error) => { - assert.equal(/^\[Exception.*\]$/.test(error.toString()), true, "broken xsl thros exc."); + assert.equal(/^XSL Transformation.*$/.test(error.message), true, "broken xsl thros exc."); done(); }); }); @@ -125,7 +147,7 @@ QUnit.test("str2xml", function(assert) { assert.ok(xml); // make sure this is a document: - assert.equal(xml.contentType, "text/xml", "has contentType=text/xml"); + assert.ok(xml.contentType.endsWith("/xml"), "has contentType=*/xml"); assert.ok(xml.documentElement, "has documentElement"); assert.equal(xml.documentElement.outerHTML, '<root/>', "has outerHTML"); @@ -214,9 +236,9 @@ QUnit.test("transformEntities", function(assert) { QUnit.test("mergeXsltScripts", function(assert) { assert.ok(transformation.mergeXsltScripts, 'function available.'); - let xslMainStr = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"/>'; + let xslMainStr = '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"/>'; assert.equal(xml2str(transformation.mergeXsltScripts(str2xml(xslMainStr), [])), xslMainStr, 'no includes returns same as xslMain.'); - let xslIncludeStr = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template name="bla"/></xsl:stylesheet>' + let xslIncludeStr = '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"><xsl:template name="bla"/></xsl:stylesheet>' let xslInclude = str2xml(xslIncludeStr); assert.ok($(transformation.mergeXsltScripts(str2xml(xslMainStr), [xslInclude])).find("[name='bla']")[0], 'template bla is there.'); }); @@ -1522,7 +1544,7 @@ QUnit.test("convertNewCommentResponse", function(assert) { let expectedResult = "<li xmlns=\"http://www.w3.org/1999/xhtml\" class=\"list-group-item markdowned\"><div class=\"media\"><div class=\"media-left\"><h3>ยป</h3></div><div class=\"media-body\"><h4 class=\"media-heading\">someuser<small><i> posted on 2015-12-24T20:15:00</i></small></h4><p class=\"caosdb-comment-annotation-text\"><p>This is a comment</p></p></div></div></li>"; convertNewAnnotationResponse(str2xml(testResponse), annotation.loadAnnotationXsl("../../")).then(function(result) { assert.equal(result.length, 1, "one element returned."); - assert.equal(xml2str(result[0]), expectedResult, "result converted correctly"); + assert.equal(xml2str(result[0]).replace(/\n/g,""), expectedResult, "result converted correctly"); done(); }, function(error) { console.log(error); @@ -1992,7 +2014,7 @@ QUnit.test("init_load_history_buttons and init_load_history_buttons", async func // load_button triggers retrieval of history load_button.click(); - await sleep(200); + await sleep(500); var gone_button = $(html).find(".caosdb-f-entity-version-load-history-btn"); assert.equal(gone_button.length, 0, "button is gone");