Testing Depth-First Search Algorithm Implementation in javascript-algorithms
This test suite validates the implementation of Depth-First Search (DFS) algorithm in a graph data structure. It verifies both the standard traversal behavior and customizable vertex visiting logic through comprehensive test cases.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
trekhleb/javascript-algorithms
src/algorithms/graph/depth-first-search/__test__/depthFirstSearch.test.js
import Graph from '../../../../data-structures/graph/Graph';
import GraphVertex from '../../../../data-structures/graph/GraphVertex';
import GraphEdge from '../../../../data-structures/graph/GraphEdge';
import depthFirstSearch from '../depthFirstSearch';
describe('depthFirstSearch', () => {
it('should perform DFS operation on graph', () => {
const graph = new Graph(true);
const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
const vertexC = new GraphVertex('C');
const vertexD = new GraphVertex('D');
const vertexE = new GraphVertex('E');
const vertexF = new GraphVertex('F');
const vertexG = new GraphVertex('G');
const edgeAB = new GraphEdge(vertexA, vertexB);
const edgeBC = new GraphEdge(vertexB, vertexC);
const edgeCG = new GraphEdge(vertexC, vertexG);
const edgeAD = new GraphEdge(vertexA, vertexD);
const edgeAE = new GraphEdge(vertexA, vertexE);
const edgeEF = new GraphEdge(vertexE, vertexF);
const edgeFD = new GraphEdge(vertexF, vertexD);
const edgeDG = new GraphEdge(vertexD, vertexG);
graph
.addEdge(edgeAB)
.addEdge(edgeBC)
.addEdge(edgeCG)
.addEdge(edgeAD)
.addEdge(edgeAE)
.addEdge(edgeEF)
.addEdge(edgeFD)
.addEdge(edgeDG);
expect(graph.toString()).toBe('A,B,C,G,D,E,F');
const enterVertexCallback = jest.fn();
const leaveVertexCallback = jest.fn();
// Traverse graphs without callbacks first to check default ones.
depthFirstSearch(graph, vertexA);
// Traverse graph with enterVertex and leaveVertex callbacks.
depthFirstSearch(graph, vertexA, {
enterVertex: enterVertexCallback,
leaveVertex: leaveVertexCallback,
});
expect(enterVertexCallback).toHaveBeenCalledTimes(graph.getAllVertices().length);
expect(leaveVertexCallback).toHaveBeenCalledTimes(graph.getAllVertices().length);
const enterVertexParamsMap = [
{ currentVertex: vertexA, previousVertex: null },
{ currentVertex: vertexB, previousVertex: vertexA },
{ currentVertex: vertexC, previousVertex: vertexB },
{ currentVertex: vertexG, previousVertex: vertexC },
{ currentVertex: vertexD, previousVertex: vertexA },
{ currentVertex: vertexE, previousVertex: vertexA },
{ currentVertex: vertexF, previousVertex: vertexE },
];
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
const params = enterVertexCallback.mock.calls[callIndex][0];
expect(params.currentVertex).toEqual(enterVertexParamsMap[callIndex].currentVertex);
expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex);
}
const leaveVertexParamsMap = [
{ currentVertex: vertexG, previousVertex: vertexC },
{ currentVertex: vertexC, previousVertex: vertexB },
{ currentVertex: vertexB, previousVertex: vertexA },
{ currentVertex: vertexD, previousVertex: vertexA },
{ currentVertex: vertexF, previousVertex: vertexE },
{ currentVertex: vertexE, previousVertex: vertexA },
{ currentVertex: vertexA, previousVertex: null },
];
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
const params = leaveVertexCallback.mock.calls[callIndex][0];
expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex);
expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex);
}
});
it('allow users to redefine vertex visiting logic', () => {
const graph = new Graph(true);
const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
const vertexC = new GraphVertex('C');
const vertexD = new GraphVertex('D');
const vertexE = new GraphVertex('E');
const vertexF = new GraphVertex('F');
const vertexG = new GraphVertex('G');
const edgeAB = new GraphEdge(vertexA, vertexB);
const edgeBC = new GraphEdge(vertexB, vertexC);
const edgeCG = new GraphEdge(vertexC, vertexG);
const edgeAD = new GraphEdge(vertexA, vertexD);
const edgeAE = new GraphEdge(vertexA, vertexE);
const edgeEF = new GraphEdge(vertexE, vertexF);
const edgeFD = new GraphEdge(vertexF, vertexD);
const edgeDG = new GraphEdge(vertexD, vertexG);
graph
.addEdge(edgeAB)
.addEdge(edgeBC)
.addEdge(edgeCG)
.addEdge(edgeAD)
.addEdge(edgeAE)
.addEdge(edgeEF)
.addEdge(edgeFD)
.addEdge(edgeDG);
expect(graph.toString()).toBe('A,B,C,G,D,E,F');
const enterVertexCallback = jest.fn();
const leaveVertexCallback = jest.fn();
depthFirstSearch(graph, vertexA, {
enterVertex: enterVertexCallback,
leaveVertex: leaveVertexCallback,
allowTraversal: ({ currentVertex, nextVertex }) => {
return !(currentVertex === vertexA && nextVertex === vertexB);
},
});
expect(enterVertexCallback).toHaveBeenCalledTimes(7);
expect(leaveVertexCallback).toHaveBeenCalledTimes(7);
const enterVertexParamsMap = [
{ currentVertex: vertexA, previousVertex: null },
{ currentVertex: vertexD, previousVertex: vertexA },
{ currentVertex: vertexG, previousVertex: vertexD },
{ currentVertex: vertexE, previousVertex: vertexA },
{ currentVertex: vertexF, previousVertex: vertexE },
{ currentVertex: vertexD, previousVertex: vertexF },
{ currentVertex: vertexG, previousVertex: vertexD },
];
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
const params = enterVertexCallback.mock.calls[callIndex][0];
expect(params.currentVertex).toEqual(enterVertexParamsMap[callIndex].currentVertex);
expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex);
}
const leaveVertexParamsMap = [
{ currentVertex: vertexG, previousVertex: vertexD },
{ currentVertex: vertexD, previousVertex: vertexA },
{ currentVertex: vertexG, previousVertex: vertexD },
{ currentVertex: vertexD, previousVertex: vertexF },
{ currentVertex: vertexF, previousVertex: vertexE },
{ currentVertex: vertexE, previousVertex: vertexA },
{ currentVertex: vertexA, previousVertex: null },
];
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
const params = leaveVertexCallback.mock.calls[callIndex][0];
expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex);
expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex);
}
});
});