Return-Path: X-Original-To: apmail-sling-commits-archive@www.apache.org Delivered-To: apmail-sling-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 814E11812C for ; Tue, 19 May 2015 10:05:46 +0000 (UTC) Received: (qmail 41672 invoked by uid 500); 19 May 2015 10:05:46 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 41618 invoked by uid 500); 19 May 2015 10:05:46 -0000 Mailing-List: contact commits-help@sling.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sling.apache.org Delivered-To: mailing list commits@sling.apache.org Received: (qmail 41609 invoked by uid 99); 19 May 2015 10:05:46 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 19 May 2015 10:05:46 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id 4D5D6AC0041 for ; Tue, 19 May 2015 10:05:46 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1680224 - in /sling/trunk/contrib/explorers/resourceeditor: frontend/ src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/ src/test/javascript/e2e/spec/ src/test/javascript/spec/ Date: Tue, 19 May 2015 10:05:46 -0000 To: commits@sling.apache.org From: sboehme@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150519100546.4D5D6AC0041@hades.apache.org> Author: sboehme Date: Tue May 19 10:05:45 2015 New Revision: 1680224 URL: http://svn.apache.org/r1680224 Log: SLING-4555 Resource Editor: added JavaScript unit tests, smaller JavaScript refactorings, fixed "multi delete" e2e test, added spec reporter for Jasmine Karma unit tests Modified: sling/trunk/contrib/explorers/resourceeditor/frontend/Gruntfile.js sling/trunk/contrib/explorers/resourceeditor/frontend/package.json sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/JSTreeAdapter.js sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/TreeController.js sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/e2e/spec/e2e_spec.js sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/spec/resource_editor_spec.js Modified: sling/trunk/contrib/explorers/resourceeditor/frontend/Gruntfile.js URL: http://svn.apache.org/viewvc/sling/trunk/contrib/explorers/resourceeditor/frontend/Gruntfile.js?rev=1680224&r1=1680223&r2=1680224&view=diff ============================================================================== --- sling/trunk/contrib/explorers/resourceeditor/frontend/Gruntfile.js (original) +++ sling/trunk/contrib/explorers/resourceeditor/frontend/Gruntfile.js Tue May 19 10:05:45 2015 @@ -50,8 +50,10 @@ module.exports = function(grunt) { }, karma : { files:[ - staticContentFolder+'/js/**/*.js', - '../src/test/javascript/**/*spec.js'], + staticContentFolder+'/generated/3rd_party/js/**/*.js', + staticContentFolder+'/js/**/*.js', + '../src/test/javascript/spec/*spec.js' + ], tasks: ['karma:desktop_build'] } @@ -102,27 +104,29 @@ module.exports = function(grunt) { runnerPort: 9999, singleRun: true, browsers: ['Chrome', 'Firefox', 'PhantomJS'], - plugins : ['karma-jasmine', 'karma-phantomjs-launcher', 'karma-chrome-launcher', 'karma-firefox-launcher', 'karma-ie-launcher'], + reporters: ["spec"], + specReporter: {maxLogLines: 5}, + plugins : ['karma-jasmine', 'karma-phantomjs-launcher', 'karma-chrome-launcher', 'karma-firefox-launcher', 'karma-ie-launcher', + 'karma-spec-reporter'], frameworks: ['jasmine'], - files: ['../src/test/javascript/spec/*spec.js', + files: [ staticContentFolder+'/generated/3rd_party/js/jquery.min.js', staticContentFolder+'/generated/3rd_party/js/**/*.js', - staticContentFolder+'/js/**/*.js' + staticContentFolder+'/js/**/*.js', + '../src/test/javascript/spec/*spec.js' ] }, desktop_build: { singleRun: true, browsers: ['Chrome', 'Firefox'] }, + multi_run: { + singleRun: false, + browsers: ['Chrome', 'Firefox'] + }, build: { singleRun: true, browsers: ['PhantomJS'] - }, - watch: { - reporters: 'dots', - autoWatch: true, - background: true, - singleRun: false } }, webdriver: { Modified: sling/trunk/contrib/explorers/resourceeditor/frontend/package.json URL: http://svn.apache.org/viewvc/sling/trunk/contrib/explorers/resourceeditor/frontend/package.json?rev=1680224&r1=1680223&r2=1680224&view=diff ============================================================================== --- sling/trunk/contrib/explorers/resourceeditor/frontend/package.json (original) +++ sling/trunk/contrib/explorers/resourceeditor/frontend/package.json Tue May 19 10:05:45 2015 @@ -23,6 +23,7 @@ "grunt-contrib-watch": "0.6.1", "grunt-karma": "0.10.1", "karma-jasmine": "0.3.5", + "karma-spec-reporter": "0.0.19", "karma-phantomjs-launcher": "~0.1.4", "karma-chrome-launcher": "0.1.7", "karma-firefox-launcher": "0.1.4", Modified: sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/JSTreeAdapter.js URL: http://svn.apache.org/viewvc/sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/JSTreeAdapter.js?rev=1680224&r1=1680223&r2=1680224&view=diff ============================================================================== --- sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/JSTreeAdapter.js (original) +++ sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/JSTreeAdapter.js Tue May 19 10:05:45 2015 @@ -37,22 +37,14 @@ org.apache.sling.reseditor.JSTreeAdapter this.settings = settings; this.treeController = treeController; this.mainController = mainController; -var pathSuffix = ".html"; -var pathEndsWithPathSuffix = settings.resourcePath.substring(settings.resourcePath.length-pathSuffix.length) == pathSuffix; -var resourcePath = (pathEndsWithPathSuffix) ? settings.resourcePath.substring(0,settings.resourcePath.length-pathSuffix.length) : settings.resourcePath; -var currentNodePath = this.mainController.encodeToHTML(resourcePath); -var paths = currentNodePath.substring(1).split("/"); -var selectingNodeWhileOpeningTree=true; -var thisJSTreeAdapter = this; + var thisJSTreeAdapter = this; $(document).ready(function() { $(window).resize( function() { thisJSTreeAdapter.mainController.adjust_height(); }); - var selectorFromCurrentPath = treeController.getSelectorFromPath(currentNodePath); - var scrollToPathFinished=false; thisJSTreeAdapter.mainController.adjust_height(); @@ -62,10 +54,12 @@ $(document).ready(function() { // select the tree container using jQuery $("#tree") .bind("loaded.jstree", function (event, data) { - if (currentNodePath != "/") { - treeController.openElement($("#tree > ul > li[nodename=''] > ul"), paths); + var pathElements = treeController.getPathElements(settings.resourcePath); + + if (pathElements.length >= 1 && pathElements[0] != "") { + treeController.openElement($("#tree > ul > li[nodename=''] > ul"), pathElements); } - selectingNodeWhileOpeningTree=false; + // position the info-icon $('#tree-info-icon').show(); $('#root i:first').before($('#tree-info-icon')); Modified: sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/TreeController.js URL: http://svn.apache.org/viewvc/sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/TreeController.js?rev=1680224&r1=1680223&r2=1680224&view=diff ============================================================================== --- sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/TreeController.js (original) +++ sling/trunk/contrib/explorers/resourceeditor/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/TreeController.js Tue May 19 10:05:45 2015 @@ -132,6 +132,14 @@ org.apache.sling.reseditor.TreeControlle } } + TreeController.prototype.getPathElements = function(resourcePath){ + var pathSuffix = ".html"; + var pathEndsWithPathSuffix = resourcePath.substring(resourcePath.length-pathSuffix.length) == pathSuffix; + var resourcePathWithoutSuffix = (pathEndsWithPathSuffix) ? resourcePath.substring(0,resourcePath.length-pathSuffix.length) : resourcePath; + var currentNodePath = this.mainController.encodeToHTML(resourcePathWithoutSuffix); + return currentNodePath.substring(1).split("/"); + } + TreeController.prototype.getSelectorFromPath = function(path){ var paths = path.substring(1).split("/"); return "#tree > ul [nodename='"+paths.join("'] > ul > [nodename='")+"']"; @@ -166,9 +174,7 @@ org.apache.sling.reseditor.TreeControlle if (paths.length>0){ thisTreeController.openElement($("#"+pathElementLi.attr('id')).children("ul"), paths); } else { - selectingNodeWhileOpeningTree=true; - $('#tree').jstree('select_node', pathElementLi.attr('id'), 'true'/*doesn't seem to work*/); - selectingNodeWhileOpeningTree=false; + $('#tree').jstree('select_node', pathElementLi.attr('id'), 'true'); var target = $('#'+pathElementLi.attr('id')+' a:first'); target.focus(); } @@ -237,7 +243,7 @@ org.apache.sling.reseditor.TreeControlle var confirmationMsg = "You are about to delete '"+resourcePathToDelete+"' and all its sub nodes. Are you sure?"; var decodedResourcePath = this.mainController.decodeFromHTML(resourcePathToDelete); var encodedResourcePathToDelete = this.mainController.encodeURL(decodedResourcePath); - bootbox.confirm(confirmationMsg, function(result) { + var sendDeletePost = function(result) { if (result){ $.ajax({ type: 'POST', @@ -256,7 +262,8 @@ org.apache.sling.reseditor.TreeControlle } }); } - }); + }; + bootbox.confirm(confirmationMsg, sendDeletePost); } TreeController.prototype.openAddNodeDialog = function(li) { Modified: sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/e2e/spec/e2e_spec.js URL: http://svn.apache.org/viewvc/sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/e2e/spec/e2e_spec.js?rev=1680224&r1=1680223&r2=1680224&view=diff ============================================================================== --- sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/e2e/spec/e2e_spec.js (original) +++ sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/e2e/spec/e2e_spec.js Tue May 19 10:05:45 2015 @@ -168,7 +168,7 @@ describe('A user of the Apache Sling Res client = client.url(homeURL); client .waitForExist('#last-element').click("#root li[nodename=\"aTestNode\"] i.add-icon") - .waitForVisible('#addNodeDialog.add-node-finished').click('#addNodeDialog .btn.btn-primary.submit') + .waitForVisible('#addNodeDialog.add-node-finished', 1000).click('#addNodeDialog .btn.btn-primary.submit') // The open node animation will take longer than 500ms thus setting 2000ms as max. .waitForExist('#root li[nodename="aTestNode"].opened', 2000).elements('#root li[nodename="aTestNode"].opened li a .jstree-themeicon', function(err, res) { client Modified: sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/spec/resource_editor_spec.js URL: http://svn.apache.org/viewvc/sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/spec/resource_editor_spec.js?rev=1680224&r1=1680223&r2=1680224&view=diff ============================================================================== --- sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/spec/resource_editor_spec.js (original) +++ sling/trunk/contrib/explorers/resourceeditor/src/test/javascript/spec/resource_editor_spec.js Tue May 19 10:05:45 2015 @@ -18,18 +18,245 @@ */ describe('The Resource Editor', function() { + MockNodeTypeManager = (function() { + function MockNodeTypeManager(settings, ntManager){ + this.settings = settings; + this.ntManager = ntManager; + this.nodeTypesJson = { + "nt:unstructured": { + "declaredChildNodeDefinitions": [{ + "name": "*", + "onParentVersion": "VERSION", + "allowsSameNameSiblings": true, + "defaultPrimaryType": "nt:unstructured" + }], + "declaredPropertyDefinitions": [ + { + "name": "*", + "requiredType": "undefined", + "multiple": true + }, + { + "name": "*", + "requiredType": "undefined" + } + ], + "orderableChildNodes": true + }, + "nt:folder": { + "declaredChildNodeDefinitions": [{ + "name": "*", + "onParentVersion": "VERSION", + "requiredPrimaryTypes": ["nt:hierarchyNode"] + }], + "declaredSupertypes": ["nt:hierarchyNode"] + }, + "nt:hierarchyNode": {"declaredSupertypes": [ + "mix:created", + "nt:base" + ]}, + "nt:base": { + } + } + } + + MockNodeTypeManager.prototype.getNodeTypeNames = function() { + return ["nt:unstructured", "nt:folder", "nt:hierarchyNode", "nt:base"]; + } + + MockNodeTypeManager.prototype.getNodeType = function(name) { + var nt = this.nodeTypesJson[name]; + if (typeof nt.name === "undefined"){ + nt.name = name; + nt.canAddProperty = function(propertyName, propertyType){ + return true; + }; + } + return nt; + } + + return MockNodeTypeManager; + }()); + + var mockNtManager = new MockNodeTypeManager(); - it('\'s MainController', function() { -// it('can encode a URL', function() { - // Mock it! -// var ntManager = new de.sandroboehme.NodeTypeManager(); - var mainControllerSettings = { - contextPath: "/"//, -// nodeTypes: ntManager.getNodeTypeNames() - }; - var mainController = new org.apache.sling.reseditor.MainController(mainControllerSettings, null); + var mainControllerSettings = { + contextPath: "/", + nodeTypes: mockNtManager.getNodeTypeNames() + }; + + describe('\'s MainController', function() { + var mainController; + + beforeEach(function() { + mainController = new org.apache.sling.reseditor.MainController(mainControllerSettings, mockNtManager); + }); + + it('can encode a URL', function() { var urlToEncode = "/reseditor/testnode/$&?äöß<> test.html"; expect(mainController.encodeURL(urlToEncode)).toEqual("/reseditor/testnode/%24%26%3F%C3%A4%C3%B6%C3%9F%3C%3E%20test.html"); -// }); + }); + + it('can encode HTML', function() { + expect(mainController.encodeToHTML("a<>b")).toEqual("a<>b"); + }); + + it('can dencode HTML', function() { + expect(mainController.decodeFromHTML("a<>b")).toEqual("a<>b"); + }); + + }); + + describe('\'s TreeController', function() { + var treeControllerSettings = { + contextPath: "/", + nodeTypes: mockNtManager.getNodeTypeNames() + }; + + var mainController; + var treeController; + + beforeEach(function() { + mainController = new org.apache.sling.reseditor.MainController(mainControllerSettings, mockNtManager); + treeController = new org.apache.sling.reseditor.TreeController(treeControllerSettings, mainController); + }); + + it("can rename nodes", function() { + var htmlEncodedNewName = "newNodeName"; + var data = { + text: htmlEncodedNewName, + old: "oldNodeName", + node: {id: "li_id"} + }; + spyOn(treeController, "getPathFromLi").and.returnValue("/testnode/oldNodeName"); + spyOn($, "ajax"); + treeController.renameNode(null, data); + + expect($.ajax.calls.mostRecent().args[0]["url"]).toEqual("/testnode/oldNodeName"); + expect($.ajax.calls.mostRecent().args[0]["data"][":dest"]).toEqual("/testnode/newNodeName"); + expect($.ajax.calls.mostRecent().args[0]["data"][":operation"]).toEqual("move"); + expect($.ajax.calls.mostRecent().args[0]["data"]["_charset_"]).toEqual("utf-8"); + }); + + it("can split the URL into path elements for deep links", function(){ + var pathElements = treeController.getPathElements("/"); + expect(pathElements.length).toBe(1); + expect(pathElements).toContain(""); + + pathElements = treeController.getPathElements("/testnodes"); + expect(pathElements.length).toBe(1); + expect(pathElements).toContain("testnodes"); + + pathElements = treeController.getPathElements("/testnodes/level2/level3"); + expect(pathElements.length).toBe(3); + expect(pathElements[0]).toBe("testnodes"); + expect(pathElements[1]).toBe("level2"); + expect(pathElements[2]).toBe("level3"); + + // Sometimes the path contains the ".html" suffix. Check that it still works + pathElements = treeController.getPathElements("/.html"); + expect(pathElements.length).toBe(1); + expect(pathElements).toContain(""); + + pathElements = treeController.getPathElements("/testnodes.html"); + expect(pathElements.length).toBe(1); + expect(pathElements).toContain("testnodes"); + + pathElements = treeController.getPathElements("/testnodes/level2/level3.html"); + expect(pathElements.length).toBe(3); + expect(pathElements[0]).toBe("testnodes"); + expect(pathElements[1]).toBe("level2"); + expect(pathElements[2]).toBe("level3"); + }); + + it("can delete nodes", function(){ + spyOn(treeController, "getURLEncodedPathFromLi").and.returnValue("/testnode"); + spyOn($.fn, "jstree").and.returnValue(["j1_18", "j1_20"]); + var getPathFromLiCount = 0; + spyOn(treeController,"getPathFromLi").and.callFake(function(){ + getPathFromLiCount++; + switch(getPathFromLiCount) { + case 1: + return "/testnode/node1_to_delete"; + case 2: + return "/testnode/node3_to_delete"; + } + }); + + spyOn(bootbox, "confirm").and.callFake(function(){ + var result = true; + /* + * The confirm dialog cannot be clicked in this test. We spy on this dialog, retrieve the function + * that gets called after the user clicked ok and then call this function to check our expectations. + */ + // calling 'sendDeletePost()' + bootbox.confirm.calls.argsFor(0)[1](result); + }); + + spyOn($, "ajax"); + + treeController.deleteNodes({}); + + expect(treeController.getPathFromLi.calls.count()).toBe(2); + + expect($.ajax.calls.mostRecent().args[0]["url"]).toEqual("/testnode"); + expect($.ajax.calls.mostRecent().args[0]["data"][":operation"]).toEqual("delete"); + expect($.ajax.calls.mostRecent().args[0]["data"]["_charset_"]).toEqual("utf-8"); + expect($.ajax.calls.mostRecent().args[0]["data"][":applyTo"][0]).toEqual("/testnode/node1_to_delete"); + expect($.ajax.calls.mostRecent().args[0]["data"][":applyTo"][1]).toEqual("/testnode/node3_to_delete"); + }); + + it("can delete a single node", function(){ + spyOn(treeController,"getPathFromLi").and.returnValue("/testnode/node2delete"); + + spyOn(bootbox, "confirm").and.callFake(function(){ + var result = true; + /* + * The confirm dialog cannot be clicked in this test. We spy on this dialog, retrieve the function + * that gets called after the user clicked ok and then call this function to check our expectations. + */ + // calling 'sendDeletePost()' + bootbox.confirm.calls.argsFor(0)[1](result); + }); + + spyOn($, "ajax"); + + treeController.deleteSingleNode({}); + + expect($.ajax.calls.mostRecent().args[0]["url"]).toEqual("/testnode/node2delete"); + expect($.ajax.calls.mostRecent().args[0]["data"][":operation"]).toEqual("delete"); + }); + }); + + describe('\'s AddNodeController', function() { + var mainController; + var addNodeController; + + beforeEach(function() { + mainController = new org.apache.sling.reseditor.MainController(mainControllerSettings, mockNtManager); + addNodeController = new org.apache.sling.reseditor.AddNodeController({}, mainController); + }); + + it("can add a node", function(){ + spyOn(mainController, "encodeURL").and.returnValue("/testnode"); + + spyOn($.fn, "select2").and.returnValue("nt:unstructured"); + + spyOn($, 'ajax').and.callFake(function (req) { + var d = $.Deferred(); + d.resolve({}); + return d.promise(); + }); + + spyOn(mainController,"redirectTo").and.returnValue(null); + + addNodeController.addNode(); + + expect($.ajax.calls.mostRecent().args[0]["url"]).toEqual("/testnode"); + expect($.ajax.calls.mostRecent().args[0]["data"]["_charset_"]).toEqual("utf-8"); + expect($.ajax.calls.mostRecent().args[0]["data"]["jcr:primaryType"]).toEqual("nt:unstructured"); + }); }); + + }); \ No newline at end of file