browser_treeWidget_mouse_interaction.js (4746B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Tests that mouse interaction works fine with tree widget 7 8 const TEST_URI = 9 "data:text/html;charset=utf-8,<head>" + 10 "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" + 11 "ets.css'></head><body><div></div><span></span></body>"; 12 const { 13 TreeWidget, 14 } = require("resource://devtools/client/shared/widgets/TreeWidget.js"); 15 16 add_task(async function () { 17 await SpecialPowers.pushPrefEnv({ 18 set: [["security.allow_unsafe_parent_loads", true]], 19 }); 20 21 await addTab("about:blank"); 22 const { host, doc } = await createHost("bottom", TEST_URI); 23 24 // Creating a host is not correctly waiting when DevTools run in content frame 25 // See Bug 1571421. 26 await wait(1000); 27 28 const tree = new TreeWidget(doc.querySelector("div"), { 29 defaultType: "store", 30 }); 31 32 populateTree(tree, doc); 33 await testMouseInteraction(tree); 34 35 tree.destroy(); 36 host.destroy(); 37 gBrowser.removeCurrentTab(); 38 }); 39 40 function populateTree(tree, doc) { 41 tree.add([ 42 { 43 id: "level1", 44 label: "Level 1", 45 }, 46 { 47 id: "level2-1", 48 label: "Level 2", 49 }, 50 { 51 id: "level3-1", 52 label: "Level 3 - Child 1", 53 type: "dir", 54 }, 55 ]); 56 tree.add([ 57 "level1", 58 "level2-1", 59 { id: "level3-2", label: "Level 3 - Child 2" }, 60 ]); 61 tree.add([ 62 "level1", 63 "level2-1", 64 { id: "level3-3", label: "Level 3 - Child 3" }, 65 ]); 66 tree.add([ 67 "level1", 68 { 69 id: "level2-2", 70 label: "Level 2.1", 71 }, 72 { 73 id: "level3-1", 74 label: "Level 3.1", 75 }, 76 ]); 77 tree.add([ 78 { 79 id: "level1", 80 label: "Level 1", 81 }, 82 { 83 id: "level2", 84 label: "Level 2", 85 }, 86 { 87 id: "level3", 88 label: "Level 3", 89 type: "js", 90 }, 91 ]); 92 tree.add(["level1.1", "level2", { id: "level3", type: "url" }]); 93 94 // Adding a new non text item in the tree. 95 const node = doc.createElement("div"); 96 node.textContent = "Foo Bar"; 97 node.className = "foo bar"; 98 tree.add([ 99 { 100 id: "level1.2", 101 node, 102 attachment: { 103 foo: "bar", 104 }, 105 }, 106 ]); 107 } 108 109 // Sends a click event on the passed DOM node in an async manner 110 function click(node) { 111 const win = node.ownerDocument.defaultView; 112 executeSoon(() => EventUtils.synthesizeMouseAtCenter(node, {}, win)); 113 } 114 115 /** 116 * Tests if clicking the tree items does the expected behavior 117 */ 118 async function testMouseInteraction(tree) { 119 info("Testing mouse interaction with the tree"); 120 const waitForSelect = () => 121 new Promise(resolve => { 122 tree.once("select", (d, a) => resolve({ data: d, attachment: a })); 123 }); 124 125 ok(!tree.selectedItem, "Nothing should be selected beforehand"); 126 127 let onTreeSelect = waitForSelect(); 128 const node = tree.root.children.firstChild.firstChild; 129 info("clicking on first top level item"); 130 ok( 131 !node.classList.contains("theme-selected"), 132 "Node should not have selected class before clicking" 133 ); 134 click(node); 135 let { data, attachment } = await onTreeSelect; 136 ok( 137 node.classList.contains("theme-selected"), 138 "Node has selected class after click" 139 ); 140 is(data[0], "level1.2", "Correct tree path is emitted"); 141 ok(attachment && attachment.foo, "Correct attachment is emitted"); 142 is(attachment.foo, "bar", "Correct attachment value is emitted"); 143 144 info("clicking second top level item with children to check if it expands"); 145 const node2 = tree.root.children.firstChild.nextSibling.firstChild; 146 // node should not have selected class 147 ok( 148 !node2.classList.contains("theme-selected"), 149 "New node should not have selected class before clicking" 150 ); 151 ok( 152 !node2.hasAttribute("expanded"), 153 "New node is not expanded before clicking" 154 ); 155 onTreeSelect = waitForSelect(); 156 click(node2); 157 ({ data, attachment } = await onTreeSelect); 158 ok( 159 node2.classList.contains("theme-selected"), 160 "New node has selected class after clicking" 161 ); 162 is(data[0], "level1", "Correct tree path is emitted for new node"); 163 ok(!attachment, "null attachment should be emitted for new node"); 164 ok(node2.hasAttribute("expanded"), "New node expanded after click"); 165 166 ok( 167 !node.classList.contains("theme-selected"), 168 "Old node should not have selected class after the click on new node" 169 ); 170 171 // clicking again should just collapse 172 // this will not emit "select" event 173 const onClick = new Promise(resolve => { 174 node2.addEventListener( 175 "click", 176 () => { 177 executeSoon(() => resolve(null)); 178 }, 179 { once: true } 180 ); 181 }); 182 click(node2); 183 await onClick; 184 ok(!node2.hasAttribute("expanded"), "New node collapsed after click again"); 185 }