nx/packages/eslint-plugin-nx/src/utils/graph-utils.spec.ts

195 lines
3.8 KiB
TypeScript

import type { ProjectGraph } from '@nrwl/devkit';
import { checkCircularPath } from './graph-utils';
describe('should find the path between nodes', () => {
it('should return empty path when when there are no connecting edges', () => {
/*
A -> B -> C - > E
*/
const graph = {
nodes: ['A', 'B'],
dependencies: {
A: ['B'],
},
};
const g = transformGraph(graph);
const path = getPath(g, { from: 'B', to: 'A' });
expect(path).toEqual([]);
});
it('should find direct path', () => {
/*
A -> B
*/
const graph = {
nodes: ['A', 'B'],
dependencies: {
A: ['B'],
},
};
const g = transformGraph(graph);
const path = getPath(g, { from: 'A', to: 'B' });
expect(path).toEqual(['A', 'B']);
});
it('should find indirect path', () => {
/*
A -> B -> E -> F
\
C -> D
*/
const graph = {
nodes: ['A', 'B', 'C', 'D', 'E', 'F'],
dependencies: {
A: ['B'],
B: ['C', 'E'],
C: ['D'],
E: ['F'],
},
};
const g = transformGraph(graph);
const path = getPath(g, { from: 'A', to: 'F' });
expect(path).toEqual(['A', 'B', 'E', 'F']);
});
it('should find indirect path in a graph that has a simple cycle', () => {
/*
A -> B -> C -> F
\ /
E <-- D
*/
const graph = {
nodes: ['A', 'B', 'C', 'D', 'E', 'F'],
dependencies: {
A: ['B'],
B: ['C'],
C: ['D', 'F'],
D: ['E'],
E: ['A'],
},
};
const g = transformGraph(graph);
const path = getPath(g, { from: 'A', to: 'F' });
expect(path).toEqual(['A', 'B', 'C', 'F']);
});
it('should find indirect path in a graph that has a cycle', () => {
/*
B <- A -> D -> E
\ /^
C
*/
const graph = {
nodes: ['A', 'B', 'C', 'D', 'E'],
dependencies: {
A: ['B', 'D'],
B: ['C'],
C: ['A'],
D: ['E'],
},
};
const g = transformGraph(graph);
const path = getPath(g, { from: 'A', to: 'E' });
expect(path).toEqual(['A', 'D', 'E']);
});
it('should find indirect path in a graph with inner and outer cycles', () => {
/*
A --> B
/^ \ /^ \
C D E F
^\ / ^\ /
G <-- H
*/
const graph = {
nodes: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'],
dependencies: {
A: ['B', 'D'],
B: ['F'],
C: ['A'],
D: ['G'],
E: ['B'],
F: ['H'],
G: ['C'],
H: ['G', 'E'],
},
};
const g = transformGraph(graph);
const path1 = getPath(g, { from: 'A', to: 'H' });
expect(path1).toEqual(['A', 'B', 'F', 'H']);
const path2 = getPath(g, { from: 'A', to: 'G' });
expect(path2).toEqual(['A', 'D', 'G']);
const path3 = getPath(g, { from: 'B', to: 'D' });
expect(path3).toEqual(['B', 'F', 'H', 'G', 'C', 'A', 'D']);
});
});
interface SimplifiedGraph {
nodes: Array<string>;
dependencies: Record<string, Array<string>>;
}
function transformGraph(graph: SimplifiedGraph): ProjectGraph {
const nodes = graph.nodes.reduce((acc, name) => {
return {
...acc,
[name]: { name, type: 'app' },
};
}, {});
const dependencies = Object.keys(graph.dependencies).reduce((acc, dep) => {
const targets = graph.dependencies[dep].reduce((acc, name) => {
return [...acc, { source: dep, target: name }];
}, []);
return {
...acc,
[dep]: targets,
};
}, {});
return {
nodes,
dependencies,
};
}
function getPath(graph: ProjectGraph, node: { from: string; to: string }) {
const src = graph.nodes[node.to];
const dest = graph.nodes[node.from];
const path = checkCircularPath(graph, src, dest);
return path.map((n) => n.name);
}