browser/devtools/markupview/test/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 const Cu = Components.utils;
michael@0 6 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
michael@0 7 let TargetFactory = devtools.TargetFactory;
michael@0 8 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
michael@0 9 let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables");
michael@0 10 let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
michael@0 11
michael@0 12 // All test are asynchronous
michael@0 13 waitForExplicitFinish();
michael@0 14
michael@0 15 //Services.prefs.setBoolPref("devtools.dump.emit", true);
michael@0 16
michael@0 17 // Set the testing flag on gDevTools and reset it when the test ends
michael@0 18 gDevTools.testing = true;
michael@0 19 registerCleanupFunction(() => gDevTools.testing = false);
michael@0 20
michael@0 21 // Clear preferences that may be set during the course of tests.
michael@0 22 registerCleanupFunction(() => {
michael@0 23 Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
michael@0 24 Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
michael@0 25 Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
michael@0 26 Services.prefs.clearUserPref("devtools.dump.emit");
michael@0 27 Services.prefs.clearUserPref("devtools.markup.pagesize");
michael@0 28 });
michael@0 29
michael@0 30 // Auto close the toolbox and close the test tabs when the test ends
michael@0 31 registerCleanupFunction(() => {
michael@0 32 try {
michael@0 33 let target = TargetFactory.forTab(gBrowser.selectedTab);
michael@0 34 gDevTools.closeToolbox(target);
michael@0 35 } catch (ex) {
michael@0 36 dump(ex);
michael@0 37 }
michael@0 38 while (gBrowser.tabs.length > 1) {
michael@0 39 gBrowser.removeCurrentTab();
michael@0 40 }
michael@0 41 });
michael@0 42
michael@0 43 const TEST_URL_ROOT = "http://mochi.test:8888/browser/browser/devtools/markupview/test/";
michael@0 44
michael@0 45 /**
michael@0 46 * Define an async test based on a generator function
michael@0 47 */
michael@0 48 function asyncTest(generator) {
michael@0 49 return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
michael@0 50 }
michael@0 51
michael@0 52 /**
michael@0 53 * Add a new test tab in the browser and load the given url.
michael@0 54 * @param {String} url The url to be loaded in the new tab
michael@0 55 * @return a promise that resolves to the tab object when the url is loaded
michael@0 56 */
michael@0 57 function addTab(url) {
michael@0 58 info("Adding a new tab with URL: '" + url + "'");
michael@0 59 let def = promise.defer();
michael@0 60
michael@0 61 let tab = gBrowser.selectedTab = gBrowser.addTab();
michael@0 62 gBrowser.selectedBrowser.addEventListener("load", function onload() {
michael@0 63 gBrowser.selectedBrowser.removeEventListener("load", onload, true);
michael@0 64 info("URL '" + url + "' loading complete");
michael@0 65 waitForFocus(() => {
michael@0 66 def.resolve(tab);
michael@0 67 }, content);
michael@0 68 }, true);
michael@0 69 content.location = url;
michael@0 70
michael@0 71 return def.promise;
michael@0 72 }
michael@0 73
michael@0 74 /**
michael@0 75 * Some tests may need to import one or more of the test helper scripts.
michael@0 76 * A test helper script is simply a js file that contains common test code that
michael@0 77 * is either not common-enough to be in head.js, or that is located in a separate
michael@0 78 * directory.
michael@0 79 * The script will be loaded synchronously and in the test's scope.
michael@0 80 * @param {String} filePath The file path, relative to the current directory.
michael@0 81 * Examples:
michael@0 82 * - "helper_attributes_test_runner.js"
michael@0 83 * - "../../../commandline/test/helpers.js"
michael@0 84 */
michael@0 85 function loadHelperScript(filePath) {
michael@0 86 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
michael@0 87 Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
michael@0 88 }
michael@0 89
michael@0 90 /**
michael@0 91 * Reload the current page
michael@0 92 * @return a promise that resolves when the inspector has emitted the event
michael@0 93 * new-root
michael@0 94 */
michael@0 95 function reloadPage(inspector) {
michael@0 96 info("Reloading the page");
michael@0 97 let newRoot = inspector.once("new-root");
michael@0 98 content.location.reload();
michael@0 99 return newRoot;
michael@0 100 }
michael@0 101
michael@0 102 /**
michael@0 103 * Open the toolbox, with the inspector tool visible.
michael@0 104 * @return a promise that resolves when the inspector is ready
michael@0 105 */
michael@0 106 function openInspector() {
michael@0 107 info("Opening the inspector panel");
michael@0 108 let def = promise.defer();
michael@0 109
michael@0 110 let target = TargetFactory.forTab(gBrowser.selectedTab);
michael@0 111 gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
michael@0 112 info("The toolbox is open");
michael@0 113 let inspector = toolbox.getCurrentPanel();
michael@0 114 inspector.once("inspector-updated", () => {
michael@0 115 info("The inspector panel is active and ready");
michael@0 116 def.resolve({toolbox: toolbox, inspector: inspector});
michael@0 117 });
michael@0 118 }).then(null, console.error);
michael@0 119
michael@0 120 return def.promise;
michael@0 121 }
michael@0 122
michael@0 123 /**
michael@0 124 * Simple DOM node accesor function that takes either a node or a string css
michael@0 125 * selector as argument and returns the corresponding node
michael@0 126 * @param {String|DOMNode} nodeOrSelector
michael@0 127 * @return {DOMNode}
michael@0 128 */
michael@0 129 function getNode(nodeOrSelector) {
michael@0 130 info("Getting the node for '" + nodeOrSelector + "'");
michael@0 131 return typeof nodeOrSelector === "string" ?
michael@0 132 content.document.querySelector(nodeOrSelector) :
michael@0 133 nodeOrSelector;
michael@0 134 }
michael@0 135
michael@0 136 /**
michael@0 137 * Set the inspector's current selection to a node or to the first match of the
michael@0 138 * given css selector
michael@0 139 * @param {String|DOMNode} nodeOrSelector
michael@0 140 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
michael@0 141 * @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
michael@0 142 * @return a promise that resolves when the inspector is updated with the new
michael@0 143 * node
michael@0 144 */
michael@0 145 function selectNode(nodeOrSelector, inspector, reason="test") {
michael@0 146 info("Selecting the node for '" + nodeOrSelector + "'");
michael@0 147 let node = getNode(nodeOrSelector);
michael@0 148 let updated = inspector.once("inspector-updated");
michael@0 149 inspector.selection.setNode(node, reason);
michael@0 150 return updated;
michael@0 151 }
michael@0 152
michael@0 153 /**
michael@0 154 * Get the MarkupContainer object instance that corresponds to the given
michael@0 155 * HTML node
michael@0 156 * @param {DOMNode|String} nodeOrSelector The DOM node for which the
michael@0 157 * container is required
michael@0 158 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
michael@0 159 * loaded in the toolbox
michael@0 160 * @return {MarkupContainer}
michael@0 161 */
michael@0 162 function getContainerForRawNode(nodeOrSelector, {markup}) {
michael@0 163 let front = markup.walker.frontForRawNode(getNode(nodeOrSelector));
michael@0 164 let container = markup.getContainer(front);
michael@0 165 info("Markup-container object for " + nodeOrSelector + " " + container);
michael@0 166 return container;
michael@0 167 }
michael@0 168
michael@0 169 /**
michael@0 170 * Using the markupview's _waitForChildren function, wait for all queued
michael@0 171 * children updates to be handled.
michael@0 172 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
michael@0 173 * loaded in the toolbox
michael@0 174 * @return a promise that resolves when all queued children updates have been
michael@0 175 * handled
michael@0 176 */
michael@0 177 function waitForChildrenUpdated({markup}) {
michael@0 178 info("Waiting for queued children updates to be handled");
michael@0 179 let def = promise.defer();
michael@0 180 markup._waitForChildren().then(() => {
michael@0 181 executeSoon(def.resolve);
michael@0 182 });
michael@0 183 return def.promise;
michael@0 184 }
michael@0 185
michael@0 186 /**
michael@0 187 * Simulate a mouse-over on the markup-container (a line in the markup-view)
michael@0 188 * that corresponds to the node or selector passed.
michael@0 189 * @param {String|DOMNode} nodeOrSelector
michael@0 190 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
michael@0 191 * @return a promise that resolves when the container is hovered and the higlighter
michael@0 192 * is shown on the corresponding node
michael@0 193 */
michael@0 194 function hoverContainer(nodeOrSelector, inspector) {
michael@0 195 info("Hovering over the markup-container for node " + nodeOrSelector);
michael@0 196 let highlit = inspector.toolbox.once("node-highlight");
michael@0 197 let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
michael@0 198 EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousemove"},
michael@0 199 inspector.markup.doc.defaultView);
michael@0 200 return highlit;
michael@0 201 }
michael@0 202
michael@0 203 /**
michael@0 204 * Simulate a click on the markup-container (a line in the markup-view)
michael@0 205 * that corresponds to the node or selector passed.
michael@0 206 * @param {String|DOMNode} nodeOrSelector
michael@0 207 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
michael@0 208 * @return a promise that resolves when the node has been selected.
michael@0 209 */
michael@0 210 function clickContainer(nodeOrSelector, inspector) {
michael@0 211 info("Clicking on the markup-container for node " + nodeOrSelector);
michael@0 212 let updated = inspector.once("inspector-updated");
michael@0 213 let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
michael@0 214 EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousedown"},
michael@0 215 inspector.markup.doc.defaultView);
michael@0 216 EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mouseup"},
michael@0 217 inspector.markup.doc.defaultView);
michael@0 218 return updated;
michael@0 219 }
michael@0 220
michael@0 221 /**
michael@0 222 * Checks if the highlighter is visible currently
michael@0 223 * @return {Boolean}
michael@0 224 */
michael@0 225 function isHighlighterVisible() {
michael@0 226 let highlighter = gBrowser.selectedBrowser.parentNode
michael@0 227 .querySelector(".highlighter-container .box-model-root");
michael@0 228 return highlighter && !highlighter.hasAttribute("hidden");
michael@0 229 }
michael@0 230
michael@0 231 /**
michael@0 232 * Simulate the mouse leaving the markup-view area
michael@0 233 * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
michael@0 234 * @return a promise when done
michael@0 235 */
michael@0 236 function mouseLeaveMarkupView(inspector) {
michael@0 237 info("Leaving the markup-view area");
michael@0 238 let def = promise.defer();
michael@0 239
michael@0 240 // Find another element to mouseover over in order to leave the markup-view
michael@0 241 let btn = inspector.toolbox.doc.querySelector(".toolbox-dock-button");
michael@0 242
michael@0 243 EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"},
michael@0 244 inspector.toolbox.doc.defaultView);
michael@0 245 executeSoon(def.resolve);
michael@0 246
michael@0 247 return def.promise;
michael@0 248 }
michael@0 249
michael@0 250 /**
michael@0 251 * Focus a given editable element, enter edit mode, set value, and commit
michael@0 252 * @param {DOMNode} field The element that gets editable after receiving focus
michael@0 253 * and <ENTER> keypress
michael@0 254 * @param {String} value The string value to be set into the edited field
michael@0 255 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
michael@0 256 * loaded in the toolbox
michael@0 257 */
michael@0 258 function setEditableFieldValue(field, value, inspector) {
michael@0 259 field.focus();
michael@0 260 EventUtils.sendKey("return", inspector.panelWin);
michael@0 261 let input = inplaceEditor(field).input;
michael@0 262 ok(input, "Found editable field for setting value: " + value);
michael@0 263 input.value = value;
michael@0 264 EventUtils.sendKey("return", inspector.panelWin);
michael@0 265 }
michael@0 266
michael@0 267 /**
michael@0 268 * Focus the new-attribute inplace-editor field of the nodeOrSelector's markup
michael@0 269 * container, and enters the given text, then wait for it to be applied and the
michael@0 270 * for the node to mutates (when new attribute(s) is(are) created)
michael@0 271 * @param {DOMNode|String} nodeOrSelector The node or node selector to edit.
michael@0 272 * @param {String} text The new attribute text to be entered (e.g. "id='test'")
michael@0 273 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
michael@0 274 * loaded in the toolbox
michael@0 275 * @return a promise that resolves when the node has mutated
michael@0 276 */
michael@0 277 function addNewAttributes(nodeOrSelector, text, inspector) {
michael@0 278 info("Entering text '" + text + "' in node '" + nodeOrSelector + "''s new attribute field");
michael@0 279
michael@0 280 let container = getContainerForRawNode(nodeOrSelector, inspector);
michael@0 281 ok(container, "The container for '" + nodeOrSelector + "' was found");
michael@0 282
michael@0 283 info("Listening for the markupmutation event");
michael@0 284 let nodeMutated = inspector.once("markupmutation");
michael@0 285 setEditableFieldValue(container.editor.newAttr, text, inspector);
michael@0 286 return nodeMutated;
michael@0 287 }
michael@0 288
michael@0 289 /**
michael@0 290 * Checks that a node has the given attributes
michael@0 291 *
michael@0 292 * @param {DOMNode|String} nodeOrSelector The node or node selector to check.
michael@0 293 * @param {Object} attrs An object containing the attributes to check.
michael@0 294 * e.g. {id: "id1", class: "someclass"}
michael@0 295 *
michael@0 296 * Note that node.getAttribute() returns attribute values provided by the HTML
michael@0 297 * parser. The parser only provides unescaped entities so &amp; will return &.
michael@0 298 */
michael@0 299 function assertAttributes(nodeOrSelector, attrs) {
michael@0 300 let node = getNode(nodeOrSelector);
michael@0 301
michael@0 302 is(node.attributes.length, Object.keys(attrs).length,
michael@0 303 "Node has the correct number of attributes.");
michael@0 304 for (let attr in attrs) {
michael@0 305 is(node.getAttribute(attr), attrs[attr],
michael@0 306 "Node has the correct " + attr + " attribute.");
michael@0 307 }
michael@0 308 }
michael@0 309
michael@0 310 /**
michael@0 311 * Undo the last markup-view action and wait for the corresponding mutation to
michael@0 312 * occur
michael@0 313 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
michael@0 314 * loaded in the toolbox
michael@0 315 * @return a promise that resolves when the markup-mutation has been treated or
michael@0 316 * rejects if no undo action is possible
michael@0 317 */
michael@0 318 function undoChange(inspector) {
michael@0 319 let canUndo = inspector.markup.undo.canUndo();
michael@0 320 ok(canUndo, "The last change in the markup-view can be undone");
michael@0 321 if (!canUndo) {
michael@0 322 return promise.reject();
michael@0 323 }
michael@0 324
michael@0 325 let mutated = inspector.once("markupmutation");
michael@0 326 inspector.markup.undo.undo();
michael@0 327 return mutated;
michael@0 328 }
michael@0 329
michael@0 330 /**
michael@0 331 * Redo the last markup-view action and wait for the corresponding mutation to
michael@0 332 * occur
michael@0 333 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
michael@0 334 * loaded in the toolbox
michael@0 335 * @return a promise that resolves when the markup-mutation has been treated or
michael@0 336 * rejects if no redo action is possible
michael@0 337 */
michael@0 338 function redoChange(inspector) {
michael@0 339 let canRedo = inspector.markup.undo.canRedo();
michael@0 340 ok(canRedo, "The last change in the markup-view can be redone");
michael@0 341 if (!canRedo) {
michael@0 342 return promise.reject();
michael@0 343 }
michael@0 344
michael@0 345 let mutated = inspector.once("markupmutation");
michael@0 346 inspector.markup.undo.redo();
michael@0 347 return mutated;
michael@0 348 }
michael@0 349
michael@0 350 /**
michael@0 351 * Get the selector-search input box from the inspector panel
michael@0 352 * @return {DOMNode}
michael@0 353 */
michael@0 354 function getSelectorSearchBox(inspector) {
michael@0 355 return inspector.panelWin.document.getElementById("inspector-searchbox");
michael@0 356 }
michael@0 357
michael@0 358 /**
michael@0 359 * Using the inspector panel's selector search box, search for a given selector.
michael@0 360 * The selector input string will be entered in the input field and the <ENTER>
michael@0 361 * keypress will be simulated.
michael@0 362 * This function won't wait for any events and is not async. It's up to callers
michael@0 363 * to subscribe to events and react accordingly.
michael@0 364 */
michael@0 365 function searchUsingSelectorSearch(selector, inspector) {
michael@0 366 info("Entering \"" + selector + "\" into the selector-search input field");
michael@0 367 let field = getSelectorSearchBox(inspector);
michael@0 368 field.focus();
michael@0 369 field.value = selector;
michael@0 370 EventUtils.sendKey("return", inspector.panelWin);
michael@0 371 }
michael@0 372
michael@0 373 /**
michael@0 374 * This shouldn't be used in the tests, but is useful when writing new tests or
michael@0 375 * debugging existing tests in order to introduce delays in the test steps
michael@0 376 * @param {Number} ms The time to wait
michael@0 377 * @return A promise that resolves when the time is passed
michael@0 378 */
michael@0 379 function wait(ms) {
michael@0 380 let def = promise.defer();
michael@0 381 content.setTimeout(def.resolve, ms);
michael@0 382 return def.promise;
michael@0 383 }

mercurial