| b69ab31 | | | 1 | /** |
| b69ab31 | | | 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| b69ab31 | | | 3 | * |
| b69ab31 | | | 4 | * This source code is licensed under the MIT license found in the |
| b69ab31 | | | 5 | * LICENSE file in the root directory of this source tree. |
| b69ab31 | | | 6 | */ |
| b69ab31 | | | 7 | |
| b69ab31 | | | 8 | import type {UseUncommittedSelection} from '../partialSelection'; |
| b69ab31 | | | 9 | import type {PathTree} from '../pathTree'; |
| b69ab31 | | | 10 | |
| b69ab31 | | | 11 | import {buildPathTree, calculateTreeSelectionStates} from '../pathTree'; |
| b69ab31 | | | 12 | |
| b69ab31 | | | 13 | type FakeData = {name: string}; |
| b69ab31 | | | 14 | describe('pathTree', () => { |
| b69ab31 | | | 15 | it('constructs tree', () => { |
| b69ab31 | | | 16 | const tree = buildPathTree<FakeData>({ |
| b69ab31 | | | 17 | 'a/b/file1.txt': {name: 'file1.txt'}, |
| b69ab31 | | | 18 | 'a/b/file2.txt': {name: 'file2.txt'}, |
| b69ab31 | | | 19 | 'a/file3.txt': {name: 'file3.txt'}, |
| b69ab31 | | | 20 | 'a/d/e/f/file4.txt': {name: 'file4.txt'}, |
| b69ab31 | | | 21 | 'q/file5.txt': {name: 'file5.txt'}, |
| b69ab31 | | | 22 | 'file6.txt': {name: 'file5.txt'}, |
| b69ab31 | | | 23 | }); |
| b69ab31 | | | 24 | |
| b69ab31 | | | 25 | expect(tree).toEqual( |
| b69ab31 | | | 26 | testTree([ |
| b69ab31 | | | 27 | [ |
| b69ab31 | | | 28 | 'a', |
| b69ab31 | | | 29 | [ |
| b69ab31 | | | 30 | [ |
| b69ab31 | | | 31 | 'b', |
| b69ab31 | | | 32 | [ |
| b69ab31 | | | 33 | ['file1.txt', {name: 'file1.txt'}], |
| b69ab31 | | | 34 | ['file2.txt', {name: 'file2.txt'}], |
| b69ab31 | | | 35 | ], |
| b69ab31 | | | 36 | ], |
| b69ab31 | | | 37 | ['file3.txt', {name: 'file3.txt'}], |
| b69ab31 | | | 38 | ['d/e/f', [['file4.txt', {name: 'file4.txt'}]]], |
| b69ab31 | | | 39 | ], |
| b69ab31 | | | 40 | ], |
| b69ab31 | | | 41 | ['q', [['file5.txt', {name: 'file5.txt'}]]], |
| b69ab31 | | | 42 | ['file6.txt', {name: 'file5.txt'}], |
| b69ab31 | | | 43 | ]), |
| b69ab31 | | | 44 | ); |
| b69ab31 | | | 45 | }); |
| b69ab31 | | | 46 | |
| b69ab31 | | | 47 | it('groups out of order elements tree', () => { |
| b69ab31 | | | 48 | const tree = buildPathTree<FakeData>({ |
| b69ab31 | | | 49 | 'a/b/file1.txt': {name: 'file1.txt'}, |
| b69ab31 | | | 50 | 'file6.txt': {name: 'file5.txt'}, |
| b69ab31 | | | 51 | 'a/file3.txt': {name: 'file3.txt'}, |
| b69ab31 | | | 52 | 'a/d/e/f/file4.txt': {name: 'file4.txt'}, |
| b69ab31 | | | 53 | 'q/file5.txt': {name: 'file5.txt'}, |
| b69ab31 | | | 54 | 'a/b/file2.txt': {name: 'file2.txt'}, |
| b69ab31 | | | 55 | }); |
| b69ab31 | | | 56 | |
| b69ab31 | | | 57 | expect(tree).toEqual( |
| b69ab31 | | | 58 | testTree([ |
| b69ab31 | | | 59 | [ |
| b69ab31 | | | 60 | 'a', |
| b69ab31 | | | 61 | [ |
| b69ab31 | | | 62 | [ |
| b69ab31 | | | 63 | 'b', |
| b69ab31 | | | 64 | [ |
| b69ab31 | | | 65 | ['file1.txt', {name: 'file1.txt'}], |
| b69ab31 | | | 66 | ['file2.txt', {name: 'file2.txt'}], |
| b69ab31 | | | 67 | ], |
| b69ab31 | | | 68 | ], |
| b69ab31 | | | 69 | ['file3.txt', {name: 'file3.txt'}], |
| b69ab31 | | | 70 | ['d/e/f', [['file4.txt', {name: 'file4.txt'}]]], |
| b69ab31 | | | 71 | ], |
| b69ab31 | | | 72 | ], |
| b69ab31 | | | 73 | ['q', [['file5.txt', {name: 'file5.txt'}]]], |
| b69ab31 | | | 74 | ['file6.txt', {name: 'file5.txt'}], |
| b69ab31 | | | 75 | ]), |
| b69ab31 | | | 76 | ); |
| b69ab31 | | | 77 | }); |
| b69ab31 | | | 78 | |
| b69ab31 | | | 79 | it('groups with condensed prefixes', () => { |
| b69ab31 | | | 80 | const tree = buildPathTree<FakeData>({ |
| b69ab31 | | | 81 | 'a/b/file1.txt': {name: 'file1.txt'}, |
| b69ab31 | | | 82 | 'a/b/file2.txt': {name: 'file2.txt'}, |
| b69ab31 | | | 83 | 'a/b/c/d/e/file3.txt': {name: 'file3.txt'}, |
| b69ab31 | | | 84 | 'a/b/c/d/e/file4.txt': {name: 'file4.txt'}, |
| b69ab31 | | | 85 | }); |
| b69ab31 | | | 86 | |
| b69ab31 | | | 87 | expect(tree).toEqual( |
| b69ab31 | | | 88 | testTree([ |
| b69ab31 | | | 89 | [ |
| b69ab31 | | | 90 | 'a/b', |
| b69ab31 | | | 91 | [ |
| b69ab31 | | | 92 | ['file1.txt', {name: 'file1.txt'}], |
| b69ab31 | | | 93 | ['file2.txt', {name: 'file2.txt'}], |
| b69ab31 | | | 94 | [ |
| b69ab31 | | | 95 | 'c/d/e', |
| b69ab31 | | | 96 | [ |
| b69ab31 | | | 97 | ['file3.txt', {name: 'file3.txt'}], |
| b69ab31 | | | 98 | ['file4.txt', {name: 'file4.txt'}], |
| b69ab31 | | | 99 | ], |
| b69ab31 | | | 100 | ], |
| b69ab31 | | | 101 | ], |
| b69ab31 | | | 102 | ], |
| b69ab31 | | | 103 | ]), |
| b69ab31 | | | 104 | ); |
| b69ab31 | | | 105 | }); |
| b69ab31 | | | 106 | |
| b69ab31 | | | 107 | it('testtree util works', () => { |
| b69ab31 | | | 108 | expect( |
| b69ab31 | | | 109 | testTree([ |
| b69ab31 | | | 110 | [ |
| b69ab31 | | | 111 | 'a', |
| b69ab31 | | | 112 | [ |
| b69ab31 | | | 113 | [ |
| b69ab31 | | | 114 | 'b', |
| b69ab31 | | | 115 | [ |
| b69ab31 | | | 116 | ['file1.txt', {name: 'file1.txt'}], |
| b69ab31 | | | 117 | ['file2.txt', {name: 'file2.txt'}], |
| b69ab31 | | | 118 | ], |
| b69ab31 | | | 119 | ], |
| b69ab31 | | | 120 | ['file3.txt', {name: 'file3.txt'}], |
| b69ab31 | | | 121 | ], |
| b69ab31 | | | 122 | ], |
| b69ab31 | | | 123 | ['file4.txt', {name: 'file4.txt'}], |
| b69ab31 | | | 124 | ]), |
| b69ab31 | | | 125 | ).toEqual( |
| b69ab31 | | | 126 | new Map<string, FakeData | Map<string, FakeData | Map<string, FakeData>>>([ |
| b69ab31 | | | 127 | [ |
| b69ab31 | | | 128 | 'a', |
| b69ab31 | | | 129 | new Map<string, FakeData | Map<string, FakeData>>([ |
| b69ab31 | | | 130 | [ |
| b69ab31 | | | 131 | 'b', |
| b69ab31 | | | 132 | new Map([ |
| b69ab31 | | | 133 | ['file1.txt', {name: 'file1.txt'}], |
| b69ab31 | | | 134 | ['file2.txt', {name: 'file2.txt'}], |
| b69ab31 | | | 135 | ]), |
| b69ab31 | | | 136 | ], |
| b69ab31 | | | 137 | ['file3.txt', {name: 'file3.txt'}], |
| b69ab31 | | | 138 | ]), |
| b69ab31 | | | 139 | ], |
| b69ab31 | | | 140 | ['file4.txt', {name: 'file4.txt'}], |
| b69ab31 | | | 141 | ]), |
| b69ab31 | | | 142 | ); |
| b69ab31 | | | 143 | }); |
| b69ab31 | | | 144 | }); |
| b69ab31 | | | 145 | |
| b69ab31 | | | 146 | type Data = Array<[string, Data | FakeData]>; |
| b69ab31 | | | 147 | // make testing slightly easier so we don't need to construct maps in expected result |
| b69ab31 | | | 148 | function testTree(data: Data): PathTree<FakeData> { |
| b69ab31 | | | 149 | return new Map( |
| b69ab31 | | | 150 | data.map(([k, v]): [string, FakeData | PathTree<FakeData>] => |
| b69ab31 | | | 151 | Array.isArray(v) ? [k, testTree(v)] : [k, v], |
| b69ab31 | | | 152 | ), |
| b69ab31 | | | 153 | ); |
| b69ab31 | | | 154 | } |
| b69ab31 | | | 155 | |
| b69ab31 | | | 156 | describe('calculateTreeSelectionStates', () => { |
| b69ab31 | | | 157 | it('computes selection states', () => { |
| b69ab31 | | | 158 | const selection = { |
| b69ab31 | | | 159 | isFullySelected: (path: string) => { |
| b69ab31 | | | 160 | switch (path) { |
| b69ab31 | | | 161 | case 'file1.txt': |
| b69ab31 | | | 162 | case 'file2.txt': |
| b69ab31 | | | 163 | case 'file3.txt': |
| b69ab31 | | | 164 | case 'file5.txt': |
| b69ab31 | | | 165 | return true; |
| b69ab31 | | | 166 | default: |
| b69ab31 | | | 167 | return false; |
| b69ab31 | | | 168 | } |
| b69ab31 | | | 169 | }, |
| b69ab31 | | | 170 | isFullyOrPartiallySelected: (path: string) => { |
| b69ab31 | | | 171 | switch (path) { |
| b69ab31 | | | 172 | case 'file1.txt': |
| b69ab31 | | | 173 | case 'file2.txt': |
| b69ab31 | | | 174 | case 'file3.txt': |
| b69ab31 | | | 175 | case 'file6.txt': |
| b69ab31 | | | 176 | return true; |
| b69ab31 | | | 177 | default: |
| b69ab31 | | | 178 | return false; |
| b69ab31 | | | 179 | } |
| b69ab31 | | | 180 | }, |
| b69ab31 | | | 181 | } as UseUncommittedSelection; |
| b69ab31 | | | 182 | const tree = buildPathTree<{path: string}>({ |
| b69ab31 | | | 183 | 'a/b/file1.txt': {path: 'file1.txt'}, // checked |
| b69ab31 | | | 184 | 'a/b/file2.txt': {path: 'file2.txt'}, // checked |
| b69ab31 | | | 185 | 'a/c/file3.txt': {path: 'file3.txt'}, // checked |
| b69ab31 | | | 186 | 'a/c/file4.txt': {path: 'file4.txt'}, // UNchecked |
| b69ab31 | | | 187 | 'a/d/file5.txt': {path: 'file5.txt'}, // checked |
| b69ab31 | | | 188 | 'a/d/file6.txt': {path: 'file6.txt'}, // partially checked |
| b69ab31 | | | 189 | 'q/file7.txt': {path: 'file7.txt'}, // UNchecked |
| b69ab31 | | | 190 | }); |
| b69ab31 | | | 191 | |
| b69ab31 | | | 192 | expect(calculateTreeSelectionStates(tree, selection)).toEqual( |
| b69ab31 | | | 193 | new Map<string, boolean | 'indeterminate'>([ |
| b69ab31 | | | 194 | ['', 'indeterminate'], |
| b69ab31 | | | 195 | ['/a', 'indeterminate'], |
| b69ab31 | | | 196 | ['/a/b', true], |
| b69ab31 | | | 197 | ['/a/c', 'indeterminate'], |
| b69ab31 | | | 198 | ['/a/d', 'indeterminate'], |
| b69ab31 | | | 199 | ['/q', false], |
| b69ab31 | | | 200 | ]), |
| b69ab31 | | | 201 | ); |
| b69ab31 | | | 202 | }); |
| b69ab31 | | | 203 | }); |