Bug 1407561 - Lazy loading of tooltip text when user hovers the status column. r=gasolin draft
authorabhinav <abhinav.koppula@gmail.com>
Tue, 31 Oct 2017 08:27:21 +0530
changeset 692206 9a5d9b5bb0333246c18dc525255bea571d8b5016
parent 688337 d3910b7628b8066d3f30d58b17b5824b05768854
child 738703 3811f5571862d269dd24fe4c2676500245e5234e
push id87439
push userbmo:abhinav.koppula@gmail.com
push dateThu, 02 Nov 2017 18:37:52 +0000
reviewersgasolin
bugs1407561
milestone58.0a1
Bug 1407561 - Lazy loading of tooltip text when user hovers the status column. r=gasolin MozReview-Commit-ID: BXP8B10Q5LT
devtools/client/netmonitor/src/components/RequestListColumnStatus.js
devtools/client/netmonitor/test/browser_net_brotli.js
devtools/client/netmonitor/test/browser_net_cached-status.js
devtools/client/netmonitor/test/browser_net_content-type.js
devtools/client/netmonitor/test/browser_net_cyrillic-01.js
devtools/client/netmonitor/test/browser_net_cyrillic-02.js
devtools/client/netmonitor/test/browser_net_filter-01.js
devtools/client/netmonitor/test/browser_net_filter-02.js
devtools/client/netmonitor/test/browser_net_filter-flags.js
devtools/client/netmonitor/test/browser_net_json-long.js
devtools/client/netmonitor/test/browser_net_json-malformed.js
devtools/client/netmonitor/test/browser_net_json_custom_mime.js
devtools/client/netmonitor/test/browser_net_json_text_mime.js
devtools/client/netmonitor/test/browser_net_jsonp.js
devtools/client/netmonitor/test/browser_net_large-response.js
devtools/client/netmonitor/test/browser_net_post-data-01.js
devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
devtools/client/netmonitor/test/browser_net_service-worker-status.js
devtools/client/netmonitor/test/browser_net_simple-request-data.js
devtools/client/netmonitor/test/browser_net_sort-01.js
devtools/client/netmonitor/test/browser_net_sort-02.js
devtools/client/netmonitor/test/browser_net_status-codes.js
devtools/client/netmonitor/test/browser_net_streaming-response.js
--- a/devtools/client/netmonitor/src/components/RequestListColumnStatus.js
+++ b/devtools/client/netmonitor/src/components/RequestListColumnStatus.js
@@ -28,47 +28,58 @@ class RequestListColumnStatus extends Co
     };
   }
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_STATUS_PROPS, this.props.item, nextProps.item);
   }
 
   render() {
-    let { fromCache, fromServiceWorker, status, statusText } = this.props.item;
-    let code, title;
+    let { item } = this.props;
+    let { fromCache, fromServiceWorker, status, statusText } = item;
+    let code;
 
     if (status) {
       if (fromCache) {
         code = "cached";
       } else if (fromServiceWorker) {
         code = "service worker";
       } else {
         code = status;
       }
-
-      if (statusText) {
-        if (fromCache && fromServiceWorker) {
-          title = L10N.getFormatStr("netmonitor.status.tooltip.cachedworker",
-            status, statusText);
-        } else if (fromCache) {
-          title = L10N.getFormatStr("netmonitor.status.tooltip.cached",
-            status, statusText);
-        } else if (fromServiceWorker) {
-          title = L10N.getFormatStr("netmonitor.status.tooltip.worker",
-            status, statusText);
-        } else {
-          title = L10N.getFormatStr("netmonitor.status.tooltip.simple",
-            status, statusText);
-        }
-      }
     }
 
     return (
-      div({ className: "requests-list-column requests-list-status", title },
-        div({ className: "requests-list-status-icon", "data-code": code }),
+      div({
+        className: "requests-list-column requests-list-status",
+        onMouseOver: function ({ target }) {
+          if (status && statusText && !target.title) {
+            target.title = getColumnTitle(item);
+          }
+        },
+      },
+      div({ className: "requests-list-status-icon", "data-code": code }),
         div({ className: "requests-list-status-code" }, status)
       )
     );
   }
 }
 
+function getColumnTitle(item) {
+  let { fromCache, fromServiceWorker, status, statusText } = item;
+  let title;
+  if (fromCache && fromServiceWorker) {
+    title = L10N.getFormatStr("netmonitor.status.tooltip.cachedworker",
+      status, statusText);
+  } else if (fromCache) {
+    title = L10N.getFormatStr("netmonitor.status.tooltip.cached",
+      status, statusText);
+  } else if (fromServiceWorker) {
+    title = L10N.getFormatStr("netmonitor.status.tooltip.worker",
+      status, statusText);
+  } else {
+    title = L10N.getFormatStr("netmonitor.status.tooltip.simple",
+      status, statusText);
+  }
+  return title;
+}
+
 module.exports = RequestListColumnStatus;
--- a/devtools/client/netmonitor/test/browser_net_brotli.js
+++ b/devtools/client/netmonitor/test/browser_net_brotli.js
@@ -26,16 +26,21 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, BROTLI_REQUESTS);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItem = document.querySelector(".request-list-item");
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", {
       status: 200,
       statusText: "Connected",
       type: "plain",
--- a/devtools/client/netmonitor/test/browser_net_cached-status.js
+++ b/devtools/client/netmonitor/test/browser_net_cached-status.js
@@ -91,16 +91,22 @@ add_task(function* () {
   info("Performing requests #1...");
   yield performRequestsAndWait();
 
   info("Performing requests #2...");
   yield performRequestsAndWait();
 
   let index = 0;
   for (let request of REQUEST_DATA) {
+    let requestItem = document.querySelectorAll(".request-list-item")[index];
+    requestItem.scrollIntoView();
+    let requestsListStatus = requestItem.querySelector(".requests-list-status");
+    EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+    yield waitUntil(() => requestsListStatus.title);
+
     info("Verifying request #" + index);
     yield verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(index),
       request.method,
       request.uri,
       request.details
--- a/devtools/client/netmonitor/test/browser_net_content-type.js
+++ b/devtools/client/netmonitor/test/browser_net_content-type.js
@@ -22,16 +22,23 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  for (let requestItem of document.querySelectorAll(".request-list-item")) {
+    let requestsListStatus = requestItem.querySelector(".requests-list-status");
+    requestItem.scrollIntoView();
+    EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+    yield waitUntil(() => requestsListStatus.title);
+  }
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=xml",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_cyrillic-01.js
+++ b/devtools/client/netmonitor/test/browser_net_cyrillic-01.js
@@ -21,16 +21,22 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItem = document.querySelectorAll(".request-list-item")[0];
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  requestItem.scrollIntoView();
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=txt",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_cyrillic-02.js
+++ b/devtools/client/netmonitor/test/browser_net_cyrillic-02.js
@@ -17,16 +17,22 @@ add_task(function* () {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
 
+  let requestItem = document.querySelectorAll(".request-list-item")[0];
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  requestItem.scrollIntoView();
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CYRILLIC_URL,
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_filter-01.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-01.js
@@ -158,169 +158,177 @@ add_task(function* () {
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should render correctly.");
 
   // First test with single filters...
   testFilterButtons(monitor, "all");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-html-button"));
   testFilterButtons(monitor, "html");
-  testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Reset filters
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-css-button"));
   testFilterButtons(monitor, "css");
-  testContents([0, 1, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 1, 0, 0, 0, 0, 0, 0, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-js-button"));
   testFilterButtons(monitor, "js");
-  testContents([0, 0, 1, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 1, 0, 0, 0, 0, 0, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-xhr-button"));
   testFilterButtons(monitor, "xhr");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 0]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
      document.querySelector(".requests-list-filter-fonts-button"));
   testFilterButtons(monitor, "fonts");
-  testContents([0, 0, 0, 1, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 1, 0, 0, 0, 0, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-images-button"));
   testFilterButtons(monitor, "images");
-  testContents([0, 0, 0, 0, 1, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 1, 0, 0, 0, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-media-button"));
   testFilterButtons(monitor, "media");
-  testContents([0, 0, 0, 0, 0, 1, 1, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 1, 1, 0, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-flash-button"));
   testFilterButtons(monitor, "flash");
-  testContents([0, 0, 0, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-ws-button"));
   testFilterButtons(monitor, "ws");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 1]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 1]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
 
   testFilterButtons(monitor, "all");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   // Text in filter box that matches nothing should hide all.
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   setFreetextFilter("foobar");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Text in filter box that matches should filter out everything else.
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   setFreetextFilter("sample");
-  testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]);
 
   // Text in filter box that matches should filter out everything else.
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   setFreetextFilter("SAMPLE");
-  testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]);
 
   // Test negative filtering (only show unmatched items)
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   setFreetextFilter("-sample");
-  testContents([0, 0, 0, 1, 1, 1, 1, 1, 1]);
+  yield testContents([0, 0, 0, 1, 1, 1, 1, 1, 1]);
 
   // ...then combine multiple filters together.
 
   // Enable filtering for html and css; should show request of both type.
   setFreetextFilter("");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-html-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-css-button"));
   testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
-  testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]);
 
   // Html and css filter enabled and text filter should show just the html and css match.
   // Should not show both the items matching the button plus the items matching the text.
   setFreetextFilter("sample");
-  testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]);
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-flash-button"));
   setFreetextFilter("");
   testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0]);
-  testContents([1, 1, 0, 0, 0, 0, 0, 1, 0]);
+  yield testContents([1, 1, 0, 0, 0, 0, 0, 1, 0]);
 
   // Disable some filters. Only one left active.
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-css-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-flash-button"));
   testFilterButtons(monitor, "html");
-  testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Disable last active filter. Should toggle to all.
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-html-button"));
   testFilterButtons(monitor, "all");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   // Enable few filters and click on all. Only "all" should be checked.
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-html-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-css-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-ws-button"));
   testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 1]);
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   testFilterButtons(monitor, "all");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   yield teardown(monitor);
 
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
-  function testContents(visibility) {
+  function* testContents(visibility) {
+    let requestItems = document.querySelectorAll(".request-list-item");
+    for (let requestItem of requestItems) {
+      requestItem.scrollIntoView();
+      let requestsListStatus = requestItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      yield waitUntil(() => requestsListStatus.title);
+    }
+
     isnot(getSelectedRequest(store.getState()), undefined,
       "There should still be a selected item after filtering.");
     is(getSelectedIndex(store.getState()), 0,
       "The first item should be still selected after filtering.");
 
     const items = getSortedRequests(store.getState());
     const visibleItems = getDisplayedRequests(store.getState());
 
--- a/devtools/client/netmonitor/test/browser_net_filter-02.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-02.js
@@ -157,60 +157,68 @@ add_task(function* () {
   isnot(getSelectedRequest(store.getState()), null,
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
   testFilterButtons(monitor, "all");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   info("Testing html filtering.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-html-button"));
   testFilterButtons(monitor, "html");
-  testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   info("Performing more requests.");
   wait = waitForNetworkEvents(monitor, 9);
   yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS);
   yield wait;
 
   info("Testing html filtering again.");
   testFilterButtons(monitor, "html");
-  testContents([1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   info("Performing more requests.");
   wait = waitForNetworkEvents(monitor, 9);
   yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS);
   yield wait;
 
   info("Testing html filtering again.");
   testFilterButtons(monitor, "html");
-  testContents([1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
-                0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+                      0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   info("Resetting filters.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-all-button"));
   testFilterButtons(monitor, "all");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   yield teardown(monitor);
 
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
-  function testContents(visibility) {
+  function* testContents(visibility) {
+    let requestItems = document.querySelectorAll(".request-list-item");
+    for (let requestItem of requestItems) {
+      requestItem.scrollIntoView();
+      let requestsListStatus = requestItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      yield waitUntil(() => requestsListStatus.title);
+    }
+
     isnot(getSelectedRequest(store.getState()), null,
       "There should still be a selected item after filtering.");
     is(getSelectedIndex(store.getState()), 0,
       "The first item should be still selected after filtering.");
     is(!!document.querySelector(".network-details-panel"), true,
       "The network details panel should still be visible after filtering.");
 
     const items = getSortedRequests(store.getState());
--- a/devtools/client/netmonitor/test/browser_net_filter-flags.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-flags.js
@@ -153,200 +153,208 @@ add_task(function* () {
 
   let waitNetwork = waitForNetworkEvents(monitor, REQUESTS.length);
   loadCommonFrameScript();
   yield performRequestsInContent(REQUESTS);
   yield waitNetwork;
 
   // Test running flag once requests finish running
   setFreetextFilter("is:running");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test cached flag
   setFreetextFilter("is:from-cache");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
 
   setFreetextFilter("is:cached");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
 
   // Test negative cached flag
   setFreetextFilter("-is:from-cache");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 0]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 0]);
 
   setFreetextFilter("-is:cached");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 0]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 0]);
 
   // Test status-code flag
   setFreetextFilter("status-code:200");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 0]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 0]);
 
   // Test status-code negative flag
   setFreetextFilter("-status-code:200");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
 
   // Test mime-type flag
   setFreetextFilter("mime-type:HtmL");
-  testContents([1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test mime-type negative flag
   setFreetextFilter("-mime-type:HtmL");
-  testContents([0, 0, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([0, 0, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   // Test method flag
   setFreetextFilter("method:get");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   // Test unmatched method flag
   setFreetextFilter("method:post");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test scheme flag (all requests are http)
   setFreetextFilter("scheme:http");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("scheme:https");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test regex filter
   setFreetextFilter("regexp:content.*?Sam");
-  testContents([1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test set-cookie-name flag
   setFreetextFilter("set-cookie-name:name2");
-  testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   setFreetextFilter("set-cookie-name:not-existing");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test set-cookie-value flag
   setFreetextFilter("set-cookie-value:value2");
-  testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   setFreetextFilter("set-cookie-value:not-existing");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test set-cookie-domain flag
   setFreetextFilter("set-cookie-domain:.example.com");
-  testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   setFreetextFilter("set-cookie-domain:.foo.example.com");
-  testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   setFreetextFilter("set-cookie-domain:.not-existing.example.com");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test size
   setFreetextFilter("size:-1");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   setFreetextFilter("size:0");
-  testContents([0, 0, 0, 0, 1, 1, 1, 1, 0, 1]);
+  yield testContents([0, 0, 0, 0, 1, 1, 1, 1, 0, 1]);
 
   setFreetextFilter("size:34");
-  testContents([0, 0, 1, 1, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 1, 1, 0, 0, 0, 0, 0, 0]);
 
   // Testing the lower bound
   setFreetextFilter("size:9.659k");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   // Testing the actual value
   setFreetextFilter("size:10989");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   // Testing the upper bound
   setFreetextFilter("size:11.804k");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   // Test transferred
   setFreetextFilter("transferred:200");
-  testContents([0, 0, 0, 0, 1, 1, 1, 1, 0, 0]);
+  yield testContents([0, 0, 0, 0, 1, 1, 1, 1, 0, 0]);
 
   setFreetextFilter("transferred:234");
-  testContents([1, 0, 1, 0, 0, 0, 0, 0, 0, 1]);
+  yield testContents([1, 0, 1, 0, 0, 0, 0, 0, 0, 1]);
 
   setFreetextFilter("transferred:248");
-  testContents([0, 0, 1, 1, 0, 0, 0, 0, 0, 1]);
+  yield testContents([0, 0, 1, 1, 0, 0, 0, 0, 0, 1]);
 
   // Test larger-than
   setFreetextFilter("larger-than:-1");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("larger-than:0");
-  testContents([1, 1, 1, 1, 0, 0, 0, 0, 1, 0]);
+  yield testContents([1, 1, 1, 1, 0, 0, 0, 0, 1, 0]);
 
   setFreetextFilter("larger-than:33");
-  testContents([0, 0, 1, 1, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 0, 1, 1, 0, 0, 0, 0, 1, 0]);
 
   setFreetextFilter("larger-than:34");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   setFreetextFilter("larger-than:10.73k");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   setFreetextFilter("larger-than:10.732k");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test transferred-larger-than
   setFreetextFilter("transferred-larger-than:-1");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("transferred-larger-than:214");
-  testContents([1, 1, 1, 1, 0, 0, 0, 0, 1, 1]);
+  yield testContents([1, 1, 1, 1, 0, 0, 0, 0, 1, 1]);
 
   setFreetextFilter("transferred-larger-than:247");
-  testContents([0, 1, 1, 1, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 1, 1, 1, 0, 0, 0, 0, 1, 0]);
 
   setFreetextFilter("transferred-larger-than:248");
-  testContents([0, 1, 0, 1, 0, 0, 0, 0, 1, 0]);
+  yield testContents([0, 1, 0, 1, 0, 0, 0, 0, 1, 0]);
 
   setFreetextFilter("transferred-larger-than:10.73k");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test cause
   setFreetextFilter("cause:xhr");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("cause:script");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test has-response-header
   setFreetextFilter("has-response-header:Content-Type");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("has-response-header:Last-Modified");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test remote-ip
   setFreetextFilter("remote-ip:127.0.0.1");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("remote-ip:192.168.1.2");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test domain
   setFreetextFilter("domain:example.com");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("domain:wrongexample.com");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test protocol
   setFreetextFilter("protocol:http/1");
-  testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+  yield testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("protocol:http/2");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  yield testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test mixing flags
   setFreetextFilter("-mime-type:HtmL status-code:200");
-  testContents([0, 0, 1, 1, 1, 1, 1, 1, 1, 0]);
+  yield testContents([0, 0, 1, 1, 1, 1, 1, 1, 1, 0]);
 
   yield teardown(monitor);
 
-  function testContents(visibility) {
+  function* testContents(visibility) {
+    let requestItems = document.querySelectorAll(".request-list-item");
+    for (let requestItem of requestItems) {
+      requestItem.scrollIntoView();
+      let requestsListStatus = requestItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      yield waitUntil(() => requestsListStatus.title);
+    }
+
     const items = getSortedRequests(store.getState());
     const visibleItems = getDisplayedRequests(store.getState());
 
     is(items.size, visibility.length,
       "There should be a specific amount of items in the requests menu.");
     is(visibleItems.size, visibility.filter(e => e).length,
       "There should be a specific amount of visible items in the requests menu.");
 
--- a/devtools/client/netmonitor/test/browser_net_json-long.js
+++ b/devtools/client/netmonitor/test/browser_net_json-long.js
@@ -27,16 +27,21 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItem = document.querySelector(".request-list-item");
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-long",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_json-malformed.js
+++ b/devtools/client/netmonitor/test/browser_net_json-malformed.js
@@ -22,16 +22,21 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItem = document.querySelector(".request-list-item");
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-malformed",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
@@ -22,16 +22,21 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItem = document.querySelector(".request-list-item");
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-custom-mime",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_json_text_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js
@@ -23,16 +23,21 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItem = document.querySelector(".request-list-item");
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-text-mime",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_jsonp.js
+++ b/devtools/client/netmonitor/test/browser_net_jsonp.js
@@ -23,16 +23,24 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItems = document.querySelectorAll(".request-list-item");
+  for (let requestItem of requestItems) {
+    requestItem.scrollIntoView();
+    let requestsListStatus = requestItem.querySelector(".requests-list-status");
+    EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+    yield waitUntil(() => requestsListStatus.title);
+  }
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_large-response.js
+++ b/devtools/client/netmonitor/test/browser_net_large-response.js
@@ -27,16 +27,22 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, HTML_LONG_URL, function* (url) {
     content.wrappedJSObject.performRequests(1, url);
   });
   yield wait;
 
+  let requestItem = document.querySelector(".request-list-item");
+  requestItem.scrollIntoView();
+  let requestsListStatus = requestItem.querySelector(".requests-list-status");
+  EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+  yield waitUntil(() => requestsListStatus.title);
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=html-long",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js
@@ -26,16 +26,24 @@ add_task(function* () {
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItems = document.querySelectorAll(".request-list-item");
+  for (let requestItem of requestItems) {
+    requestItem.scrollIntoView();
+    let requestsListStatus = requestItem.querySelector(".requests-list-status");
+    EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+    yield waitUntil(() => requestsListStatus.title);
+  }
+
   verifyRequestItemTarget(
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "POST",
     SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded",
     {
       status: 200,
--- a/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
+++ b/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
@@ -24,17 +24,17 @@ add_task(function* () {
 
   // Perform first batch of requests.
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
-  verifyRequest(0);
+  yield verifyRequest(0);
 
   // Switch to the webconsole.
   let onWebConsole = monitor.toolbox.once("webconsole-selected");
   monitor.toolbox.selectTool("webconsole");
   yield onWebConsole;
 
   // Switch back to the netmonitor.
   let onNetMonitor = monitor.toolbox.once("netmonitor-selected");
@@ -48,21 +48,28 @@ add_task(function* () {
 
   // Perform another batch of requests.
   wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
-  verifyRequest(1);
+  yield verifyRequest(1);
 
   return teardown(monitor);
 
-  function verifyRequest(index) {
+  function* verifyRequest(index) {
+    let requestItems = document.querySelectorAll(".request-list-item");
+    for (let requestItem of requestItems) {
+      requestItem.scrollIntoView();
+      let requestsListStatus = requestItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      yield waitUntil(() => requestsListStatus.title);
+    }
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(index),
       "GET",
       CONTENT_TYPE_SJS + "?fmt=json-long",
       {
         status: 200,
--- a/devtools/client/netmonitor/test/browser_net_service-worker-status.js
+++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js
@@ -47,16 +47,24 @@ add_task(function* () {
 
   info("Performing requests...");
   let wait = waitForNetworkEvents(monitor, REQUEST_DATA.length);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  let requestItems = document.querySelectorAll(".request-list-item");
+  for (let requestItem of requestItems) {
+    requestItem.scrollIntoView();
+    let requestsListStatus = requestItem.querySelector(".requests-list-status");
+    EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+    yield waitUntil(() => requestsListStatus.title);
+  }
+
   let index = 0;
   for (let request of REQUEST_DATA) {
     let item = getSortedRequests(store.getState()).get(index);
 
     info(`Verifying request #${index}`);
     yield verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -226,16 +226,22 @@ function test() {
         "The httpVersion data has an incorrect value.");
       is(requestItem.status, "200",
         "The status data has an incorrect value.");
       is(requestItem.statusText, "Och Aye",
         "The statusText data has an incorrect value.");
       is(requestItem.headersSize, 330,
         "The headersSize data has an incorrect value.");
 
+      let requestListItem = document.querySelector(".request-list-item");
+      requestListItem.scrollIntoView();
+      let requestsListStatus = requestListItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      await waitUntil(() => requestsListStatus.title);
+
       verifyRequestItemTarget(
         document,
         getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS,
         {
           status: "200",
--- a/devtools/client/netmonitor/test/browser_net_sort-01.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-01.js
@@ -57,59 +57,59 @@ add_task(function* () {
   isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
   testHeaders();
-  testContents([0, 2, 4, 3, 1], 0);
+  yield testContents([0, 2, 4, 3, 1], 0);
 
   info("Testing status sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-status-button"));
   testHeaders("status", "ascending");
-  testContents([0, 1, 2, 3, 4], 0);
+  yield testContents([0, 1, 2, 3, 4], 0);
 
   info("Performing more requests.");
   wait = waitForNetworkEvents(monitor, 5);
   yield performRequestsInContent(requests);
   yield wait;
 
   info("Testing status sort again, ascending.");
   testHeaders("status", "ascending");
-  testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0);
+  yield testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0);
 
   info("Testing status sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-status-button"));
   testHeaders("status", "descending");
-  testContents([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 9);
+  yield testContents([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 9);
 
   info("Performing more requests.");
   wait = waitForNetworkEvents(monitor, 5);
   yield performRequestsInContent(requests);
   yield wait;
 
   info("Testing status sort again, descending.");
   testHeaders("status", "descending");
-  testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14);
+  yield testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14);
 
   info("Testing status sort yet again, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-status-button"));
   testHeaders("status", "ascending");
-  testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 0);
+  yield testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 0);
 
   info("Testing status sort yet again, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-status-button"));
   testHeaders("status", "descending");
-  testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14);
+  yield testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 14);
 
   return teardown(monitor);
 
   function testHeaders(sortType, direction) {
     let doc = monitor.panelWin.document;
     let target = doc.querySelector("#requests-list-" + sortType + "-button");
     let headers = doc.querySelectorAll(".requests-list-header-button");
 
@@ -136,31 +136,39 @@ add_task(function* () {
 
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
-  function testContents(order, selection) {
+  function* testContents(order, selection) {
     isnot(getSelectedRequest(store.getState()), undefined,
       "There should still be a selected item after sorting.");
     is(getSelectedIndex(store.getState()), selection,
       "The first item should be still selected after sorting.");
     is(!!document.querySelector(".network-details-panel"), true,
       "The network details panel should still be visible after sorting.");
 
     is(getSortedRequests(store.getState()).length, order.length,
       "There should be a specific number of items in the requests menu.");
     is(getDisplayedRequests(store.getState()).length, order.length,
       "There should be a specific number of visbile items in the requests menu.");
     is(document.querySelectorAll(".request-list-item").length, order.length,
       "The visible items in the requests menu are, in fact, visible!");
 
+    let requestItems = document.querySelectorAll(".request-list-item");
+    for (let requestItem of requestItems) {
+      requestItem.scrollIntoView();
+      let requestsListStatus = requestItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      yield waitUntil(() => requestsListStatus.title);
+    }
+
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
         getDisplayedRequests(store.getState()),
         getSortedRequests(store.getState()).get(order[i]),
         "GET1", SORTING_SJS + "?index=1", {
           fuzzyUrl: true,
           status: 101,
--- a/devtools/client/netmonitor/test/browser_net_sort-02.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-02.js
@@ -57,143 +57,143 @@ add_task(function* () {
   isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
   testHeaders();
-  testContents([0, 2, 4, 3, 1]);
+  yield testContents([0, 2, 4, 3, 1]);
 
   info("Testing status sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-status-button"));
   testHeaders("status", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing status sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-status-button"));
   testHeaders("status", "descending");
-  testContents([4, 3, 2, 1, 0]);
+  yield testContents([4, 3, 2, 1, 0]);
 
   info("Testing status sort, ascending. Checking sort loops correctly.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-status-button"));
   testHeaders("status", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing method sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-method-button"));
   testHeaders("method", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing method sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-method-button"));
   testHeaders("method", "descending");
-  testContents([4, 3, 2, 1, 0]);
+  yield testContents([4, 3, 2, 1, 0]);
 
   info("Testing method sort, ascending. Checking sort loops correctly.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-method-button"));
   testHeaders("method", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing file sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-file-button"));
   testHeaders("file", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing file sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-file-button"));
   testHeaders("file", "descending");
-  testContents([4, 3, 2, 1, 0]);
+  yield testContents([4, 3, 2, 1, 0]);
 
   info("Testing file sort, ascending. Checking sort loops correctly.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-file-button"));
   testHeaders("file", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing type sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-type-button"));
   testHeaders("type", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing type sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-type-button"));
   testHeaders("type", "descending");
-  testContents([4, 3, 2, 1, 0]);
+  yield testContents([4, 3, 2, 1, 0]);
 
   info("Testing type sort, ascending. Checking sort loops correctly.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-type-button"));
   testHeaders("type", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing transferred sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-transferred-button"));
   testHeaders("transferred", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing transferred sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-transferred-button"));
   testHeaders("transferred", "descending");
-  testContents([4, 3, 2, 1, 0]);
+  yield testContents([4, 3, 2, 1, 0]);
 
   info("Testing transferred sort, ascending. Checking sort loops correctly.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-transferred-button"));
   testHeaders("transferred", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing size sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-contentSize-button"));
   testHeaders("contentSize", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing size sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-contentSize-button"));
   testHeaders("contentSize", "descending");
-  testContents([4, 3, 2, 1, 0]);
+  yield testContents([4, 3, 2, 1, 0]);
 
   info("Testing size sort, ascending. Checking sort loops correctly.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-contentSize-button"));
   testHeaders("contentSize", "ascending");
-  testContents([0, 1, 2, 3, 4]);
+  yield testContents([0, 1, 2, 3, 4]);
 
   info("Testing waterfall sort, ascending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-waterfall-button"));
   testHeaders("waterfall", "ascending");
-  testContents([0, 2, 4, 3, 1]);
+  yield testContents([0, 2, 4, 3, 1]);
 
   info("Testing waterfall sort, descending.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-waterfall-button"));
   testHeaders("waterfall", "descending");
-  testContents([4, 2, 0, 1, 3]);
+  yield testContents([4, 2, 0, 1, 3]);
 
   info("Testing waterfall sort, ascending. Checking sort loops correctly.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-waterfall-button"));
   testHeaders("waterfall", "ascending");
-  testContents([0, 2, 4, 3, 1]);
+  yield testContents([0, 2, 4, 3, 1]);
 
   return teardown(monitor);
 
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
@@ -220,31 +220,39 @@ add_task(function* () {
           : L10N.getStr("networkMenu.sortedDesc");
         ok(header.getAttribute("title").includes(sorted),
           "The " + header.id +
           " header includes the used sorting in the 'title' attribute.");
       }
     }
   }
 
-  function testContents([a, b, c, d, e]) {
+  function* testContents([a, b, c, d, e]) {
     isnot(getSelectedRequest(store.getState()), undefined,
       "There should still be a selected item after sorting.");
     is(getSelectedIndex(store.getState()), a,
       "The first item should be still selected after sorting.");
     is(!!document.querySelector(".network-details-panel"), true,
       "The network details panel should still be visible after sorting.");
 
     is(getSortedRequests(store.getState()).length, 5,
       "There should be a total of 5 items in the requests menu.");
     is(getDisplayedRequests(store.getState()).length, 5,
       "There should be a total of 5 visible items in the requests menu.");
     is(document.querySelectorAll(".request-list-item").length, 5,
       "The visible items in the requests menu are, in fact, visible!");
 
+    let requestItems = document.querySelectorAll(".request-list-item");
+    for (let requestItem of requestItems) {
+      requestItem.scrollIntoView();
+      let requestsListStatus = requestItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      yield waitUntil(() => requestsListStatus.title);
+    }
+
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(a),
       "GET1", SORTING_SJS + "?index=1", {
         fuzzyUrl: true,
         status: 101,
         statusText: "Meh",
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js
+++ b/devtools/client/netmonitor/test/browser_net_status-codes.js
@@ -106,16 +106,24 @@ add_task(function* () {
 
   return teardown(monitor);
 
   /**
    * A helper that verifies all requests show the correct information and caches
    * request list items to requestItems array.
    */
   function* verifyRequests() {
+    let requestListItems = document.querySelectorAll(".request-list-item");
+    for (let requestItem of requestListItems) {
+      requestItem.scrollIntoView();
+      let requestsListStatus = requestItem.querySelector(".requests-list-status");
+      EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+      yield waitUntil(() => requestsListStatus.title);
+    }
+
     info("Verifying requests contain correct information.");
     let index = 0;
     for (let request of REQUEST_DATA) {
       let item = getSortedRequests(store.getState()).get(index);
       requestItems[index] = item;
 
       info("Verifying request #" + index);
       yield verifyRequestItemTarget(
--- a/devtools/client/netmonitor/test/browser_net_streaming-response.js
+++ b/devtools/client/netmonitor/test/browser_net_streaming-response.js
@@ -30,16 +30,24 @@ add_task(function* () {
   for (let [fmt] of REQUESTS) {
     let url = CONTENT_TYPE_SJS + "?fmt=" + fmt;
     yield ContentTask.spawn(tab.linkedBrowser, { url }, function* (args) {
       content.wrappedJSObject.performRequests(1, args.url);
     });
   }
   yield wait;
 
+  let requestItems = document.querySelectorAll(".request-list-item");
+  for (let requestItem of requestItems) {
+    requestItem.scrollIntoView();
+    let requestsListStatus = requestItem.querySelector(".requests-list-status");
+    EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
+    yield waitUntil(() => requestsListStatus.title);
+  }
+
   REQUESTS.forEach(([ fmt ], i) => {
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(i),
       "GET",
       CONTENT_TYPE_SJS + "?fmt=" + fmt,
       {