browser/devtools/markupview/test/head.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/markupview/test/head.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,383 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +const Cu = Components.utils;
     1.9 +let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
    1.10 +let TargetFactory = devtools.TargetFactory;
    1.11 +let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
    1.12 +let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables");
    1.13 +let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
    1.14 +
    1.15 +// All test are asynchronous
    1.16 +waitForExplicitFinish();
    1.17 +
    1.18 +//Services.prefs.setBoolPref("devtools.dump.emit", true);
    1.19 +
    1.20 +// Set the testing flag on gDevTools and reset it when the test ends
    1.21 +gDevTools.testing = true;
    1.22 +registerCleanupFunction(() => gDevTools.testing = false);
    1.23 +
    1.24 +// Clear preferences that may be set during the course of tests.
    1.25 +registerCleanupFunction(() => {
    1.26 +  Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
    1.27 +  Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
    1.28 +  Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
    1.29 +  Services.prefs.clearUserPref("devtools.dump.emit");
    1.30 +  Services.prefs.clearUserPref("devtools.markup.pagesize");
    1.31 +});
    1.32 +
    1.33 +// Auto close the toolbox and close the test tabs when the test ends
    1.34 +registerCleanupFunction(() => {
    1.35 +  try {
    1.36 +    let target = TargetFactory.forTab(gBrowser.selectedTab);
    1.37 +    gDevTools.closeToolbox(target);
    1.38 +  } catch (ex) {
    1.39 +    dump(ex);
    1.40 +  }
    1.41 +  while (gBrowser.tabs.length > 1) {
    1.42 +    gBrowser.removeCurrentTab();
    1.43 +  }
    1.44 +});
    1.45 +
    1.46 +const TEST_URL_ROOT = "http://mochi.test:8888/browser/browser/devtools/markupview/test/";
    1.47 +
    1.48 +/**
    1.49 + * Define an async test based on a generator function
    1.50 + */
    1.51 +function asyncTest(generator) {
    1.52 +  return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
    1.53 +}
    1.54 +
    1.55 +/**
    1.56 + * Add a new test tab in the browser and load the given url.
    1.57 + * @param {String} url The url to be loaded in the new tab
    1.58 + * @return a promise that resolves to the tab object when the url is loaded
    1.59 + */
    1.60 +function addTab(url) {
    1.61 +  info("Adding a new tab with URL: '" + url + "'");
    1.62 +  let def = promise.defer();
    1.63 +
    1.64 +  let tab = gBrowser.selectedTab = gBrowser.addTab();
    1.65 +  gBrowser.selectedBrowser.addEventListener("load", function onload() {
    1.66 +    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
    1.67 +    info("URL '" + url + "' loading complete");
    1.68 +    waitForFocus(() => {
    1.69 +      def.resolve(tab);
    1.70 +    }, content);
    1.71 +  }, true);
    1.72 +  content.location = url;
    1.73 +
    1.74 +  return def.promise;
    1.75 +}
    1.76 +
    1.77 +/**
    1.78 + * Some tests may need to import one or more of the test helper scripts.
    1.79 + * A test helper script is simply a js file that contains common test code that
    1.80 + * is either not common-enough to be in head.js, or that is located in a separate
    1.81 + * directory.
    1.82 + * The script will be loaded synchronously and in the test's scope.
    1.83 + * @param {String} filePath The file path, relative to the current directory.
    1.84 + *                 Examples:
    1.85 + *                 - "helper_attributes_test_runner.js"
    1.86 + *                 - "../../../commandline/test/helpers.js"
    1.87 + */
    1.88 +function loadHelperScript(filePath) {
    1.89 +  let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
    1.90 +  Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
    1.91 +}
    1.92 +
    1.93 +/**
    1.94 + * Reload the current page
    1.95 + * @return a promise that resolves when the inspector has emitted the event
    1.96 + * new-root
    1.97 + */
    1.98 +function reloadPage(inspector) {
    1.99 +  info("Reloading the page");
   1.100 +  let newRoot = inspector.once("new-root");
   1.101 +  content.location.reload();
   1.102 +  return newRoot;
   1.103 +}
   1.104 +
   1.105 +/**
   1.106 + * Open the toolbox, with the inspector tool visible.
   1.107 + * @return a promise that resolves when the inspector is ready
   1.108 + */
   1.109 +function openInspector() {
   1.110 +  info("Opening the inspector panel");
   1.111 +  let def = promise.defer();
   1.112 +
   1.113 +  let target = TargetFactory.forTab(gBrowser.selectedTab);
   1.114 +  gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
   1.115 +    info("The toolbox is open");
   1.116 +    let inspector = toolbox.getCurrentPanel();
   1.117 +    inspector.once("inspector-updated", () => {
   1.118 +      info("The inspector panel is active and ready");
   1.119 +      def.resolve({toolbox: toolbox, inspector: inspector});
   1.120 +    });
   1.121 +  }).then(null, console.error);
   1.122 +
   1.123 +  return def.promise;
   1.124 +}
   1.125 +
   1.126 +/**
   1.127 + * Simple DOM node accesor function that takes either a node or a string css
   1.128 + * selector as argument and returns the corresponding node
   1.129 + * @param {String|DOMNode} nodeOrSelector
   1.130 + * @return {DOMNode}
   1.131 + */
   1.132 +function getNode(nodeOrSelector) {
   1.133 +  info("Getting the node for '" + nodeOrSelector + "'");
   1.134 +  return typeof nodeOrSelector === "string" ?
   1.135 +    content.document.querySelector(nodeOrSelector) :
   1.136 +    nodeOrSelector;
   1.137 +}
   1.138 +
   1.139 +/**
   1.140 + * Set the inspector's current selection to a node or to the first match of the
   1.141 + * given css selector
   1.142 + * @param {String|DOMNode} nodeOrSelector
   1.143 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
   1.144 + * @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
   1.145 + * @return a promise that resolves when the inspector is updated with the new
   1.146 + * node
   1.147 + */
   1.148 +function selectNode(nodeOrSelector, inspector, reason="test") {
   1.149 +  info("Selecting the node for '" + nodeOrSelector + "'");
   1.150 +  let node = getNode(nodeOrSelector);
   1.151 +  let updated = inspector.once("inspector-updated");
   1.152 +  inspector.selection.setNode(node, reason);
   1.153 +  return updated;
   1.154 +}
   1.155 +
   1.156 +/**
   1.157 + * Get the MarkupContainer object instance that corresponds to the given
   1.158 + * HTML node
   1.159 + * @param {DOMNode|String} nodeOrSelector The DOM node for which the
   1.160 + * container is required
   1.161 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   1.162 + * loaded in the toolbox
   1.163 + * @return {MarkupContainer}
   1.164 + */
   1.165 +function getContainerForRawNode(nodeOrSelector, {markup}) {
   1.166 +  let front = markup.walker.frontForRawNode(getNode(nodeOrSelector));
   1.167 +  let container = markup.getContainer(front);
   1.168 +  info("Markup-container object for " + nodeOrSelector + " " + container);
   1.169 +  return container;
   1.170 +}
   1.171 +
   1.172 +/**
   1.173 + * Using the markupview's _waitForChildren function, wait for all queued
   1.174 + * children updates to be handled.
   1.175 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   1.176 + * loaded in the toolbox
   1.177 + * @return a promise that resolves when all queued children updates have been
   1.178 + * handled
   1.179 + */
   1.180 +function waitForChildrenUpdated({markup}) {
   1.181 +  info("Waiting for queued children updates to be handled");
   1.182 +  let def = promise.defer();
   1.183 +  markup._waitForChildren().then(() => {
   1.184 +    executeSoon(def.resolve);
   1.185 +  });
   1.186 +  return def.promise;
   1.187 +}
   1.188 +
   1.189 +/**
   1.190 + * Simulate a mouse-over on the markup-container (a line in the markup-view)
   1.191 + * that corresponds to the node or selector passed.
   1.192 + * @param {String|DOMNode} nodeOrSelector
   1.193 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
   1.194 + * @return a promise that resolves when the container is hovered and the higlighter
   1.195 + * is shown on the corresponding node
   1.196 + */
   1.197 +function hoverContainer(nodeOrSelector, inspector) {
   1.198 +  info("Hovering over the markup-container for node " + nodeOrSelector);
   1.199 +  let highlit = inspector.toolbox.once("node-highlight");
   1.200 +  let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
   1.201 +  EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousemove"},
   1.202 +    inspector.markup.doc.defaultView);
   1.203 +  return highlit;
   1.204 +}
   1.205 +
   1.206 +/**
   1.207 + * Simulate a click on the markup-container (a line in the markup-view)
   1.208 + * that corresponds to the node or selector passed.
   1.209 + * @param {String|DOMNode} nodeOrSelector
   1.210 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
   1.211 + * @return a promise that resolves when the node has been selected.
   1.212 + */
   1.213 +function clickContainer(nodeOrSelector, inspector) {
   1.214 +  info("Clicking on the markup-container for node " + nodeOrSelector);
   1.215 +  let updated = inspector.once("inspector-updated");
   1.216 +  let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
   1.217 +  EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousedown"},
   1.218 +    inspector.markup.doc.defaultView);
   1.219 +  EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mouseup"},
   1.220 +    inspector.markup.doc.defaultView);
   1.221 +  return updated;
   1.222 +}
   1.223 +
   1.224 +/**
   1.225 + * Checks if the highlighter is visible currently
   1.226 + * @return {Boolean}
   1.227 + */
   1.228 +function isHighlighterVisible() {
   1.229 +  let highlighter = gBrowser.selectedBrowser.parentNode
   1.230 +                            .querySelector(".highlighter-container .box-model-root");
   1.231 +  return highlighter && !highlighter.hasAttribute("hidden");
   1.232 +}
   1.233 +
   1.234 +/**
   1.235 + * Simulate the mouse leaving the markup-view area
   1.236 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
   1.237 + * @return a promise when done
   1.238 + */
   1.239 +function mouseLeaveMarkupView(inspector) {
   1.240 +  info("Leaving the markup-view area");
   1.241 +  let def = promise.defer();
   1.242 +
   1.243 +  // Find another element to mouseover over in order to leave the markup-view
   1.244 +  let btn = inspector.toolbox.doc.querySelector(".toolbox-dock-button");
   1.245 +
   1.246 +  EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"},
   1.247 +    inspector.toolbox.doc.defaultView);
   1.248 +  executeSoon(def.resolve);
   1.249 +
   1.250 +  return def.promise;
   1.251 +}
   1.252 +
   1.253 +/**
   1.254 + * Focus a given editable element, enter edit mode, set value, and commit
   1.255 + * @param {DOMNode} field The element that gets editable after receiving focus
   1.256 + * and <ENTER> keypress
   1.257 + * @param {String} value The string value to be set into the edited field
   1.258 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   1.259 + * loaded in the toolbox
   1.260 + */
   1.261 +function setEditableFieldValue(field, value, inspector) {
   1.262 +  field.focus();
   1.263 +  EventUtils.sendKey("return", inspector.panelWin);
   1.264 +  let input = inplaceEditor(field).input;
   1.265 +  ok(input, "Found editable field for setting value: " + value);
   1.266 +  input.value = value;
   1.267 +  EventUtils.sendKey("return", inspector.panelWin);
   1.268 +}
   1.269 +
   1.270 +/**
   1.271 + * Focus the new-attribute inplace-editor field of the nodeOrSelector's markup
   1.272 + * container, and enters the given text, then wait for it to be applied and the
   1.273 + * for the node to mutates (when new attribute(s) is(are) created)
   1.274 + * @param {DOMNode|String} nodeOrSelector The node or node selector to edit.
   1.275 + * @param {String} text The new attribute text to be entered (e.g. "id='test'")
   1.276 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   1.277 + * loaded in the toolbox
   1.278 + * @return a promise that resolves when the node has mutated
   1.279 + */
   1.280 +function addNewAttributes(nodeOrSelector, text, inspector) {
   1.281 +  info("Entering text '" + text + "' in node '" + nodeOrSelector + "''s new attribute field");
   1.282 +
   1.283 +  let container = getContainerForRawNode(nodeOrSelector, inspector);
   1.284 +  ok(container, "The container for '" + nodeOrSelector + "' was found");
   1.285 +
   1.286 +  info("Listening for the markupmutation event");
   1.287 +  let nodeMutated = inspector.once("markupmutation");
   1.288 +  setEditableFieldValue(container.editor.newAttr, text, inspector);
   1.289 +  return nodeMutated;
   1.290 +}
   1.291 +
   1.292 +/**
   1.293 + * Checks that a node has the given attributes
   1.294 + *
   1.295 + * @param {DOMNode|String} nodeOrSelector The node or node selector to check.
   1.296 + * @param {Object} attrs An object containing the attributes to check.
   1.297 + *        e.g. {id: "id1", class: "someclass"}
   1.298 + *
   1.299 + * Note that node.getAttribute() returns attribute values provided by the HTML
   1.300 + * parser. The parser only provides unescaped entities so &amp; will return &.
   1.301 + */
   1.302 +function assertAttributes(nodeOrSelector, attrs) {
   1.303 +  let node = getNode(nodeOrSelector);
   1.304 +
   1.305 +  is(node.attributes.length, Object.keys(attrs).length,
   1.306 +    "Node has the correct number of attributes.");
   1.307 +  for (let attr in attrs) {
   1.308 +    is(node.getAttribute(attr), attrs[attr],
   1.309 +      "Node has the correct " + attr + " attribute.");
   1.310 +  }
   1.311 +}
   1.312 +
   1.313 +/**
   1.314 + * Undo the last markup-view action and wait for the corresponding mutation to
   1.315 + * occur
   1.316 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   1.317 + * loaded in the toolbox
   1.318 + * @return a promise that resolves when the markup-mutation has been treated or
   1.319 + * rejects if no undo action is possible
   1.320 + */
   1.321 +function undoChange(inspector) {
   1.322 +  let canUndo = inspector.markup.undo.canUndo();
   1.323 +  ok(canUndo, "The last change in the markup-view can be undone");
   1.324 +  if (!canUndo) {
   1.325 +    return promise.reject();
   1.326 +  }
   1.327 +
   1.328 +  let mutated = inspector.once("markupmutation");
   1.329 +  inspector.markup.undo.undo();
   1.330 +  return mutated;
   1.331 +}
   1.332 +
   1.333 +/**
   1.334 + * Redo the last markup-view action and wait for the corresponding mutation to
   1.335 + * occur
   1.336 + * @param {InspectorPanel} inspector The instance of InspectorPanel currently
   1.337 + * loaded in the toolbox
   1.338 + * @return a promise that resolves when the markup-mutation has been treated or
   1.339 + * rejects if no redo action is possible
   1.340 + */
   1.341 +function redoChange(inspector) {
   1.342 +  let canRedo = inspector.markup.undo.canRedo();
   1.343 +  ok(canRedo, "The last change in the markup-view can be redone");
   1.344 +  if (!canRedo) {
   1.345 +    return promise.reject();
   1.346 +  }
   1.347 +
   1.348 +  let mutated = inspector.once("markupmutation");
   1.349 +  inspector.markup.undo.redo();
   1.350 +  return mutated;
   1.351 +}
   1.352 +
   1.353 +/**
   1.354 + * Get the selector-search input box from the inspector panel
   1.355 + * @return {DOMNode}
   1.356 + */
   1.357 +function getSelectorSearchBox(inspector) {
   1.358 +  return inspector.panelWin.document.getElementById("inspector-searchbox");
   1.359 +}
   1.360 +
   1.361 +/**
   1.362 + * Using the inspector panel's selector search box, search for a given selector.
   1.363 + * The selector input string will be entered in the input field and the <ENTER>
   1.364 + * keypress will be simulated.
   1.365 + * This function won't wait for any events and is not async. It's up to callers
   1.366 + * to subscribe to events and react accordingly.
   1.367 + */
   1.368 +function searchUsingSelectorSearch(selector, inspector) {
   1.369 +  info("Entering \"" + selector + "\" into the selector-search input field");
   1.370 +  let field = getSelectorSearchBox(inspector);
   1.371 +  field.focus();
   1.372 +  field.value = selector;
   1.373 +  EventUtils.sendKey("return", inspector.panelWin);
   1.374 +}
   1.375 +
   1.376 +/**
   1.377 + * This shouldn't be used in the tests, but is useful when writing new tests or
   1.378 + * debugging existing tests in order to introduce delays in the test steps
   1.379 + * @param {Number} ms The time to wait
   1.380 + * @return A promise that resolves when the time is passed
   1.381 + */
   1.382 +function wait(ms) {
   1.383 +  let def = promise.defer();
   1.384 +  content.setTimeout(def.resolve, ms);
   1.385 +  return def.promise;
   1.386 +}

mercurial