Bug 1495558 - Make utils.waitFor() accept function to properly retrieve state of objects after failure (idea by Tooru Fujisawa). r=jorgk
authoraceman <acelists@atlas.sk>
Mon, 01 Oct 2018 14:46:00 +0200
changeset 33281 1d9d688d566980be56ad1c86b805c5675f9412fa
parent 33280 064d247285aaf55f3dcbdd999d5c36ae7a809cbf
child 33282 1bc7b3e7b9dbf36520251860b7bee9a3ee8653a9
push id387
push userclokep@gmail.com
push dateMon, 10 Dec 2018 21:30:47 +0000
reviewersjorgk
bugs1495558
Bug 1495558 - Make utils.waitFor() accept function to properly retrieve state of objects after failure (idea by Tooru Fujisawa). r=jorgk
mail/test/mozmill/content-policy/test-js-content-policy.js
mail/test/mozmill/downloads/test-about-downloads.js
mail/test/mozmill/message-header/test-message-header.js
mail/test/mozmill/shared-modules/test-folder-display-helpers.js
mail/test/mozmill/shared-modules/test-window-helpers.js
mail/test/resources/mozmill/mozmill/extension/content/modules/controller.js
mail/test/resources/mozmill/mozmill/extension/content/modules/utils.js
--- a/mail/test/mozmill/content-policy/test-js-content-policy.js
+++ b/mail/test/mozmill/content-policy/test-js-content-policy.js
@@ -156,17 +156,17 @@ function checkJsInFeedContent() {
 
   // The above just ensures local "inline" content have loaded. We need to wait
   // for the remote content to load too before we check anything.
   let mc = mozmill.getMail3PaneController();
   let feedUrl = url + "remote-noscript.html";
   mc.waitFor(() => mc.window.content.wrappedJSObject.location.href == feedUrl &&
                    mc.window.content.wrappedJSObject.document &&
                    mc.window.content.wrappedJSObject.document.querySelector("body") != null,
-             "Timeout waiting for remote feed doc to load; url=" + mc.window.content.wrappedJSObject.location);
+             () => ("Timeout waiting for remote feed doc to load; url=" + mc.window.content.wrappedJSObject.location));
 
   if (!mc.window.content.wrappedJSObject.jsIsTurnedOn)
     throw new Error("JS is turned off for remote feed content - it should be on.");
 
   let noscript = mc.window.content.wrappedJSObject.document
                    .getElementsByTagName("noscript")[0];
   let display = mc.window.getComputedStyle(noscript).getPropertyValue("display");
   if (display != "none")
@@ -200,18 +200,18 @@ function checkJsInFeedTab() {
 
   open_selected_message_in_new_tab();
 
   // The above just ensures local "inline" content have loaded. We need to wait
   // for the remote content to load too before we check anything.
   mc.waitFor(() => mc.window.content.wrappedJSObject.location.href == feedUrl &&
                    mc.window.content.wrappedJSObject.document &&
                    mc.window.content.wrappedJSObject.document.querySelector("body") != null,
-             "Timeout waiting for remote feed doc to load; url=" +
-              mc.window.content.wrappedJSObject.location);
+             () => ("Timeout waiting for remote feed doc to load; url=" +
+                    mc.window.content.wrappedJSObject.location));
 
   if (!mc.window.content.wrappedJSObject.jsIsTurnedOn)
     throw new Error("JS is turned off for remote feed content - it should be on.");
 
   let noscript = mc.window.content.wrappedJSObject.document
                    .getElementsByTagName("noscript")[0];
   let display = mc.window.getComputedStyle(noscript).getPropertyValue("display");
   if (display != "none")
--- a/mail/test/mozmill/downloads/test-about-downloads.js
+++ b/mail/test/mozmill/downloads/test-about-downloads.js
@@ -162,17 +162,18 @@ function save_attachment_files() {
 function test_save_attachment_files_in_list() {
   save_attachment_files();
 
   mc.tabmail.switchToTab(downloadsTab);
   let list = content_tab_e(downloadsTab, "msgDownloadsRichListBox");
 
   let length = attachmentFileNames.length;
   mc.waitFor(() => downloadsView.count == length,
-             "Timeout waiting for saving three attachment files; downloadsView.count=" + downloadsView.count);
+             () => ("Timeout waiting for saving three attachment files; " +
+                    "downloadsView.count=" + downloadsView.count));
 
   assert_equals(length, list.childNodes.length);
   assert_equals(downloadsView.count, list.childNodes.length);
 
   let actualNames = [];
   let child = list.firstChild;
   while (child) {
     actualNames.push(child.getAttribute("displayName"));
--- a/mail/test/mozmill/message-header/test-message-header.js
+++ b/mail/test/mozmill/message-header/test-message-header.js
@@ -385,18 +385,18 @@ function test_clicking_star_opens_inline
   // the contact has been added to our AB.
   let addrs = toDescription.getElementsByTagName('mail-emailaddress');
   let lastAddr = get_last_visible_address(addrs);
 
   // Click on the star, and ensure that the inline contact
   // editing panel opens
   mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
   mc.waitFor(() => contactPanel.state == "open",
-             "Timeout waiting for contactPanel to open; state=" +
-             contactPanel.state);
+             () => ("Timeout waiting for contactPanel to open; state=" +
+                    contactPanel.state));
   contactPanel.hidePopup();
 }
 
 /**
  * Ensure that the specified element is visible/hidden
  *
  * @param id the id of the element to check
  * @param visible true if the element should be visible, false otherwise
@@ -473,18 +473,18 @@ function test_address_book_switch_disabl
   // the contact has been added to our AB.
   let addrs = toDescription.getElementsByTagName('mail-emailaddress');
   let lastAddr = get_last_visible_address(addrs);
 
   // Click on the star, and ensure that the inline contact
   // editing panel opens
   mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
   mc.waitFor(() => contactPanel.state == "open",
-             "Timeout waiting for contactPanel to open; state=" +
-             contactPanel.state);
+             () => ("Timeout waiting for contactPanel to open; state=" +
+                    contactPanel.state));
 
   let abDrop = mc.eid('editContactAddressBookList').getNode();
   let warningMsg = mc.eid('contactMoveDisabledText').getNode();
 
   // Ensure that the address book dropdown is not disabled
   assert_true(!abDrop.disabled);
   // We should not be displaying any warning
   assert_true(warningMsg.collapsed);
@@ -520,18 +520,18 @@ function test_address_book_switch_disabl
   // delete cards from it.
   ml = get_mailing_list_from_address_book(ab, MAILING_LIST_DIRNAME);
 
   ml.addressLists.appendElement(card);
 
   // Re-open the inline contact editing panel
   mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
   mc.waitFor(() => contactPanel.state == "open",
-             "Timeout waiting for contactPanel to open; state=" +
-             contactPanel.state);
+             () => ("Timeout waiting for contactPanel to open; state=" +
+                    contactPanel.state));
 
   // The dropdown should be disabled now
   assert_true(abDrop.disabled);
   // We should be displaying a warning
   assert_true(!warningMsg.collapsed);
 
   contactPanel.hidePopup();
 
@@ -542,18 +542,18 @@ function test_address_book_switch_disabl
   let cardArray = Cc["@mozilla.org/array;1"]
                   .createInstance(Ci.nsIMutableArray);
   cardArray.appendElement(card);
   ml.deleteCards(cardArray);
 
   // Re-open the inline contact editing panel
   mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
   mc.waitFor(() => contactPanel.state == "open",
-             "Timeout waiting for contactPanel to open; state=" +
-             contactPanel.state);
+             () => ("Timeout waiting for contactPanel to open; state=" +
+                    contactPanel.state));
 
   // Ensure that the address book dropdown is not disabled
   assert_true(!abDrop.disabled);
   // We should not be displaying any warning
   assert_true(warningMsg.collapsed);
 
   contactPanel.hidePopup();
 }
--- a/mail/test/mozmill/shared-modules/test-folder-display-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-folder-display-helpers.js
@@ -1425,17 +1425,17 @@ function delete_via_popup() {
   // for reasons unknown, the pop-up does not close itself?
   close_popup(mc, mc.eid("mailContext"));
   wait_for_folder_events();
 }
 
 function wait_for_popup_to_open(popupElem) {
   mark_action("fdh", "wait_for_popup_to_open", [popupElem]);
   utils.waitFor(() => popupElem.state == "open",
-                "Timeout waiting for popup to open, state=" + popupElem.state, 1000, 50);
+                () => ("Timeout waiting for popup to open, state=" + popupElem.state), 1000, 50);
 }
 
 /**
  * Close the open pop-up.
  */
 function close_popup(aController, eid) {
   if (aController == null)
     aController = mc;
--- a/mail/test/mozmill/shared-modules/test-window-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-window-helpers.js
@@ -975,18 +975,18 @@ var AugmentEverybodyWith = {
      * @param aKeepOpen  If set to true the popups are not closed after last click.
      *
      * @return  An array of popup elements that were left open. It will be
      *          an empty array if aKeepOpen was set to false.
      */
     click_menus_in_sequence: function _click_menus(aRootPopup, aActions, aKeepOpen) {
       if (aRootPopup.state != "open") { // handle "showing"
         utils.waitFor(() => aRootPopup.state == "open",
-                      "Popup never opened! id=" + aRootPopup.id +
-                      ", state=" + aRootPopup.state);
+                      () => ("Popup never opened! id=" + aRootPopup.id +
+                             ", state=" + aRootPopup.state));
       }
       // These popups sadly do not close themselves, so we need to keep track
       // of them so we can make sure they end up closed.
       let closeStack = [aRootPopup];
 
       let curPopup = aRootPopup;
       for (let [iAction, actionObj] of aActions.entries()) {
         /**
@@ -1047,19 +1047,19 @@ var AugmentEverybodyWith = {
                  ("menupopup" in matchingNode.menu)) {
           // We should actually fetch matchingNode.menu.menupopup here,
           // but it doesn't seem to work.
           newPopup = matchingNode.querySelector("menupopup");
         }
         if (newPopup) {
           curPopup = newPopup;
           closeStack.push(curPopup);
-          utils.waitFor(function() { return curPopup.state == "open"; },
-                        "Popup never opened at action depth " + iAction +
-                        "; id=" + curPopup.id + ", state=" + curPopup.state,
+          utils.waitFor(() => curPopup.state == "open",
+                        () => ("Popup never opened at action depth " + iAction +
+                               "; id=" + curPopup.id + ", state=" + curPopup.state),
                         5000, 50);
         }
       }
 
       if (!aKeepOpen) {
         this.close_popup_sequence(closeStack);
         return [];
       } else {
@@ -1074,19 +1074,19 @@ var AugmentEverybodyWith = {
      *                     The elements are processed from the end of the array
      *                     to the front (a stack).
      */
     close_popup_sequence: function _close_popup_sequence(aCloseStack) {
       while (aCloseStack.length) {
         let curPopup = aCloseStack.pop();
         if (curPopup.state == "open")
           this.keypress(new elib.Elem(curPopup), "VK_ESCAPE", {});
-        utils.waitFor(function() { return curPopup.state == "closed"; },
-                      "Popup did not close! id=" + curPopup.id +
-                      ", state=" +  curPopup.state, 5000, 50);
+        utils.waitFor(() => curPopup.state == "closed",
+                      () => ("Popup did not close! id=" + curPopup.id +
+                             ", state=" + curPopup.state), 5000, 50);
       }
     },
 
     /**
      * Get dropmarker arrow element from
      *
      * @param aNode  An element containing a dropmarker, e.g. menulist or menu-button
      */
--- a/mail/test/resources/mozmill/mozmill/extension/content/modules/controller.js
+++ b/mail/test/resources/mozmill/mozmill/extension/content/modules/controller.js
@@ -1354,16 +1354,16 @@ MozMillAsyncTest.prototype.run = functio
   for (var i in this) {
     if (withs.startsWith(i, 'test') && typeof(this[i]) == 'function') {
       this[i]();
     }
   }
 
   utils.waitFor(function() {
     return this._done == true;
-  }, "MozMillAsyncTest timed out. Done is " + this._done, 500, 100);
+  }, () => ("MozMillAsyncTest timed out. Done is " + this._done), 500, 100);
 
   return true;
 }
 
 MozMillAsyncTest.prototype.finish = function () {
   this._done = true;
 }
--- a/mail/test/resources/mozmill/mozmill/extension/content/modules/utils.js
+++ b/mail/test/resources/mozmill/mozmill/extension/content/modules/utils.js
@@ -402,16 +402,25 @@ function TimeoutError(message, fileName,
   this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
 };
 TimeoutError.prototype = new Error();
 TimeoutError.prototype.constructor = TimeoutError;
 TimeoutError.prototype.name = 'TimeoutError';
 
 /**
  * Waits for the callback evaluates to true
+ *
+ * @param callback    Function that returns true when the waiting thould end.
+ * @param message {string or function}  A message to throw if the callback didn't
+ *                                      succeed until the timeout. Use a function
+ *                                      if the message is to show some object state
+ *                                      after the end of the wait (not before wait).
+ * @param timeout     Milliseconds to wait until callback succeeds.
+ * @param interval    Milliseconds to 'sleep' between checks of callback.
+ * @param thisObject (optional) 'this' to be passed into the callback.
  */
 function waitFor(callback, message, timeout, interval, thisObject) {
   timeout = timeout || 5000;
   interval = interval || 100;
 
   var self = {counter: 0, result: callback.call(thisObject)};
 
   function wait() {
@@ -425,18 +434,27 @@ function waitFor(callback, message, time
 
   while((self.result != true) && (self.counter < timeout))  {
     thread.processNextEvent(true);
   }
 
   hwindow.clearInterval(timeoutInterval);
 
   if (self.counter >= timeout) {
-    message = message || "waitFor: Timeout exceeded for '" + callback + "'";
-    throw new TimeoutError(message);
+    let messageText;
+    if (message) {
+      if (typeof(message) === "function")
+        messageText = message();
+      else
+        messageText = message;
+    } else {
+      messageText = "waitFor: Timeout exceeded for '" + callback + "'";
+    }
+
+    throw new TimeoutError(messageText);
   }
 
   return true;
 }
 
 /**
  * Waits until the expression evaluates to true
  */