github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/cluster/containers/dataDistribution/tree.spec.ts (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import { assert } from "chai"; 12 13 import { 14 flatten, layoutTreeHorizontal, sumValuesUnderPaths, TreePath, LayoutCell, 15 } from "./tree"; 16 17 describe("tree", () => { 18 19 describe("layoutTreeHorizontal", () => { 20 21 it("lays out a simple tree", () => { 22 const tree = { 23 name: "a", data: "a", 24 children: [ 25 { name: "b", data: "b" }, 26 { name: "c", data: "c" }, 27 ], 28 }; 29 30 // | a | 31 // | b | c | 32 const expectedLayout: LayoutCell<string>[][] = [ 33 [ { width: 2, data: "a", path: [], isCollapsed: false, isPlaceholder: false, isLeaf: false } ], 34 [ 35 { width: 1, path: ["b"], data: "b", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 36 { width: 1, path: ["c"], data: "c", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 37 ], 38 ]; 39 assert.deepEqual(layoutTreeHorizontal(tree, []), expectedLayout); 40 }); 41 42 it("lays out a tree of inconsistent depth, inserting a placeholder", () => { 43 const tree = { 44 name: "a", data: "a", 45 children: [ 46 { name: "b", data: "b" }, 47 { 48 name: "c", data: "c", 49 children: [ 50 { name: "d", data: "d" }, 51 { name: "e", data: "e" }, 52 ], 53 }, 54 ], 55 }; 56 57 // | a | 58 // | <P> | c | 59 // | b | d | e | 60 const expectedLayout = [ 61 [ { width: 3, data: "a", path: [], isCollapsed: false, isPlaceholder: false, isLeaf: false } ], 62 [ { width: 1, path: ["b"], data: "b", isCollapsed: false, isPlaceholder: true, isLeaf: false }, 63 { width: 2, data: "c", path: ["c" ], isCollapsed: false, isPlaceholder: false, isLeaf: false }, 64 ], 65 [ { width: 1, path: ["b"], data: "b", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 66 { width: 1, path: ["c", "d"], data: "d", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 67 { width: 1, path: ["c", "e"], data: "e", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 68 ], 69 ]; 70 const actualLayout = layoutTreeHorizontal(tree, []); 71 assert.deepEqual(actualLayout, expectedLayout); 72 }); 73 74 it("inserts placeholders under a collapsed node, if other subtrees are deeper", () => { 75 const tree = { 76 name: "a", data: "a", 77 children: [ 78 { name: "b", data: "b", 79 children: [ 80 { name: "c", data: "c" }, 81 { name: "d", data: "d" }, 82 ], 83 }, 84 { 85 name: "e", data: "e", 86 children: [ 87 { name: "f", data: "f" }, 88 { name: "g", data: "g" }, 89 ], 90 }, 91 ], 92 }; 93 94 // Without anything collapsed: 95 // | a | 96 // | b | e | 97 // | c | d | f | g | 98 const expectedLayout = [ 99 [ { width: 4, data: "a", path: [], isCollapsed: false, isPlaceholder: false, isLeaf: false } ], 100 [ { width: 2, path: ["b"], data: "b", isCollapsed: false, isPlaceholder: false, isLeaf: false }, 101 { width: 2, path: ["e"], data: "e", isCollapsed: false, isPlaceholder: false, isLeaf: false }, 102 ], 103 [ { width: 1, path: ["b", "c"], data: "c", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 104 { width: 1, path: ["b", "d"], data: "d", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 105 { width: 1, path: ["e", "f"], data: "f", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 106 { width: 1, path: ["e", "g"], data: "g", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 107 ], 108 ]; 109 const actualLayout = layoutTreeHorizontal(tree, []); 110 assert.deepEqual(actualLayout, expectedLayout); 111 112 // Collapse e: 113 // | a | 114 // | b | e | 115 // | c | d | <P> | 116 const expectedLayoutCollapseE = [ 117 [ { width: 3, data: "a", path: [], isCollapsed: false, isPlaceholder: false, isLeaf: false } ], 118 [ { width: 2, path: ["b"], data: "b", isCollapsed: false, isPlaceholder: false, isLeaf: false }, 119 { width: 1, path: ["e"], data: "e", isCollapsed: true, isPlaceholder: false, isLeaf: false }, 120 ], 121 [ { width: 1, path: ["b", "c"], data: "c", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 122 { width: 1, path: ["b", "d"], data: "d", isCollapsed: false, isPlaceholder: false, isLeaf: true }, 123 { width: 1, path: ["e"], data: "e", isCollapsed: false, isPlaceholder: true, isLeaf: false }, 124 ], 125 ]; 126 const actualLayoutCollapseE = layoutTreeHorizontal(tree, [["e"]]); 127 assert.deepEqual(actualLayoutCollapseE, expectedLayoutCollapseE); 128 129 // Collapse e and b: 130 // | a | 131 // | b | e | 132 const expectedLayoutCollapseBE: LayoutCell<string>[][] = [ 133 [ { width: 2, data: "a", path: [], isCollapsed: false, isPlaceholder: false, isLeaf: false } ], 134 [ { width: 1, path: ["b"], data: "b", isCollapsed: true, isPlaceholder: false, isLeaf: false }, 135 { width: 1, path: ["e"], data: "e", isCollapsed: true, isPlaceholder: false, isLeaf: false }, 136 ], 137 ]; 138 const actualLayoutCollapseBE = layoutTreeHorizontal(tree, [["b"], ["e"]]); 139 assert.deepEqual(actualLayoutCollapseBE, expectedLayoutCollapseBE); 140 141 }); 142 143 }); 144 145 describe("flatten", () => { 146 147 const tree = { 148 name: "a", data: "a", 149 children: [ 150 { name: "b", data: "b", 151 children: [ 152 { name: "c", data: "c" }, 153 { name: "d", data: "d" }, 154 ], 155 }, 156 { 157 name: "e", data: "e", 158 children: [ 159 { name: "f", data: "f" }, 160 { name: "g", data: "g" }, 161 ], 162 }, 163 ], 164 }; 165 166 describe("with includeNodes = true", () => { 167 168 it("lays out a tree with nothing collapsed", () => { 169 const actualFlattened = flatten(tree, [], true); 170 const expectedFlattened = [ 171 { depth: 0, isLeaf: false, isCollapsed: false, data: "a", path: [] }, 172 { depth: 1, isLeaf: false, isCollapsed: false, data: "b", path: ["b"] }, 173 { depth: 2, isLeaf: true, isCollapsed: false, data: "c", path: ["b", "c"] }, 174 { depth: 2, isLeaf: true, isCollapsed: false, data: "d", path: ["b", "d"] }, 175 { depth: 1, isLeaf: false, isCollapsed: false, data: "e", path: ["e"] }, 176 { depth: 2, isLeaf: true, isCollapsed: false, data: "f", path: ["e", "f"] }, 177 { depth: 2, isLeaf: true, isCollapsed: false, data: "g", path: ["e", "g"] }, 178 ]; 179 180 assert.deepEqual(actualFlattened, expectedFlattened); 181 }); 182 183 it("lays out a tree with a node collapsed", () => { 184 const actualFlattened = flatten(tree, [["b"]], true); 185 const expectedFlattened = [ 186 { depth: 0, isLeaf: false, isCollapsed: false, data: "a", path: [] }, 187 { depth: 1, isLeaf: false, isCollapsed: true, data: "b", path: ["b"] }, 188 { depth: 1, isLeaf: false, isCollapsed: false, data: "e", path: ["e"] }, 189 { depth: 2, isLeaf: true, isCollapsed: false, data: "f", path: ["e", "f"] }, 190 { depth: 2, isLeaf: true, isCollapsed: false, data: "g", path: ["e", "g"] }, 191 ]; 192 193 assert.deepEqual(actualFlattened, expectedFlattened); 194 }); 195 196 }); 197 198 describe("with includeNodes = false", () => { 199 200 it("lays out a tree with nothing collapsed", () => { 201 const actualFlattened = flatten(tree, [], false); 202 const expectedFlattened = [ 203 { depth: 2, isLeaf: true, isCollapsed: false, data: "c", path: ["b", "c"] }, 204 { depth: 2, isLeaf: true, isCollapsed: false, data: "d", path: ["b", "d"] }, 205 { depth: 2, isLeaf: true, isCollapsed: false, data: "f", path: ["e", "f"] }, 206 { depth: 2, isLeaf: true, isCollapsed: false, data: "g", path: ["e", "g"] }, 207 ]; 208 209 assert.deepEqual(actualFlattened, expectedFlattened); 210 }); 211 212 it("lays out a tree with a node collapsed", () => { 213 const actualFlattened = flatten(tree, [["b"]], false); 214 const expectedFlattened = [ 215 { depth: 1, isLeaf: false, isCollapsed: true, data: "b", path: ["b"] }, 216 { depth: 2, isLeaf: true, isCollapsed: false, data: "f", path: ["e", "f"] }, 217 { depth: 2, isLeaf: true, isCollapsed: false, data: "g", path: ["e", "g"] }, 218 ]; 219 220 assert.deepEqual(actualFlattened, expectedFlattened); 221 }); 222 223 }); 224 225 }); 226 227 describe("sumValuesUnderPaths", () => { 228 229 // | | C_1 | 230 // | | C_2 | C_3 | 231 // |-------|-----|-----| 232 // | R_a | | | 233 // | R_b | 1 | 2 | 234 // | R_c | 3 | 4 | 235 236 const rowTree = { 237 name: "a", 238 children: [ 239 { name: "b" }, 240 { name: "c" }, 241 ], 242 }; 243 const colTree = { 244 name: "1", 245 children: [ 246 { name: "2" }, 247 { name: "3" }, 248 ], 249 }; 250 // by row, then col. 251 const values: {[name: string]: {[name: string]: number}} = { 252 "b": {"2": 1, "3": 2}, 253 "c": {"2": 3, "3": 4}, 254 }; 255 function getValue(rowPath: TreePath, colPath: TreePath): number { 256 return values[rowPath[0]][colPath[0]]; 257 } 258 259 it("computes a sum for the roots of both trees", () => { 260 const actualSum = sumValuesUnderPaths(rowTree, colTree, [], [], getValue); 261 const expectedSum = 1 + 2 + 3 + 4; 262 assert.equal(actualSum, expectedSum); 263 }); 264 265 it("computes a sum for the root of one tree and the leaf of another", () => { 266 const actualSum = sumValuesUnderPaths(rowTree, colTree, ["b"], [], getValue); 267 const expectedSum = 1 + 2; 268 assert.equal(actualSum, expectedSum); 269 }); 270 271 it("computes a sum for a single cell (two leaves)", () => { 272 const actualSum = sumValuesUnderPaths(rowTree, colTree, ["b"], ["3"], getValue); 273 const expectedSum = 2; 274 assert.equal(actualSum, expectedSum); 275 }); 276 277 }); 278 279 });