ngraph.graph
Advanced tools
Comparing version 0.0.5 to 0.0.6
497
index.js
@@ -12,3 +12,7 @@ /** | ||
*/ | ||
module.exports = function() { | ||
module.exports = createGraph; | ||
var eventify = require('ngraph.events'); | ||
function createGraph() { | ||
// Graph structure is maintained as dictionary of nodes | ||
@@ -27,3 +31,12 @@ // and array of links. Each node has 'links' property which | ||
// Accumlates all changes made during graph updates. | ||
forEachNode = createNodeIterator(), | ||
linkConnectionSymbol = '👉 ', | ||
// Our graph API provides means to listen to graph changes. Users can subscribe | ||
// to be notified about changes in the graph by using `on` method. However | ||
// in some cases they don't use it. To avoid unnecessary memory consumption | ||
// we will not record graph changes until we have at least one subscriber. | ||
// Code below supports this optimization. | ||
// | ||
// Accumulates all changes made during graph updates. | ||
// Each change element contains: | ||
@@ -34,37 +47,9 @@ // changeType - one of the strings: 'add', 'remove' or 'update'; | ||
changes = [], | ||
recordLinkChange = noop, | ||
recordNodeChange = noop, | ||
enterModification = noop, | ||
exitModification = noop; | ||
fireGraphChanged = function(graph) { | ||
graph.fire('changed', changes); | ||
}, | ||
// Enter, Exit Mofidication allows bulk graph updates without firing events. | ||
enterModification = function() { | ||
suspendEvents += 1; | ||
}, | ||
exitModification = function(graph) { | ||
suspendEvents -= 1; | ||
if (suspendEvents === 0 && changes.length > 0) { | ||
fireGraphChanged(graph); | ||
changes.length = 0; | ||
} | ||
}, | ||
recordNodeChange = function(node, changeType) { | ||
changes.push({ | ||
node: node, | ||
changeType: changeType | ||
}); | ||
}, | ||
recordLinkChange = function(link, changeType) { | ||
changes.push({ | ||
link: link, | ||
changeType: changeType | ||
}); | ||
}, | ||
linkConnectionSymbol = '👉 '; | ||
// this is our public API: | ||
var graphPart = { | ||
/** | ||
@@ -81,28 +66,4 @@ * Adds node to the graph. If node with given id already exists in the graph | ||
*/ | ||
addNode: function(nodeId, data) { | ||
if (typeof nodeId === 'undefined') { | ||
throw new Error('Invalid node identifier'); | ||
} | ||
addNode: addNode, | ||
enterModification(); | ||
var node = this.getNode(nodeId); | ||
if (!node) { | ||
// TODO: Should I check for linkConnectionSymbol here? | ||
node = new Node(nodeId); | ||
nodesCount++; | ||
recordNodeChange(node, 'add'); | ||
} else { | ||
recordNodeChange(node, 'update'); | ||
} | ||
node.data = data; | ||
nodes[nodeId] = node; | ||
exitModification(this); | ||
return node; | ||
}, | ||
/** | ||
@@ -119,32 +80,4 @@ * Adds a link to the graph. The function always create a new | ||
*/ | ||
addLink: function(fromId, toId, data) { | ||
enterModification(); | ||
addLink: addLink, | ||
var fromNode = this.getNode(fromId) || this.addNode(fromId); | ||
var toNode = this.getNode(toId) || this.addNode(toId); | ||
var linkId = fromId.toString() + linkConnectionSymbol + toId.toString(); | ||
var isMultiEdge = multiEdges.hasOwnProperty(linkId); | ||
if (isMultiEdge || this.hasLink(fromId, toId)) { | ||
if (!isMultiEdge) { | ||
multiEdges[linkId] = 0; | ||
} | ||
linkId += '@' + (++multiEdges[linkId]); | ||
} | ||
var link = new Link(fromId, toId, data, linkId); | ||
links.push(link); | ||
// TODO: this is not cool. On large graphs potentially would consume more memory. | ||
fromNode.links.push(link); | ||
toNode.links.push(link); | ||
recordLinkChange(link, 'add'); | ||
exitModification(this); | ||
return link; | ||
}, | ||
/** | ||
@@ -157,39 +90,4 @@ * Removes link from the graph. If link does not exist does nothing. | ||
*/ | ||
removeLink: function(link) { | ||
if (!link) { | ||
return false; | ||
} | ||
var idx = indexOfElementInArray(link, links); | ||
if (idx < 0) { | ||
return false; | ||
} | ||
removeLink: removeLink, | ||
enterModification(); | ||
links.splice(idx, 1); | ||
var fromNode = this.getNode(link.fromId); | ||
var toNode = this.getNode(link.toId); | ||
if (fromNode) { | ||
idx = indexOfElementInArray(link, fromNode.links); | ||
if (idx >= 0) { | ||
fromNode.links.splice(idx, 1); | ||
} | ||
} | ||
if (toNode) { | ||
idx = indexOfElementInArray(link, toNode.links); | ||
if (idx >= 0) { | ||
toNode.links.splice(idx, 1); | ||
} | ||
} | ||
recordLinkChange(link, 'remove'); | ||
exitModification(this); | ||
return true; | ||
}, | ||
/** | ||
@@ -203,25 +101,4 @@ * Removes node with given id from the graph. If node does not exist in the graph | ||
*/ | ||
removeNode: function(nodeId) { | ||
var node = this.getNode(nodeId); | ||
if (!node) { | ||
return false; | ||
} | ||
removeNode: removeNode, | ||
enterModification(); | ||
while (node.links.length) { | ||
var link = node.links[0]; | ||
this.removeLink(link); | ||
} | ||
delete nodes[nodeId]; | ||
nodesCount--; | ||
recordNodeChange(node, 'remove'); | ||
exitModification(this); | ||
return true; | ||
}, | ||
/** | ||
@@ -234,5 +111,3 @@ * Gets node with given identifier. If node does not exist undefined value is returned. | ||
*/ | ||
getNode: function(nodeId) { | ||
return nodes[nodeId]; | ||
}, | ||
getNode: getNode, | ||
@@ -264,6 +139,3 @@ /** | ||
*/ | ||
getLinks: function(nodeId) { | ||
var node = this.getNode(nodeId); | ||
return node ? node.links : null; | ||
}, | ||
getLinks: getLinks, | ||
@@ -276,3 +148,3 @@ /** | ||
*/ | ||
forEachNode: createNodeIterator(), | ||
forEachNode: forEachNode, | ||
@@ -287,28 +159,4 @@ /** | ||
*/ | ||
forEachLinkedNode: function(nodeId, callback, oriented) { | ||
var node = this.getNode(nodeId), | ||
i, | ||
link, | ||
linkedNodeId; | ||
forEachLinkedNode: forEachLinkedNode, | ||
if (node && node.links && typeof callback === 'function') { | ||
// Extraced orientation check out of the loop to increase performance | ||
if (oriented) { | ||
for (i = 0; i < node.links.length; ++i) { | ||
link = node.links[i]; | ||
if (link.fromId === nodeId) { | ||
callback(nodes[link.toId], link); | ||
} | ||
} | ||
} else { | ||
for (i = 0; i < node.links.length; ++i) { | ||
link = node.links[i]; | ||
linkedNodeId = link.fromId === nodeId ? link.toId : link.fromId; | ||
callback(nodes[linkedNodeId], link); | ||
} | ||
} | ||
} | ||
}, | ||
/** | ||
@@ -325,10 +173,3 @@ * Enumerates all links in the graph | ||
*/ | ||
forEachLink: function(callback) { | ||
var i, length; | ||
if (typeof callback === 'function') { | ||
for (i = 0, length = links.length; i < length; ++i) { | ||
callback(links[i]); | ||
} | ||
} | ||
}, | ||
forEachLink: forEachLink, | ||
@@ -339,5 +180,3 @@ /** | ||
*/ | ||
beginUpdate: function() { | ||
enterModification(); | ||
}, | ||
beginUpdate: enterModification, | ||
@@ -348,5 +187,3 @@ /** | ||
*/ | ||
endUpdate: function() { | ||
exitModification(this); | ||
}, | ||
endUpdate: exitModification, | ||
@@ -356,10 +193,3 @@ /** | ||
*/ | ||
clear: function() { | ||
var that = this; | ||
that.beginUpdate(); | ||
that.forEachNode(function(node) { | ||
that.removeNode(node.id); | ||
}); | ||
that.endUpdate(); | ||
}, | ||
clear: clear, | ||
@@ -372,27 +202,250 @@ /** | ||
*/ | ||
hasLink: function(fromNodeId, toNodeId) { | ||
// TODO: Use adjacency matrix to speed up this operation. | ||
var node = this.getNode(fromNodeId), | ||
i; | ||
if (!node) { | ||
return null; | ||
hasLink: hasLink | ||
}; | ||
// this will add `on()` and `fire()` methods. | ||
eventify(graphPart); | ||
monitorSubscribers(); | ||
return graphPart; | ||
function monitorSubscribers() { | ||
var realOn = graphPart.on; | ||
// replace real `on` with our temporary on, which will trigger change | ||
// modification monitoring: | ||
graphPart.on = on; | ||
function on() { | ||
// now it's time to start tracking stuff: | ||
enterModification = enterModificationReal; | ||
exitModification = exitModificationReal; | ||
recordLinkChange = recordLinkChangeReal; | ||
recordNodeChange = recordNodeChangeReal; | ||
// this will replace current `on` method with real pub/sub from `eventify`. | ||
graphPart.on = realOn; | ||
// delegate to real `on` handler: | ||
return realOn.apply(graphPart, arguments); | ||
} | ||
} | ||
function recordLinkChangeReal(link, changeType) { | ||
changes.push({ | ||
link: link, | ||
changeType: changeType | ||
}); | ||
} | ||
function recordNodeChangeReal(node, changeType) { | ||
changes.push({ | ||
node: node, | ||
changeType: changeType | ||
}); | ||
} | ||
function addNode(nodeId, data) { | ||
if (nodeId === undefined) { | ||
throw new Error('Invalid node identifier'); | ||
} | ||
enterModification(); | ||
var node = getNode(nodeId); | ||
if (!node) { | ||
// TODO: Should I check for linkConnectionSymbol here? | ||
node = new Node(nodeId); | ||
nodesCount++; | ||
recordNodeChange(node, 'add'); | ||
} else { | ||
recordNodeChange(node, 'update'); | ||
} | ||
node.data = data; | ||
nodes[nodeId] = node; | ||
exitModification(); | ||
return node; | ||
} | ||
function getNode(nodeId) { | ||
return nodes[nodeId]; | ||
} | ||
function removeNode(nodeId) { | ||
var node = getNode(nodeId); | ||
if (!node) { | ||
return false; | ||
} | ||
enterModification(); | ||
while (node.links.length) { | ||
var link = node.links[0]; | ||
removeLink(link); | ||
} | ||
delete nodes[nodeId]; | ||
nodesCount--; | ||
recordNodeChange(node, 'remove'); | ||
exitModification(); | ||
return true; | ||
} | ||
function addLink(fromId, toId, data) { | ||
enterModification(); | ||
var fromNode = getNode(fromId) || addNode(fromId); | ||
var toNode = getNode(toId) || addNode(toId); | ||
var linkId = fromId.toString() + linkConnectionSymbol + toId.toString(); | ||
var isMultiEdge = multiEdges.hasOwnProperty(linkId); | ||
if (isMultiEdge || hasLink(fromId, toId)) { | ||
if (!isMultiEdge) { | ||
multiEdges[linkId] = 0; | ||
} | ||
linkId += '@' + (++multiEdges[linkId]); | ||
} | ||
for (i = 0; i < node.links.length; ++i) { | ||
var link = node.links[i]; | ||
if (link.fromId === fromNodeId && link.toId === toNodeId) { | ||
return link; | ||
} | ||
var link = new Link(fromId, toId, data, linkId); | ||
links.push(link); | ||
// TODO: this is not cool. On large graphs potentially would consume more memory. | ||
fromNode.links.push(link); | ||
toNode.links.push(link); | ||
recordLinkChange(link, 'add'); | ||
exitModification(); | ||
return link; | ||
} | ||
function getLinks(nodeId) { | ||
var node = getNode(nodeId); | ||
return node ? node.links : null; | ||
} | ||
function removeLink(link) { | ||
if (!link) { | ||
return false; | ||
} | ||
var idx = indexOfElementInArray(link, links); | ||
if (idx < 0) { | ||
return false; | ||
} | ||
enterModification(); | ||
links.splice(idx, 1); | ||
var fromNode = getNode(link.fromId); | ||
var toNode = getNode(link.toId); | ||
if (fromNode) { | ||
idx = indexOfElementInArray(link, fromNode.links); | ||
if (idx >= 0) { | ||
fromNode.links.splice(idx, 1); | ||
} | ||
} | ||
return null; // no link. | ||
if (toNode) { | ||
idx = indexOfElementInArray(link, toNode.links); | ||
if (idx >= 0) { | ||
toNode.links.splice(idx, 1); | ||
} | ||
} | ||
}; | ||
// Let graph fire events before we return it to the caller. | ||
var eventify = require('ngraph.events'); | ||
eventify(graphPart); | ||
recordLinkChange(link, 'remove'); | ||
return graphPart; | ||
exitModification(); | ||
return true; | ||
} | ||
function hasLink(fromNodeId, toNodeId) { | ||
// TODO: Use adjacency matrix to speed up this operation. | ||
var node = getNode(fromNodeId), | ||
i; | ||
if (!node) { | ||
return null; | ||
} | ||
for (i = 0; i < node.links.length; ++i) { | ||
var link = node.links[i]; | ||
if (link.fromId === fromNodeId && link.toId === toNodeId) { | ||
return link; | ||
} | ||
} | ||
return null; // no link. | ||
} | ||
function clear() { | ||
enterModification(); | ||
forEachNode(function(node) { | ||
removeNode(node.id); | ||
}); | ||
exitModification(); | ||
} | ||
function forEachLink(callback) { | ||
var i, length; | ||
if (typeof callback === 'function') { | ||
for (i = 0, length = links.length; i < length; ++i) { | ||
callback(links[i]); | ||
} | ||
} | ||
} | ||
function forEachLinkedNode(nodeId, callback, oriented) { | ||
var node = getNode(nodeId), | ||
i, | ||
link, | ||
linkedNodeId; | ||
if (node && node.links && typeof callback === 'function') { | ||
// Extracted orientation check out of the loop to increase performance | ||
if (oriented) { | ||
for (i = 0; i < node.links.length; ++i) { | ||
link = node.links[i]; | ||
if (link.fromId === nodeId) { | ||
callback(nodes[link.toId], link); | ||
} | ||
} | ||
} else { | ||
for (i = 0; i < node.links.length; ++i) { | ||
link = node.links[i]; | ||
linkedNodeId = link.fromId === nodeId ? link.toId : link.fromId; | ||
callback(nodes[linkedNodeId], link); | ||
} | ||
} | ||
} | ||
} | ||
// we will not fire anything until users of this library explicitly call `on()` | ||
// method. | ||
function noop() {} | ||
// Enter, Exit modification allows bulk graph updates without firing events. | ||
function enterModificationReal() { | ||
suspendEvents += 1; | ||
} | ||
function exitModificationReal() { | ||
suspendEvents -= 1; | ||
if (suspendEvents === 0 && changes.length > 0) { | ||
graphPart.fire('changed', changes); | ||
changes.length = 0; | ||
} | ||
} | ||
function createNodeIterator() { | ||
@@ -413,3 +466,3 @@ // Object.keys iterator is 1.3x faster than `for in` loop. | ||
if (callback(nodes[keys[i]])) { | ||
return; // client doesn't want to proceed. return. | ||
return; // client doesn't want to proceed. Return. | ||
} | ||
@@ -427,7 +480,7 @@ } | ||
if (callback(nodes[node])) { | ||
return; // client doesn't want to proceed. return. | ||
return; // client doesn't want to proceed. Return. | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
@@ -434,0 +487,0 @@ // need this for old browsers. Should this be a separate module? |
{ | ||
"name": "ngraph.graph", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "Base graph structure in ngraph.*", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "tap test/*.js" | ||
"test": "node node_modules/argg test/*.js", | ||
"plato": "plato -d reports/plato index.js", | ||
"cover": "istanbul cover --dir reports/coverage node_modules/argg test/*.js" | ||
}, | ||
@@ -23,2 +25,5 @@ "repository": { | ||
"devDependencies": { | ||
"argg": "0.0.1", | ||
"istanbul": "^0.3.5", | ||
"plato": "^1.3.0", | ||
"tap": "~0.4.4" | ||
@@ -25,0 +30,0 @@ }, |
@@ -20,4 +20,4 @@ ngraph.graph | ||
``` js | ||
g.addNode('hello'); | ||
g.addNode('world'); | ||
g.addNode('hello'); | ||
g.addNode('world'); | ||
``` | ||
@@ -72,3 +72,3 @@ | ||
g.forEachLinkedNode('hello', function(linkedNode, link){ | ||
console.log("Connected node: ", linkedNode.id, linkedNode.data); | ||
console.log("Connected node: ", linkedNode.id, linkedNode.data); | ||
console.dir(link); // link object itself | ||
@@ -99,3 +99,3 @@ }); | ||
g.forEachLinkedNode('hello', function(linkedNode, link){ | ||
g.removeLink(link); | ||
g.removeLink(link); | ||
}); | ||
@@ -102,0 +102,0 @@ ``` |
var test = require('tap').test, | ||
createGraph = require('..'); | ||
createGraph = require('..'); | ||
@@ -10,3 +10,4 @@ test('add node adds node', function(t) { | ||
t.equal(graph.getNodesCount(), 1, 'addNode failed'); | ||
t.equal(graph.getNodesCount(), 1, 'exactly one node'); | ||
t.equal(graph.getLinksCount(), 0, 'no links'); | ||
t.equal(graph.getNode(1), node, 'invalid node returned by addNode (or getNode)'); | ||
@@ -18,4 +19,47 @@ t.equal(node.data, customData, 'data was not set properly'); | ||
test('add nodeid same as a prototype property', function (t) { | ||
test('hasLink checks links', function (t) { | ||
var graph = createGraph(); | ||
graph.addLink(1, 2); | ||
var link12 = graph.hasLink(1, 2); | ||
t.ok(link12.fromId === 1 && link12.toId === 2, 'link is found'); | ||
// this is somewhat doubtful... has link will return null, but forEachLinkedNode | ||
// will actually iterate over this link. Not sure how to have consistency here | ||
// for now documenting behavior in the test: | ||
var noLink = graph.hasLink(2, 1); | ||
t.notOk(noLink, 'hasLink is always directional'); | ||
var obviousNo = graph.hasLink(); | ||
t.notOk(obviousNo, 'No links there'); | ||
t.end(); | ||
}); | ||
test('it throw if no node id is passed', function(t) { | ||
var graph = createGraph(); | ||
t.throws(function() { | ||
graph.addNode(); // no id, should throw | ||
}, 'It throws on undefined node'); | ||
t.end(); | ||
}); | ||
test('it fires update event when node is updated', function(t) { | ||
t.plan(3); | ||
var graph = createGraph(); | ||
graph.addNode(1, 'hello'); | ||
graph.on('changed', checkChangedEvent); | ||
graph.addNode(1, 'world'); | ||
t.end(); | ||
function checkChangedEvent(changes) { | ||
var change = changes[0]; | ||
t.equals(change.node.id, 1); | ||
t.equals(change.node.data, 'world'); | ||
t.equals(change.changeType, 'update'); | ||
} | ||
}); | ||
test('it can add node with id similar to reserved prototype property', function(t) { | ||
var graph = createGraph(); | ||
graph.addNode('constructor'); | ||
@@ -25,3 +69,3 @@ graph.addLink('watch', 'constructor'); | ||
var iterated = 0; | ||
graph.forEachNode(function () { | ||
graph.forEachNode(function() { | ||
iterated += 1; | ||
@@ -31,2 +75,3 @@ }); | ||
t.ok(graph.hasLink('watch', 'constructor')); | ||
t.equals(graph.getLinksCount(), 1, 'one link'); | ||
t.equal(iterated, 2, 'has two nodes'); | ||
@@ -40,6 +85,7 @@ t.end(); | ||
var link = graph.addLink(1, 2), | ||
firstNodeLinks = graph.getLinks(1), | ||
secondNodeLinks = graph.getLinks(2); | ||
firstNodeLinks = graph.getLinks(1), | ||
secondNodeLinks = graph.getLinks(2); | ||
t.equal(graph.getNodesCount(), 2, 'addLink failed'); | ||
t.equal(graph.getNodesCount(), 2, 'Two nodes'); | ||
t.equal(graph.getLinksCount(), 1, 'One link'); | ||
t.equal(firstNodeLinks.length, 1, 'number of links of the first node is wrong'); | ||
@@ -52,2 +98,19 @@ t.equal(secondNodeLinks.length, 1, 'number of links of the second node is wrong'); | ||
test('it can add multi-edges', function (t) { | ||
t.plan(5); | ||
var graph = createGraph(); | ||
graph.addLink(1, 2, 'first'); | ||
graph.addLink(1, 2, 'second'); | ||
graph.addLink(1, 2, 'third'); | ||
t.equal(graph.getLinksCount(), 3, 'three links!'); | ||
t.equal(graph.getNodesCount(), 2, 'Two nodes'); | ||
graph.forEachLinkedNode(1, function (otherNode, link) { | ||
t.ok(link.data === 'first' || link.data === 'second' || link.data === 'third', 'Link is here'); | ||
}); | ||
t.end(); | ||
}); | ||
test('add one node fires changed event', function(t) { | ||
@@ -59,5 +122,5 @@ t.plan(3); | ||
graph.on('changed', function(changes) { | ||
t.ok(changes && changes.length === 1, "Only one change should be recorded"); | ||
t.equal(changes[0].node.id, testNodeId, "Wrong node change notification"); | ||
t.equal(changes[0].changeType, 'add', "Add change type expected."); | ||
t.ok(changes && changes.length === 1, "Only one change should be recorded"); | ||
t.equal(changes[0].node.id, testNodeId, "Wrong node change notification"); | ||
t.equal(changes[0].changeType, 'add', "Add change type expected."); | ||
}); | ||
@@ -73,9 +136,10 @@ | ||
var graph = createGraph(); | ||
var fromId = 1, toId = 2; | ||
var fromId = 1, | ||
toId = 2; | ||
graph.on('changed', function(changes) { | ||
t.ok(changes && changes.length === 3, "Three change should be recorded: node, node and link"); | ||
t.equal(changes[2].link.fromId, fromId, "Wrong link from Id"); | ||
t.equal(changes[2].link.toId, toId, "Wrong link toId"); | ||
t.equal(changes[2].changeType, 'add', "Add change type expected."); | ||
t.ok(changes && changes.length === 3, "Three change should be recorded: node, node and link"); | ||
t.equal(changes[2].link.fromId, fromId, "Wrong link from Id"); | ||
t.equal(changes[2].link.toId, toId, "Wrong link toId"); | ||
t.equal(changes[2].changeType, 'add', "Add change type expected."); | ||
}); | ||
@@ -98,3 +162,3 @@ | ||
test('remove link removes it', function(t) { | ||
t.plan(3); | ||
t.plan(5); | ||
@@ -104,16 +168,31 @@ var graph = createGraph(); | ||
graph.removeLink(link); | ||
var linkIsRemoved = graph.removeLink(link); | ||
t.equal(graph.getNodesCount(), 2, 'remove link should not remove nodes'); | ||
t.equal(graph.getLinksCount(), 0, 'No Links'); | ||
t.equal(graph.getLinks(1).length, 0, 'link should be removed from the first node'); | ||
t.equal(graph.getLinks(2).length, 0, 'link should be removed from the second node'); | ||
t.ok(linkIsRemoved, 'Link removal is successful'); | ||
graph.forEachLink(function(link){ | ||
test.ok(false, 'No links should be in graph'); | ||
graph.forEachLink(function(link) { | ||
test.ok(false, 'No links should be in graph'); | ||
}); | ||
t.end(); | ||
}); | ||
test('remove link returns false if no link removed', function (t) { | ||
var graph = createGraph(); | ||
var link = graph.addLink(1, 2); | ||
var result = graph.removeLink('blah'); | ||
t.notOk(result, 'Link is not removed'); | ||
var alsoNo = graph.removeLink(); | ||
t.notOk(alsoNo, 'No link - no removal'); | ||
t.end(); | ||
}); | ||
test('remove isolated node fires changed event', function(t) { | ||
t.plan(3); | ||
t.plan(4); | ||
var graph = createGraph(); | ||
@@ -123,8 +202,9 @@ graph.addNode(1); | ||
graph.on('changed', function(changes) { | ||
t.ok(changes && changes.length === 1, "One change should be recorded: node removed"); | ||
t.equal(changes[0].node.id, 1, "Wrong node Id"); | ||
t.equal(changes[0].changeType, 'remove', "'remove' change type expected."); | ||
t.ok(changes && changes.length === 1, "One change should be recorded: node removed"); | ||
t.equal(changes[0].node.id, 1, "Wrong node Id"); | ||
t.equal(changes[0].changeType, 'remove', "'remove' change type expected."); | ||
}); | ||
graph.removeNode(1); | ||
var result = graph.removeNode(1); | ||
t.ok(result, 'node is removed'); | ||
t.end(); | ||
@@ -139,5 +219,5 @@ }); | ||
graph.on('changed', function(changes) { | ||
t.ok(changes && changes.length === 1, "One change should be recorded: link removed"); | ||
t.equal(changes[0].link, link, "Wrong link removed"); | ||
t.equal(changes[0].changeType, 'remove', "'remove' change type expected."); | ||
t.ok(changes && changes.length === 1, "One change should be recorded: link removed"); | ||
t.equal(changes[0].link, link, "Wrong link removed"); | ||
t.equal(changes[0].changeType, 'remove', "'remove' change type expected."); | ||
}); | ||
@@ -152,11 +232,11 @@ | ||
var graph = createGraph(), | ||
link = graph.addLink(1, 2), | ||
nodeIdToRemove = 1; | ||
link = graph.addLink(1, 2), | ||
nodeIdToRemove = 1; | ||
graph.on('changed', function(changes) { | ||
t.ok(changes && changes.length === 2, "Two changes should be recorded: link and node removed"); | ||
t.equal(changes[0].link, link, "Wrong link removed"); | ||
t.equal(changes[0].changeType, 'remove', "'remove' change type expected."); | ||
t.equal(changes[1].node.id, nodeIdToRemove, "Wrong node removed"); | ||
t.equal(changes[1].changeType, 'remove', "'remove' change type expected."); | ||
t.ok(changes && changes.length === 2, "Two changes should be recorded: link and node removed"); | ||
t.equal(changes[0].link, link, "Wrong link removed"); | ||
t.equal(changes[0].changeType, 'remove', "'remove' change type expected."); | ||
t.equal(changes[1].node.id, nodeIdToRemove, "Wrong node removed"); | ||
t.equal(changes[1].changeType, 'remove', "'remove' change type expected."); | ||
}); | ||
@@ -170,5 +250,5 @@ | ||
var graph = createGraph(), | ||
link12 = graph.addLink(1, 2), | ||
link13 = graph.addLink(1, 3), | ||
nodeIdToRemove = 1; | ||
link12 = graph.addLink(1, 2), | ||
link13 = graph.addLink(1, 3), | ||
nodeIdToRemove = 1; | ||
@@ -182,5 +262,25 @@ graph.removeNode(1); | ||
graph.forEachLink(function(link) { | ||
test.ok(false, 'No links should be in graph'); | ||
test.ok(false, 'No links should be in graph'); | ||
}); | ||
t.end(); | ||
}); | ||
test('remove node returns false when no node removed', function (t) { | ||
var graph = createGraph(); | ||
graph.addNode('hello'); | ||
var result = graph.removeNode('blah'); | ||
t.notOk(result, 'No "blah" node'); | ||
t.end(); | ||
}); | ||
test('clearGraph clears graph', function (t) { | ||
var graph = createGraph(); | ||
graph.addNode('hello'); | ||
graph.addLink(1, 2); | ||
graph.clear(); | ||
t.equal(graph.getNodesCount(), 0, 'No nodes'); | ||
t.equal(graph.getLinksCount(), 0, 'No links'); | ||
t.end(); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29817
8
688
4