vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1178 lines
39 KiB

  1. /**
  2. *
  3. * Useful during debugging
  4. * =======================
  5. *
  6. * console.log(JSON.stringify(output, null, 2));
  7. *
  8. * for (let i in network.body.edges) {
  9. * let edge = network.body.edges[i];
  10. * console.log("" + i + ": from: " + edge.fromId + ", to: " + edge.toId);
  11. * }
  12. */
  13. var fs = require('fs');
  14. var assert = require('assert');
  15. var vis = require('../dist/vis');
  16. var Network = vis.network;
  17. var jsdom_global = require('jsdom-global');
  18. var stdout = require('test-console').stdout;
  19. var Validator = require("./../lib/shared/Validator").default;
  20. //var {printStyle} = require('./../lib/shared/Validator');
  21. /**
  22. * Merge all options of object b into object b
  23. * @param {Object} a
  24. * @param {Object} b
  25. * @return {Object} a
  26. *
  27. * Adapted merge() in dotparser.js
  28. */
  29. function merge (a, b) {
  30. if (!a) {
  31. a = {};
  32. }
  33. if (b) {
  34. for (var name in b) {
  35. if (b.hasOwnProperty(name)) {
  36. if (typeof b[name] === 'object') {
  37. a[name] = merge(a[name], b[name]);
  38. } else {
  39. a[name] = b[name];
  40. }
  41. }
  42. }
  43. }
  44. return a;
  45. }
  46. /**
  47. * Load legacy-style (i.e. not module) javascript files into the given context.
  48. */
  49. function include(list, context) {
  50. if (!(list instanceof Array)) {
  51. list = [list];
  52. }
  53. for (var n in list) {
  54. var path = list[n];
  55. var arr = [fs.readFileSync(path) + ''];
  56. eval.apply(context, arr);
  57. }
  58. }
  59. /**
  60. * Defined network consists of two sub-networks:
  61. *
  62. * - 1-2-3-4
  63. * - 11-12-13-14
  64. *
  65. * For reference, this is the sample network of issue #1218
  66. */
  67. function createSampleNetwork(options) {
  68. var NumInitialNodes = 8;
  69. var NumInitialEdges = 6;
  70. var nodes = new vis.DataSet([
  71. {id: 1, label: '1'},
  72. {id: 2, label: '2'},
  73. {id: 3, label: '3'},
  74. {id: 4, label: '4'},
  75. {id: 11, label: '11'},
  76. {id: 12, label: '12'},
  77. {id: 13, label: '13'},
  78. {id: 14, label: '14'},
  79. ]);
  80. var edges = new vis.DataSet([
  81. {from: 1, to: 2},
  82. {from: 2, to: 3},
  83. {from: 3, to: 4},
  84. {from: 11, to: 12},
  85. {from: 12, to: 13},
  86. {from: 13, to: 14},
  87. ]);
  88. // create a network
  89. var container = document.getElementById('mynetwork');
  90. var data = {
  91. nodes: nodes,
  92. edges: edges
  93. };
  94. var defaultOptions = {
  95. layout: {
  96. randomSeed: 8
  97. },
  98. edges: {
  99. smooth: {
  100. type: 'continuous' // avoid dynamic here, it adds extra hidden nodes
  101. }
  102. }
  103. };
  104. options = merge(defaultOptions, options);
  105. var network = new vis.Network(container, data, options);
  106. assertNumNodes(network, NumInitialNodes);
  107. assertNumEdges(network, NumInitialEdges);
  108. return [network, data, NumInitialNodes, NumInitialEdges];
  109. };
  110. /**
  111. * Create a cluster for the dynamic data change cases.
  112. *
  113. * Works on the network created by createSampleNetwork().
  114. *
  115. * This is actually a pathological case; there are two separate sub-networks and
  116. * a cluster is made of two nodes, each from one of the sub-networks.
  117. */
  118. function createCluster(network) {
  119. var clusterOptionsByData = {
  120. joinCondition: function(node) {
  121. if (node.id == 1 || node.id == 11) return true;
  122. return false;
  123. },
  124. clusterNodeProperties: {id:"c1", label:'c1'}
  125. }
  126. network.cluster(clusterOptionsByData);
  127. }
  128. /**
  129. * Display node/edge state, useful during debugging
  130. */
  131. function log(network) {
  132. console.log(Object.keys(network.body.nodes));
  133. console.log(network.body.nodeIndices);
  134. console.log(Object.keys(network.body.edges));
  135. console.log(network.body.edgeIndices);
  136. };
  137. /**
  138. * Note that only the node and edges counts are asserted.
  139. * This might be done more thoroughly by explicitly checking the id's
  140. */
  141. function assertNumNodes(network, expectedPresent, expectedVisible) {
  142. if (expectedVisible === undefined) expectedVisible = expectedPresent;
  143. assert.equal(Object.keys(network.body.nodes).length, expectedPresent, "Total number of nodes does not match");
  144. assert.equal(network.body.nodeIndices.length, expectedVisible, "Number of visible nodes does not match");
  145. };
  146. /**
  147. * Comment at assertNumNodes() also applies.
  148. */
  149. function assertNumEdges(network, expectedPresent, expectedVisible) {
  150. if (expectedVisible === undefined) expectedVisible = expectedPresent;
  151. assert.equal(Object.keys(network.body.edges).length, expectedPresent, "Total number of edges does not match");
  152. assert.equal(network.body.edgeIndices.length, expectedVisible, "Number of visible edges does not match");
  153. };
  154. describe('Network', function () {
  155. before(function() {
  156. this.jsdom_global = jsdom_global(
  157. "<div id='mynetwork'></div>",
  158. { skipWindowCheck: true}
  159. );
  160. this.container = document.getElementById('mynetwork');
  161. });
  162. after(function() {
  163. try {
  164. this.jsdom_global();
  165. } catch(e) {
  166. if (e.message() === 'window is undefined') {
  167. console.warning("'" + e.message() + "' happened again");
  168. } else {
  169. throw e;
  170. }
  171. }
  172. });
  173. /////////////////////////////////////////////////////
  174. // Local helper methods for Edge and Node testing
  175. /////////////////////////////////////////////////////
  176. /**
  177. * Simplify network creation for local tests
  178. */
  179. function createNetwork(options) {
  180. var [network, data, numNodes, numEdges] = createSampleNetwork(options);
  181. return network;
  182. }
  183. function firstNode(network) {
  184. for (var id in network.body.nodes) {
  185. return network.body.nodes[id];
  186. }
  187. return undefined;
  188. }
  189. function firstEdge(network) {
  190. for (var id in network.body.edges) {
  191. return network.body.edges[id];
  192. }
  193. return undefined;
  194. }
  195. function checkChooserValues(item, chooser, labelChooser) {
  196. if (chooser === 'function') {
  197. assert.equal(typeof item.chooser, 'function');
  198. } else {
  199. assert.equal(item.chooser, chooser);
  200. }
  201. if (labelChooser === 'function') {
  202. assert.equal(typeof item.labelModule.fontOptions.chooser, 'function');
  203. } else {
  204. assert.equal(item.labelModule.fontOptions.chooser, labelChooser);
  205. }
  206. }
  207. describe('Node', function () {
  208. /**
  209. * NOTE: choosify tests of Node and Edge are parallel
  210. * TODO: consolidate this is necessary
  211. */
  212. it('properly handles choosify input', function () {
  213. // check defaults
  214. var options = {};
  215. var network = createNetwork(options);
  216. checkChooserValues(firstNode(network), true, true);
  217. // There's no point in checking invalid values here; these are detected by the options parser
  218. // and subsequently handled as missing input, thus assigned defaults
  219. // check various combinations of valid input
  220. options = {nodes: {chosen: false}};
  221. network = createNetwork(options);
  222. checkChooserValues(firstNode(network), false, false);
  223. options = {nodes: {chosen: { node:true, label:false}}};
  224. network = createNetwork(options);
  225. checkChooserValues(firstNode(network), true, false);
  226. options = {nodes: {chosen: {
  227. node:true,
  228. label: function(value, id, selected, hovering) {}
  229. }}};
  230. network = createNetwork(options);
  231. checkChooserValues(firstNode(network), true, 'function');
  232. options = {nodes: {chosen: {
  233. node: function(value, id, selected, hovering) {},
  234. label:false,
  235. }}};
  236. network = createNetwork(options);
  237. checkChooserValues(firstNode(network), 'function', false);
  238. });
  239. }); // Node
  240. describe('Edge', function () {
  241. /**
  242. * NOTE: choosify tests of Node and Edge are parallel
  243. * TODO: consolidate this is necessary
  244. */
  245. it('properly handles choosify input', function () {
  246. // check defaults
  247. var options = {};
  248. var network = createNetwork(options);
  249. checkChooserValues(firstEdge(network), true, true);
  250. // There's no point in checking invalid values here; these are detected by the options parser
  251. // and subsequently handled as missing input, thus assigned defaults
  252. // check various combinations of valid input
  253. options = {edges: {chosen: false}};
  254. network = createNetwork(options);
  255. checkChooserValues(firstEdge(network), false, false);
  256. options = {edges: {chosen: { edge:true, label:false}}};
  257. network = createNetwork(options);
  258. checkChooserValues(firstEdge(network), true, false);
  259. options = {edges: {chosen: {
  260. edge:true,
  261. label: function(value, id, selected, hovering) {}
  262. }}};
  263. network = createNetwork(options);
  264. checkChooserValues(firstEdge(network), true, 'function');
  265. options = {edges: {chosen: {
  266. edge: function(value, id, selected, hovering) {},
  267. label:false,
  268. }}};
  269. network = createNetwork(options);
  270. checkChooserValues(firstEdge(network), 'function', false);
  271. });
  272. /**
  273. * Support routine for next unit test
  274. */
  275. function createDataforColorChange() {
  276. var nodes = new vis.DataSet([
  277. {id: 1, label: 'Node 1' }, // group:'Group1'},
  278. {id: 2, label: 'Node 2', group:'Group2'},
  279. {id: 3, label: 'Node 3'},
  280. ]);
  281. // create an array with edges
  282. var edges = new vis.DataSet([
  283. {id: 1, from: 1, to: 2},
  284. {id: 2, from: 1, to: 3, color: { inherit: 'to'}},
  285. {id: 3, from: 3, to: 3, color: { color: '#00FF00'}},
  286. {id: 4, from: 2, to: 3, color: { inherit: 'from'}},
  287. ]);
  288. var data = {
  289. nodes: nodes,
  290. edges: edges
  291. };
  292. return data;
  293. }
  294. /**
  295. * Unit test for fix of #3350
  296. *
  297. * The issue is that changing color options is not registered in the nodes.
  298. * We test the updates the color options in the general edges options here.
  299. */
  300. it('sets inherit color option for edges on call to Network.setOptions()', function () {
  301. var container = document.getElementById('mynetwork');
  302. var data = createDataforColorChange();
  303. var options = {
  304. "edges" : { "color" : { "inherit" : "to" } },
  305. };
  306. // Test passing options on init.
  307. var network = new vis.Network(container, data, options);
  308. var edges = network.body.edges;
  309. assert.equal(edges[1].options.color.inherit, 'to'); // new default
  310. assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
  311. assert.equal(edges[3].options.color.inherit, false); // has explicit color
  312. assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
  313. // Sanity check: colors should still be defaults
  314. assert.equal(edges[1].options.color.color, network.edgesHandler.options.color.color);
  315. // Override the color value - inherit returns to default
  316. network.setOptions({ edges:{color: {}}});
  317. assert.equal(edges[1].options.color.inherit, 'from'); // default
  318. assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
  319. assert.equal(edges[3].options.color.inherit, false); // has explicit color
  320. assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
  321. // Check no options
  322. network = new vis.Network(container, data, {});
  323. edges = network.body.edges;
  324. assert.equal(edges[1].options.color.inherit, 'from'); // default
  325. assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
  326. assert.equal(edges[3].options.color.inherit, false); // has explicit color
  327. assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
  328. // Set new value
  329. network.setOptions(options);
  330. assert.equal(edges[1].options.color.inherit, 'to');
  331. assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
  332. assert.equal(edges[3].options.color.inherit, false); // has explicit color
  333. assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
  334. /*
  335. // Useful for debugging
  336. console.log('===================================');
  337. console.log(edges[1].options.color);
  338. console.log(edges[1].options.color.__proto__);
  339. console.log(edges[1].options);
  340. console.log(edges[1].options.__proto__);
  341. console.log(edges[1].edgeOptions);
  342. */
  343. });
  344. it('sets inherit color option for specific edge', function () {
  345. var container = document.getElementById('mynetwork');
  346. var data = createDataforColorChange();
  347. // Check no options
  348. var network = new vis.Network(container, data, {});
  349. var edges = network.body.edges;
  350. assert.equal(edges[1].options.color.inherit, 'from'); // default
  351. assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
  352. assert.equal(edges[3].options.color.inherit, false); // has explicit color
  353. assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
  354. // Set new value
  355. data.edges.update({id: 1, color: { inherit: 'to'}});
  356. assert.equal(edges[1].options.color.inherit, 'to'); // Only this changed
  357. assert.equal(edges[2].options.color.inherit, 'to');
  358. assert.equal(edges[3].options.color.inherit, false); // has explicit color
  359. assert.equal(edges[4].options.color.inherit, 'from');
  360. });
  361. /**
  362. * Perhaps TODO: add unit test for passing string value for color option
  363. */
  364. it('sets color value for edges on call to Network.setOptions()', function () {
  365. var container = document.getElementById('mynetwork');
  366. var data = createDataforColorChange();
  367. var defaultColor = '#848484'; // From defaults
  368. var color = '#FF0000';
  369. var options = {
  370. "edges" : { "color" : { "color" : color } },
  371. };
  372. // Test passing options on init.
  373. var network = new vis.Network(container, data, options);
  374. var edges = network.body.edges;
  375. assert.equal(edges[1].options.color.color, color);
  376. assert.equal(edges[1].options.color.inherit, false); // Explicit color, so no inherit
  377. assert.equal(edges[2].options.color.color, color);
  378. assert.equal(edges[2].options.color.inherit, 'to'); // Local value overrides! (bug according to docs)
  379. assert.notEqual(edges[3].options.color.color, color); // Has own value
  380. assert.equal(edges[3].options.color.inherit, false); // Explicit color, so no inherit
  381. assert.equal(edges[4].options.color.color, color);
  382. // Override the color value - all should return to default
  383. network.setOptions({ edges:{color: {}}});
  384. assert.equal(edges[1].options.color.color, defaultColor);
  385. assert.equal(edges[1].options.color.inherit, 'from');
  386. assert.equal(edges[2].options.color.color, defaultColor);
  387. assert.notEqual(edges[3].options.color.color, color); // Has own value
  388. assert.equal(edges[4].options.color.color, defaultColor);
  389. // Check no options
  390. network = new vis.Network(container, data, {});
  391. edges = network.body.edges;
  392. // At this point, color has not changed yet
  393. assert.equal(edges[1].options.color.color, defaultColor);
  394. assert.equal(edges[1].options.color.highlight, defaultColor);
  395. assert.equal(edges[1].options.color.inherit, 'from');
  396. assert.notEqual(edges[3].options.color.color, color); // Has own value
  397. // Set new Value
  398. network.setOptions(options);
  399. assert.equal(edges[1].options.color.color, color);
  400. assert.equal(edges[1].options.color.highlight, defaultColor); // Should not be changed
  401. assert.equal(edges[1].options.color.inherit, false); // Explicit color, so no inherit
  402. assert.equal(edges[2].options.color.color, color);
  403. assert.notEqual(edges[3].options.color.color, color); // Has own value
  404. assert.equal(edges[4].options.color.color, color);
  405. });
  406. }); // Edge
  407. describe('Clustering', function () {
  408. /**
  409. * Helper function for clustering
  410. */
  411. function clusterTo(network, clusterId, nodeList, allowSingle) {
  412. var options = {
  413. joinCondition: function(node) {
  414. return nodeList.indexOf(node.id) !== -1;
  415. },
  416. clusterNodeProperties: {
  417. id: clusterId,
  418. label: clusterId
  419. }
  420. }
  421. if (allowSingle === true) {
  422. options.clusterNodeProperties.allowSingleNodeCluster = true
  423. }
  424. network.cluster(options);
  425. }
  426. it('properly handles options allowSingleNodeCluster', function() {
  427. var [network, data, numNodes, numEdges] = createSampleNetwork();
  428. data.edges.update({from: 1, to: 11,});
  429. numEdges += 1;
  430. assertNumNodes(network, numNodes);
  431. assertNumEdges(network, numEdges);
  432. clusterTo(network, 'c1', [3,4]);
  433. numNodes += 1; // A clustering node is now hiding two nodes
  434. numEdges += 1; // One clustering edges now hiding two edges
  435. assertNumNodes(network, numNodes, numNodes - 2);
  436. assertNumEdges(network, numEdges, numEdges - 2);
  437. // Cluster of single node should fail, because by default allowSingleNodeCluster == false
  438. clusterTo(network, 'c2', [14]);
  439. assertNumNodes(network, numNodes, numNodes - 2); // Nothing changed
  440. assertNumEdges(network, numEdges, numEdges - 2);
  441. assert(network.body.nodes['c2'] === undefined); // Cluster not created
  442. // Redo with allowSingleNodeCluster == true
  443. clusterTo(network, 'c2', [14], true);
  444. numNodes += 1;
  445. numEdges += 1;
  446. assertNumNodes(network, numNodes, numNodes - 3);
  447. assertNumEdges(network, numEdges, numEdges - 3);
  448. assert(network.body.nodes['c2'] !== undefined); // Cluster created
  449. // allowSingleNodeCluster: true with two nodes
  450. // removing one clustered node should retain cluster
  451. clusterTo(network, 'c3', [11, 12], true);
  452. numNodes += 1; // Added cluster
  453. numEdges += 2;
  454. assertNumNodes(network, numNodes, 6);
  455. assertNumEdges(network, numEdges, 5);
  456. data.nodes.remove(12);
  457. assert(network.body.nodes['c3'] !== undefined); // Cluster should still be present
  458. numNodes -= 1; // removed node
  459. numEdges -= 3; // cluster edge C3-13 should be removed
  460. assertNumNodes(network, numNodes, 6);
  461. assertNumEdges(network, numEdges, 4);
  462. });
  463. it('removes nested clusters with allowSingleNodeCluster === true', function() {
  464. var [network, data, numNodes, numEdges] = createSampleNetwork();
  465. // Create a chain of nested clusters, three deep
  466. clusterTo(network, 'c1', [4], true);
  467. clusterTo(network, 'c2', ['c1'], true);
  468. clusterTo(network, 'c3', ['c2'], true);
  469. numNodes += 3;
  470. numEdges += 3;
  471. assertNumNodes(network, numNodes, numNodes - 3);
  472. assertNumEdges(network, numEdges, numEdges - 3);
  473. assert(network.body.nodes['c1'] !== undefined);
  474. assert(network.body.nodes['c2'] !== undefined);
  475. assert(network.body.nodes['c3'] !== undefined);
  476. // The whole chain should be removed when the bottom-most node is deleted
  477. data.nodes.remove(4);
  478. numNodes -= 4;
  479. numEdges -= 4;
  480. assertNumNodes(network, numNodes);
  481. assertNumEdges(network, numEdges);
  482. assert(network.body.nodes['c1'] === undefined);
  483. assert(network.body.nodes['c2'] === undefined);
  484. assert(network.body.nodes['c3'] === undefined);
  485. });
  486. /**
  487. * Check on fix for #1218
  488. */
  489. it('connects a new edge to a clustering node instead of the clustered node', function () {
  490. var [network, data, numNodes, numEdges] = createSampleNetwork();
  491. createCluster(network);
  492. numNodes += 1; // A clustering node is now hiding two nodes
  493. numEdges += 2; // Two clustering edges now hide two edges
  494. assertNumNodes(network, numNodes, numNodes - 2);
  495. assertNumEdges(network, numEdges, numEdges - 2);
  496. //console.log("Creating node 21")
  497. data.nodes.update([{id: 21, label: '21'}]);
  498. numNodes += 1; // New unconnected node added
  499. assertNumNodes(network, numNodes, numNodes - 2);
  500. assertNumEdges(network, numEdges, numEdges - 2); // edges unchanged
  501. //console.log("Creating edge 21 pointing to 1");
  502. // '1' is part of the cluster so should
  503. // connect to cluster instead
  504. data.edges.update([{from: 21, to: 1}]);
  505. numEdges += 2; // A new clustering edge is hiding a new edge
  506. assertNumNodes(network, numNodes, numNodes - 2); // nodes unchanged
  507. assertNumEdges(network, numEdges, numEdges - 3);
  508. });
  509. /**
  510. * Check on fix for #1315
  511. */
  512. it('can uncluster a clustered node when a node is removed that has an edge to that cluster', function () {
  513. // NOTE: this block is same as previous test
  514. var [network, data, numNodes, numEdges] = createSampleNetwork();
  515. createCluster(network);
  516. numNodes += 1; // A clustering node is now hiding two nodes
  517. numEdges += 2; // Two clustering edges now hide two edges
  518. assertNumNodes(network, numNodes, numNodes - 2);
  519. assertNumEdges(network, numEdges, numEdges - 2);
  520. // End block same as previous test
  521. //console.log("removing 12");
  522. data.nodes.remove(12);
  523. // NOTE:
  524. // At this particular point, there are still the two edges for node 12 in the edges DataSet.
  525. // If you want to do the delete correctly, these should also be deleted explictly from
  526. // the edges DataSet. In the Network instance, however, this.body.nodes and this.body.edges
  527. // should be correct, with the edges of 12 all cleared out.
  528. // 12 was connected to 11, which is clustered
  529. numNodes -= 1; // 12 removed, one less node
  530. numEdges -= 3; // clustering edge c1-12 and 2 edges of 12 gone
  531. assertNumNodes(network, numNodes, numNodes - 2);
  532. assertNumEdges(network, numEdges, numEdges - 1);
  533. //console.log("Unclustering c1");
  534. network.openCluster("c1");
  535. numNodes -= 1; // cluster node removed, one less node
  536. numEdges -= 1; // clustering edge gone, regular edge visible
  537. assertNumNodes(network, numNodes, numNodes); // all are visible again
  538. assertNumEdges(network, numEdges, numEdges); // all are visible again
  539. });
  540. /**
  541. * Check on fix for #1291
  542. */
  543. it('can remove a node inside a cluster and then open that cluster', function () {
  544. var [network, data, numNodes, numEdges] = createSampleNetwork();
  545. var clusterOptionsByData = {
  546. joinCondition: function(node) {
  547. if (node.id == 1 || node.id == 2 || node.id == 3) return true;
  548. return false;
  549. },
  550. clusterNodeProperties: {id:"c1", label:'c1'}
  551. }
  552. network.cluster(clusterOptionsByData);
  553. numNodes += 1; // new cluster node
  554. numEdges += 1; // 1 cluster edge expected
  555. assertNumNodes(network, numNodes, numNodes - 3); // 3 clustered nodes
  556. assertNumEdges(network, numEdges, numEdges - 3); // 3 edges hidden
  557. //console.log("removing node 2, which is inside the cluster");
  558. data.nodes.remove(2);
  559. numNodes -= 1; // clustered node removed
  560. numEdges -= 2; // edges removed hidden in cluster
  561. assertNumNodes(network, numNodes, numNodes - 2); // view doesn't change
  562. assertNumEdges(network, numEdges, numEdges - 1); // view doesn't change
  563. //console.log("Unclustering c1");
  564. network.openCluster("c1")
  565. numNodes -= 1; // cluster node gone
  566. numEdges -= 1; // cluster edge gone
  567. assertNumNodes(network, numNodes, numNodes); // all visible
  568. assertNumEdges(network, numEdges, numEdges); // all visible
  569. //log(network);
  570. });
  571. /**
  572. * Helper function for setting up a graph for testing clusterByEdgeCount()
  573. */
  574. function createOutlierGraph() {
  575. // create an array with nodes
  576. var nodes = new vis.DataSet([
  577. {id: 1, label: '1', group:'Group1'},
  578. {id: 2, label: '2', group:'Group2'},
  579. {id: 3, label: '3', group:'Group3'},
  580. {id: 4, label: '4', group:'Group4'},
  581. {id: 5, label: '5', group:'Group4'}
  582. ]);
  583. // create an array with edges
  584. var edges = new vis.DataSet([
  585. {from: 1, to: 3},
  586. {from: 1, to: 2},
  587. {from: 2, to: 4},
  588. {from: 2, to: 5}
  589. ]);
  590. // create a network
  591. var container = document.getElementById('mynetwork');
  592. var data = {
  593. nodes: nodes,
  594. edges: edges
  595. };
  596. var options = {
  597. "groups" : {
  598. "Group1" : { level:1 },
  599. "Group2" : { level:2 },
  600. "Group3" : { level:3 },
  601. "Group4" : { level:4 }
  602. }
  603. };
  604. var network = new vis.Network (container, data, options);
  605. return network;
  606. }
  607. /**
  608. * Check on fix for #3367
  609. */
  610. it('correctly handles edge cases of clusterByEdgeCount()', function () {
  611. /**
  612. * Collect clustered id's
  613. *
  614. * All node id's in clustering nodes are collected into an array;
  615. * The results for all clusters are returned as an array.
  616. *
  617. * Ordering of output depends on the order in which they are defined
  618. * within nodes.clustering; strictly, speaking, the array and its items
  619. * are collections, so order should not matter.
  620. */
  621. var collectClusters = function(network) {
  622. var clusters = [];
  623. for(var n in network.body.nodes) {
  624. var node = network.body.nodes[n];
  625. if (node.containedNodes === undefined) continue; // clusters only
  626. // Collect id's of nodes in the cluster
  627. var nodes = [];
  628. for(var m in node.containedNodes) {
  629. nodes.push(m);
  630. }
  631. clusters.push(nodes);
  632. }
  633. return clusters;
  634. }
  635. /**
  636. * Compare cluster data
  637. *
  638. * params are arrays of arrays of id's, e.g:
  639. *
  640. * [[1,3],[2,4]]
  641. *
  642. * Item arrays are the id's of nodes in a given cluster
  643. *
  644. * This comparison depends on the ordering; better
  645. * would be to treat the items and values as collections.
  646. */
  647. var compareClusterInfo = function(recieved, expected) {
  648. if (recieved.length !== expected.length) return false;
  649. for (var n = 0; n < recieved.length; ++n) {
  650. var itema = recieved[n];
  651. var itemb = expected[n];
  652. if (itema.length !== itemb.length) return false;
  653. for (var m = 0; m < itema.length; ++m) {
  654. if (itema[m] != itemb[m]) return false; // != because values can be string or number
  655. }
  656. }
  657. return true;
  658. }
  659. var assertJoinCondition = function(joinCondition, expected) {
  660. var network = createOutlierGraph();
  661. network.clusterOutliers({joinCondition: joinCondition});
  662. var recieved = collectClusters(network);
  663. //console.log(recieved);
  664. assert(compareClusterInfo(recieved, expected),
  665. 'recieved:' + JSON.stringify(recieved) + '; '
  666. + 'expected: ' + JSON.stringify(expected));
  667. };
  668. // Should cluster 3,4,5:
  669. var joinAll_ = function(n) { return true ; }
  670. // Should cluster none:
  671. var joinNone_ = function(n) { return false ; }
  672. // Should cluster 4 & 5:
  673. var joinLevel_ = function(n) { return n.level > 3 ; }
  674. assertJoinCondition(undefined , [[1,3],[2,4,5]]);
  675. assertJoinCondition(null , [[1,3],[2,4,5]]);
  676. assertJoinCondition(joinNone_ , []);
  677. assertJoinCondition(joinLevel_ , [[2,4,5]]);
  678. });
  679. ///////////////////////////////////////////////////////////////
  680. // Automatic opening of clusters due to dynamic data change
  681. ///////////////////////////////////////////////////////////////
  682. /**
  683. * Helper function, created nested clusters, three deep
  684. */
  685. function createNetwork1() {
  686. var [network, data, numNodes, numEdges] = createSampleNetwork();
  687. clusterTo(network, 'c1', [3,4]);
  688. numNodes += 1; // new cluster node
  689. numEdges += 1; // 1 cluster edge expected
  690. assertNumNodes(network, numNodes, numNodes - 2); // 2 clustered nodes
  691. assertNumEdges(network, numEdges, numEdges - 2); // 2 edges hidden
  692. clusterTo(network, 'c2', [2,'c1']);
  693. numNodes += 1; // new cluster node
  694. numEdges += 1; // 2 cluster edges expected
  695. assertNumNodes(network, numNodes, numNodes - 4); // 4 clustered nodes, including c1
  696. assertNumEdges(network, numEdges, numEdges - 4); // 4 edges hidden, including edge for c1
  697. clusterTo(network, 'c3', [1,'c2']);
  698. // Attempt at visualization: parentheses belong to the cluster one level above
  699. // c3
  700. // ( -c2 )
  701. // ( -c1 )
  702. // 14-13-12-11 1 -2 (-3-4)
  703. numNodes += 1; // new cluster node
  704. numEdges += 0; // No new cluster edge expected
  705. assertNumNodes(network, numNodes, numNodes - 6); // 6 clustered nodes, including c1 and c2
  706. assertNumEdges(network, numEdges, numEdges - 5); // 5 edges hidden, including edges for c1 and c2
  707. return [network, data, numNodes, numEdges];
  708. }
  709. it('opens clusters automatically when nodes deleted', function () {
  710. var [network, data, numNodes, numEdges] = createSampleNetwork();
  711. // Simple case: cluster of two nodes, delete one node
  712. clusterTo(network, 'c1', [3,4]);
  713. numNodes += 1; // new cluster node
  714. numEdges += 1; // 1 cluster edge expected
  715. assertNumNodes(network, numNodes, numNodes - 2); // 2 clustered nodes
  716. assertNumEdges(network, numEdges, numEdges - 2); // 2 edges hidden
  717. data.nodes.remove(4);
  718. numNodes -= 2; // deleting clustered node also removes cluster node
  719. numEdges -= 2; // cluster edge should also be removed
  720. assertNumNodes(network, numNodes, numNodes);
  721. assertNumEdges(network, numEdges, numEdges);
  722. // Extended case: nested nodes, three deep
  723. [network, data, numNodes, numEdges] = createNetwork1();
  724. data.nodes.remove(4);
  725. // c3
  726. // ( -c2 )
  727. // 14-13-12-11 1 (-2 -3)
  728. numNodes -= 2; // node removed, c1 also gone
  729. numEdges -= 2;
  730. assertNumNodes(network, numNodes, numNodes - 4);
  731. assertNumEdges(network, numEdges, numEdges - 3);
  732. data.nodes.remove(1);
  733. // c2
  734. // 14-13-12-11 (2 -3)
  735. numNodes -= 2; // node removed, c3 also gone
  736. numEdges -= 2;
  737. assertNumNodes(network, numNodes, numNodes - 2);
  738. assertNumEdges(network, numEdges, numEdges - 1);
  739. data.nodes.remove(2);
  740. // 14-13-12-11 3
  741. numNodes -= 2; // node removed, c2 also gone
  742. numEdges -= 1;
  743. assertNumNodes(network, numNodes); // All visible again
  744. assertNumEdges(network, numEdges);
  745. // Same as previous step, but remove all the given nodes in one go
  746. // The result should be the same.
  747. [network, data, numNodes, numEdges] = createNetwork1(); // nested nodes, three deep
  748. data.nodes.remove([1,2,4]);
  749. // 14-13-12-11 3
  750. assertNumNodes(network, 5);
  751. assertNumEdges(network, 3);
  752. });
  753. ///////////////////////////////////////////////////////////////
  754. // Opening of clusters at various clustering depths
  755. ///////////////////////////////////////////////////////////////
  756. /**
  757. * Check correct opening of a single cluster.
  758. * This is the 'simple' case.
  759. */
  760. it('properly opens 1-level clusters', function () {
  761. var [network, data, numNodes, numEdges] = createSampleNetwork();
  762. // Pedantic: make a cluster of everything
  763. clusterTo(network, 'c1', [1,2,3,4,11, 12, 13, 14]);
  764. // c1(14-13-12-11 1-2-3-4)
  765. numNodes += 1;
  766. assertNumNodes(network, numNodes, 1); // Just the clustering node visible
  767. assertNumEdges(network, numEdges, 0); // No extra edges!
  768. network.clustering.openCluster('c1', {});
  769. numNodes -= 1;
  770. assertNumNodes(network, numNodes, numNodes); // Expecting same as original
  771. assertNumEdges(network, numEdges, numEdges);
  772. // One external connection
  773. [network, data, numNodes, numEdges] = createSampleNetwork();
  774. // 14-13-12-11 1-2-3-4
  775. clusterTo(network, 'c1', [3,4]);
  776. network.clustering.openCluster('c1', {});
  777. assertNumNodes(network, numNodes, numNodes); // Expecting same as original
  778. assertNumEdges(network, numEdges, numEdges);
  779. // Two external connections
  780. clusterTo(network, 'c1', [2,3]);
  781. network.clustering.openCluster('c1', {});
  782. assertNumNodes(network, numNodes, numNodes); // Expecting same as original
  783. assertNumEdges(network, numEdges, numEdges);
  784. // One external connection to cluster
  785. clusterTo(network, 'c1', [1,2]);
  786. clusterTo(network, 'c2', [3,4]);
  787. // 14-13-12-11 c1(1-2-)-c2(-3-4)
  788. network.clustering.openCluster('c1', {});
  789. // 14-13-12-11 1-2-c2(-3-4)
  790. numNodes += 1;
  791. numEdges += 1;
  792. assertNumNodes(network, numNodes, numNodes - 2);
  793. assertNumEdges(network, numEdges, numEdges - 2);
  794. // two external connections to clusters
  795. [network, data, numNodes, numEdges] = createSampleNetwork();
  796. data.edges.update({
  797. from: 1,
  798. to: 11,
  799. });
  800. numEdges += 1;
  801. assertNumNodes(network, numNodes, numNodes);
  802. assertNumEdges(network, numEdges, numEdges);
  803. clusterTo(network, 'c1', [1,2]);
  804. // 14-13-12-11-c1(-1-2-)-3-4
  805. numNodes += 1;
  806. numEdges += 2;
  807. clusterTo(network, 'c2', [3,4]);
  808. // 14-13-12-11-c1(-1-2-)-c2(-3-4)
  809. // NOTE: clustering edges are hidden by clustering here!
  810. numNodes += 1;
  811. numEdges += 1;
  812. clusterTo(network, 'c3', [11,12]);
  813. // 14-13-c3(-12-11-)-c1(-1-2-)-c2(-3-4)
  814. numNodes += 1;
  815. numEdges += 2;
  816. assertNumNodes(network, numNodes, numNodes - 6);
  817. assertNumEdges(network, numEdges, numEdges - 8); // 6 regular edges hidden; also 2 clustering!!!!!
  818. network.clustering.openCluster('c1', {});
  819. numNodes -= 1;
  820. numEdges -= 2;
  821. // 14-13-c3(-12-11-)-1-2-c2(-3-4)
  822. assertNumNodes(network, numNodes, numNodes - 4);
  823. assertNumEdges(network, numEdges, numEdges - 5);
  824. });
  825. /**
  826. * Check correct opening of nested clusters.
  827. * The test uses clustering three levels deep and opens the middle one.
  828. */
  829. it('properly opens clustered clusters', function () {
  830. var [network, data, numNodes, numEdges] = createSampleNetwork();
  831. data.edges.update({from: 1, to: 11,});
  832. numEdges += 1;
  833. clusterTo(network, 'c1', [3,4]);
  834. clusterTo(network, 'c2', [2,'c1']);
  835. clusterTo(network, 'c3', [1,'c2']);
  836. // Attempt at visualization: parentheses belong to the cluster one level above
  837. // -c3
  838. // ( -c2 )
  839. // ( -c1 )
  840. // 14-13-12-11 -1 -2 (-3-4)
  841. numNodes += 3;
  842. numEdges += 3;
  843. //console.log("numNodes: " + numNodes + "; numEdges: " + numEdges);
  844. assertNumNodes(network, numNodes, numNodes - 6);
  845. assertNumEdges(network, numEdges, numEdges - 6);
  846. // Open the middle cluster
  847. network.clustering.openCluster('c2', {});
  848. // -c3
  849. // ( -c1 )
  850. // 14-13-12-11 -1 -2 (-3-4)
  851. numNodes -= 1;
  852. numEdges -= 1;
  853. assertNumNodes(network, numNodes, numNodes - 5);
  854. assertNumEdges(network, numEdges, numEdges - 5);
  855. //
  856. // Same, with one external connection to cluster
  857. //
  858. var [network, data, numNodes, numEdges] = createSampleNetwork();
  859. data.edges.update({from: 1, to: 11,});
  860. data.edges.update({from: 2, to: 12,});
  861. numEdges += 2;
  862. // 14-13-12-11-1-2-3-4
  863. // |------|
  864. assertNumNodes(network, numNodes);
  865. assertNumEdges(network, numEdges);
  866. clusterTo(network, 'c0', [11,12]);
  867. clusterTo(network, 'c1', [3,4]);
  868. clusterTo(network, 'c2', [2,'c1']);
  869. clusterTo(network, 'c3', [1,'c2']);
  870. // +----------------+
  871. // | c3 |
  872. // | +----------+ |
  873. // | | c2 | |
  874. // +-------+ | | +----+ | |
  875. // | c0 | | | | c1 | | |
  876. // 14-13-|-12-11-|-|-1-|-2-|-3-4| | |
  877. // | | | | | | +----+ | |
  878. // +-------+ | | | | |
  879. // | | +----------+ |
  880. // | | | |
  881. // | +----------------+
  882. // |------------|
  883. // (I)
  884. numNodes += 4;
  885. numEdges = 15;
  886. assertNumNodes(network, numNodes, 4);
  887. assertNumEdges(network, numEdges, 3); // (I) link 2-12 is combined into cluster edge for 11-1
  888. // Open the middle cluster
  889. network.clustering.openCluster('c2', {});
  890. // +--------------+
  891. // | c3 |
  892. // | |
  893. // +-------+ | +----+ |
  894. // | c0 | | | c1 | |
  895. // 14-13-|-12-11-|-|-1--2-|-3-4| |
  896. // | | | | | +----+ |
  897. // +-------+ | | |
  898. // | | | |
  899. // | +--------------+
  900. // |-----------|
  901. // (I)
  902. numNodes -= 1;
  903. numEdges -= 2;
  904. assertNumNodes(network, numNodes, 4); // visibility doesn't change, cluster opened within cluster
  905. assertNumEdges(network, numEdges, 3); // (I)
  906. // Open the top cluster
  907. network.clustering.openCluster('c3', {});
  908. //
  909. // +-------+ +----+
  910. // | c0 | | c1 |
  911. // 14-13-|-12-11-|-1-2-|-3-4|
  912. // | | | | +----+
  913. // +-------+ |
  914. // | |
  915. // |--------|
  916. // (II)
  917. numNodes -= 1;
  918. numEdges = 12;
  919. assertNumNodes(network, numNodes, 6); // visibility doesn't change, cluster opened within cluster
  920. assertNumEdges(network, numEdges, 6); // (II) link 2-12 visible again
  921. });
  922. }); // Clustering
  923. describe('on node.js', function () {
  924. it('should be running', function () {
  925. assert(this.container !== null, 'Container div not found');
  926. // The following should now just plain succeed
  927. var [network, data] = createSampleNetwork();
  928. assert.equal(Object.keys(network.body.nodes).length, 8);
  929. assert.equal(Object.keys(network.body.edges).length, 6);
  930. });
  931. describe('runs example ', function () {
  932. function loadExample(path, noPhysics) {
  933. include(path, this);
  934. var container = document.getElementById('mynetwork');
  935. // create a network
  936. var data = {
  937. nodes: new vis.DataSet(nodes),
  938. edges: new vis.DataSet(edges)
  939. };
  940. if (noPhysics) {
  941. // Avoid excessive processor time due to load.
  942. // We're just interested that the load itself is good
  943. options.physics = false;
  944. }
  945. var network = new vis.Network(container, data, options);
  946. return network;
  947. };
  948. it('basicUsage', function () {
  949. var network = loadExample('./test/network/basicUsage.js');
  950. //console.log(Object.keys(network.body.edges));
  951. // Count in following also contains the helper nodes for dynamic edges
  952. assert.equal(Object.keys(network.body.nodes).length, 10);
  953. assert.equal(Object.keys(network.body.edges).length, 5);
  954. });
  955. it('WorlCup2014', function () {
  956. // This is a huge example (which is why it's tested here!), so it takes a long time to load.
  957. this.timeout(10000);
  958. var network = loadExample('./examples/network/datasources/WorldCup2014.js', true);
  959. // Count in following also contains the helper nodes for dynamic edges
  960. assert.equal(Object.keys(network.body.nodes).length, 9964);
  961. assert.equal(Object.keys(network.body.edges).length, 9228);
  962. });
  963. // This actually failed to load, added for this reason
  964. it('disassemblerExample', function () {
  965. var network = loadExample('./examples/network/exampleApplications/disassemblerExample.js');
  966. // console.log(Object.keys(network.body.nodes));
  967. // console.log(Object.keys(network.body.edges));
  968. // Count in following also contains the helper nodes for dynamic edges
  969. assert.equal(Object.keys(network.body.nodes).length, 9);
  970. assert.equal(Object.keys(network.body.edges).length, 14 - 3); // NB 3 edges in data not displayed
  971. });
  972. }); // runs example
  973. }); // on node.js
  974. }); // Network