browser_treeWidget_basic.js (9842B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 // Tests that the tree widget api works fine 5 "use strict"; 6 7 const TEST_URI = 8 "data:text/html;charset=utf-8,<head>" + 9 "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" + 10 "ets.css'></head><body><div></div><span></span></body>"; 11 const { 12 TreeWidget, 13 } = require("resource://devtools/client/shared/widgets/TreeWidget.js"); 14 15 add_task(async function () { 16 await SpecialPowers.pushPrefEnv({ 17 set: [["security.allow_unsafe_parent_loads", true]], 18 }); 19 await addTab("about:blank"); 20 const { host, doc } = await createHost("bottom", TEST_URI); 21 22 const tree = new TreeWidget(doc.querySelector("div"), { 23 defaultType: "store", 24 }); 25 26 populateTree(tree, doc); 27 testTreeItemInsertedCorrectly(tree, doc); 28 testAPI(tree, doc); 29 populateUnsortedTree(tree, doc); 30 testUnsortedTreeItemInsertedCorrectly(tree, doc); 31 32 tree.destroy(); 33 host.destroy(); 34 gBrowser.removeCurrentTab(); 35 }); 36 37 function populateTree(tree) { 38 tree.add([ 39 { 40 id: "level1", 41 label: "Level 1", 42 }, 43 { 44 id: "level2-1", 45 label: "Level 2", 46 }, 47 { 48 id: "level3-1", 49 label: "Level 3 - Child 1", 50 type: "dir", 51 }, 52 ]); 53 tree.add([ 54 "level1", 55 "level2-1", 56 { 57 id: "level3-2", 58 label: "Level 3 - Child 2", 59 }, 60 ]); 61 tree.add([ 62 "level1", 63 "level2-1", 64 { 65 id: "level3-3", 66 label: "Level 3 - Child 3", 67 }, 68 ]); 69 tree.add([ 70 "level1", 71 { 72 id: "level2-2", 73 label: "Level 2.1", 74 }, 75 { 76 id: "level3-1", 77 label: "Level 3.1", 78 }, 79 ]); 80 tree.add([ 81 { 82 id: "level1", 83 label: "Level 1", 84 }, 85 { 86 id: "level2", 87 label: "Level 2", 88 }, 89 { 90 id: "level3", 91 label: "Level 3", 92 type: "js", 93 }, 94 ]); 95 tree.add(["level1.1", "level2", { id: "level3", type: "url" }]); 96 } 97 98 /** 99 * Test if the nodes are inserted correctly in the tree. 100 */ 101 function testTreeItemInsertedCorrectly(tree, doc) { 102 is( 103 tree.root.children.children.length, 104 2, 105 "Number of top level elements match" 106 ); 107 is( 108 tree.root.children.firstChild.lastChild.children.length, 109 3, 110 "Number of first second level elements match" 111 ); 112 is( 113 tree.root.children.lastChild.lastChild.children.length, 114 1, 115 "Number of second second level elements match" 116 ); 117 118 ok(tree.root.items.has("level1"), "Level1 top level element exists"); 119 is( 120 tree.root.children.firstChild.dataset.id, 121 JSON.stringify(["level1"]), 122 "Data id of first top level element matches" 123 ); 124 is( 125 tree.root.children.firstChild.firstChild.textContent, 126 "Level 1", 127 "Text content of first top level element matches" 128 ); 129 130 ok(tree.root.items.has("level1.1"), "Level1.1 top level element exists"); 131 is( 132 tree.root.children.firstChild.nextSibling.dataset.id, 133 JSON.stringify(["level1.1"]), 134 "Data id of second top level element matches" 135 ); 136 is( 137 tree.root.children.firstChild.nextSibling.firstChild.textContent, 138 "level1.1", 139 "Text content of second top level element matches" 140 ); 141 142 // Adding a new non text item in the tree. 143 const node = doc.createElement("div"); 144 node.textContent = "Foo Bar"; 145 node.className = "foo bar"; 146 tree.add([ 147 { 148 id: "level1.2", 149 node, 150 attachment: { 151 foo: "bar", 152 }, 153 }, 154 ]); 155 156 is( 157 tree.root.children.children.length, 158 3, 159 "Number of top level elements match after update" 160 ); 161 ok(tree.root.items.has("level1.2"), "New level node got added"); 162 ok( 163 tree.attachments.has(JSON.stringify(["level1.2"])), 164 "Attachment is present for newly added node" 165 ); 166 // The item should be added before level1 and level 1.1 as lexical sorting 167 is( 168 tree.root.children.firstChild.dataset.id, 169 JSON.stringify(["level1.2"]), 170 "Data id of last top level element matches" 171 ); 172 is( 173 tree.root.children.firstChild.firstChild.firstChild, 174 node, 175 "Newly added node is inserted at the right location" 176 ); 177 } 178 179 /** 180 * Populate the unsorted tree. 181 */ 182 function populateUnsortedTree(tree) { 183 tree.sorted = false; 184 185 tree.add([{ id: "g-1", label: "g-1" }]); 186 tree.add(["g-1", { id: "d-2", label: "d-2.1" }]); 187 tree.add(["g-1", { id: "b-2", label: "b-2.2" }]); 188 tree.add(["g-1", { id: "a-2", label: "a-2.3" }]); 189 } 190 191 /** 192 * Test if the nodes are inserted correctly in the unsorted tree. 193 */ 194 function testUnsortedTreeItemInsertedCorrectly(tree) { 195 ok(tree.root.items.has("g-1"), "g-1 top level element exists"); 196 197 is( 198 tree.root.children.firstChild.lastChild.children.length, 199 3, 200 "Number of children for g-1 matches" 201 ); 202 is( 203 tree.root.children.firstChild.dataset.id, 204 JSON.stringify(["g-1"]), 205 "Data id of g-1 matches" 206 ); 207 is( 208 tree.root.children.firstChild.firstChild.textContent, 209 "g-1", 210 "Text content of g-1 matches" 211 ); 212 is( 213 tree.root.children.firstChild.lastChild.firstChild.dataset.id, 214 JSON.stringify(["g-1", "d-2"]), 215 "Data id of d-2 matches" 216 ); 217 is( 218 tree.root.children.firstChild.lastChild.firstChild.textContent, 219 "d-2.1", 220 "Text content of d-2 matches" 221 ); 222 is( 223 tree.root.children.firstChild.lastChild.firstChild.nextSibling.textContent, 224 "b-2.2", 225 "Text content of b-2 matches" 226 ); 227 is( 228 tree.root.children.firstChild.lastChild.lastChild.textContent, 229 "a-2.3", 230 "Text content of a-2 matches" 231 ); 232 } 233 234 /** 235 * Tests if the API exposed by TreeWidget works properly 236 */ 237 function testAPI(tree, doc) { 238 info("Testing TreeWidget API"); 239 // Check if selectItem and selectedItem setter works as expected 240 // Nothing should be selected beforehand 241 ok(!doc.querySelector(".theme-selected"), "Nothing is selected"); 242 tree.selectItem(["level1"]); 243 const node = doc.querySelector(".theme-selected"); 244 ok(!!node, "Something got selected"); 245 is( 246 node.parentNode.dataset.id, 247 JSON.stringify(["level1"]), 248 "Correct node selected" 249 ); 250 251 tree.selectItem(["level1", "level2"]); 252 const node2 = doc.querySelector(".theme-selected"); 253 ok(!!node2, "Something is still selected"); 254 isnot(node, node2, "Newly selected node is different from previous"); 255 is( 256 node2.parentNode.dataset.id, 257 JSON.stringify(["level1", "level2"]), 258 "Correct node selected" 259 ); 260 261 // test if selectedItem getter works 262 is(tree.selectedItem.length, 2, "Correct length of selected item"); 263 is(tree.selectedItem[0], "level1", "Correct selected item"); 264 is(tree.selectedItem[1], "level2", "Correct selected item"); 265 266 // test if isSelected works 267 ok(tree.isSelected(["level1", "level2"]), "isSelected works"); 268 269 tree.selectedItem = ["level1"]; 270 const node3 = doc.querySelector(".theme-selected"); 271 ok(!!node3, "Something is still selected"); 272 isnot(node2, node3, "Newly selected node is different from previous"); 273 is(node3, node, "First and third selected nodes should be same"); 274 is( 275 node3.parentNode.dataset.id, 276 JSON.stringify(["level1"]), 277 "Correct node selected" 278 ); 279 280 // test if selectedItem getter works 281 is(tree.selectedItem.length, 1, "Correct length of selected item"); 282 is(tree.selectedItem[0], "level1", "Correct selected item"); 283 284 // test if clear selection works 285 tree.clearSelection(); 286 ok( 287 !doc.querySelector(".theme-selected"), 288 "Nothing selected after clear selection call" 289 ); 290 291 // test if collapseAll/expandAll work 292 ok(!!doc.querySelectorAll("[expanded]").length, "Some nodes are expanded"); 293 tree.collapseAll(); 294 is( 295 doc.querySelectorAll("[expanded]").length, 296 0, 297 "Nothing is expanded after collapseAll call" 298 ); 299 tree.expandAll(); 300 is( 301 doc.querySelectorAll("[expanded]").length, 302 13, 303 "All tree items expanded after expandAll call" 304 ); 305 306 // test if selectNextItem and selectPreviousItem work 307 tree.selectedItem = ["level1", "level2"]; 308 ok(tree.isSelected(["level1", "level2"]), "Correct item selected"); 309 tree.selectNextItem(); 310 ok( 311 tree.isSelected(["level1", "level2", "level3"]), 312 "Correct item selected after selectNextItem call" 313 ); 314 315 tree.selectNextItem(); 316 ok( 317 tree.isSelected(["level1", "level2-1"]), 318 "Correct item selected after second selectNextItem call" 319 ); 320 321 tree.selectNextItem(); 322 ok( 323 tree.isSelected(["level1", "level2-1", "level3-1"]), 324 "Correct item selected after third selectNextItem call" 325 ); 326 327 tree.selectPreviousItem(); 328 ok( 329 tree.isSelected(["level1", "level2-1"]), 330 "Correct item selected after selectPreviousItem call" 331 ); 332 333 tree.selectPreviousItem(); 334 ok( 335 tree.isSelected(["level1", "level2", "level3"]), 336 "Correct item selected after second selectPreviousItem call" 337 ); 338 339 // test if remove works 340 ok( 341 doc.querySelector( 342 "[data-id='" + JSON.stringify(["level1", "level2", "level3"]) + "']" 343 ), 344 "level1-level2-level3 item exists before removing" 345 ); 346 tree.remove(["level1", "level2", "level3"]); 347 ok( 348 !doc.querySelector( 349 "[data-id='" + JSON.stringify(["level1", "level2", "level3"]) + "']" 350 ), 351 "level1-level2-level3 item does not exist after removing" 352 ); 353 const level2item = doc.querySelector( 354 "[data-id='" + 355 JSON.stringify(["level1", "level2"]) + 356 "'] > .tree-widget-item" 357 ); 358 ok( 359 level2item.hasAttribute("empty"), 360 "level1-level2 item is marked as empty after removing" 361 ); 362 363 tree.add([ 364 { 365 id: "level1", 366 label: "Level 1", 367 }, 368 { 369 id: "level2", 370 label: "Level 2", 371 }, 372 { 373 id: "level3", 374 label: "Level 3", 375 type: "js", 376 }, 377 ]); 378 379 // test if clearing the tree works 380 is( 381 doc.querySelectorAll("[level='1']").length, 382 3, 383 "Correct number of top level items before clearing" 384 ); 385 tree.clear(); 386 is( 387 doc.querySelectorAll("[level='1']").length, 388 0, 389 "No top level item after clearing the tree" 390 ); 391 }