Bug 1471764 - Add unit tests for the toggling the flexbox and grid highlighter from the markup display badges. r=jdescottes
☠☠ backed out by bdf475b97f93 ☠ ☠
authorGabriel Luong <gabriel.luong@gmail.com>
Mon, 03 Sep 2018 13:11:24 -0400
changeset 434511 d8ff6c1c6f3a518e16ce458d97bee8e9e351c895
parent 434510 51b3ba58b19e012e11a34a2bacc033a24f0ebd97
child 434512 a3eb8e5020063cd518cb7028a0607286e01d50a0
push id34563
push userdvarga@mozilla.com
push dateMon, 03 Sep 2018 21:54:32 +0000
treeherdermozilla-central@d14aaf65a80b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1471764
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1471764 - Add unit tests for the toggling the flexbox and grid highlighter from the markup display badges. r=jdescottes
devtools/.eslintrc.mochitests.js
devtools/client/inspector/computed/computed.js
devtools/client/inspector/flexbox/flexbox.js
devtools/client/inspector/grids/grid-inspector.js
devtools/client/inspector/grids/test/.eslintrc.js
devtools/client/inspector/markup/test/.eslintrc.js
devtools/client/inspector/markup/test/browser.ini
devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js
devtools/client/inspector/markup/test/head.js
devtools/client/inspector/rules/rules.js
devtools/client/inspector/test/shared-head.js
devtools/client/shared/redux/middleware/test/.eslintrc.js
--- a/devtools/.eslintrc.mochitests.js
+++ b/devtools/.eslintrc.mochitests.js
@@ -6,16 +6,17 @@ module.exports = {
   // All globals made available in the test environment.
   "globals": {
     "DevToolsUtils": true,
     "gDevTools": true,
     "once": true,
     "synthesizeKeyFromKeyTag": true,
     "TargetFactory": true,
     "waitForTick": true,
+    "waitUntilState": true,
   },
 
   "parserOptions": {
     "ecmaFeatures": {
       "jsx": true,
     }
   },
 
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -1,20 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const promise = require("promise");
+const flags = require("devtools/shared/flags");
 const ToolDefinitions = require("devtools/client/definitions").Tools;
 const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
-const promise = require("promise");
 const OutputParser = require("devtools/client/shared/output-parser");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 const {createChild} = require("devtools/client/inspector/shared/utils");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
@@ -178,24 +179,30 @@ function CssComputedView(inspector, docu
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
   this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
   this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
   this.styleDocument.addEventListener("copy", this._onCopy);
   this.styleDocument.addEventListener("mousedown", this.focusWindow);
   this.element.addEventListener("click", this._onClick);
   this.element.addEventListener("contextmenu", this._onContextMenu);
-  this.element.addEventListener("mousemove", () => {
-    this.addHighlightersToView();
-  }, { once: true });
   this.searchField.addEventListener("input", this._onFilterStyles);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
   this.includeBrowserStylesCheckbox.addEventListener("input",
     this._onIncludeBrowserStyles);
 
+  if (flags.testing) {
+    // In tests, we start listening immediately to avoid having to simulate a mousemove.
+    this.highlighters.addToView(this);
+  } else {
+    this.element.addEventListener("mousemove", () => {
+      this.highlighters.addToView(this);
+    }, { once: true });
+  }
+
   this.searchClearButton.hidden = true;
 
   // No results text.
   this.noResults = this.styleDocument.getElementById("computed-no-results");
 
   // Refresh panel when color unit changed or pref for showing
   // original sources changes.
   this._handlePrefChange = this._handlePrefChange.bind(this);
@@ -728,24 +735,16 @@ CssComputedView.prototype = {
 
       clipboardHelper.copyString(text);
     } catch (e) {
       console.error(e);
     }
   },
 
   /**
-   * Adds the highlighters overlay to the computed view. This is called by the "mousemove"
-   * event handler and in shared-head.js when opening and selecting the computed view.
-   */
-  addHighlightersToView() {
-    this.highlighters.addToView(this);
-  },
-
-  /**
    * Destructor for CssComputedView.
    */
   destroy: function() {
     this._viewedElement = null;
     this._outputParser = null;
 
     this._prefObserver.off("devtools.defaultColorUnit", this._handlePrefChange);
     this._prefObserver.destroy();
--- a/devtools/client/inspector/flexbox/flexbox.js
+++ b/devtools/client/inspector/flexbox/flexbox.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { throttle } = require("devtools/client/inspector/shared/utils");
+const flags = require("devtools/shared/flags");
 
 const {
   clearFlexbox,
   toggleFlexItemShown,
   updateFlexbox,
   updateFlexboxColor,
   updateFlexboxHighlighted,
 } = require("./actions/flexbox");
@@ -57,20 +58,26 @@ class FlexboxInspector {
         "getCurrentFlexbox");
       this.layoutInspector = await this.walker.getLayoutInspector();
     } catch (e) {
       // These calls might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
-    this.document.addEventListener("mousemove", () => {
+    if (flags.testing) {
+      // In tests, we start listening immediately to avoid having to simulate a mousemove.
       this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterHidden);
       this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterShown);
-    }, { once: true });
+    } else {
+      this.document.addEventListener("mousemove", () => {
+        this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterHidden);
+        this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterShown);
+      }, { once: true });
+    }
 
     this.inspector.sidebar.on("select", this.onSidebarSelect);
 
     this.onSidebarSelect();
   }
 
   destroy() {
     if (this._highlighters) {
@@ -299,105 +306,83 @@ class FlexboxInspector {
    * with new flexbox data.
    *
    * @param  {FlexboxFront|Null} flexboxFront
    *         The FlexboxFront of the flex container for the current node selection.
    */
   async update(flexboxFront) {
     // Stop refreshing if the inspector or store is already destroyed or no node is
     // selected.
-    if (!this.inspector || !this.store || !this.inspector.selection.nodeFront) {
-      return;
-    }
-
-    // Fetch the current flexbox if no flexbox front was passed into this update.
-    if (!flexboxFront) {
-      try {
-        if (!this.hasGetCurrentFlexbox) {
-          return;
-        }
-
-        flexboxFront = await this.layoutInspector.getCurrentFlexbox(
-          this.inspector.selection.nodeFront);
-      } catch (e) {
-        // This call might fail if called asynchrously after the toolbox is finished
-        // closing.
-        return;
-      }
-    }
-
-    // Clear the flexbox panel if there is no flex container for the current node
-    // selection.
-    if (!flexboxFront) {
-      try {
-        this.store.dispatch(clearFlexbox());
-      } catch (e) {
-        // This call might fail if called asynchrously after the toolbox is finished
-        // closing.
-      }
+    if (!this.inspector ||
+        !this.store ||
+        !this.inspector.selection.nodeFront ||
+        !this.hasGetCurrentFlexbox) {
       return;
     }
 
-    let containerNodeFront = flexboxFront.containerNodeFront;
+    try {
+      // Fetch the current flexbox if no flexbox front was passed into this update.
+      if (!flexboxFront) {
+        flexboxFront = await this.layoutInspector.getCurrentFlexbox(
+          this.inspector.selection.nodeFront);
+      }
 
-    // If the FlexboxFront doesn't yet have access to the NodeFront for its container,
-    // then get it from the walker. This happens when the walker hasn't seen this
-    // particular DOM Node in the tree yet or when we are connected to an older server.
-    if (!containerNodeFront) {
-      try {
-        containerNodeFront = await this.walker.getNodeFromActor(flexboxFront.actorID,
-          ["containerEl"]);
-      } catch (e) {
-        // This call might fail if called asynchrously after the toolbox is finished
-        // closing.
+      // Clear the flexbox panel if there is no flex container for the current node
+      // selection.
+      if (!flexboxFront) {
+        this.store.dispatch(clearFlexbox());
         return;
       }
-    }
-
-    const highlighted = this._highlighters &&
-      containerNodeFront == this.highlighters.flexboxHighlighterShown;
 
-    // Fetch the flex items for the given flex container and the flex item NodeFronts.
-    const flexItems = [];
-    const flexItemFronts = await flexboxFront.getFlexItems();
+      // If the FlexboxFront doesn't yet have access to the NodeFront for its container,
+      // then get it from the walker. This happens when the walker hasn't seen this
+      // particular DOM Node in the tree yet or when we are connected to an older server.
+      let containerNodeFront = flexboxFront.containerNodeFront;
+      if (!containerNodeFront) {
+        containerNodeFront = await this.walker.getNodeFromActor(flexboxFront.actorID,
+          ["containerEl"]);
+      }
 
-    for (const flexItemFront of flexItemFronts) {
-      let itemNodeFront = flexItemFront.nodeFront;
+      // Fetch the flex items for the given flex container and the flex item NodeFronts.
+      const flexItems = [];
+      const flexItemFronts = await flexboxFront.getFlexItems();
 
-      if (!itemNodeFront) {
-        try {
+      for (const flexItemFront of flexItemFronts) {
+        let itemNodeFront = flexItemFront.nodeFront;
+        if (!itemNodeFront) {
           itemNodeFront = await this.walker.getNodeFromActor(flexItemFront.actorID,
             ["element"]);
-        } catch (e) {
-          // This call might fail if called asynchrously after the toolbox is finished
-          // closing.
-          return;
         }
+
+        flexItems.push({
+          actorID: flexItemFront.actorID,
+          shown: false,
+          flexItemSizing: flexItemFront.flexItemSizing,
+          nodeFront: itemNodeFront,
+          properties: flexItemFront.properties,
+        });
       }
 
-      flexItems.push({
-        actorID: flexItemFront.actorID,
-        shown: false,
-        flexItemSizing: flexItemFront.flexItemSizing,
-        nodeFront: itemNodeFront,
-        properties: flexItemFront.properties,
-      });
-    }
+      const highlighted = this._highlighters &&
+        containerNodeFront == this.highlighters.flexboxHighlighterShown;
+      const currentUrl = this.inspector.target.url;
+      // Get the hostname, if there is no hostname, fall back on protocol
+      // ex: `data:` uri, and `about:` pages
+      const hostname = parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
+      const customColors = await this.getCustomFlexboxColors();
+      const color = customColors[hostname] ? customColors[hostname] : FLEXBOX_COLOR;
 
-    const currentUrl = this.inspector.target.url;
-    // Get the hostname, if there is no hostname, fall back on protocol
-    // ex: `data:` uri, and `about:` pages
-    const hostname = parseURL(currentUrl).hostname || parseURL(currentUrl).protocol;
-    const customColors = await this.getCustomFlexboxColors();
-    const color = customColors[hostname] ? customColors[hostname] : FLEXBOX_COLOR;
-
-    this.store.dispatch(updateFlexbox({
-      actorID: flexboxFront.actorID,
-      color,
-      flexItems,
-      highlighted,
-      nodeFront: containerNodeFront,
-      properties: flexboxFront.properties,
-    }));
+      this.store.dispatch(updateFlexbox({
+        actorID: flexboxFront.actorID,
+        color,
+        flexItems,
+        highlighted,
+        nodeFront: containerNodeFront,
+        properties: flexboxFront.properties,
+      }));
+    } catch (e) {
+      // This call might fail if called asynchrously after the toolbox is finished
+      // closing.
+    }
   }
 }
 
 module.exports = FlexboxInspector;
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { throttle } = require("devtools/client/inspector/shared/utils");
+const flags = require("devtools/shared/flags");
 
 const {
   updateGridColor,
   updateGridHighlighted,
   updateGrids,
 } = require("./actions/grids");
 const {
   updateShowGridAreas,
@@ -92,20 +93,26 @@ class GridInspector {
     try {
       this.layoutInspector = await this.inspector.walker.getLayoutInspector();
     } catch (e) {
       // This call might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
-    this.document.addEventListener("mousemove", () => {
+    if (flags.testing) {
+      // In tests, we start listening immediately to avoid having to simulate a mousemove.
       this.highlighters.on("grid-highlighter-hidden", this.onHighlighterHidden);
       this.highlighters.on("grid-highlighter-shown", this.onHighlighterShown);
-    }, { once: true });
+    } else {
+      this.document.addEventListener("mousemove", () => {
+        this.highlighters.on("grid-highlighter-hidden", this.onHighlighterHidden);
+        this.highlighters.on("grid-highlighter-shown", this.onHighlighterShown);
+      }, { once: true });
+    }
 
     this.inspector.sidebar.on("select", this.onSidebarSelect);
     this.inspector.on("new-root", this.onNavigate);
 
     this.onSidebarSelect();
   }
 
   /**
--- a/devtools/client/inspector/grids/test/.eslintrc.js
+++ b/devtools/client/inspector/grids/test/.eslintrc.js
@@ -1,9 +1,6 @@
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
   "extends": "../../../../.eslintrc.mochitests.js",
-  "globals": {
-    "waitUntilState": true
-  }
 };
--- a/devtools/client/inspector/markup/test/.eslintrc.js
+++ b/devtools/client/inspector/markup/test/.eslintrc.js
@@ -1,6 +1,6 @@
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
-  "extends": "../../../../.eslintrc.mochitests.js"
+  "extends": "../../../../.eslintrc.mochitests.js",
 };
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -68,16 +68,17 @@ support-files =
   lib_react_dom_16.2.0_min.js
   lib_react_with_addons_15.3.1_min.js
   lib_react_with_addons_15.4.1.js
   react_external_listeners.js
   !/devtools/client/debugger/new/test/mochitest/helpers.js
   !/devtools/client/inspector/test/head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-head.js
+  !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_markup_accessibility_focus_blur.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_navigation.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
@@ -125,16 +126,18 @@ skip-if = true # Bug 1177550
 [browser_markup_events_react_development_15.4.1.js]
 [browser_markup_events_react_development_15.4.1_jsx.js]
 [browser_markup_events_react_production_15.3.1.js]
 [browser_markup_events_react_production_15.3.1_jsx.js]
 [browser_markup_events_react_production_16.2.0.js]
 [browser_markup_events_react_production_16.2.0_jsx.js]
 [browser_markup_events_source_map.js]
 [browser_markup_events-windowed-host.js]
+[browser_markup_flex_display_badge.js]
+[browser_markup_grid_display_badge.js]
 [browser_markup_links_01.js]
 [browser_markup_links_02.js]
 [browser_markup_links_03.js]
 [browser_markup_links_04.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_markup_links_05.js]
 [browser_markup_links_06.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the flex display badge toggles on the flexbox highlighter.
+
+const TEST_URI = `
+  <style type="text/css">
+    #flex {
+      display: flex;
+    }
+  </style>
+  <div id="flex"></div>
+`;
+
+const HIGHLIGHTER_TYPE = "FlexboxHighlighter";
+
+add_task(async function() {
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector } = await openLayoutView();
+  const { highlighters, store } = inspector;
+
+  info("Check the flex display badge is shown and not active.");
+  await selectNode("#flex", inspector);
+  const flexContainer = await getContainerForSelector("#flex", inspector);
+  const flexDisplayBadge = flexContainer.elt.querySelector(".markup-badge[data-display]");
+  ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+
+  info("Check the initial state of the flex highlighter.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No flexbox highlighter exists in the highlighters overlay.");
+  ok(!highlighters.flexboxHighlighterShown, "No flexbox highlighter is shown.");
+
+  info("Toggling ON the flexbox highlighter from the flex display badge.");
+  const onHighlighterShown = highlighters.once("flexbox-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state => state.flexbox.highlighted);
+  flexDisplayBadge.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Check the flexbox highlighter is created and flex display badge state.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "Flexbox highlighter is created in the highlighters overlay.");
+  ok(highlighters.flexboxHighlighterShown, "Flexbox highlighter is shown.");
+  ok(flexDisplayBadge.classList.contains("active"), "flex display badge is active.");
+
+  info("Toggling OFF the flexbox highlighter from the flex display badge.");
+  const onHighlighterHidden = highlighters.once("flexbox-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state => !state.flexbox.highlighted);
+  flexDisplayBadge.click();
+  await onHighlighterHidden;
+  await onCheckboxChange;
+
+  ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid display badge toggles on the grid highlighter.
+
+const TEST_URI = `
+  <style type="text/css">
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid"></div>
+`;
+
+const HIGHLIGHTER_TYPE = "CssGridHighlighter";
+
+add_task(async function() {
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector } = await openLayoutView();
+  const { highlighters, store } = inspector;
+
+  info("Check the grid display badge is shown and not active.");
+  await selectNode("#grid", inspector);
+  const gridContainer = await getContainerForSelector("#grid", inspector);
+  const gridDisplayBadge = gridContainer.elt.querySelector(".markup-badge[data-display]");
+  ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active.");
+
+  info("Check the initial state of the grid highlighter.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS grid highlighter exists in the highlighters overlay.");
+  ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter from the grid display badge.");
+  const onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length === 1 &&
+    state.grids[0].highlighted);
+  gridDisplayBadge.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Check the CSS grid highlighter is created and grid display badge state.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+  ok(gridDisplayBadge.classList.contains("active"), "grid display badge is active.");
+
+  info("Toggling OFF the CSS grid highlighter from the grid display badge.");
+  const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 1 &&
+    !state.grids[0].highlighted);
+  gridDisplayBadge.click();
+  await onHighlighterHidden;
+  await onCheckboxChange;
+
+  ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active.");
+});
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -1,21 +1,25 @@
-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* import-globals-from ../../test/head.js */
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
+// Load the shared Redux helpers into this compartment.
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-redux-head.js",
+  this);
+
 var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor");
 var clipboard = require("devtools/shared/platform/clipboard");
 
 // If a test times out we want to see the complete log and not just the last few
 // lines.
 SimpleTest.requestCompleteLog();
 
 // Toggle this pref on to see all DevTools event communication. This is hugely
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const promise = require("promise");
 const Services = require("Services");
+const flags = require("devtools/shared/flags");
 const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const OutputParser = require("devtools/client/shared/output-parser");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 const ElementStyle = require("devtools/client/inspector/rules/models/element-style");
 const Rule = require("devtools/client/inspector/rules/models/rule");
 const RuleEditor = require("devtools/client/inspector/rules/views/rule-editor");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
@@ -146,28 +147,34 @@ function CssRuleView(inspector, document
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
   this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
   this.shortcuts.on("Return", event => this._onShortcut("Return", event));
   this.shortcuts.on("Space", event => this._onShortcut("Space", event));
   this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
   this.element.addEventListener("copy", this._onCopy);
   this.element.addEventListener("contextmenu", this._onContextMenu);
-  this.element.addEventListener("mousemove", () => {
-    this.addHighlightersToView();
-  }, { once: true });
   this.addRuleButton.addEventListener("click", this._onAddRule);
   this.searchField.addEventListener("input", this._onFilterStyles);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
   this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel);
   this.classToggle.addEventListener("click", this._onToggleClassPanel);
   this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass);
   this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass);
   this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass);
 
+  if (flags.testing) {
+    // In tests, we start listening immediately to avoid having to simulate a mousemove.
+    this.highlighters.addToView(this);
+  } else {
+    this.element.addEventListener("mousemove", () => {
+      this.highlighters.addToView(this);
+    }, { once: true });
+  }
+
   this._handlePrefChange = this._handlePrefChange.bind(this);
   this._handleUAStylePrefChange = this._handleUAStylePrefChange.bind(this);
   this._handleDefaultColorUnitPrefChange =
     this._handleDefaultColorUnitPrefChange.bind(this);
 
   this._prefObserver = new PrefObserver("devtools.");
   this._prefObserver.on(PREF_UA_STYLES, this._handleUAStylePrefChange);
   this._prefObserver.on(PREF_DEFAULT_COLOR_UNIT, this._handleDefaultColorUnitPrefChange);
@@ -1632,24 +1639,16 @@ CssRuleView.prototype = {
                event.target === this.searchField &&
                this._onClearSearch()) {
       // Handle the search box's keypress event. If the escape key is pressed,
       // clear the search box field.
       event.preventDefault();
       event.stopPropagation();
     }
   },
-
-  /**
-   * Adds the highlighters overlay to the rule view. This is called by the "mousemove"
-   * event handler and in shared-head.js when opening and selecting the rule view.
-   */
-  addHighlightersToView() {
-    this.highlighters.addToView(this);
-  },
 };
 
 /**
  * Helper functions
  */
 
 /**
  * Walk up the DOM from a given node until a parent property holder is found.
--- a/devtools/client/inspector/test/shared-head.js
+++ b/devtools/client/inspector/test/shared-head.js
@@ -86,19 +86,16 @@ var openInspectorSidebarTab = async func
 function openRuleView() {
   return openInspector().then(data => {
     const view = data.inspector.getPanel("ruleview").view;
 
     // Replace the view to use a custom debounce function that can be triggered manually
     // through an additional ".flush()" property.
     view.debounce = manualDebounce();
 
-    // Adds the highlighters overlay in the rule view.
-    view.addHighlightersToView();
-
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
       testActor: data.testActor,
       view,
     };
   });
 }
@@ -108,18 +105,16 @@ function openRuleView() {
  * sidebar tab selected.
  *
  * @return a promise that resolves when the inspector is ready and the computed
  * view is visible and ready
  */
 function openComputedView() {
   return openInspectorSidebarTab("computedview").then(data => {
     const view = data.inspector.getPanel("computedview").computedView;
-    // Adds the highlighters overlay in the computed view.
-    view.addHighlightersToView();
 
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
       testActor: data.testActor,
       view,
     };
   });
@@ -160,33 +155,29 @@ function openLayoutView() {
 /**
  * Select the rule view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
  *        The opened inspector panel
  * @return {CssRuleView} the rule view
  */
 function selectRuleView(inspector) {
-  const view = inspector.getPanel("ruleview").view;
-  view.addHighlightersToView();
-  return view;
+  return inspector.getPanel("ruleview").view;
 }
 
 /**
  * Select the computed view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
  *        The opened inspector panel
  * @return {CssComputedView} the computed view
  */
 function selectComputedView(inspector) {
   inspector.sidebar.select("computedview");
-  const view = inspector.getPanel("computedview").computedView;
-  view.addHighlightersToView();
-  return view;
+  return inspector.getPanel("computedview").computedView;
 }
 
 /**
  * Select the layout view sidebar tab on an already opened inspector panel.
  *
  * @param  {InspectorPanel} inspector
  * @return {BoxModel} the box model
  */
--- a/devtools/client/shared/redux/middleware/test/.eslintrc.js
+++ b/devtools/client/shared/redux/middleware/test/.eslintrc.js
@@ -3,15 +3,14 @@
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
   "extends": "../../../../../.eslintrc.mochitests.js",
   "globals": {
     "run_test": true,
     "run_next_test": true,
     "equal": true,
     "do_print": true,
-    "waitUntilState": true
   },
   "rules": {
     // Stop giving errors for run_test
     "camelcase": "off"
   }
 };