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.

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