Merge branch 'main' into 21158-wf-page-list refs #21158
[arvados.git] / services / workbench2 / src / models / tree.test.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as Tree from './tree';
6 import { initTreeNode } from './tree';
7 import { pipe } from 'lodash/fp';
8
9 describe('Tree', () => {
10     let tree: Tree.Tree<string>;
11
12     beforeEach(() => {
13         tree = Tree.createTree();
14     });
15
16     it('sets new node', () => {
17         const newTree = Tree.setNode(initTreeNode({ id: 'Node 1', value: 'Value 1' }))(tree);
18         expect(Tree.getNode('Node 1')(newTree)).toEqual(initTreeNode({ id: 'Node 1', value: 'Value 1' }));
19     });
20
21     it('appends a subtree', () => {
22         const newTree = Tree.setNode(initTreeNode({ id: 'Node 1', value: 'Value 1' }))(tree);
23         const subtree = Tree.setNode(initTreeNode({ id: 'Node 2', value: 'Value 2' }))(Tree.createTree());
24         const mergedTree = Tree.appendSubtree('Node 1', subtree)(newTree);
25         expect(Tree.getNode('Node 1')(mergedTree)).toBeDefined();
26         expect(Tree.getNode('Node 2')(mergedTree)).toBeDefined();
27     });
28
29     it('adds new node reference to parent children', () => {
30         const newTree = pipe(
31             Tree.setNode(initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' })),
32             Tree.setNode(initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 'Value 2' })),
33         )(tree);
34
35         expect(Tree.getNode('Node 1')(newTree)).toEqual({
36             ...initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' }),
37             children: ['Node 2']
38         });
39     });
40
41     it('gets node ancestors', () => {
42         const newTree = [
43             initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' }),
44             initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
45             initTreeNode({ id: 'Node 3', parent: 'Node 2', value: 'Value 1' }),
46         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
47         expect(Tree.getNodeAncestorsIds('Node 3')(newTree)).toEqual(['Node 1', 'Node 2']);
48     });
49
50     it('gets node descendants', () => {
51         const newTree = [
52             initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' }),
53             initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
54             initTreeNode({ id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
55             initTreeNode({ id: 'Node 3', parent: 'Node 1', value: 'Value 1' }),
56             initTreeNode({ id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
57         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
58         expect(Tree.getNodeDescendantsIds('Node 1')(newTree)).toEqual(['Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
59     });
60
61     it('gets root descendants', () => {
62         const newTree = [
63             initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' }),
64             initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
65             initTreeNode({ id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
66             initTreeNode({ id: 'Node 3', parent: 'Node 1', value: 'Value 1' }),
67             initTreeNode({ id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
68         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
69         expect(Tree.getNodeDescendantsIds('')(newTree)).toEqual(['Node 1', 'Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
70     });
71
72     it('gets node children', () => {
73         const newTree = [
74             initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' }),
75             initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
76             initTreeNode({ id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
77             initTreeNode({ id: 'Node 3', parent: 'Node 1', value: 'Value 1' }),
78             initTreeNode({ id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
79         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
80         expect(Tree.getNodeChildrenIds('Node 1')(newTree)).toEqual(['Node 2', 'Node 3']);
81     });
82
83     it('gets root children', () => {
84         const newTree = [
85             initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' }),
86             initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 'Value 1' }),
87             initTreeNode({ id: 'Node 2.1', parent: 'Node 2', value: 'Value 1' }),
88             initTreeNode({ id: 'Node 3', parent: '', value: 'Value 1' }),
89             initTreeNode({ id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }),
90         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
91         expect(Tree.getNodeChildrenIds('')(newTree)).toEqual(['Node 1', 'Node 3']);
92     });
93
94     it('maps tree', () => {
95         const newTree = [
96             initTreeNode({ id: 'Node 1', parent: '', value: 'Value 1' }),
97             initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 'Value 2' }),
98         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
99         const mappedTree = Tree.mapTreeValues<string, number>(value => parseInt(value.split(' ')[1], 10))(newTree);
100         expect(Tree.getNode('Node 2')(mappedTree)).toEqual(initTreeNode({ id: 'Node 2', parent: 'Node 1', value: 2 }));
101     });
102
103     it('expands node ancestor chains', () => {
104         const newTree = [
105             initTreeNode({ id: 'Root Node 1', parent: '', value: 'Value 1' }),
106             initTreeNode({ id: 'Node 1.1', parent: 'Root Node 1', value: 'Value 1' }),
107             initTreeNode({ id: 'Node 1.1.1', parent: 'Node 1.1', value: 'Value 1' }),
108             initTreeNode({ id: 'Node 1.2', parent: 'Root Node 1', value: 'Value 1' }),
109
110             initTreeNode({ id: 'Root Node 2', parent: '', value: 'Value 1' }),
111             initTreeNode({ id: 'Node 2.1', parent: 'Root Node 2', value: 'Value 1' }),
112             initTreeNode({ id: 'Node 2.1.1', parent: 'Node 2.1', value: 'Value 1' }),
113
114             initTreeNode({ id: 'Root Node 3', parent: '', value: 'Value 1' }),
115             initTreeNode({ id: 'Node 3.1', parent: 'Root Node 3', value: 'Value 1' }),
116         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
117
118         const expandedTree = Tree.expandNodeAncestors(
119             'Node 1.1.1', // Expands 1.1 and 1
120             'Node 2.1', // Expands 2
121         )(newTree);
122
123         expect(Tree.getNode('Root Node 1')(expandedTree)?.expanded).toEqual(true);
124         expect(Tree.getNode('Node 1.1')(expandedTree)?.expanded).toEqual(true);
125         expect(Tree.getNode('Node 1.1.1')(expandedTree)?.expanded).toEqual(false);
126         expect(Tree.getNode('Node 1.2')(expandedTree)?.expanded).toEqual(false);
127         expect(Tree.getNode('Root Node 2')(expandedTree)?.expanded).toEqual(true);
128         expect(Tree.getNode('Node 2.1')(expandedTree)?.expanded).toEqual(false);
129         expect(Tree.getNode('Node 2.1.1')(expandedTree)?.expanded).toEqual(false);
130         expect(Tree.getNode('Root Node 3')(expandedTree)?.expanded).toEqual(false);
131         expect(Tree.getNode('Node 3.1')(expandedTree)?.expanded).toEqual(false);
132     });
133 });