2020-08-27 23:30:53 +00:00
|
|
|
import { expect } from 'chai';
|
|
|
|
|
2020-08-28 04:07:58 +00:00
|
|
|
import {
|
|
|
|
cleanName,
|
2020-08-29 16:05:16 +00:00
|
|
|
COLOR_CHANGE,
|
|
|
|
COLOR_LABEL,
|
2020-08-28 04:07:58 +00:00
|
|
|
dotGraph,
|
|
|
|
Edge,
|
|
|
|
edgeStyle,
|
|
|
|
EdgeType,
|
|
|
|
graphChange,
|
|
|
|
graphProject,
|
2020-08-29 16:05:16 +00:00
|
|
|
graphState,
|
2020-08-28 04:07:58 +00:00
|
|
|
labelEdges,
|
|
|
|
mergeEdges,
|
|
|
|
} from '../src/graph';
|
2020-08-27 23:30:53 +00:00
|
|
|
import { ChangeVerb } from '../src/resolve';
|
|
|
|
|
2020-08-20 22:08:57 +00:00
|
|
|
describe('graph tools', () => {
|
|
|
|
describe('label edges', () => {
|
2020-08-27 23:30:53 +00:00
|
|
|
it('should include rule labels', () => {
|
|
|
|
const edges: Array<Edge> = [];
|
|
|
|
|
|
|
|
labelEdges({
|
|
|
|
adds: [{
|
|
|
|
name: 'foo',
|
|
|
|
}],
|
|
|
|
name: 'source',
|
|
|
|
priority: 1,
|
|
|
|
removes: [{
|
|
|
|
name: 'bar',
|
|
|
|
}],
|
|
|
|
requires: [{
|
|
|
|
name: 'bin',
|
|
|
|
}],
|
|
|
|
}, edges);
|
|
|
|
|
|
|
|
const EXPECTED_EDGES = 3;
|
|
|
|
expect(edges).to.be.an.instanceOf(Array).and.to.have.lengthOf(EXPECTED_EDGES);
|
|
|
|
expect(edges).to.deep.include({
|
|
|
|
source: 'source',
|
|
|
|
target: 'foo',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
});
|
|
|
|
expect(edges).to.deep.include({
|
|
|
|
source: 'source',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.REMOVED,
|
|
|
|
});
|
|
|
|
expect(edges).to.deep.include({
|
|
|
|
source: 'source',
|
|
|
|
target: 'bin',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.REQUIRED,
|
|
|
|
});
|
|
|
|
});
|
2020-08-20 22:08:57 +00:00
|
|
|
});
|
|
|
|
|
2020-08-30 22:33:10 +00:00
|
|
|
describe('graph project', () => {
|
2020-08-27 23:30:53 +00:00
|
|
|
it('should create nodes for each label', () => {
|
|
|
|
const graph = graphProject({
|
|
|
|
flags: [{
|
|
|
|
adds: [],
|
|
|
|
color: 'aabbcc',
|
|
|
|
name: 'foo',
|
|
|
|
priority: 0,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
}],
|
|
|
|
name: '',
|
|
|
|
states: [],
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(graph.nodes).to.be.an.instanceOf(Array);
|
|
|
|
expect(graph.nodes).to.deep.include({
|
|
|
|
color: 'aabbcc',
|
|
|
|
name: 'foo',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-08-30 22:33:10 +00:00
|
|
|
it('should create children for each state', () => {
|
|
|
|
const graph = graphProject({
|
|
|
|
flags: [],
|
|
|
|
name: '',
|
|
|
|
states: [{
|
|
|
|
adds: [],
|
|
|
|
divider: '/',
|
|
|
|
name: 'foo',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
values: [],
|
|
|
|
}, {
|
|
|
|
adds: [],
|
|
|
|
divider: '/',
|
|
|
|
name: 'bar',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
values: [],
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
|
|
|
|
const EXPECTED_CHILDREN = 2;
|
|
|
|
expect(graph.children).to.have.lengthOf(EXPECTED_CHILDREN);
|
|
|
|
});
|
|
|
|
|
2020-08-20 22:08:57 +00:00
|
|
|
it('should create edges for each change rule');
|
|
|
|
it('should create edges for each state change');
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('clean name', () => {
|
2020-08-27 23:30:53 +00:00
|
|
|
it('should remove special characters', () => {
|
|
|
|
expect(cleanName('$foo')).to.equal('foo');
|
|
|
|
expect(cleanName('foo$')).to.equal('foo');
|
|
|
|
expect(cleanName('foo$bar')).to.equal('foo_bar');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should remove duplicate escape characters', () => {
|
|
|
|
expect(cleanName('foo$$bar')).to.equal('foo_bar');
|
|
|
|
expect(cleanName('foo__bar')).to.equal('foo_bar');
|
|
|
|
});
|
2020-08-20 22:08:57 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('edge style', () => {
|
2020-08-27 23:30:53 +00:00
|
|
|
it('should color edges', () => {
|
2020-08-31 03:41:48 +00:00
|
|
|
for (const verb of [
|
|
|
|
ChangeVerb.BECAME,
|
|
|
|
ChangeVerb.CONFLICTED,
|
|
|
|
ChangeVerb.CREATED,
|
|
|
|
ChangeVerb.REMOVED,
|
|
|
|
ChangeVerb.REQUIRED,
|
|
|
|
]) {
|
|
|
|
expect(edgeStyle({
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.BOTH,
|
|
|
|
verb,
|
|
|
|
})).to.include('color=');
|
|
|
|
}
|
2020-08-27 23:30:53 +00:00
|
|
|
});
|
2020-08-20 22:08:57 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('node style', () => {
|
|
|
|
it('should label nodes');
|
|
|
|
it('should color nodes');
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('dot formatter', () => {
|
2020-08-27 23:30:53 +00:00
|
|
|
it('should print nodes', () => {
|
|
|
|
const graph = dotGraph({
|
|
|
|
children: [],
|
|
|
|
edges: [],
|
|
|
|
name: '',
|
|
|
|
nodes: [{
|
|
|
|
color: '',
|
|
|
|
name: 'foo',
|
|
|
|
}, {
|
|
|
|
color: '',
|
|
|
|
name: 'bar',
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(graph).to.include('foo [color=');
|
|
|
|
expect(graph).to.include('bar [color=');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should print edges', () => {
|
|
|
|
const graph = dotGraph({
|
|
|
|
children: [],
|
|
|
|
edges: [{
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.BOTH,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}],
|
|
|
|
name: '',
|
|
|
|
nodes: [],
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(graph).to.include('foo -> bar');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should cluster children', () => {
|
|
|
|
const graph = dotGraph({
|
|
|
|
children: [{
|
|
|
|
children: [],
|
|
|
|
edges: [],
|
|
|
|
name: 'foo',
|
|
|
|
nodes: [],
|
|
|
|
}],
|
|
|
|
edges: [],
|
|
|
|
name: 'bar',
|
|
|
|
nodes: [],
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(graph).to.include('subgraph cluster_foo');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should label the root digraph', () => {
|
|
|
|
const graph = dotGraph({
|
|
|
|
children: [],
|
|
|
|
edges: [],
|
|
|
|
name: 'foo',
|
|
|
|
nodes: [],
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(graph).to.include('digraph foo');
|
|
|
|
});
|
2020-08-20 22:08:57 +00:00
|
|
|
});
|
2020-08-28 04:07:58 +00:00
|
|
|
|
|
|
|
describe('merge edges helper', () => {
|
|
|
|
it('should merge opposing edges', () => {
|
|
|
|
const edges = mergeEdges([{
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}, {
|
|
|
|
source: 'bar',
|
|
|
|
target: 'foo',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}]);
|
|
|
|
|
|
|
|
expect(edges).to.have.lengthOf(1).and.to.deep.include({
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.BOTH,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should merge identical edges', () => {
|
|
|
|
const edges = mergeEdges([{
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}, {
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}]);
|
|
|
|
|
|
|
|
expect(edges).to.have.lengthOf(1).and.to.deep.include({
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should prefer both type to forward', () => {
|
|
|
|
const edges = mergeEdges([{
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}, {
|
|
|
|
source: 'bar',
|
|
|
|
target: 'foo',
|
|
|
|
type: EdgeType.BOTH,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}]);
|
|
|
|
|
|
|
|
expect(edges).to.have.lengthOf(1).and.to.deep.include({
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.BOTH,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not merge different verbs', () => {
|
|
|
|
const edges = mergeEdges([{
|
|
|
|
source: 'foo',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
}, {
|
|
|
|
source: 'bar',
|
|
|
|
target: 'foo',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.REQUIRED,
|
|
|
|
}]);
|
|
|
|
|
|
|
|
const EXPECTED_EDGES = 2;
|
|
|
|
expect(edges).to.have.lengthOf(EXPECTED_EDGES);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('graph change', () => {
|
|
|
|
it('should add state change node', () => {
|
|
|
|
const graph = {
|
|
|
|
children: [],
|
|
|
|
edges: [],
|
|
|
|
name: '',
|
|
|
|
nodes: [],
|
|
|
|
};
|
|
|
|
graphChange(graph, {
|
|
|
|
adds: [],
|
|
|
|
matches: [{
|
|
|
|
name: 'bar',
|
|
|
|
}, {
|
|
|
|
name: 'bin',
|
|
|
|
}],
|
|
|
|
removes: [],
|
|
|
|
}, 'foo', 0);
|
|
|
|
|
|
|
|
expect(graph.nodes).to.have.lengthOf(1).and.to.deep.include({
|
2020-08-29 16:05:16 +00:00
|
|
|
color: COLOR_CHANGE,
|
2020-08-28 04:07:58 +00:00
|
|
|
name: 'foo with (bar,bin)',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should add state change edge', () => {
|
|
|
|
const graph = {
|
|
|
|
children: [],
|
|
|
|
edges: [],
|
|
|
|
name: '',
|
|
|
|
nodes: [],
|
|
|
|
};
|
|
|
|
graphChange(graph, {
|
|
|
|
adds: [],
|
|
|
|
matches: [{
|
|
|
|
name: 'bar',
|
|
|
|
}, {
|
|
|
|
name: 'bin',
|
|
|
|
}],
|
|
|
|
removes: [],
|
|
|
|
}, 'foo', 0);
|
|
|
|
|
|
|
|
const EXPECTED_EDGES = 3;
|
|
|
|
expect(graph.edges).to.have.lengthOf(EXPECTED_EDGES).and.to.deep.include({
|
|
|
|
source: 'foo',
|
|
|
|
target: 'foo with (bar,bin)',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.BECAME,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should add normal edges', () => {
|
|
|
|
const graph = {
|
|
|
|
children: [],
|
|
|
|
edges: [],
|
|
|
|
name: '',
|
|
|
|
nodes: [],
|
|
|
|
};
|
|
|
|
graphChange(graph, {
|
|
|
|
adds: [{
|
|
|
|
name: 'bar',
|
|
|
|
}],
|
|
|
|
matches: [],
|
|
|
|
removes: [{
|
|
|
|
name: 'bin',
|
|
|
|
}],
|
|
|
|
}, 'foo', 0);
|
|
|
|
|
|
|
|
const EXPECTED_EDGES = 3;
|
|
|
|
expect(graph.edges).to.have.lengthOf(EXPECTED_EDGES).and.to.deep.include({
|
|
|
|
source: 'foo with ()',
|
|
|
|
target: 'bar',
|
|
|
|
type: EdgeType.FORWARD,
|
|
|
|
verb: ChangeVerb.CREATED,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2020-08-29 16:05:16 +00:00
|
|
|
|
|
|
|
describe('graph state', () => {
|
|
|
|
it('should create a node for each value', () => {
|
|
|
|
const graph = graphState({
|
|
|
|
adds: [],
|
|
|
|
divider: '/',
|
|
|
|
name: 'foo',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
values: [{
|
|
|
|
adds: [],
|
|
|
|
becomes: [],
|
|
|
|
name: 'bar',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
}, {
|
|
|
|
adds: [],
|
|
|
|
becomes: [],
|
|
|
|
name: 'bin',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
|
|
|
|
const EXPECTED_NODES = 2;
|
|
|
|
expect(graph.nodes).to.have.lengthOf(EXPECTED_NODES);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create exhaustive edges between values', () => {
|
|
|
|
const value = {
|
|
|
|
adds: [],
|
|
|
|
becomes: [],
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
};
|
|
|
|
const graph = graphState({
|
|
|
|
adds: [],
|
|
|
|
divider: '/',
|
|
|
|
name: 'foo',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
values: [{
|
|
|
|
...value,
|
|
|
|
name: 'a',
|
|
|
|
}, {
|
|
|
|
...value,
|
|
|
|
name: 'b',
|
|
|
|
}, {
|
|
|
|
...value,
|
|
|
|
name: 'c',
|
|
|
|
}, {
|
|
|
|
...value,
|
|
|
|
name: 'd',
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
|
|
|
|
const EXPECTED_EDGES = 12; // not merged, so 3 from each
|
|
|
|
expect(graph.edges).to.have.lengthOf(EXPECTED_EDGES);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should include potential state changes', () => {
|
|
|
|
const graph = graphState({
|
|
|
|
adds: [],
|
|
|
|
divider: '/',
|
|
|
|
name: 'foo',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
values: [{
|
|
|
|
adds: [],
|
|
|
|
becomes: [{
|
|
|
|
adds: [{
|
|
|
|
name: 'new',
|
|
|
|
}],
|
|
|
|
matches: [{
|
|
|
|
name: 'test',
|
|
|
|
}],
|
|
|
|
removes: [],
|
|
|
|
}],
|
|
|
|
name: 'bar',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
}, {
|
|
|
|
adds: [],
|
|
|
|
becomes: [],
|
|
|
|
name: 'bin',
|
|
|
|
priority: 1,
|
|
|
|
removes: [],
|
|
|
|
requires: [],
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(graph.nodes).to.deep.include({
|
|
|
|
color: COLOR_CHANGE,
|
|
|
|
name: 'foo/bar with (test)',
|
|
|
|
}).and.to.include({
|
|
|
|
color: COLOR_LABEL,
|
|
|
|
name: 'foo/bin',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2020-08-20 22:08:57 +00:00
|
|
|
});
|