/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */ /*global beforeEach, afterEach */ /*global describe, it, expect, jasmine */ /*global runs, spyOn, waitsFor */ /*global window, eb, loadFixtures, document, $ */ /*global EventLibrary*/ //////////////////////////////////////////////////////////////////////////////// /// @brief Graph functionality /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2012 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Michael Hackstein /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// (function () { "use strict"; describe('Event Library', function () { var eventLib, nodeShaperDummy = {}, edgeShaperDummy = {}, adapterDummy = {}; beforeEach(function() { eventLib = new EventLibrary(); nodeShaperDummy.reshapeNodes = function() {}; edgeShaperDummy.reshapeEdges = function() {}; adapterDummy.loadNode = function() {}; adapterDummy.expandCommunity = function() {}; spyOn(nodeShaperDummy, "reshapeNodes"); spyOn(edgeShaperDummy, "reshapeEdges"); }); describe('Expand', function() { var loaded, reshaped, nodes, edges, loadedNodes, started, reshapeNodesCallback = function() { reshaped++; }, startCallback = function() { started++; }, config, testee; beforeEach(function() { loaded = 0; reshaped = 0; started = 0; nodes = []; edges = []; loadedNodes = []; config = { edges: edges, nodes: nodes, startCallback: startCallback, adapter: adapterDummy, reshapeNodes: reshapeNodesCallback }; }); it('should expand a collapsed node', function() { var node = { _id: 0, _outboundCounter: 0, _inboundCounter: 0 }; nodes.push(node); spyOn(adapterDummy, "loadNode").andCallFake(function(node) { loaded++; loadedNodes.push(node); }); //config.adapter = adapterDummy.loadNode; testee = eventLib.Expand(config); testee(node); expect(node._expanded).toBeTruthy(); expect(started).toEqual(1); expect(reshaped).toEqual(1); expect(loaded).toEqual(1); expect(loadedNodes.length).toEqual(1); expect(loadedNodes[0]).toEqual(node._id); }); it('should collapse an expanded node', function() { var node = { id: 0, _expanded: true, _outboundCounter: 0, _inboundCounter: 0 }; nodes.push(node); testee = eventLib.Expand(config); testee(node); expect(node._expanded).toBeFalsy(); expect(started).toEqual(1); expect(reshaped).toEqual(1); expect(loaded).toEqual(0); }); it('should collapse a tree', function() { var root = { id: 0, _expanded: true, _outboundCounter: 2, _inboundCounter: 0 }, c1 = { id: 1, _outboundCounter: 0, _inboundCounter: 1 }, c2 = { id: 2, _outboundCounter: 0, _inboundCounter: 1 }; nodes.push(root); nodes.push(c1); nodes.push(c2); edges.push({source: root, target: c1}); edges.push({source: root, target: c2}); testee = eventLib.Expand(config); testee(root); expect(root._expanded).toBeFalsy(); expect(started).toEqual(1); expect(reshaped).toEqual(1); expect(loaded).toEqual(0); expect(nodes.length).toEqual(1); expect(edges.length).toEqual(0); }); it('should not remove referenced nodes on collapsing ', function() { var root = { _id: 0, _expanded: true, _outboundCounter: 2, _inboundCounter: 0 }, c1 = { _id: 1, _outboundCounter: 0, _inboundCounter: 2 }, c2 = { _id: 2, _outboundCounter: 1, _inboundCounter: 0 }, c3 = { _id: 3, _outboundCounter: 0, _inboundCounter: 1 }; nodes.push(root); nodes.push(c1); nodes.push(c2); nodes.push(c3); edges.push({source: root, target: c1}); edges.push({source: root, target: c3}); edges.push({source: c2, target: c1}); testee = eventLib.Expand(config); testee(root); expect(root._expanded).toBeFalsy(); expect(started).toEqual(1); expect(reshaped).toEqual(1); expect(loaded).toEqual(0); expect(nodes.length).toEqual(3); expect(edges.length).toEqual(1); expect(root._outboundCounter).toEqual(0); expect(c1._inboundCounter).toEqual(1); expect(c2._outboundCounter).toEqual(1); }); describe('with community nodes', function() { it('should expand a community node properly', function() { var comm = { _id: "*community_1" }; nodes.push(comm); spyOn(adapterDummy, "expandCommunity"); testee = eventLib.Expand(config); testee(comm); expect(adapterDummy.expandCommunity).toHaveBeenCalledWith(comm, jasmine.any(Function)); }); it('should remove a community if last pointer to it is collapsed', function() { runs(function() { var c0 = { _id: 0, _outboundCounter: 1, _inboundCounter: 0 }, c1 = { _id: 1, _expanded: true, _outboundCounter: 1, _inboundCounter: 1 }, comm = { _id: "*community_1", _outboundCounter: 1, _inboundCounter: 1 }, c2 = { _id: 1, _outboundCounter: 0, _inboundCounter: 1 }, e0 = { source: c0, target: c1 }, e1 = { source: c1, target: comm }, e2 = { source: comm, target: c2 }; nodes.push(c0); nodes.push(c1); nodes.push(comm); nodes.push(c2); edges.push(e0); edges.push(e1); edges.push(e2); testee = eventLib.Expand(config); testee(c1); expect(nodes).toEqual([c0, c1]); expect(edges).toEqual([e0]); }); }); it('should not remove a community if a pointer to it still exists', function() { runs(function() { var c0 = { _id: 0, _outboundCounter: 2, _inboundCounter: 0 }, c1 = { _id: 1, _expanded: true, _outboundCounter: 1, _inboundCounter: 1 }, comm = { _id: "*community_1", _outboundCounter: 0, _inboundCounter: 2 }, e0 = { source: c0, target: c1 }, e1 = { source: c0, target: comm }, e2 = { source: c1, target: comm }; nodes.push(c0); nodes.push(c1); nodes.push(comm); edges.push(e0); edges.push(e1); edges.push(e2); testee = eventLib.Expand(config); testee(c1); expect(nodes).toEqual([c0, c1, comm]); expect(edges).toEqual([e0, e1]); }); }); }); describe('setup process', function() { var testConfig = {}; it('should throw an error if edges are not given', function() { expect( function() { eventLib.Expand(testConfig); } ).toThrow("Edges have to be defined"); }); it('should throw an error if nodes are not given', function() { testConfig.edges = []; expect( function() { eventLib.Expand(testConfig); } ).toThrow("Nodes have to be defined"); }); it('should throw an error if start callback is not given', function() { testConfig.edges = []; testConfig.nodes = []; expect( function() { eventLib.Expand(testConfig); } ).toThrow("A callback to the Start-method has to be defined"); }); it('should throw an error if load node callback is not given', function() { testConfig.edges = []; testConfig.nodes = []; testConfig.startCallback = function(){}; expect( function() { eventLib.Expand(testConfig); } ).toThrow("An adapter to load data has to be defined"); }); it('should throw an error if reshape node callback is not given', function() { testConfig.edges = []; testConfig.nodes = []; testConfig.startCallback = function(){}; testConfig.adapter = adapterDummy; expect( function() { eventLib.Expand(testConfig); } ).toThrow("A callback to reshape nodes has to be defined"); }); }); }); describe('Drag', function() { describe('setup process', function() { it('should throw an error if layouter is not given', function() { var testConfig = {}; expect( function() { eventLib.checkDragConfig(testConfig); } ).toThrow("A layouter has to be defined"); expect( function() { eventLib.Drag(testConfig); } ).toThrow("A layouter has to be defined"); }); it('should throw an error if the layouter does not offer a drag function', function() { var testConfig = { layouter: {} }; expect( function() { eventLib.checkDragConfig(testConfig); } ).toThrow("The layouter has to offer a drag function"); expect( function() { eventLib.Drag(testConfig); } ).toThrow("The layouter has to offer a drag function"); testConfig.layouter.drag = 42; expect( function() { eventLib.checkDragConfig(testConfig); } ).toThrow("The layouter has to offer a drag function"); expect( function() { eventLib.Drag(testConfig); } ).toThrow("The layouter has to offer a drag function"); }); }); }); describe('Insert Node', function() { it('should create an event to add a node', function() { var adapterDummy = {}, nodes = [], created = null, called = false, callbackCheck = function() { called = true; }, nodeEditorConfig = { nodes: nodes, adapter: adapterDummy, shaper: nodeShaperDummy }, testee; adapterDummy.createNode = function(nodeToCreate, callback) { created = nodeToCreate; nodes.push(created); callback(created); }; runs(function() { testee = eventLib.InsertNode(nodeEditorConfig); testee(callbackCheck); }); waitsFor(function() { return called; }); runs(function() { expect(created).toBeDefined(); expect(nodeShaperDummy.reshapeNodes).toHaveBeenCalled(); }); }); }); describe('Patch Node', function() { it('should create an event to patch a node', function() { var adapterDummy = {}, patched = {id: "1"}, data = {hello: "world"}, nodes = [patched], called = false, callbackCheck = function() { called = true; }, nodeEditorConfig = { nodes: nodes, adapter: adapterDummy, shaper: nodeShaperDummy }, testee; adapterDummy.patchNode = function(nodeToPatch, patchData, callback) { patched = nodeToPatch; $.extend(patched, patchData); callback(); }; runs(function() { testee = eventLib.PatchNode(nodeEditorConfig); testee(patched, data, callbackCheck); }); waitsFor(function() { return called; }); runs(function() { expect(patched).toBeDefined(); expect(nodeShaperDummy.reshapeNodes).toHaveBeenCalled(); expect(patched.id).toEqual("1"); expect(patched.hello).toEqual("world"); }); }); }); describe('Delete Node', function() { it('should create an event to delete a node', function() { var adapterDummy = {}, toDel = {id: "2"}, nodes = [toDel], deleted = null, called = false, callbackCheck = function() { called = true; }, nodeEditorConfig = { nodes: nodes, adapter: adapterDummy, shaper: nodeShaperDummy }, testee; adapterDummy.deleteNode = function(nodeToDelete, callback) { deleted = nodeToDelete; nodes.pop(); callback(); }; runs(function() { testee = eventLib.DeleteNode(nodeEditorConfig); testee(toDel, callbackCheck); }); waitsFor(function() { return called; }); runs(function() { expect(deleted).toEqual(toDel); expect(nodeShaperDummy.reshapeNodes).toHaveBeenCalled(); }); }); }); describe('Insert Edge', function() { it('should create an event to add an edge', function() { var adapterDummy = {}, edges = [], called = false, created = null, source = {_id: 1}, target = {_id: 2}, callbackCheck = function() { called = true; }, edgeEditorConfig = { edges: edges, adapter: adapterDummy, shaper: edgeShaperDummy }, testee; adapterDummy.createEdge = function(edgeToCreate, callback) { created = edgeToCreate; edges.push(created); callback(created); }; runs(function() { testee = eventLib.InsertEdge(edgeEditorConfig); testee(source, target, callbackCheck); }); waitsFor(function() { return called; }); runs(function() { expect(created).toBeDefined(); expect(edgeShaperDummy.reshapeEdges).toHaveBeenCalled(); expect(created.source).toEqual(source); expect(created.target).toEqual(target); }); }); }); describe('Patch Edge', function() { it('should create an event to patch an edge', function() { var adapterDummy = {}, source = {_id: 1}, target = {_id: 2}, patched = {source: source, target: target}, data = {hello: "world"}, edges = [patched], called = false, callbackCheck = function() { called = true; }, edgeEditorConfig = { edges: edges, adapter: adapterDummy, shaper: edgeShaperDummy }, testee; adapterDummy.patchEdge = function(edgeToPatch, patchData, callback) { patched = edgeToPatch; $.extend(patched, patchData); callback(); }; runs(function() { testee = eventLib.PatchEdge(edgeEditorConfig); testee(patched, data, callbackCheck); }); waitsFor(function() { return called; }); runs(function() { expect(patched).toBeDefined(); expect(edgeShaperDummy.reshapeEdges).toHaveBeenCalled(); expect(patched.source).toEqual(source); expect(patched.target).toEqual(target); expect(patched.hello).toEqual("world"); }); }); }); describe('Delete Edge', function() { it('should create an event to delete an edge', function() { var adapterDummy = {}, source = {_id: 1}, target = {_id: 2}, toDel = {source: source, target: target}, edges = [toDel], deleted = null, called = false, callbackCheck = function() { called = true; }, edgeEditorConfig = { edges: edges, adapter: adapterDummy, shaper: edgeShaperDummy }, testee; adapterDummy.deleteEdge = function(edgeToDelete, callback) { deleted = edgeToDelete; edges.pop(); callback(); }; runs(function() { testee = eventLib.DeleteEdge(edgeEditorConfig); testee(toDel, callbackCheck); }); waitsFor(function() { return called; }); runs(function() { expect(deleted).toEqual(toDel); expect(edgeShaperDummy.reshapeEdges).toHaveBeenCalled(); }); }); }); }); }());