head_devtools_inspector_sidebar.js (7033B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* exported getExtensionSidebarActors, expectNoSuchActorIDs, 6 waitForObjectInspector, testSetExpressionSidebarPanel, assertTreeView, 7 assertObjectInspector, moveMouseOnObjectInspectorDOMNode, 8 moveMouseOnPanelCenter, clickOpenInspectorIcon */ 9 10 "use strict"; 11 12 const ACCORDION_LABEL_SELECTOR = ".accordion-header-label"; 13 const ACCORDION_CONTENT_SELECTOR = ".accordion-content"; 14 15 // Retrieve the array of all the objectValueGrip actors from the 16 // inspector extension sidebars state 17 // (used in browser_ext_devtools_panels_elements_sidebar.js). 18 function getExtensionSidebarActors(inspector) { 19 const state = inspector.store.getState(); 20 21 const actors = []; 22 23 for (const sidebarId of Object.keys(state.extensionsSidebar)) { 24 const sidebarState = state.extensionsSidebar[sidebarId]; 25 26 if ( 27 sidebarState.viewMode === "object-value-grip-view" && 28 sidebarState.objectValueGrip && 29 sidebarState.objectValueGrip.actor 30 ) { 31 actors.push(sidebarState.objectValueGrip.actor); 32 } 33 } 34 35 return actors; 36 } 37 38 // Test that the specified objectValueGrip actors have been released 39 // on the remote debugging server 40 // (used in browser_ext_devtools_panels_elements_sidebar.js). 41 async function expectNoSuchActorIDs(client, actors) { 42 info(`Test that all the objectValueGrip actors have been released`); 43 for (const actor of actors) { 44 await Assert.rejects( 45 client.request({ to: actor, type: "requestTypes" }), 46 err => err.message == `No such actor for ID: ${actor}` 47 ); 48 } 49 } 50 51 function waitForObjectInspector(panelDoc, waitForNodeWithType = "object") { 52 const selector = `.object-inspector .objectBox-${waitForNodeWithType}`; 53 return TestUtils.waitForCondition(() => { 54 return !!panelDoc.querySelectorAll(selector).length; 55 }, `Wait for objectInspector's node type "${waitForNodeWithType}" to be loaded`); 56 } 57 58 // Helper function used inside the sidebar.setExtensionPage test case. 59 async function testSetExtensionPageSidebarPanel(panelDoc, expectedURL) { 60 const selector = "iframe.inspector-extension-sidebar-page"; 61 const iframesCount = await TestUtils.waitForCondition(() => { 62 return panelDoc.querySelectorAll(selector).length; 63 }, "Wait for the extension page iframe"); 64 65 is( 66 iframesCount, 67 1, 68 "Got the expected number of iframes in the extension panel" 69 ); 70 71 const iframeWindow = panelDoc.querySelector(selector).contentWindow; 72 await TestUtils.waitForCondition(() => { 73 return ( 74 iframeWindow.document.readyState === "complete" && 75 iframeWindow.location.href != "about:blank" 76 ); 77 }, "Wait for the extension page iframe to complete to load"); 78 79 is( 80 iframeWindow.location.href, 81 expectedURL, 82 "Got the expected url in the extension panel iframe" 83 ); 84 } 85 86 // Helper function used inside the sidebar.setObjectValueGrip test case. 87 async function testSetExpressionSidebarPanel(panel, expected) { 88 const { nodesLength, propertiesNames, rootTitle } = expected; 89 90 await waitForObjectInspector(panel); 91 92 const objectInspectors = [...panel.querySelectorAll(".tree")]; 93 is( 94 objectInspectors.length, 95 1, 96 "There is the expected number of object inspectors" 97 ); 98 const [objectInspector] = objectInspectors; 99 100 await TestUtils.waitForCondition(() => { 101 return objectInspector.querySelectorAll(".node").length >= nodesLength; 102 }, "Wait the objectInspector to have been fully rendered"); 103 104 const oiNodes = objectInspector.querySelectorAll(".node"); 105 106 is( 107 oiNodes.length, 108 nodesLength, 109 "Got the expected number of nodes in the tree" 110 ); 111 const propertiesNodes = [ 112 ...objectInspector.querySelectorAll(".object-label"), 113 ].map(el => el.textContent); 114 is( 115 JSON.stringify(propertiesNodes), 116 JSON.stringify(propertiesNames), 117 "Got the expected property names" 118 ); 119 120 if (rootTitle) { 121 // Also check that the ObjectInspector is rendered inside 122 // an Accordion component with the expected title. 123 const accordion = panel.querySelector(".accordion"); 124 125 ok(accordion, "Got an Accordion component as expected"); 126 127 is( 128 accordion.querySelector(ACCORDION_CONTENT_SELECTOR).firstChild, 129 objectInspector, 130 "The ObjectInspector should be inside the Accordion content" 131 ); 132 133 is( 134 accordion.querySelector(ACCORDION_LABEL_SELECTOR).textContent, 135 rootTitle, 136 "The Accordion has the expected label" 137 ); 138 } else { 139 // Also check that there is no Accordion component rendered 140 // inside the sidebar panel. 141 ok( 142 !panel.querySelector(".accordion"), 143 "Got no Accordion component as expected" 144 ); 145 } 146 } 147 148 function assertTreeView(panelDoc, expectedContent) { 149 const { expectedTreeTables, expectedStringCells, expectedNumberCells } = 150 expectedContent; 151 152 if (expectedTreeTables) { 153 is( 154 panelDoc.querySelectorAll("table.treeTable").length, 155 expectedTreeTables, 156 "The panel document contains the expected number of TreeView components" 157 ); 158 } 159 160 if (expectedStringCells) { 161 is( 162 panelDoc.querySelectorAll("table.treeTable .stringCell").length, 163 expectedStringCells, 164 "The panel document contains the expected number of string cells." 165 ); 166 } 167 168 if (expectedNumberCells) { 169 is( 170 panelDoc.querySelectorAll("table.treeTable .numberCell").length, 171 expectedNumberCells, 172 "The panel document contains the expected number of number cells." 173 ); 174 } 175 } 176 177 async function assertObjectInspector(panelDoc, expectedContent) { 178 const { expectedDOMNodes, expectedOpenInspectors } = expectedContent; 179 180 // Get and verify the DOMNode and the "open inspector"" icon 181 // rendered inside the ObjectInspector. 182 const nodes = panelDoc.querySelectorAll(".objectBox-node"); 183 const nodeOpenInspectors = panelDoc.querySelectorAll( 184 ".objectBox-node .open-inspector" 185 ); 186 187 is( 188 nodes.length, 189 expectedDOMNodes, 190 "Found the expected number of ObjectInspector DOMNodes" 191 ); 192 is( 193 nodeOpenInspectors.length, 194 expectedOpenInspectors, 195 "Found the expected nuber of open-inspector icons inside the ObjectInspector" 196 ); 197 } 198 199 function moveMouseOnObjectInspectorDOMNode(panelDoc, nodeIndex = 0) { 200 const nodes = panelDoc.querySelectorAll(".objectBox-node"); 201 const node = nodes[nodeIndex]; 202 203 ok(node, "Found the ObjectInspector DOMNode"); 204 205 EventUtils.synthesizeMouseAtCenter( 206 node, 207 { type: "mousemove" }, 208 node.ownerDocument.defaultView 209 ); 210 } 211 212 function moveMouseOnPanelCenter(panelDoc) { 213 EventUtils.synthesizeMouseAtCenter( 214 panelDoc, 215 { type: "mousemove" }, 216 panelDoc.window 217 ); 218 } 219 220 function clickOpenInspectorIcon(panelDoc, nodeIndex = 0) { 221 const nodes = panelDoc.querySelectorAll(".objectBox-node .open-inspector"); 222 const node = nodes[nodeIndex]; 223 224 ok(node, "Found the ObjectInspector open-inspector icon"); 225 226 node.click(); 227 }