construction.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. var test = require('tap').test,
  2. createGraph = require('..');
  3. test('add node adds node', function(t) {
  4. var graph = createGraph();
  5. var customData = '31337';
  6. var node = graph.addNode(1, customData);
  7. t.equal(graph.getNodesCount(), 1, 'exactly one node');
  8. t.equal(graph.getLinksCount(), 0, 'no links');
  9. t.equal(graph.getNode(1), node, 'invalid node returned by addNode (or getNode)');
  10. t.equal(node.data, customData, 'data was not set properly');
  11. t.equal(node.id, 1, 'node id was not set properly');
  12. t.end();
  13. });
  14. test('hasNode checks node', function(t) {
  15. var graph = createGraph();
  16. graph.addNode(1);
  17. t.ok(graph.hasNode(1), 'node is there');
  18. t.notOk(graph.hasNode(2), 'should not be here');
  19. t.end();
  20. });
  21. test('hasLink checks links', function (t) {
  22. var graph = createGraph();
  23. graph.addLink(1, 2);
  24. var link12 = graph.hasLink(1, 2);
  25. t.ok(link12.fromId === 1 && link12.toId === 2, 'link is found');
  26. // this is somewhat doubtful... has link will return null, but forEachLinkedNode
  27. // will actually iterate over this link. Not sure how to have consistency here
  28. // for now documenting behavior in the test:
  29. var noLink = graph.hasLink(2, 1);
  30. t.notOk(noLink, 'hasLink is always directional');
  31. var obviousNo = graph.hasLink();
  32. t.notOk(obviousNo, 'No links there');
  33. t.end();
  34. });
  35. test('hasLink is the same as getLink', function (t) {
  36. var graph = createGraph();
  37. t.equals(graph.getLink, graph.hasLink, 'hasLink is synonim for getLink');
  38. t.end();
  39. });
  40. test('it throw if no node id is passed', function(t) {
  41. var graph = createGraph();
  42. t.throws(function() {
  43. graph.addNode(); // no id, should throw
  44. }, 'It throws on undefined node');
  45. t.end();
  46. });
  47. test('it fires update event when node is updated', function(t) {
  48. t.plan(3);
  49. var graph = createGraph();
  50. graph.addNode(1, 'hello');
  51. graph.on('changed', checkChangedEvent);
  52. graph.addNode(1, 'world');
  53. t.end();
  54. function checkChangedEvent(changes) {
  55. var change = changes[0];
  56. t.equals(change.node.id, 1);
  57. t.equals(change.node.data, 'world');
  58. t.equals(change.changeType, 'update');
  59. }
  60. });
  61. test('it can add node with id similar to reserved prototype property', function(t) {
  62. var graph = createGraph();
  63. graph.addNode('constructor');
  64. graph.addLink('watch', 'constructor');
  65. var iterated = 0;
  66. graph.forEachNode(function() {
  67. iterated += 1;
  68. });
  69. t.ok(graph.hasLink('watch', 'constructor'));
  70. t.equals(graph.getLinksCount(), 1, 'one link');
  71. t.equals(iterated, 2, 'has two nodes');
  72. t.end();
  73. });
  74. test('add link adds link', function(t) {
  75. var graph = createGraph();
  76. var link = graph.addLink(1, 2),
  77. firstNodeLinks = graph.getLinks(1),
  78. secondNodeLinks = graph.getLinks(2);
  79. t.equal(graph.getNodesCount(), 2, 'Two nodes');
  80. t.equal(graph.getLinksCount(), 1, 'One link');
  81. t.equal(firstNodeLinks.length, 1, 'number of links of the first node is wrong');
  82. t.equal(secondNodeLinks.length, 1, 'number of links of the second node is wrong');
  83. t.equal(link, firstNodeLinks[0], 'invalid link in the first node');
  84. t.equal(link, secondNodeLinks[0], 'invalid link in the second node');
  85. t.end();
  86. });
  87. test('it can add multi-edges', function (t) {
  88. t.plan(5);
  89. var graph = createGraph({multigraph: true});
  90. graph.addLink(1, 2, 'first');
  91. graph.addLink(1, 2, 'second');
  92. graph.addLink(1, 2, 'third');
  93. t.equal(graph.getLinksCount(), 3, 'three links!');
  94. t.equal(graph.getNodesCount(), 2, 'Two nodes');
  95. graph.forEachLinkedNode(1, function (otherNode, link) {
  96. t.ok(link.data === 'first' || link.data === 'second' || link.data === 'third', 'Link is here');
  97. });
  98. t.end();
  99. });
  100. test('it can produce unique link ids', function (t) {
  101. t.test('by default links are not unique', function (t) {
  102. var seen = {};
  103. var graph = createGraph();
  104. graph.addLink(1, 2, 'first');
  105. graph.addLink(1, 2, 'second');
  106. graph.addLink(1, 2, 'third');
  107. graph.forEachLink(verifyLinksAreNotUnique);
  108. var link = graph.getLink(1, 2);
  109. t.equals(seen[link.id], 3, 'Link 1->2 seen 3 times')
  110. function verifyLinksAreNotUnique(link) {
  111. seen[link.id] = (seen[link.id] || 0) + 1;
  112. }
  113. t.end();
  114. });
  115. t.test('You can create multigraph', function (t) {
  116. var graph = createGraph({
  117. multigraph: true
  118. });
  119. var seen = {};
  120. graph.addLink(1, 2, 'first');
  121. graph.addLink(1, 2, 'second');
  122. graph.addLink(1, 2, 'third');
  123. graph.forEachLink(verifyLinkIsUnique);
  124. t.equals(graph.getLinksCount(), 3, 'All three links are here');
  125. t.end();
  126. function verifyLinkIsUnique(link) {
  127. t.notOk(seen[link.id], link.id + ' is unique');
  128. seen[link.id] = true;
  129. }
  130. });
  131. t.test('you can sacrifice uniqueness in favor of performance', function (t) {
  132. var graph = createGraph({ });
  133. var seen = {};
  134. graph.addLink(1, 2, 'first');
  135. graph.addLink(1, 2, 'second');
  136. graph.addLink(1, 2, 'third');
  137. graph.forEachLink(noticeLink);
  138. t.equals(Object.keys(seen).length, 1, 'Only one link id');
  139. t.end();
  140. function noticeLink(link) {
  141. seen[link.id] = true;
  142. }
  143. });
  144. t.end();
  145. });
  146. test('add one node fires changed event', function(t) {
  147. t.plan(3);
  148. var graph = createGraph();
  149. var testNodeId = 'hello world';
  150. graph.on('changed', function(changes) {
  151. t.ok(changes && changes.length === 1, "Only one change should be recorded");
  152. t.equal(changes[0].node.id, testNodeId, "Wrong node change notification");
  153. t.equal(changes[0].changeType, 'add', "Add change type expected.");
  154. });
  155. graph.addNode(testNodeId);
  156. t.end();
  157. });
  158. test('add link fires changed event', function(t) {
  159. t.plan(4);
  160. var graph = createGraph();
  161. var fromId = 1,
  162. toId = 2;
  163. graph.on('changed', function(changes) {
  164. t.ok(changes && changes.length === 3, "Three change should be recorded: node, node and link");
  165. t.equal(changes[2].link.fromId, fromId, "Wrong link from Id");
  166. t.equal(changes[2].link.toId, toId, "Wrong link toId");
  167. t.equal(changes[2].changeType, 'add', "Add change type expected.");
  168. });
  169. graph.addLink(fromId, toId);
  170. t.end();
  171. });
  172. test('remove isolated node remove it', function(t) {
  173. var graph = createGraph();
  174. graph.addNode(1);
  175. graph.removeNode(1);
  176. t.equal(graph.getNodesCount(), 0, 'Remove operation failed');
  177. t.end();
  178. });
  179. test('supports plural methods', function(t) {
  180. var graph = createGraph();
  181. graph.addLink(1, 2);
  182. t.equal(graph.getNodeCount(), 2, 'two nodes are there');
  183. t.equal(graph.getLinkCount(), 1, 'two nodes are there');
  184. t.equal(graph.getNodesCount(), 2, 'two nodes are there');
  185. t.equal(graph.getLinksCount(), 1, 'two nodes are there');
  186. t.end();
  187. });
  188. test('remove link removes it', function(t) {
  189. t.plan(5);
  190. var graph = createGraph();
  191. var link = graph.addLink(1, 2);
  192. var linkIsRemoved = graph.removeLink(link);
  193. t.equal(graph.getNodesCount(), 2, 'remove link should not remove nodes');
  194. t.equal(graph.getLinksCount(), 0, 'No Links');
  195. t.equal(graph.getLinks(1).length, 0, 'link should be removed from the first node');
  196. t.equal(graph.getLinks(2).length, 0, 'link should be removed from the second node');
  197. t.ok(linkIsRemoved, 'Link removal is successful');
  198. graph.forEachLink(function() {
  199. test.ok(false, 'No links should be in graph');
  200. });
  201. t.end();
  202. });
  203. test('remove link returns false if no link removed', function (t) {
  204. var graph = createGraph();
  205. graph.addLink(1, 2);
  206. var result = graph.removeLink('blah');
  207. t.notOk(result, 'Link is not removed');
  208. var alsoNo = graph.removeLink();
  209. t.notOk(alsoNo, 'No link - no removal');
  210. t.end();
  211. });
  212. test('remove isolated node fires changed event', function(t) {
  213. t.plan(4);
  214. var graph = createGraph();
  215. graph.addNode(1);
  216. graph.on('changed', function(changes) {
  217. t.ok(changes && changes.length === 1, "One change should be recorded: node removed");
  218. t.equal(changes[0].node.id, 1, "Wrong node Id");
  219. t.equal(changes[0].changeType, 'remove', "'remove' change type expected.");
  220. });
  221. var result = graph.removeNode(1);
  222. t.ok(result, 'node is removed');
  223. t.end();
  224. });
  225. test('remove link fires changed event', function(t) {
  226. t.plan(3);
  227. var graph = createGraph();
  228. var link = graph.addLink(1, 2);
  229. graph.on('changed', function(changes) {
  230. t.ok(changes && changes.length === 1, "One change should be recorded: link removed");
  231. t.equal(changes[0].link, link, "Wrong link removed");
  232. t.equal(changes[0].changeType, 'remove', "'remove' change type expected.");
  233. });
  234. graph.removeLink(link);
  235. t.end();
  236. });
  237. test('remove linked node fires changed event', function(t) {
  238. t.plan(5);
  239. var graph = createGraph(),
  240. link = graph.addLink(1, 2),
  241. nodeIdToRemove = 1;
  242. graph.on('changed', function(changes) {
  243. t.ok(changes && changes.length === 2, "Two changes should be recorded: link and node removed");
  244. t.equal(changes[0].link, link, "Wrong link removed");
  245. t.equal(changes[0].changeType, 'remove', "'remove' change type expected.");
  246. t.equal(changes[1].node.id, nodeIdToRemove, "Wrong node removed");
  247. t.equal(changes[1].changeType, 'remove', "'remove' change type expected.");
  248. });
  249. graph.removeNode(nodeIdToRemove);
  250. t.end();
  251. });
  252. test('remove node with many links removes them all', function(t) {
  253. var graph = createGraph();
  254. graph.addLink(1, 2);
  255. graph.addLink(1, 3);
  256. graph.removeNode(1);
  257. t.equal(graph.getNodesCount(), 2, 'remove link should remove one node only');
  258. t.equal(graph.getLinks(1), null, 'link should be removed from the first node');
  259. t.equal(graph.getLinks(2).length, 0, 'link should be removed from the second node');
  260. t.equal(graph.getLinks(3).length, 0, 'link should be removed from the third node');
  261. graph.forEachLink(function() {
  262. test.ok(false, 'No links should be in graph');
  263. });
  264. t.end();
  265. });
  266. test('remove node returns false when no node removed', function (t) {
  267. var graph = createGraph();
  268. graph.addNode('hello');
  269. var result = graph.removeNode('blah');
  270. t.notOk(result, 'No "blah" node');
  271. t.end();
  272. });
  273. test('clearGraph clears graph', function (t) {
  274. var graph = createGraph();
  275. graph.addNode('hello');
  276. graph.addLink(1, 2);
  277. graph.clear();
  278. t.equal(graph.getNodesCount(), 0, 'No nodes');
  279. t.equal(graph.getLinksCount(), 0, 'No links');
  280. t.end();
  281. });
  282. test('beginUpdate holds events', function(t) {
  283. var graph = createGraph();
  284. var changedCount = 0;
  285. graph.on('changed', function () {
  286. changedCount += 1;
  287. });
  288. graph.beginUpdate();
  289. graph.addNode(1);
  290. t.equals(changedCount, 0, 'Begin update freezes updates until `endUpdate()`');
  291. graph.endUpdate();
  292. t.equals(changedCount, 1, 'event is fired only after endUpdate()');
  293. t.end();
  294. });