/** * * Useful during debugging * ======================= * * console.log(JSON.stringify(output, null, 2)); * * for (let i in network.body.edges) { * let edge = network.body.edges[i]; * console.log("" + i + ": from: " + edge.fromId + ", to: " + edge.toId); * } */ var fs = require('fs'); var assert = require('assert'); var vis = require('../dist/vis'); var Network = vis.network; var jsdom_global = require('jsdom-global'); var stdout = require('test-console').stdout; var Validator = require("./../lib/shared/Validator").default; var {allOptions, configureOptions} = require('./../lib/network/options.js'); //var {printStyle} = require('./../lib/shared/Validator'); /** * Merge all options of object b into object b * @param {Object} a * @param {Object} b * @return {Object} a * * Adapted merge() in dotparser.js */ function merge (a, b) { if (!a) { a = {}; } if (b) { for (var name in b) { if (b.hasOwnProperty(name)) { if (typeof b[name] === 'object') { a[name] = merge(a[name], b[name]); } else { a[name] = b[name]; } } } } return a; } /** * Load legacy-style (i.e. not module) javascript files into the given context. */ function include(list, context) { if (!(list instanceof Array)) { list = [list]; } for (var n in list) { var path = list[n]; var arr = [fs.readFileSync(path) + '']; eval.apply(context, arr); } } /** * Defined network consists of two sub-networks: * * - 1-2-3-4 * - 11-12-13-14 * * For reference, this is the sample network of issue #1218 */ function createSampleNetwork(options) { var NumInitialNodes = 8; var NumInitialEdges = 6; var nodes = new vis.DataSet([ {id: 1, label: '1'}, {id: 2, label: '2'}, {id: 3, label: '3'}, {id: 4, label: '4'}, {id: 11, label: '11'}, {id: 12, label: '12'}, {id: 13, label: '13'}, {id: 14, label: '14'}, ]); var edges = new vis.DataSet([ {from: 1, to: 2}, {from: 2, to: 3}, {from: 3, to: 4}, {from: 11, to: 12}, {from: 12, to: 13}, {from: 13, to: 14}, ]); // create a network var container = document.getElementById('mynetwork'); var data = { nodes: nodes, edges: edges }; var defaultOptions = { layout: { randomSeed: 8 }, edges: { smooth: { type: 'continuous' // avoid dynamic here, it adds extra hidden nodes } } }; options = merge(defaultOptions, options); var network = new vis.Network(container, data, options); assertNumNodes(network, NumInitialNodes); assertNumEdges(network, NumInitialEdges); return [network, data, NumInitialNodes, NumInitialEdges]; }; /** * Create a cluster for the dynamic data change cases. * * Works on the network created by createSampleNetwork(). * * This is actually a pathological case; there are two separate sub-networks and * a cluster is made of two nodes, each from one of the sub-networks. */ function createCluster(network) { var clusterOptionsByData = { joinCondition: function(node) { if (node.id == 1 || node.id == 11) return true; return false; }, clusterNodeProperties: {id:"c1", label:'c1'} } network.cluster(clusterOptionsByData); } /** * Display node/edge state, useful during debugging */ function log(network) { console.log(Object.keys(network.body.nodes)); console.log(network.body.nodeIndices); console.log(Object.keys(network.body.edges)); console.log(network.body.edgeIndices); }; /** * Note that only the node and edges counts are asserted. * This might be done more thoroughly by explicitly checking the id's */ function assertNumNodes(network, expectedPresent, expectedVisible) { if (expectedVisible === undefined) expectedVisible = expectedPresent; assert.equal(Object.keys(network.body.nodes).length, expectedPresent, "Total number of nodes does not match"); assert.equal(network.body.nodeIndices.length, expectedVisible, "Number of visible nodes does not match"); }; /** * Comment at assertNumNodes() also applies. */ function assertNumEdges(network, expectedPresent, expectedVisible) { if (expectedVisible === undefined) expectedVisible = expectedPresent; assert.equal(Object.keys(network.body.edges).length, expectedPresent, "Total number of edges does not match"); assert.equal(network.body.edgeIndices.length, expectedVisible, "Number of visible edges does not match"); }; /** * Check if the font options haven't changed. * * This is to guard against future code changes; a lot of the code deals with particular properties of * the font options. * If any assertion fails here, all code in Network handling fonts should be checked. */ function checkFontProperties(fontItem, checkStrict = true) { var knownProperties = [ 'color', 'size', 'face', 'background', 'strokeWidth', 'strokeColor', 'align', 'multi', 'vadjust', 'bold', 'boldital', 'ital', 'mono', ]; // All properties in fontItem should be known for (var prop in fontItem) { if (prop === '__type__') continue; // Skip special field in options definition if (!fontItem.hasOwnProperty(prop)) continue; assert(knownProperties.indexOf(prop) !== -1, "Unknown font option '" + prop + "'"); } if (!checkStrict) return; // All known properties should be present var keys = Object.keys(fontItem); for (var n in knownProperties) { var prop = knownProperties[n]; assert(keys.indexOf(prop) !== -1, "Missing known font option '" + prop + "'"); } } describe('Network', function () { before(function() { this.jsdom_global = jsdom_global( "
", { skipWindowCheck: true} ); this.container = document.getElementById('mynetwork'); }); after(function() { try { this.jsdom_global(); } catch(e) { if (e.message() === 'window is undefined') { console.warning("'" + e.message() + "' happened again"); } else { throw e; } } }); ///////////////////////////////////////////////////// // Local helper methods for Edge and Node testing ///////////////////////////////////////////////////// /** * Simplify network creation for local tests */ function createNetwork(options) { var [network, data, numNodes, numEdges] = createSampleNetwork(options); return network; } function firstNode(network) { for (var id in network.body.nodes) { return network.body.nodes[id]; } return undefined; } function firstEdge(network) { for (var id in network.body.edges) { return network.body.edges[id]; } return undefined; } function checkChooserValues(item, chooser, labelChooser) { if (chooser === 'function') { assert.equal(typeof item.chooser, 'function'); } else { assert.equal(item.chooser, chooser); } if (labelChooser === 'function') { assert.equal(typeof item.labelModule.fontOptions.chooser, 'function'); } else { assert.equal(item.labelModule.fontOptions.chooser, labelChooser); } } ///////////////////////////////////////////////////// // End Local helper methods for Edge and Node testing ///////////////////////////////////////////////////// /** * Helper function for clustering */ function clusterTo(network, clusterId, nodeList, allowSingle) { var options = { joinCondition: function(node) { return nodeList.indexOf(node.id) !== -1; }, clusterNodeProperties: { id: clusterId, label: clusterId } } if (allowSingle === true) { options.clusterNodeProperties.allowSingleNodeCluster = true } network.cluster(options); } /** * At time of writing, this test detected 22 out of 33 'illegal' loops. * The real deterrent is eslint rule 'guard-for-in`. */ it('can deal with added fields in Array.prototype', function (done) { Array.prototype.foo = 1; // Just add anything to the prototype Object.prototype.bar = 2; // Let's screw up hashes as well // The network should just run without throwing errors try { var [network, data, numNodes, numEdges] = createSampleNetwork({}); // Do some stuff to trigger more errors clusterTo(network, 'c1', [1,2,3]); data.nodes.remove(1); network.openCluster('c1'); clusterTo(network, 'c1', [4], true); clusterTo(network, 'c2', ['c1'], true); clusterTo(network, 'c3', ['c2'], true); data.nodes.remove(4); } catch(e) { delete Array.prototype.foo; // Remove it again so as not to confuse other tests. delete Object.prototype.bar; assert(false, "Got exception:\n" + e.stack); } delete Array.prototype.foo; // Remove it again so as not to confuse other tests. delete Object.prototype.bar; done(); }); describe('Node', function () { it('has known font options', function () { var network = createNetwork({}); checkFontProperties(network.nodesHandler.defaultOptions.font); checkFontProperties(allOptions.nodes.font); checkFontProperties(configureOptions.nodes.font, false); }); /** * NOTE: choosify tests of Node and Edge are parallel * TODO: consolidate this is necessary */ it('properly handles choosify input', function () { // check defaults var options = {}; var network = createNetwork(options); checkChooserValues(firstNode(network), true, true); // There's no point in checking invalid values here; these are detected by the options parser // and subsequently handled as missing input, thus assigned defaults // check various combinations of valid input options = {nodes: {chosen: false}}; network = createNetwork(options); checkChooserValues(firstNode(network), false, false); options = {nodes: {chosen: { node:true, label:false}}}; network = createNetwork(options); checkChooserValues(firstNode(network), true, false); options = {nodes: {chosen: { node:true, label: function(value, id, selected, hovering) {} }}}; network = createNetwork(options); checkChooserValues(firstNode(network), true, 'function'); options = {nodes: {chosen: { node: function(value, id, selected, hovering) {}, label:false, }}}; network = createNetwork(options); checkChooserValues(firstNode(network), 'function', false); }); }); // Node describe('Edge', function () { it('has known font options', function () { var network = createNetwork({}); checkFontProperties(network.edgesHandler.defaultOptions.font); checkFontProperties(allOptions.edges.font); checkFontProperties(configureOptions.edges.font, false); }); /** * NOTE: choosify tests of Node and Edge are parallel * TODO: consolidate this is necessary */ it('properly handles choosify input', function () { // check defaults var options = {}; var network = createNetwork(options); checkChooserValues(firstEdge(network), true, true); // There's no point in checking invalid values here; these are detected by the options parser // and subsequently handled as missing input, thus assigned defaults // check various combinations of valid input options = {edges: {chosen: false}}; network = createNetwork(options); checkChooserValues(firstEdge(network), false, false); options = {edges: {chosen: { edge:true, label:false}}}; network = createNetwork(options); checkChooserValues(firstEdge(network), true, false); options = {edges: {chosen: { edge:true, label: function(value, id, selected, hovering) {} }}}; network = createNetwork(options); checkChooserValues(firstEdge(network), true, 'function'); options = {edges: {chosen: { edge: function(value, id, selected, hovering) {}, label:false, }}}; network = createNetwork(options); checkChooserValues(firstEdge(network), 'function', false); }); /** * Support routine for next unit test */ function createDataforColorChange() { var nodes = new vis.DataSet([ {id: 1, label: 'Node 1' }, // group:'Group1'}, {id: 2, label: 'Node 2', group:'Group2'}, {id: 3, label: 'Node 3'}, ]); // create an array with edges var edges = new vis.DataSet([ {id: 1, from: 1, to: 2}, {id: 2, from: 1, to: 3, color: { inherit: 'to'}}, {id: 3, from: 3, to: 3, color: { color: '#00FF00'}}, {id: 4, from: 2, to: 3, color: { inherit: 'from'}}, ]); var data = { nodes: nodes, edges: edges }; return data; } /** * Unit test for fix of #3350 * * The issue is that changing color options is not registered in the nodes. * We test the updates the color options in the general edges options here. */ it('sets inherit color option for edges on call to Network.setOptions()', function () { var container = document.getElementById('mynetwork'); var data = createDataforColorChange(); var options = { "edges" : { "color" : { "inherit" : "to" } }, }; // Test passing options on init. var network = new vis.Network(container, data, options); var edges = network.body.edges; assert.equal(edges[1].options.color.inherit, 'to'); // new default assert.equal(edges[2].options.color.inherit, 'to'); // set in edge assert.equal(edges[3].options.color.inherit, false); // has explicit color assert.equal(edges[4].options.color.inherit, 'from'); // set in edge // Sanity check: colors should still be defaults assert.equal(edges[1].options.color.color, network.edgesHandler.options.color.color); // Override the color value - inherit returns to default network.setOptions({ edges:{color: {}}}); assert.equal(edges[1].options.color.inherit, 'from'); // default assert.equal(edges[2].options.color.inherit, 'to'); // set in edge assert.equal(edges[3].options.color.inherit, false); // has explicit color assert.equal(edges[4].options.color.inherit, 'from'); // set in edge // Check no options network = new vis.Network(container, data, {}); edges = network.body.edges; assert.equal(edges[1].options.color.inherit, 'from'); // default assert.equal(edges[2].options.color.inherit, 'to'); // set in edge assert.equal(edges[3].options.color.inherit, false); // has explicit color assert.equal(edges[4].options.color.inherit, 'from'); // set in edge // Set new value network.setOptions(options); assert.equal(edges[1].options.color.inherit, 'to'); assert.equal(edges[2].options.color.inherit, 'to'); // set in edge assert.equal(edges[3].options.color.inherit, false); // has explicit color assert.equal(edges[4].options.color.inherit, 'from'); // set in edge /* // Useful for debugging console.log('==================================='); console.log(edges[1].options.color); console.log(edges[1].options.color.__proto__); console.log(edges[1].options); console.log(edges[1].options.__proto__); console.log(edges[1].edgeOptions); */ }); it('sets inherit color option for specific edge', function () { var container = document.getElementById('mynetwork'); var data = createDataforColorChange(); // Check no options var network = new vis.Network(container, data, {}); var edges = network.body.edges; assert.equal(edges[1].options.color.inherit, 'from'); // default assert.equal(edges[2].options.color.inherit, 'to'); // set in edge assert.equal(edges[3].options.color.inherit, false); // has explicit color assert.equal(edges[4].options.color.inherit, 'from'); // set in edge // Set new value data.edges.update({id: 1, color: { inherit: 'to'}}); assert.equal(edges[1].options.color.inherit, 'to'); // Only this changed assert.equal(edges[2].options.color.inherit, 'to'); assert.equal(edges[3].options.color.inherit, false); // has explicit color assert.equal(edges[4].options.color.inherit, 'from'); }); /** * Perhaps TODO: add unit test for passing string value for color option */ it('sets color value for edges on call to Network.setOptions()', function () { var container = document.getElementById('mynetwork'); var data = createDataforColorChange(); var defaultColor = '#848484'; // From defaults var color = '#FF0000'; var options = { "edges" : { "color" : { "color" : color } }, }; // Test passing options on init. var network = new vis.Network(container, data, options); var edges = network.body.edges; assert.equal(edges[1].options.color.color, color); assert.equal(edges[1].options.color.inherit, false); // Explicit color, so no inherit assert.equal(edges[2].options.color.color, color); assert.equal(edges[2].options.color.inherit, 'to'); // Local value overrides! (bug according to docs) assert.notEqual(edges[3].options.color.color, color); // Has own value assert.equal(edges[3].options.color.inherit, false); // Explicit color, so no inherit assert.equal(edges[4].options.color.color, color); // Override the color value - all should return to default network.setOptions({ edges:{color: {}}}); assert.equal(edges[1].options.color.color, defaultColor); assert.equal(edges[1].options.color.inherit, 'from'); assert.equal(edges[2].options.color.color, defaultColor); assert.notEqual(edges[3].options.color.color, color); // Has own value assert.equal(edges[4].options.color.color, defaultColor); // Check no options network = new vis.Network(container, data, {}); edges = network.body.edges; // At this point, color has not changed yet assert.equal(edges[1].options.color.color, defaultColor); assert.equal(edges[1].options.color.highlight, defaultColor); assert.equal(edges[1].options.color.inherit, 'from'); assert.notEqual(edges[3].options.color.color, color); // Has own value // Set new Value network.setOptions(options); assert.equal(edges[1].options.color.color, color); assert.equal(edges[1].options.color.highlight, defaultColor); // Should not be changed assert.equal(edges[1].options.color.inherit, false); // Explicit color, so no inherit assert.equal(edges[2].options.color.color, color); assert.notEqual(edges[3].options.color.color, color); // Has own value assert.equal(edges[4].options.color.color, color); }); /** * Unit test for fix of #3500 * Checking to make sure edges that become unconnected due to node removal get reconnected */ it('has reconnected edges', function () { var node1 = {id:1, label:"test1"}; var node2 = {id:2, label:"test2"}; var nodes = new vis.DataSet([node1, node2]); var edge = {id:1, from: 1, to:2}; var edges = new vis.DataSet([edge]); var data = { nodes: nodes, edges: edges }; var container = document.getElementById('mynetwork'); var network = new vis.Network(container, data); //remove node causing edge to become disconnected nodes.remove(node2.id); var foundEdge = network.body.edges[edge.id]; assert.ok(foundEdge===undefined, "edge is still in state cache"); //add node back reconnecting edge nodes.add(node2); foundEdge = network.body.edges[edge.id]; assert.ok(foundEdge!==undefined, "edge is missing from state cache"); }); }); // Edge describe('Clustering', function () { it('properly handles options allowSingleNodeCluster', function() { var [network, data, numNodes, numEdges] = createSampleNetwork(); data.edges.update({from: 1, to: 11,}); numEdges += 1; assertNumNodes(network, numNodes); assertNumEdges(network, numEdges); clusterTo(network, 'c1', [3,4]); numNodes += 1; // A clustering node is now hiding two nodes numEdges += 1; // One clustering edges now hiding two edges assertNumNodes(network, numNodes, numNodes - 2); assertNumEdges(network, numEdges, numEdges - 2); // Cluster of single node should fail, because by default allowSingleNodeCluster == false clusterTo(network, 'c2', [14]); assertNumNodes(network, numNodes, numNodes - 2); // Nothing changed assertNumEdges(network, numEdges, numEdges - 2); assert(network.body.nodes['c2'] === undefined); // Cluster not created // Redo with allowSingleNodeCluster == true clusterTo(network, 'c2', [14], true); numNodes += 1; numEdges += 1; assertNumNodes(network, numNodes, numNodes - 3); assertNumEdges(network, numEdges, numEdges - 3); assert(network.body.nodes['c2'] !== undefined); // Cluster created // allowSingleNodeCluster: true with two nodes // removing one clustered node should retain cluster clusterTo(network, 'c3', [11, 12], true); numNodes += 1; // Added cluster numEdges += 2; assertNumNodes(network, numNodes, 6); assertNumEdges(network, numEdges, 5); data.nodes.remove(12); assert(network.body.nodes['c3'] !== undefined); // Cluster should still be present numNodes -= 1; // removed node numEdges -= 3; // cluster edge C3-13 should be removed assertNumNodes(network, numNodes, 6); assertNumEdges(network, numEdges, 4); }); it('removes nested clusters with allowSingleNodeCluster === true', function() { var [network, data, numNodes, numEdges] = createSampleNetwork(); // Create a chain of nested clusters, three deep clusterTo(network, 'c1', [4], true); clusterTo(network, 'c2', ['c1'], true); clusterTo(network, 'c3', ['c2'], true); numNodes += 3; numEdges += 3; assertNumNodes(network, numNodes, numNodes - 3); assertNumEdges(network, numEdges, numEdges - 3); assert(network.body.nodes['c1'] !== undefined); assert(network.body.nodes['c2'] !== undefined); assert(network.body.nodes['c3'] !== undefined); // The whole chain should be removed when the bottom-most node is deleted data.nodes.remove(4); numNodes -= 4; numEdges -= 4; assertNumNodes(network, numNodes); assertNumEdges(network, numEdges); assert(network.body.nodes['c1'] === undefined); assert(network.body.nodes['c2'] === undefined); assert(network.body.nodes['c3'] === undefined); }); /** * Check on fix for #1218 */ it('connects a new edge to a clustering node instead of the clustered node', function () { var [network, data, numNodes, numEdges] = createSampleNetwork(); createCluster(network); numNodes += 1; // A clustering node is now hiding two nodes numEdges += 2; // Two clustering edges now hide two edges assertNumNodes(network, numNodes, numNodes - 2); assertNumEdges(network, numEdges, numEdges - 2); //console.log("Creating node 21") data.nodes.update([{id: 21, label: '21'}]); numNodes += 1; // New unconnected node added assertNumNodes(network, numNodes, numNodes - 2); assertNumEdges(network, numEdges, numEdges - 2); // edges unchanged //console.log("Creating edge 21 pointing to 1"); // '1' is part of the cluster so should // connect to cluster instead data.edges.update([{from: 21, to: 1}]); numEdges += 2; // A new clustering edge is hiding a new edge assertNumNodes(network, numNodes, numNodes - 2); // nodes unchanged assertNumEdges(network, numEdges, numEdges - 3); }); /** * Check on fix for #1315 */ it('can uncluster a clustered node when a node is removed that has an edge to that cluster', function () { // NOTE: this block is same as previous test var [network, data, numNodes, numEdges] = createSampleNetwork(); createCluster(network); numNodes += 1; // A clustering node is now hiding two nodes numEdges += 2; // Two clustering edges now hide two edges assertNumNodes(network, numNodes, numNodes - 2); assertNumEdges(network, numEdges, numEdges - 2); // End block same as previous test //console.log("removing 12"); data.nodes.remove(12); // NOTE: // At this particular point, there are still the two edges for node 12 in the edges DataSet. // If you want to do the delete correctly, these should also be deleted explictly from // the edges DataSet. In the Network instance, however, this.body.nodes and this.body.edges // should be correct, with the edges of 12 all cleared out. // 12 was connected to 11, which is clustered numNodes -= 1; // 12 removed, one less node numEdges -= 3; // clustering edge c1-12 and 2 edges of 12 gone assertNumNodes(network, numNodes, numNodes - 2); assertNumEdges(network, numEdges, numEdges - 1); //console.log("Unclustering c1"); network.openCluster("c1"); numNodes -= 1; // cluster node removed, one less node numEdges -= 1; // clustering edge gone, regular edge visible assertNumNodes(network, numNodes, numNodes); // all are visible again assertNumEdges(network, numEdges, numEdges); // all are visible again }); /** * Check on fix for #1291 */ it('can remove a node inside a cluster and then open that cluster', function () { var [network, data, numNodes, numEdges] = createSampleNetwork(); var clusterOptionsByData = { joinCondition: function(node) { if (node.id == 1 || node.id == 2 || node.id == 3) return true; return false; }, clusterNodeProperties: {id:"c1", label:'c1'} } network.cluster(clusterOptionsByData); numNodes += 1; // new cluster node numEdges += 1; // 1 cluster edge expected assertNumNodes(network, numNodes, numNodes - 3); // 3 clustered nodes assertNumEdges(network, numEdges, numEdges - 3); // 3 edges hidden //console.log("removing node 2, which is inside the cluster"); data.nodes.remove(2); numNodes -= 1; // clustered node removed numEdges -= 2; // edges removed hidden in cluster assertNumNodes(network, numNodes, numNodes - 2); // view doesn't change assertNumEdges(network, numEdges, numEdges - 1); // view doesn't change //console.log("Unclustering c1"); network.openCluster("c1") numNodes -= 1; // cluster node gone numEdges -= 1; // cluster edge gone assertNumNodes(network, numNodes, numNodes); // all visible assertNumEdges(network, numEdges, numEdges); // all visible //log(network); }); /** * Helper function for setting up a graph for testing clusterByEdgeCount() */ function createOutlierGraph() { // create an array with nodes var nodes = new vis.DataSet([ {id: 1, label: '1', group:'Group1'}, {id: 2, label: '2', group:'Group2'}, {id: 3, label: '3', group:'Group3'}, {id: 4, label: '4', group:'Group4'}, {id: 5, label: '5', group:'Group4'} ]); // create an array with edges var edges = new vis.DataSet([ {from: 1, to: 3}, {from: 1, to: 2}, {from: 2, to: 4}, {from: 2, to: 5} ]); // create a network var container = document.getElementById('mynetwork'); var data = { nodes: nodes, edges: edges }; var options = { "groups" : { "Group1" : { level:1 }, "Group2" : { level:2 }, "Group3" : { level:3 }, "Group4" : { level:4 } } }; var network = new vis.Network (container, data, options); return network; } /** * Check on fix for #3367 */ it('correctly handles edge cases of clusterByEdgeCount()', function () { /** * Collect clustered id's * * All node id's in clustering nodes are collected into an array; * The results for all clusters are returned as an array. * * Ordering of output depends on the order in which they are defined * within nodes.clustering; strictly, speaking, the array and its items * are collections, so order should not matter. */ var collectClusters = function(network) { var clusters = []; for(var n in network.body.nodes) { var node = network.body.nodes[n]; if (node.containedNodes === undefined) continue; // clusters only // Collect id's of nodes in the cluster var nodes = []; for(var m in node.containedNodes) { nodes.push(m); } clusters.push(nodes); } return clusters; } /** * Compare cluster data * * params are arrays of arrays of id's, e.g: * * [[1,3],[2,4]] * * Item arrays are the id's of nodes in a given cluster * * This comparison depends on the ordering; better * would be to treat the items and values as collections. */ var compareClusterInfo = function(recieved, expected) { if (recieved.length !== expected.length) return false; for (var n = 0; n < recieved.length; ++n) { var itema = recieved[n]; var itemb = expected[n]; if (itema.length !== itemb.length) return false; for (var m = 0; m < itema.length; ++m) { if (itema[m] != itemb[m]) return false; // != because values can be string or number } } return true; } var assertJoinCondition = function(joinCondition, expected) { var network = createOutlierGraph(); network.clusterOutliers({joinCondition: joinCondition}); var recieved = collectClusters(network); //console.log(recieved); assert(compareClusterInfo(recieved, expected), 'recieved:' + JSON.stringify(recieved) + '; ' + 'expected: ' + JSON.stringify(expected)); }; // Should cluster 3,4,5: var joinAll_ = function(n) { return true ; } // Should cluster none: var joinNone_ = function(n) { return false ; } // Should cluster 4 & 5: var joinLevel_ = function(n) { return n.level > 3 ; } assertJoinCondition(undefined , [[1,3],[2,4,5]]); assertJoinCondition(null , [[1,3],[2,4,5]]); assertJoinCondition(joinNone_ , []); assertJoinCondition(joinLevel_ , [[2,4,5]]); }); /////////////////////////////////////////////////////////////// // Automatic opening of clusters due to dynamic data change /////////////////////////////////////////////////////////////// /** * Helper function, created nested clusters, three deep */ function createNetwork1() { var [network, data, numNodes, numEdges] = createSampleNetwork(); clusterTo(network, 'c1', [3,4]); numNodes += 1; // new cluster node numEdges += 1; // 1 cluster edge expected assertNumNodes(network, numNodes, numNodes - 2); // 2 clustered nodes assertNumEdges(network, numEdges, numEdges - 2); // 2 edges hidden clusterTo(network, 'c2', [2,'c1']); numNodes += 1; // new cluster node numEdges += 1; // 2 cluster edges expected assertNumNodes(network, numNodes, numNodes - 4); // 4 clustered nodes, including c1 assertNumEdges(network, numEdges, numEdges - 4); // 4 edges hidden, including edge for c1 clusterTo(network, 'c3', [1,'c2']); // Attempt at visualization: parentheses belong to the cluster one level above // c3 // ( -c2 ) // ( -c1 ) // 14-13-12-11 1 -2 (-3-4) numNodes += 1; // new cluster node numEdges += 0; // No new cluster edge expected assertNumNodes(network, numNodes, numNodes - 6); // 6 clustered nodes, including c1 and c2 assertNumEdges(network, numEdges, numEdges - 5); // 5 edges hidden, including edges for c1 and c2 return [network, data, numNodes, numEdges]; } it('opens clusters automatically when nodes deleted', function () { var [network, data, numNodes, numEdges] = createSampleNetwork(); // Simple case: cluster of two nodes, delete one node clusterTo(network, 'c1', [3,4]); numNodes += 1; // new cluster node numEdges += 1; // 1 cluster edge expected assertNumNodes(network, numNodes, numNodes - 2); // 2 clustered nodes assertNumEdges(network, numEdges, numEdges - 2); // 2 edges hidden data.nodes.remove(4); numNodes -= 2; // deleting clustered node also removes cluster node numEdges -= 2; // cluster edge should also be removed assertNumNodes(network, numNodes, numNodes); assertNumEdges(network, numEdges, numEdges); // Extended case: nested nodes, three deep [network, data, numNodes, numEdges] = createNetwork1(); data.nodes.remove(4); // c3 // ( -c2 ) // 14-13-12-11 1 (-2 -3) numNodes -= 2; // node removed, c1 also gone numEdges -= 2; assertNumNodes(network, numNodes, numNodes - 4); assertNumEdges(network, numEdges, numEdges - 3); data.nodes.remove(1); // c2 // 14-13-12-11 (2 -3) numNodes -= 2; // node removed, c3 also gone numEdges -= 2; assertNumNodes(network, numNodes, numNodes - 2); assertNumEdges(network, numEdges, numEdges - 1); data.nodes.remove(2); // 14-13-12-11 3 numNodes -= 2; // node removed, c2 also gone numEdges -= 1; assertNumNodes(network, numNodes); // All visible again assertNumEdges(network, numEdges); // Same as previous step, but remove all the given nodes in one go // The result should be the same. [network, data, numNodes, numEdges] = createNetwork1(); // nested nodes, three deep data.nodes.remove([1,2,4]); // 14-13-12-11 3 assertNumNodes(network, 5); assertNumEdges(network, 3); }); /////////////////////////////////////////////////////////////// // Opening of clusters at various clustering depths /////////////////////////////////////////////////////////////// /** * Check correct opening of a single cluster. * This is the 'simple' case. */ it('properly opens 1-level clusters', function () { var [network, data, numNodes, numEdges] = createSampleNetwork(); // Pedantic: make a cluster of everything clusterTo(network, 'c1', [1,2,3,4,11, 12, 13, 14]); // c1(14-13-12-11 1-2-3-4) numNodes += 1; assertNumNodes(network, numNodes, 1); // Just the clustering node visible assertNumEdges(network, numEdges, 0); // No extra edges! network.clustering.openCluster('c1', {}); numNodes -= 1; assertNumNodes(network, numNodes, numNodes); // Expecting same as original assertNumEdges(network, numEdges, numEdges); // One external connection [network, data, numNodes, numEdges] = createSampleNetwork(); // 14-13-12-11 1-2-3-4 clusterTo(network, 'c1', [3,4]); network.clustering.openCluster('c1', {}); assertNumNodes(network, numNodes, numNodes); // Expecting same as original assertNumEdges(network, numEdges, numEdges); // Two external connections clusterTo(network, 'c1', [2,3]); network.clustering.openCluster('c1', {}); assertNumNodes(network, numNodes, numNodes); // Expecting same as original assertNumEdges(network, numEdges, numEdges); // One external connection to cluster clusterTo(network, 'c1', [1,2]); clusterTo(network, 'c2', [3,4]); // 14-13-12-11 c1(1-2-)-c2(-3-4) network.clustering.openCluster('c1', {}); // 14-13-12-11 1-2-c2(-3-4) numNodes += 1; numEdges += 1; assertNumNodes(network, numNodes, numNodes - 2); assertNumEdges(network, numEdges, numEdges - 2); // two external connections to clusters [network, data, numNodes, numEdges] = createSampleNetwork(); data.edges.update({ from: 1, to: 11, }); numEdges += 1; assertNumNodes(network, numNodes, numNodes); assertNumEdges(network, numEdges, numEdges); clusterTo(network, 'c1', [1,2]); // 14-13-12-11-c1(-1-2-)-3-4 numNodes += 1; numEdges += 2; clusterTo(network, 'c2', [3,4]); // 14-13-12-11-c1(-1-2-)-c2(-3-4) // NOTE: clustering edges are hidden by clustering here! numNodes += 1; numEdges += 1; clusterTo(network, 'c3', [11,12]); // 14-13-c3(-12-11-)-c1(-1-2-)-c2(-3-4) numNodes += 1; numEdges += 2; assertNumNodes(network, numNodes, numNodes - 6); assertNumEdges(network, numEdges, numEdges - 8); // 6 regular edges hidden; also 2 clustering!!!!! network.clustering.openCluster('c1', {}); numNodes -= 1; numEdges -= 2; // 14-13-c3(-12-11-)-1-2-c2(-3-4) assertNumNodes(network, numNodes, numNodes - 4); assertNumEdges(network, numEdges, numEdges - 5); }); /** * Check correct opening of nested clusters. * The test uses clustering three levels deep and opens the middle one. */ it('properly opens clustered clusters', function () { var [network, data, numNodes, numEdges] = createSampleNetwork(); data.edges.update({from: 1, to: 11,}); numEdges += 1; clusterTo(network, 'c1', [3,4]); clusterTo(network, 'c2', [2,'c1']); clusterTo(network, 'c3', [1,'c2']); // Attempt at visualization: parentheses belong to the cluster one level above // -c3 // ( -c2 ) // ( -c1 ) // 14-13-12-11 -1 -2 (-3-4) numNodes += 3; numEdges += 3; //console.log("numNodes: " + numNodes + "; numEdges: " + numEdges); assertNumNodes(network, numNodes, numNodes - 6); assertNumEdges(network, numEdges, numEdges - 6); // Open the middle cluster network.clustering.openCluster('c2', {}); // -c3 // ( -c1 ) // 14-13-12-11 -1 -2 (-3-4) numNodes -= 1; numEdges -= 1; assertNumNodes(network, numNodes, numNodes - 5); assertNumEdges(network, numEdges, numEdges - 5); // // Same, with one external connection to cluster // var [network, data, numNodes, numEdges] = createSampleNetwork(); data.edges.update({from: 1, to: 11,}); data.edges.update({from: 2, to: 12,}); numEdges += 2; // 14-13-12-11-1-2-3-4 // |------| assertNumNodes(network, numNodes); assertNumEdges(network, numEdges); clusterTo(network, 'c0', [11,12]); clusterTo(network, 'c1', [3,4]); clusterTo(network, 'c2', [2,'c1']); clusterTo(network, 'c3', [1,'c2']); // +----------------+ // | c3 | // | +----------+ | // | | c2 | | // +-------+ | | +----+ | | // | c0 | | | | c1 | | | // 14-13-|-12-11-|-|-1-|-2-|-3-4| | | // | | | | | | +----+ | | // +-------+ | | | | | // | | +----------+ | // | | | | // | +----------------+ // |------------| // (I) numNodes += 4; numEdges = 15; assertNumNodes(network, numNodes, 4); assertNumEdges(network, numEdges, 3); // (I) link 2-12 is combined into cluster edge for 11-1 // Open the middle cluster network.clustering.openCluster('c2', {}); // +--------------+ // | c3 | // | | // +-------+ | +----+ | // | c0 | | | c1 | | // 14-13-|-12-11-|-|-1--2-|-3-4| | // | | | | | +----+ | // +-------+ | | | // | | | | // | +--------------+ // |-----------| // (I) numNodes -= 1; numEdges -= 2; assertNumNodes(network, numNodes, 4); // visibility doesn't change, cluster opened within cluster assertNumEdges(network, numEdges, 3); // (I) // Open the top cluster network.clustering.openCluster('c3', {}); // // +-------+ +----+ // | c0 | | c1 | // 14-13-|-12-11-|-1-2-|-3-4| // | | | | +----+ // +-------+ | // | | // |--------| // (II) numNodes -= 1; numEdges = 12; assertNumNodes(network, numNodes, 6); // visibility doesn't change, cluster opened within cluster assertNumEdges(network, numEdges, 6); // (II) link 2-12 visible again }); }); // Clustering describe('on node.js', function () { it('should be running', function () { assert(this.container !== null, 'Container div not found'); // The following should now just plain succeed var [network, data] = createSampleNetwork(); assert.equal(Object.keys(network.body.nodes).length, 8); assert.equal(Object.keys(network.body.edges).length, 6); }); describe('runs example ', function () { function loadExample(path, noPhysics) { include(path, this); var container = document.getElementById('mynetwork'); // create a network var data = { nodes: new vis.DataSet(nodes), edges: new vis.DataSet(edges) }; if (noPhysics) { // Avoid excessive processor time due to load. // We're just interested that the load itself is good options.physics = false; } var network = new vis.Network(container, data, options); return network; }; it('basicUsage', function () { var network = loadExample('./test/network/basicUsage.js'); //console.log(Object.keys(network.body.edges)); // Count in following also contains the helper nodes for dynamic edges assert.equal(Object.keys(network.body.nodes).length, 10); assert.equal(Object.keys(network.body.edges).length, 5); }); it('WorlCup2014', function (done) { // This is a huge example (which is why it's tested here!), so it takes a long time to load. this.timeout(15000); var network = loadExample('./examples/network/datasources/WorldCup2014.js', true); // Count in following also contains the helper nodes for dynamic edges assert.equal(Object.keys(network.body.nodes).length, 9964); assert.equal(Object.keys(network.body.edges).length, 9228); done(); }); // This actually failed to load, added for this reason it('disassemblerExample', function () { var network = loadExample('./examples/network/exampleApplications/disassemblerExample.js'); // console.log(Object.keys(network.body.nodes)); // console.log(Object.keys(network.body.edges)); // Count in following also contains the helper nodes for dynamic edges assert.equal(Object.keys(network.body.nodes).length, 9); assert.equal(Object.keys(network.body.edges).length, 14 - 3); // NB 3 edges in data not displayed }); }); // runs example }); // on node.js }); // Network