Bug 782810 - add text, link, and input context menu tests. r=mbrubeck
authorJim Mathies <jmathies@mozilla.com>
Tue, 19 Feb 2013 19:51:02 -0600
changeset 122443 aae721e94e2c79143f68aac5b9b75ff03178824a
parent 122442 17b80182ebd8b4502d1bef94b487018631794620
child 122444 4b6b0d48930130b9da23e9776495d4cd0423bb1b
push id24342
push userryanvm@gmail.com
push dateThu, 21 Feb 2013 13:05:06 +0000
treeherdermozilla-central@702d2814efbf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck
bugs782810
milestone22.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 782810 - add text, link, and input context menu tests. r=mbrubeck
browser/metro/base/tests/Makefile.in
browser/metro/base/tests/browser_context_menu_tests.js
browser/metro/base/tests/browser_context_menu_tests_01.html
browser/metro/base/tests/browser_context_menu_tests_02.html
browser/metro/base/tests/head.js
--- a/browser/metro/base/tests/Makefile.in
+++ b/browser/metro/base/tests/Makefile.in
@@ -20,16 +20,17 @@ BROWSER_TESTS = \
   browser_onscreen_keyboard.html \
   browser_remotetabs.js \
   browser_downloads.js \
   browser_plugin_input.html \
   browser_plugin_input_mouse.js \
   browser_plugin_input_keyboard.js \
   browser_context_menu_tests.js \
   browser_context_menu_tests_01.html \
+  browser_context_menu_tests_02.html \
   $(NULL)
 
 BROWSER_TEST_RESOURCES = \
   res\image01.png \
   $(NULL)
 
 libs:: $(BROWSER_TESTS)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/
--- a/browser/metro/base/tests/browser_context_menu_tests.js
+++ b/browser/metro/base/tests/browser_context_menu_tests.js
@@ -15,43 +15,272 @@ function debugClipFlavors(aClip)
   let count = array.Count();
   info("flavors:" + count);
   for (let idx = 0; idx < count; idx++) {
     let string = array.GetElementAt(idx).QueryInterface(Ci.nsISupportsString);
     info("[" + idx + "] " + string);
   }
 }
 
+// XXX won't work with out of process content
+function emptyClipboard() {
+  Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)
+                                       .emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
+}
+
+// Image context menu tests
+gTests.push({
+  desc: "text context menu",
+  run: function test() {
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+    info(chromeRoot + "browser_context_menu_tests_02.html");
+    yield addTab(chromeRoot + "browser_context_menu_tests_02.html");
+
+    purgeEventQueue();
+
+    let win = Browser.selectedTab.browser.contentWindow;
+
+    yield hideContextUI();
+
+    ////////////////////////////////////////////////////////////
+    // Context menu in content on selected text
+
+    // select some text
+    let span = win.document.getElementById("text1");
+    win.getSelection().selectAllChildren(span);
+
+    // invoke selection context menu
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, span, 85, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    // selected text context:
+    checkContextUIMenuItemVisibility(["context-copy",
+                                      "context-search"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+    win.getSelection().removeAllRanges();
+
+    ////////////////////////////////////////////////////////////
+    // Context menu in content on selected text that includes a link
+
+    // invoke selection with link context menu
+    let link = win.document.getElementById("text2-link");
+    win.getSelection().selectAllChildren(link);
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, link, 40, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    // selected text context:
+    checkContextUIMenuItemVisibility(["context-copy",
+                                      "context-search",
+                                      "context-open-in-new-tab",
+                                      "context-copy-link"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+    win.getSelection().removeAllRanges();
+
+    ////////////////////////////////////////////////////////////
+    // Context menu in content on a link
+
+    link = win.document.getElementById("text2-link");
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, link, 40, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    // selected text context:
+    checkContextUIMenuItemVisibility(["context-open-in-new-tab",
+                                      "context-copy-link",
+                                      "context-bookmark-link"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    ////////////////////////////////////////////////////////////
+    // context in input with no selection, no data on clipboard
+
+    emptyClipboard();
+
+    let input = win.document.getElementById("text3-input");
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, input, 20, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    checkContextUIMenuItemVisibility(["context-copy",
+                                      "context-select",
+                                      "context-select-all"]);
+
+    // copy menu item should copy all text
+    let menuItem = document.getElementById("context-copy");
+    ok(menuItem, "menu item exists");
+    ok(!menuItem.hidden, "menu item visible");
+
+    let popupPromise = waitForEvent(document, "popuphidden");
+    EventUtils.synthesizeMouse(menuItem, 10, 10, {}, win);
+    yield popupPromise;
+    ok(popupPromise && !(popupPromise instanceof Error), "promise error");
+    let string = SpecialPowers.getClipboardData("text/unicode");
+    ok(string, "hello, I'm sorry but I must be going.", "copy all");
+
+    emptyClipboard();
+
+    ////////////////////////////////////////////////////////////
+    // context in input with text selection, no data on clipboard
+
+    input = win.document.getElementById("text3-input");
+    input.select();
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, input, 20, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    // selected text context:
+    checkContextUIMenuItemVisibility(["context-copy",
+                                      "context-search"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    ////////////////////////////////////////////////////////////
+    // context in input with no selection, data on clipboard
+
+    SpecialPowers.clipboardCopyString("foo");
+    input = win.document.getElementById("text3-input");
+    input.select();
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, input, 20, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    // selected text context:
+    checkContextUIMenuItemVisibility(["context-copy",
+                                      "context-search",
+                                      "context-paste"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    ////////////////////////////////////////////////////////////
+    // context in empty input, data on clipboard (paste operation)
+
+    SpecialPowers.clipboardCopyString("foo");
+    input = win.document.getElementById("text3-input");
+    input.value = "";
+
+    promise = waitForEvent(document, "popupshown");
+    sendContextMenuClickToElement(win, input, 20, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should be visible
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+
+    // selected text context:
+    checkContextUIMenuItemVisibility(["context-paste"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    ////////////////////////////////////////////////////////////
+    // context in empty input, no data on clipboard (??)
+
+    emptyClipboard();
+
+    input = win.document.getElementById("text3-input");
+    input.value = "";
+
+    promise = waitForEvent(Elements.tray, "transitionend");
+    sendContextMenuClickToElement(win, input, 20, 10);
+    yield promise;
+    ok(promise && !(promise instanceof Error), "promise error");
+
+    // should *not* be visible
+    ok(!ContextMenuUI._menuPopup._visible, "is visible");
+
+    // the test above will invoke the app bar
+    yield hideContextUI();
+
+    Browser.closeTab(Browser.selectedTab);
+    purgeEventQueue();
+  }
+});
+
 // Image context menu tests
 gTests.push({
   desc: "image context menu",
   run: function test() {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
     info(chromeRoot + "browser_context_menu_tests_01.html");
     yield addTab(chromeRoot + "browser_context_menu_tests_01.html");
 
     let win = Browser.selectedTab.browser.contentWindow;
 
+    purgeEventQueue();
+
     yield hideContextUI();
 
+    // If we don't do this, sometimes the first sendContextMenuClick
+    // will trigger the app bar.
+    yield waitForImageLoad(win, "image01");
+
     ////////////////////////////////////////////////////////////
     // Context menu options
 
     // image01 - 1x1x100x100
     let promise = waitForEvent(document, "popupshown");
     sendContextMenuClick(win, 10, 10);
     yield promise;
     ok(promise && !(promise instanceof Error), "promise error");
 
     purgeEventQueue();
 
     ok(ContextMenuUI._menuPopup._visible, "is visible");
 
-    checkContextUIMenuItemCount(4);
+    checkContextUIMenuItemVisibility(["context-save-image-lib",
+                                      "context-copy-image",
+                                      "context-copy-image-loc",
+                                      "context-open-image-tab"]);
 
     ////////////////////////////////////////////////////////////
     // Save to image library
 
     let dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
                            .getService(Components.interfaces.nsIProperties);
     let saveLocationPath = dirSvc.get("Pict", Components.interfaces.nsIFile);
     saveLocationPath.append("image01.png");
@@ -80,74 +309,74 @@ gTests.push({
     purgeEventQueue();
 
     ok(saveLocationPath.exists(), "image saved");
 
     ////////////////////////////////////////////////////////////
     // Copy image
 
     let promise = waitForEvent(document, "popupshown");
-    sendContextMenuClick(win, 30, 30);
+    sendContextMenuClick(win, 20, 20);
     yield promise;
     ok(promise && !(promise instanceof Error), "promise error");
     ok(ContextMenuUI._menuPopup._visible, "is visible");
 
     menuItem = document.getElementById("context-copy-image");
     ok(menuItem, "menu item exists");
     ok(!menuItem.hidden, "menu item visible");
     popupPromise = waitForEvent(document, "popuphidden");
     EventUtils.synthesizeMouse(menuItem, 10, 10, {}, win);
     yield popupPromise;
     ok(popupPromise && !(popupPromise instanceof Error), "promise error");
 
     purgeEventQueue();
 
     let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
     let flavors = ["image/png"];
-    ok(clip.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard), "clip has my flavor");
+    ok(clip.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard), "clip has my png flavor");
 
     ////////////////////////////////////////////////////////////
     // Copy image location
 
     promise = waitForEvent(document, "popupshown");
-    sendContextMenuClick(win, 60, 60);
+    sendContextMenuClick(win, 30, 30);
     yield promise;
     ok(promise && !(promise instanceof Error), "promise error");
     ok(ContextMenuUI._menuPopup._visible, "is visible");
 
     menuItem = document.getElementById("context-copy-image-loc");
     ok(menuItem, "menu item exists");
     ok(!menuItem.hidden, "menu item visible");
     popupPromise = waitForEvent(document, "popuphidden");
     EventUtils.synthesizeMouse(menuItem, 10, 10, {}, win);
     yield popupPromise;
     ok(popupPromise && !(popupPromise instanceof Error), "promise error");
 
     purgeEventQueue();
 
     let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
     let flavors = ["text/unicode"];
-    ok(clip.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard), "clip has my flavor");
+    ok(clip.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard), "clip has my text flavor");
 
     let xfer = Cc["@mozilla.org/widget/transferable;1"].
                createInstance(Ci.nsITransferable);
     xfer.init(null);
     xfer.addDataFlavor("text/unicode");
     clip.getData(xfer, Ci.nsIClipboard.kGlobalClipboard);
     let str = new Object();
     let strLength = new Object();
     xfer.getTransferData("text/unicode", str, strLength);
     str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
     ok(str == "chrome://mochitests/content/metro/res/image01.png", "url copied");
 
     ////////////////////////////////////////////////////////////
     // Open image in new tab
 
     promise = waitForEvent(document, "popupshown");
-    sendContextMenuClick(win, 60, 60);
+    sendContextMenuClick(win, 40, 40);
     yield promise;
     ok(promise && !(promise instanceof Error), "promise error");
     ok(ContextMenuUI._menuPopup._visible, "is visible");
 
     menuItem = document.getElementById("context-open-image-tab");
     ok(menuItem, "menu item exists");
     ok(!menuItem.hidden, "menu item visible");
     let tabPromise = waitForEvent(document, "TabOpen");
--- a/browser/metro/base/tests/browser_context_menu_tests_01.html
+++ b/browser/metro/base/tests/browser_context_menu_tests_01.html
@@ -2,13 +2,13 @@
 <html>
   <head>
     <style>
     </style>
   </head>
 <body style="padding: 1px; margin: 1px;">
 <div style="margin: 0; padding: 0;">
   <span>
-    <img width="100" height="100" src="./res/image01.png" />
+    <img id="image01" width="100" height="100" src="./res/image01.png" />
   </span>
 </div>
 </body>
 </html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/browser_context_menu_tests_02.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+    </style>
+  </head>
+<body style="padding: 10px; margin: 10px;">
+  <div style="margin: 0; padding: 5px;">
+    <span id="text1">hello, I'm sorry but I must be going.</span>
+  </div>
+  <div style="margin: 0; padding: 5px;">
+    <span id="text2"><a id="text2-link" href="#test">hello, I'm sorry but</a> I must be going.</span>
+  </div>
+  <div style="margin: 0; padding: 5px;">
+    <span id="text3"><input id="text3-input" value="hello, I'm sorry but I must be going." style="width:200px;"/></span>
+  </div>
+</body>
+</html>
\ No newline at end of file
--- a/browser/metro/base/tests/head.js
+++ b/browser/metro/base/tests/head.js
@@ -27,23 +27,43 @@ function checkContextUIMenuItemCount(aCo
   let visibleCount = 0;
   for (let idx = 0; idx < ContextMenuUI._commands.childNodes.length; idx++) {
     if (!ContextMenuUI._commands.childNodes[idx].hidden)
       visibleCount++;
   }
   is(visibleCount, aCount, "command list count");
 }
 
+function checkContextUIMenuItemVisibility(aVisibleList)
+{
+  let errors = 0;
+  for (let idx = 0; idx < ContextMenuUI._commands.childNodes.length; idx++) {
+    let item = ContextMenuUI._commands.childNodes[idx];
+    if (aVisibleList.indexOf(item.id) != -1 && item.hidden) {
+      // item should be visible
+      errors++;
+      info("should be visible:" + item.id);
+    } else if (aVisibleList.indexOf(item.id) == -1 && !item.hidden) {
+      // item should be hidden
+      errors++;
+      info("should be hidden:" + item.id);
+    }
+  }
+  is(errors, 0, "context menu item list visibility");
+}
+
 /*=============================================================================
   Asynchronous Metro ui helpers
 =============================================================================*/
 
 function hideContextUI()
 {
+  purgeEventQueue();
   if (ContextUI.isVisible) {
+    info("is visible, waiting...");
     let promise = waitForEvent(Elements.tray, "transitionend");
     ContextUI.dismiss();
     return promise;
   }
 }
 
 /*=============================================================================
   Asynchronous test helpers
@@ -88,16 +108,17 @@ function addTab(aUrl) {
  *    }
  *
  * @param aSubject the element that should receive the event
  * @param aEventName the event to wait for
  * @param aTimeoutMs the number of miliseconds to wait before giving up
  * @returns a Promise that resolves to the received event, or to an Error
  */
 function waitForEvent(aSubject, aEventName, aTimeoutMs) {
+  info("waitForEvent: on " + aSubject + " event: " + aEventName);
   let eventDeferred = Promise.defer();
   let timeoutMs = aTimeoutMs || kDefaultWait;
   let timerID = setTimeout(function wfe_canceller() {
     aSubject.removeEventListener(aEventName, onEvent);
     eventDeferred.reject( new Error(aEventName+" event timeout") );
   }, timeoutMs);
 
   function onEvent(aEvent) {
@@ -184,16 +205,33 @@ function waitForCondition(aCondition, aT
       setTimeout(testCondition, intervalMs);
     }
   }
 
   setTimeout(testCondition, 0);
   return deferred.promise;
 }
 
+/*
+ * Waits for an image in a page to load. Wrapper around waitForCondition.
+ *
+ * @param aWindow the tab or window that contains the image.
+ * @param aImageId the id of the image in the page.
+ * @returns a Promise that resolves to true, or to an Error
+ */
+function waitForImageLoad(aWindow, aImageId) {
+  let elem = aWindow.document.getElementById(aImageId);
+  return waitForCondition(function () {
+    let request = elem.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+    if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
+      return true;
+    return false;
+  }, 5000, 100);
+}
+
 /**
  * Waits a specified number of miliseconds for an observer event.
  *
  * @param aObsEvent the observer event to wait for
  * @param aTimeoutMs the number of miliseconds to wait before giving up
  * @returns a Promise that resolves to true, or to an Error
  */
 function waitForObserver(aObsEvent, aTimeoutMs) {
@@ -334,16 +372,24 @@ function synthesizeNativeMouseMUp(aEleme
 function sendContextMenuClick(aWindow, aX, aY) {
   let utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIDOMWindowUtils);
 
   utils.sendMouseEventToWindow("contextmenu", aX, aY, 2, 1, 0, true,
                                 1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
 }
 
+function sendContextMenuClickToElement(aWindow, aElement, aX, aY) {
+  let utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                      .getInterface(Components.interfaces.nsIDOMWindowUtils);
+  let rect = aElement.getBoundingClientRect();
+  utils.sendMouseEventToWindow("contextmenu", rect.left + aX, rect.top + aY, 2, 1, 0, true,
+                                1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
+}
+
 /*=============================================================================
   System utilities
 =============================================================================*/
 
 /*
  * purgeEventQueue - purges the event queue on the calling thread.
  * Pumps latent in-process message manager events awaiting delivery.
  */