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.

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

mercurial