import { faker } from '@faker-js/faker'; import { Tree, type TreeNode } from './tree'; function populateTree( tree: Tree, depth: number, branches: number, filterValue: string = '', currentLevel = 0, parentNodeKey: string = 'root', ) { if (currentLevel >= depth) return; Array.from({ length: branches }).forEach((_, idx) => { const key = faker.string.uuid(); const value = idx % 2 === 0 ? faker.word.noun() : `${filterValue}${faker.word.noun()}`; // Random word tree.insert(parentNodeKey, key, value as unknown as T); // Recursively populate the tree populateTree(tree, depth, branches, filterValue, currentLevel + 1, key); }); } function convertTreeToList(tree: Tree) { const nodeList: TreeNode[] = []; const traverse = (node: TreeNode) => { nodeList.push(node); node.children.forEach(traverse); }; traverse(tree.root); return nodeList; } describe('Tree tests', () => { it('Should filter the tree appropriately', () => { const filterString = 'Test-'; const tree = new Tree('root', 'rootValue'); populateTree(tree, 4, 3, filterString); const filteredTree = tree.filterTree((node) => node.includes(filterString)); expect(filteredTree).not.toBeNull(); const filteredTreeValues = convertTreeToList(filteredTree!); expect( filteredTreeValues // We only care about the leaves of the tree in this case, since for each // matching node we want to preserve its ancestry line .filter((it) => it.children.length === 0) .every((it) => it.value.includes(filterString)), ).toEqual(true); }); it('Should return null if no search matches', () => { const filterString = 'some-random-string'; const tree = new Tree('root', 'rootValue'); populateTree(tree, 4, 3); const filteredTree = tree.filterTree((node) => node.includes(filterString)); expect(filteredTree).toBeNull(); }); });