Bug 1522508 - Port browser_ext_omnibox.js to work with QuantumBar. r=mak
authorMark Banner <standard8@mozilla.com>
Thu, 28 Feb 2019 09:25:36 +0000
changeset 461709 bacf3a98194bc506c75c437ed23e64e97b2c5e0f
parent 461708 ff76466ee236c1d23bed762eb5de658db038147e
child 461710 694837e25cc6bb0b68fc3e8771bef3f92249346b
push id35627
push useropoprus@mozilla.com
push dateThu, 28 Feb 2019 21:44:07 +0000
treeherdermozilla-central@db533ea3d561 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1522508
milestone67.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 1522508 - Port browser_ext_omnibox.js to work with QuantumBar. r=mak The timer cancelling in UnifiedComplete.js seems to make the tests more stable with QuantumBar for some reason. Without it, some of the sub-tests would tend to time out. This might be better once we can implement a proper waitForResult() that doesn't have to wait for the entire search to be complete. Differential Revision: https://phabricator.services.mozilla.com/D21006
browser/components/extensions/test/browser/browser_ext_omnibox.js
browser/components/urlbar/tests/UrlbarTestUtils.jsm
toolkit/components/places/UnifiedComplete.jsm
--- a/browser/components/extensions/test/browser/browser_ext_omnibox.js
+++ b/browser/components/extensions/test/browser/browser_ext_omnibox.js
@@ -1,12 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+const {UrlbarTestUtils} = ChromeUtils.import("resource://testing-common/UrlbarTestUtils.jsm");
+
 add_task(async function() {
   // This keyword needs to be unique to prevent history entries from unrelated
   // tests from appearing in the suggestions list.
   let keyword = "VeryUniqueKeywordThatDoesNeverMatchAnyTestUrl";
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "omnibox": {
@@ -76,47 +78,44 @@ add_task(async function() {
          `Expected "${event}" to have fired with text: "${expected.text}".`);
     }
     if (expected.disposition) {
       is(actual.disposition, expected.disposition,
          `Expected "${event}" to have fired with disposition: "${expected.disposition}".`);
     }
   }
 
-  async function waitForAutocompleteResultAt(index) {
-    let searchString = gURLBar.controller.searchString;
-    await BrowserTestUtils.waitForCondition(
-      () => gURLBar.popup.richlistbox.itemChildren.length > index &&
-            gURLBar.popup.richlistbox.itemChildren[index].getAttribute("ac-text") == searchString,
-      `Waiting for the autocomplete result for "${searchString}" at [${index}] to appear`);
+  async function waitForResult(index, searchString) {
+    let result = await UrlbarTestUtils.getDetailsOfResultAt(window, index);
     // Ensure the addition is complete, for proper mouse events on the entries.
     await new Promise(resolve => window.requestIdleCallback(resolve, {timeout: 1000}));
-    return gURLBar.popup.richlistbox.itemChildren[index];
+    return result;
   }
 
-  async function promiseClickOnItem(item, details) {
+  async function promiseClickOnItem(index, details) {
     // The Address Bar panel is animated and updated on a timer, thus it may not
     // yet be listening to events when we try to click on it.  This uses a
     // polling strategy to repeat the click, if it doesn't go through.
     let clicked = false;
-    item.addEventListener("mousedown", () => { clicked = true; }, {once: true});
+    let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, index);
+    element.addEventListener("mousedown", () => { clicked = true; }, {once: true});
     while (!clicked) {
-      EventUtils.synthesizeMouseAtCenter(item, details);
+      EventUtils.synthesizeMouseAtCenter(element, details);
       await new Promise(r => window.requestIdleCallback(r, {timeout: 1000}));
     }
   }
 
   let inputSessionSerial = 0;
   async function startInputSession() {
     gURLBar.focus();
     gURLBar.value = keyword;
     EventUtils.sendString(" ");
     await expectEvent("on-input-started-fired");
     // Always use a different input at every invokation, so that
-    // waitForAutocompleteResultAt can distinguish different cases.
+    // waitForResult can distinguish different cases.
     let char = ((inputSessionSerial++) % 10).toString();
     EventUtils.sendString(char);
 
     await expectEvent("on-input-changed-fired", {text: char});
     return char;
   }
 
   async function testInputEvents() {
@@ -182,88 +181,85 @@ add_task(async function() {
         suggestion: {
           description: expectedText,
         },
       });
       await extension.awaitMessage("default-suggestion-set");
     }
 
     let text = await startInputSession();
-    await waitForAutocompleteResultAt(0);
-
-    let item = gURLBar.popup.richlistbox.itemChildren[0];
+    let result = await waitForResult(0);
 
-    is(item.getAttribute("title"), expectedText,
-       `Expected heuristic result to have title: "${expectedText}".`);
+    Assert.equal(result.displayed.title, expectedText,
+                 `Expected heuristic result to have title: "${expectedText}".`);
 
-    is(item.getAttribute("displayurl"), `${keyword} ${text}`,
-       `Expected heuristic result to have displayurl: "${keyword} ${text}".`);
+    Assert.equal(result.displayed.action, `${keyword} ${text}`,
+                 `Expected heuristic result to have displayurl: "${keyword} ${text}".`);
 
     let promiseEvent = expectEvent("on-input-entered-fired", {
       text,
       disposition: "currentTab",
     });
-    await promiseClickOnItem(item, {});
+    await promiseClickOnItem(0, {});
     await promiseEvent;
   }
 
   async function testDisposition(suggestionIndex, expectedDisposition, expectedText) {
     await startInputSession();
-    await waitForAutocompleteResultAt(suggestionIndex);
+    await waitForResult(suggestionIndex);
 
     // Select the suggestion.
     EventUtils.synthesizeKey("KEY_ArrowDown", {repeat: suggestionIndex});
 
     let promiseEvent = expectEvent("on-input-entered-fired", {
       text: expectedText,
       disposition: expectedDisposition,
     });
 
-    let item = gURLBar.popup.richlistbox.itemChildren[suggestionIndex];
     if (expectedDisposition == "currentTab") {
-      await promiseClickOnItem(item, {});
+      await promiseClickOnItem(suggestionIndex, {});
     } else if (expectedDisposition == "newForegroundTab") {
-      await promiseClickOnItem(item, {accelKey: true});
+      await promiseClickOnItem(suggestionIndex, {accelKey: true});
     } else if (expectedDisposition == "newBackgroundTab") {
-      await promiseClickOnItem(item, {shiftKey: true, accelKey: true});
+      await promiseClickOnItem(suggestionIndex, {shiftKey: true, accelKey: true});
     }
     await promiseEvent;
   }
 
   async function testSuggestions(info) {
     extension.sendMessage("set-synchronous", {synchronous: false});
     await extension.awaitMessage("set-synchronous-set");
 
-    function expectSuggestion({content, description}, index) {
-      let item = gURLBar.popup.richlistbox.itemChildren[index + 1]; // Skip the heuristic result.
-
-      ok(!!item, "Expected item to exist");
-      is(item.getAttribute("title"), description,
-         `Expected suggestion to have title: "${description}".`);
-
-      is(item.getAttribute("displayurl"), `${keyword} ${content}`,
-         `Expected suggestion to have displayurl: "${keyword} ${content}".`);
+    let text = await startInputSession();
+    if (!UrlbarPrefs.get("quantumbar")) {
+      // TODO Bug 1530338: We can't yet wait for a specific result for the
+      // quantumbar. Therefore we just skip this for now.
+      await waitForResult(0);
     }
 
-    let text = await startInputSession();
-    // Even if the results are generated asynchronously,
-    // the heuristic result should always be present.
-    await waitForAutocompleteResultAt(0);
-
     extension.sendMessage(info.test);
     await extension.awaitMessage("test-ready");
 
-    await waitForAutocompleteResultAt(info.suggestions.length - 1);
-    info.suggestions.forEach(expectSuggestion);
+    await waitForResult(info.suggestions.length - 1);
+    // Skip the heuristic result.
+    let index = 1;
+    for (let {content, description} of info.suggestions) {
+      let item = await UrlbarTestUtils.getDetailsOfResultAt(window, index);
+      Assert.equal(item.displayed.title, description,
+                   `Expected suggestion to have title: "${description}".`);
+      Assert.equal(item.displayed.action, `${keyword} ${content}`,
+                   `Expected suggestion to have displayurl: "${keyword} ${content}".`);
+      index++;
+    }
 
     let promiseEvent = expectEvent("on-input-entered-fired", {
       text,
       disposition: "currentTab",
     });
-    await promiseClickOnItem(gURLBar.popup.richlistbox.itemChildren[0], {});
+    await promiseClickOnItem(0, {});
     await promiseEvent;
   }
 
   await extension.startup();
 
   await SimpleTest.promiseFocus(window);
 
   await testInputEvents();
--- a/browser/components/urlbar/tests/UrlbarTestUtils.jsm
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.jsm
@@ -294,17 +294,17 @@ class UrlbarAbstraction {
 
   promiseSearchComplete() {
     if (this.quantumbar) {
       return this.urlbar.lastQueryContextPromise;
     }
     return BrowserTestUtils.waitForCondition(
       () => this.urlbar.controller.searchStatus >=
               Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH,
-      "waiting urlbar search to complete");
+      "waiting urlbar search to complete", 100, 50);
   }
 
   async promiseResultAt(index) {
     if (!this.quantumbar) {
       // In the legacy address bar, old results are replaced when new results
       // arrive, thus it's possible for a result to be present but refer to
       // a previous query. This ensures the given result refers to the current
       // query by checking its query string against the string being searched
--- a/toolkit/components/places/UnifiedComplete.jsm
+++ b/toolkit/components/places/UnifiedComplete.jsm
@@ -1707,17 +1707,20 @@ Search.prototype = {
     // matches may appear stale for a long time.
     // This is necessary because WebExtensions don't have a method to notify
     // that they are done providing results, so they could be pending forever.
     setTimeout(() => this._cleanUpNonCurrentMatches(UrlbarUtils.RESULT_GROUP.EXTENSION), 100);
 
     // Since the extension has no way to signale when it's done pushing
     // results, we add a timeout racing with the addition.
     let timeoutPromise = new Promise(resolve => {
-      setTimeout(resolve, MAXIMUM_ALLOWED_EXTENSION_TIME_MS);
+      let timer = setTimeout(resolve, MAXIMUM_ALLOWED_EXTENSION_TIME_MS);
+      // TODO Bug 1531268: Figure out why this cancel helps makes the tests
+      // stable.
+      promise.then(timer.cancel);
     });
     return Promise.race([timeoutPromise, promise]).catch(Cu.reportError);
   },
 
   async _matchRemoteTabs() {
     // Bail out early for non-sync users.
     if (!syncUsernamePref) {
       return;