noflo
Version:
Flow-Based Programming environment for JavaScript
214 lines (210 loc) • 9.67 kB
JavaScript
var chai, graph, journal;
if (typeof process !== 'undefined' && process.execPath && process.execPath.match(/node|iojs/)) {
if (!chai) {
chai = require('chai');
}
graph = require('../src/lib/Graph.coffee');
journal = require('../src/lib/Journal.coffee');
} else {
graph = require('noflo/src/lib/Graph.js');
journal = require('noflo/src/lib/Journal.js');
}
describe('Journal', function() {
describe('connected to initialized graph', function() {
var g, j;
g = new graph.Graph;
g.addNode('Foo', 'Bar');
g.addNode('Baz', 'Foo');
g.addEdge('Foo', 'out', 'Baz', 'in');
j = new journal.Journal(g);
return it('should have just the initial transaction', function() {
return chai.expect(j.store.lastRevision).to.equal(0);
});
});
describe('following basic graph changes', function() {
var g, j;
g = new graph.Graph;
j = new journal.Journal(g);
return it('should create one transaction per change', function() {
g.addNode('Foo', 'Bar');
g.addNode('Baz', 'Foo');
g.addEdge('Foo', 'out', 'Baz', 'in');
chai.expect(j.store.lastRevision).to.equal(3);
g.removeNode('Baz');
return chai.expect(j.store.lastRevision).to.equal(4);
});
});
describe('pretty printing', function() {
var g, j;
g = new graph.Graph;
j = new journal.Journal(g);
g.startTransaction('test1');
g.addNode('Foo', 'Bar');
g.addNode('Baz', 'Foo');
g.addEdge('Foo', 'out', 'Baz', 'in');
g.addInitial(42, 'Foo', 'in');
g.removeNode('Foo');
g.endTransaction('test1');
g.startTransaction('test2');
g.removeNode('Baz');
g.endTransaction('test2');
return it('should be human readable', function() {
var ref;
ref = ">>> 0: initial\n<<< 0: initial\n>>> 1: test1\nFoo(Bar)\nBaz(Foo)\nFoo out -> in Baz\n'42' -> in Foo\nMETA Foo out -> in Baz\nFoo out -X> in Baz\n'42' -X> in Foo\nMETA Foo\nDEL Foo(Bar)\n<<< 1: test1";
return chai.expect(j.toPrettyString(0, 2)).to.equal(ref);
});
});
describe('jumping to revision', function() {
var g, j;
g = new graph.Graph;
j = new journal.Journal(g);
g.addNode('Foo', 'Bar');
g.addNode('Baz', 'Foo');
g.addEdge('Foo', 'out', 'Baz', 'in');
g.addInitial(42, 'Foo', 'in');
g.removeNode('Foo');
return it('should change the graph', function() {
j.moveToRevision(0);
chai.expect(g.nodes.length).to.equal(0);
j.moveToRevision(2);
chai.expect(g.nodes.length).to.equal(2);
j.moveToRevision(5);
return chai.expect(g.nodes.length).to.equal(1);
});
});
describe('linear undo/redo', function() {
var g, graphBeforeError, j;
g = new graph.Graph;
j = new journal.Journal(g);
g.addNode('Foo', 'Bar');
g.addNode('Baz', 'Foo');
g.addEdge('Foo', 'out', 'Baz', 'in');
g.addInitial(42, 'Foo', 'in');
graphBeforeError = g.toJSON();
chai.expect(g.nodes.length).to.equal(2);
it('undo should restore previous revision', function() {
g.removeNode('Foo');
chai.expect(g.nodes.length).to.equal(1);
j.undo();
chai.expect(g.nodes.length).to.equal(2);
return chai.expect(g.toJSON()).to.deep.equal(graphBeforeError);
});
it('redo should apply the same change again', function() {
j.redo();
return chai.expect(g.nodes.length).to.equal(1);
});
return it('undo should also work multiple revisions back', function() {
g.removeNode('Baz');
j.undo();
j.undo();
chai.expect(g.nodes.length).to.equal(2);
return chai.expect(g.toJSON()).to.deep.equal(graphBeforeError);
});
});
return describe('undo/redo of metadata changes', function() {
var g, j;
g = new graph.Graph;
j = new journal.Journal(g);
g.addNode('Foo', 'Bar');
g.addNode('Baz', 'Foo');
g.addEdge('Foo', 'out', 'Baz', 'in');
it('adding group', function() {
g.addGroup('all', ['Foo', 'Bax'], {
'label': 'all nodes'
});
chai.expect(g.groups.length).to.equal(1);
return chai.expect(g.groups[0].name).to.equal('all');
});
it('undoing group add', function() {
j.undo();
return chai.expect(g.groups.length).to.equal(0);
});
it('redoing group add', function() {
j.redo();
return chai.expect(g.groups[0].metadata['label']).to.equal('all nodes');
});
it('changing group metadata adds revision', function() {
var r;
r = j.store.lastRevision;
g.setGroupMetadata('all', {
'label': 'ALL NODES!'
});
return chai.expect(j.store.lastRevision).to.equal(r + 1);
});
it('undoing group metadata change', function() {
j.undo();
return chai.expect(g.groups[0].metadata['label']).to.equal("all nodes");
});
it('redoing group metadata change', function() {
j.redo();
return chai.expect(g.groups[0].metadata['label']).to.equal("ALL NODES!");
});
it('setting node metadata', function() {
g.setNodeMetadata('Foo', {
"oneone": 11,
2: "two"
});
return chai.expect(Object.keys(g.getNode('Foo').metadata).length).to.equal(2);
});
it('undoing set node metadata', function() {
j.undo();
return chai.expect(Object.keys(g.getNode('Foo').metadata).length).to.equal(0);
});
return it('redoing set node metadata', function() {
j.redo();
return chai.expect(g.getNode('Foo').metadata["oneone"]).to.equal(11);
});
});
});
describe('Journalling of graph merges', function() {
var A, B, a, b, g, j;
A = "{\n\"properties\": { \"name\": \"Example\", \"foo\": \"Baz\", \"bar\": \"Foo\" },\n\"inports\": {\n \"in\": { \"process\": \"Foo\", \"port\": \"in\", \"metadata\": { \"x\": 5, \"y\": 100 } }\n},\n\"outports\": {\n \"out\": { \"process\": \"Bar\", \"port\": \"out\", \"metadata\": { \"x\": 500, \"y\": 505 } }\n},\n\"groups\": [\n { \"name\": \"first\", \"nodes\": [ \"Foo\" ], \"metadata\": { \"label\": \"Main\" } },\n { \"name\": \"second\", \"nodes\": [ \"Foo2\", \"Bar2\" ], \"metadata\": {} }\n],\n\"processes\": {\n \"Foo\": { \"component\": \"Bar\", \"metadata\": { \"display\": { \"x\": 100, \"y\": 200 }, \"hello\": \"World\" } },\n \"Bar\": { \"component\": \"Baz\", \"metadata\": {} },\n \"Foo2\": { \"component\": \"foo\", \"metadata\": {} },\n \"Bar2\": { \"component\": \"bar\", \"metadata\": {} }\n},\n\"connections\": [\n { \"src\": { \"process\": \"Foo\", \"port\": \"out\" }, \"tgt\": { \"process\": \"Bar\", \"port\": \"in\" }, \"metadata\": { \"route\": \"foo\", \"hello\": \"World\" } },\n { \"src\": { \"process\": \"Foo\", \"port\": \"out2\" }, \"tgt\": { \"process\": \"Bar\", \"port\": \"in2\" } },\n { \"data\": \"Hello, world!\", \"tgt\": { \"process\": \"Foo\", \"port\": \"in\" } },\n { \"data\": \"Hello, world, 2!\", \"tgt\": { \"process\": \"Foo\", \"port\": \"in2\" } },\n { \"data\": \"Cheers, world!\", \"tgt\": { \"process\": \"Foo\", \"port\": \"arr\" } }\n]\n}";
B = "{\n\"properties\": { \"name\": \"Example\", \"foo\": \"Baz\", \"bar\": \"Foo\" },\n\"inports\": {\n \"in\": { \"process\": \"Foo\", \"port\": \"in\", \"metadata\": { \"x\": 500, \"y\": 1 } }\n},\n\"outports\": {\n \"out\": { \"process\": \"Bar\", \"port\": \"out\", \"metadata\": { \"x\": 500, \"y\": 505 } }\n},\n\"groups\": [\n { \"name\": \"second\", \"nodes\": [ \"Foo\", \"Bar\" ] }\n],\n\"processes\": {\n \"Foo\": { \"component\": \"Bar\", \"metadata\": { \"display\": { \"x\": 100, \"y\": 200 }, \"hello\": \"World\" } },\n \"Bar\": { \"component\": \"Baz\", \"metadata\": {} },\n \"Bar2\": { \"component\": \"bar\", \"metadata\": {} },\n \"Bar3\": { \"component\": \"bar2\", \"metadata\": {} }\n},\n\"connections\": [\n { \"src\": { \"process\": \"Foo\", \"port\": \"out\" }, \"tgt\": { \"process\": \"Bar\", \"port\": \"in\" }, \"metadata\": { \"route\": \"foo\", \"hello\": \"World\" } },\n { \"src\": { \"process\": \"Foo2\", \"port\": \"out2\" }, \"tgt\": { \"process\": \"Bar3\", \"port\": \"in2\" } },\n { \"data\": \"Hello, world!\", \"tgt\": { \"process\": \"Foo\", \"port\": \"in\" } },\n { \"data\": \"Hello, world, 2!\", \"tgt\": { \"process\": \"Bar3\", \"port\": \"in2\" } },\n { \"data\": \"Cheers, world!\", \"tgt\": { \"process\": \"Bar2\", \"port\": \"arr\" } }\n]\n}";
a = null;
b = null;
g = null;
j = null;
return describe('G -> B', function() {
it('G starts out as A', function(done) {
return graph.loadJSON(JSON.parse(A), function(err, instance) {
if (err) {
return done(err);
}
a = instance;
return graph.loadJSON(JSON.parse(A), function(err, instance) {
if (err) {
return done(err);
}
g = instance;
chai.expect(graph.equivalent(a, g)).to.equal(true);
return done();
});
});
});
it('G and B starts out different', function(done) {
return graph.loadJSON(JSON.parse(B), function(err, instance) {
if (err) {
return done(err);
}
b = instance;
chai.expect(graph.equivalent(g, b)).to.equal(false);
return done();
});
});
it('merge should make G equivalent to B', function(done) {
j = new journal.Journal(g);
g.startTransaction('merge');
graph.mergeResolveTheirs(g, b);
g.endTransaction('merge');
chai.expect(graph.equivalent(g, b)).to.equal(true);
return done();
});
return it('undoing merge should make G equivalent to A again', function(done) {
var res;
j.undo();
res = graph.equivalent(g, a);
chai.expect(res).to.equal(true);
return done();
});
});
});