Bug 1330111 - Test changes for opening username autocomplete. r=johannh
☠☠ backed out by 566eedfbb5bf ☠ ☠
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 03 Feb 2017 16:41:48 -0800
changeset 341059 d71bc4a09493d50b118b850d3f53b47bfdf9bd87
parent 341058 9c3763aa4bed5036916ea0b9e6a4a0b331a7e24f
child 341060 a09d1e7976a715205327cd2e19b9ceb6304286e7
push id86621
push usermozilla@noorenberghe.ca
push dateTue, 07 Feb 2017 05:12:02 +0000
treeherdermozilla-inbound@d71bc4a09493 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1330111
milestone54.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 1330111 - Test changes for opening username autocomplete. r=johannh MozReview-Commit-ID: CJKpsRmWorm
toolkit/components/passwordmgr/test/mochitest/mochitest.ini
toolkit/components/passwordmgr/test/mochitest/test_autofocus_js.html
toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
toolkit/components/passwordmgr/test/mochitest/test_username_focus.html
toolkit/components/passwordmgr/test/pwmgr_common.js
toolkit/components/satchel/test/satchel_common.js
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
+++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
@@ -59,9 +59,11 @@ skip-if = os == "linux" || toolkit == 'a
 skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_prompt_noWindow.html]
 skip-if = e10s || toolkit == 'android' # Tests desktop prompts. e10s: bug 1217876
 [test_prompt_promptAuth.html]
 skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_prompt_promptAuth_proxy.html]
 skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_recipe_login_fields.html]
+[test_username_focus.html]
+skip-if = toolkit == 'android' # android:autocomplete.
 [test_xhr_2.html]
--- a/toolkit/components/passwordmgr/test/mochitest/test_autofocus_js.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autofocus_js.html
@@ -51,38 +51,64 @@ let iframeDoc;
 add_task(function* setup() {
   yield new Promise(resolve => {
     iframe.addEventListener("load", function() {
       resolve();
     }, {once: true});
   });
 
   iframeDoc = iframe.contentDocument;
+
+  SimpleTest.requestFlakyTimeout("Giving a chance for the unexpected popupshown to occur");
 });
 
 add_task(function* test_initial_focus() {
   let results = yield notifyMenuChanged(2, "name");
   checkArrayValues(results, ["name", "name1"], "Two results");
   doKey("down");
   doKey("return");
   yield promiseFormsProcessed();
   is(iframeDoc.getElementById("form-basic-password").value, "pass", "Check first password filled");
   let popupState = yield getPopupState();
   is(popupState.open, false, "Check popup is now closed");
 });
 
+// This depends on the filling from the previous test.
+add_task(function* test_not_reopened_if_filled() {
+  listenForUnexpectedPopupShown();
+  let usernameField = iframeDoc.getElementById("form-basic-username");
+  usernameField.focus();
+  info("Waiting to see if a popupshown occurs");
+  yield new Promise(resolve => setTimeout(resolve, 1000));
+
+  // cleanup
+  gPopupShownExpected = true;
+  iframeDoc.getElementById("form-basic-submit").focus();
+});
+
+add_task(function* test_reopened_after_edit_not_matching_saved() {
+  let usernameField = iframeDoc.getElementById("form-basic-username");
+  usernameField.value = "nam";
+  let shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+  iframeDoc.getElementById("form-basic-submit").focus();
+});
+
 add_task(function* test_not_reopened_after_selecting() {
   let formFillController = SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].
                            getService(SpecialPowers.Ci.nsIFormFillController);
-let usernameField = iframeDoc.getElementById("form-basic-username");
+  let usernameField = iframeDoc.getElementById("form-basic-username");
+  usernameField.value = "";
+  iframeDoc.getElementById("form-basic-password").value = "";
   listenForUnexpectedPopupShown();
   formFillController.markAsLoginManagerField(usernameField);
-  SimpleTest.requestFlakyTimeout("Giving a chance for the unexpected popupshown to occur");
+  info("Waiting to see if a popupshown occurs");
   yield new Promise(resolve => setTimeout(resolve, 1000));
 
-  // cleanup
+  // Cleanup
   gPopupShownExpected = true;
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -828,16 +828,19 @@ add_task(function* test_form12_formless(
 });
 
 add_task(function* test_form12_open_on_trusted_focus() {
   uname = $_(12, "uname");
   pword = $_(12, "pword");
   uname.value = "";
   pword.value = "";
 
+  // Move focus to the password field so we can test the first click on the
+  // username field.
+  pword.focus();
   checkACForm("", "");
   const firePrivEventPromise = new Promise((resolve) => {
     uname.addEventListener("click", (e) => {
       ok(e.isTrusted, "Ensure event is trusted");
       resolve();
     });
   });
   const shownPromise = promiseACShown();
--- a/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
@@ -1,22 +1,21 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Test basic login autocomplete</title>
+  <title>Test insecure form field autocomplete</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <script type="text/javascript" src="pwmgr_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
-Login Manager test: multiple login autocomplete
 
 <script>
 var chromeScript = runChecksAfterCommonInit();
 
 var setupScript = runInParent(function setup() {
   const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
   Cu.import("resource://gre/modules/Services.jsm");
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/mochitest/test_username_focus.html
@@ -0,0 +1,263 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test interaction between autocomplete and focus on username fields</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="satchel_common.js"></script>
+  <script type="text/javascript" src="pwmgr_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+let pwmgrCommonScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
+
+let readyPromise = registerRunTests();
+let chromeScript = runInParent(function chromeSetup() {
+  const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+  let pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+
+  let login1A  = Cc["@mozilla.org/login-manager/loginInfo;1"].
+                 createInstance(Ci.nsILoginInfo);
+  let login1B  = Cc["@mozilla.org/login-manager/loginInfo;1"].
+                 createInstance(Ci.nsILoginInfo);
+  let login2A  = Cc["@mozilla.org/login-manager/loginInfo;1"].
+                 createInstance(Ci.nsILoginInfo);
+  let login2B  = Cc["@mozilla.org/login-manager/loginInfo;1"].
+                 createInstance(Ci.nsILoginInfo);
+  let login2C  = Cc["@mozilla.org/login-manager/loginInfo;1"].
+                 createInstance(Ci.nsILoginInfo);
+
+  login1A.init("http://mochi.test:8888", "http://username-focus-1", null,
+               "testuser1A", "testpass1A", "", "");
+
+  login2A.init("http://mochi.test:8888", "http://username-focus-2", null,
+               "testuser2A", "testpass2A", "", "");
+  login2B.init("http://mochi.test:8888", "http://username-focus-2", null,
+               "testuser2B", "testpass2B", "", "");
+
+  pwmgr.addLogin(login1A);
+  pwmgr.addLogin(login2A);
+  pwmgr.addLogin(login2B);
+});
+</script>
+
+<p id="display"></p>
+<div id="content">
+  <!-- first 3 forms have a matching user+pass login -->
+
+  <!-- user+pass form. -->
+  <form id="form-autofilled" action="http://username-focus-1">
+    <input  type="text"     name="uname">
+    <input  type="password" name="pword">
+    <button type="submit" name="submit">Submit</button>
+  </form>
+
+  <!-- user+pass form, username prefilled -->
+  <form id="form-autofilled-prefilled-un" action="http://username-focus-1">
+    <input  type="text"     name="uname" value="testuser1A">
+    <input  type="password" name="pword">
+    <button type="submit">Submit</button>
+  </form>
+
+  <!-- user+pass form. -->
+  <form id="form-autofilled-focused-dynamic" action="http://username-focus-1">
+    <input  type="text"             name="uname">
+    <input  type="not-yet-password" name="pword">
+    <button type="submit">Submit</button>
+  </form>
+
+
+  <!-- next 5 forms have matching user+pass (2x) logins -->
+
+  <!-- user+pass form. -->
+  <form id="form-multiple" action="http://username-focus-2">
+    <input  type="text"     name="uname">
+    <input  type="password" name="pword">
+    <button type="submit">Submit</button>
+  </form>
+
+  <!-- user+pass form dynamic with existing focus -->
+  <form id="form-multiple-dynamic" action="http://username-focus-2">
+    <input  type="text"             name="uname">
+    <input  type="not-yet-password" name="pword">
+    <button type="submit">Submit</button>
+  </form>
+
+  <!-- user+pass form, username prefilled -->
+  <form id="form-multiple-prefilled-un1" action="http://username-focus-2">
+    <input  type="text"     name="uname" value="testuser2A">
+    <input  type="password" name="pword">
+    <button type="submit">Submit</button>
+  </form>
+
+  <!-- user+pass form, different username prefilled -->
+  <form id="form-multiple-prefilled-un2" action="http://username-focus-2">
+    <input  type="text"     name="uname" value="testuser2B">
+    <input  type="password" name="pword">
+    <button type="submit">Submit</button>
+  </form>
+
+  <!-- user+pass form, username prefilled with existing focus -->
+  <form id="form-multiple-prefilled-focused-dynamic" action="http://username-focus-2">
+    <input  type="text"             name="uname" value="testuser2B">
+    <input  type="not-yet-password" name="pword">
+    <button type="submit">Submit</button>
+  </form>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+function removeFocus() {
+  $_("-autofilled", "submit").focus();
+}
+
+add_task(function* setup() {
+  yield SpecialPowers.pushPrefEnv({"set": [
+    ["security.insecure_field_warning.contextual.enabled", false],
+  ]});
+
+  ok(readyPromise, "check promise is available");
+  yield readyPromise;
+});
+
+add_task(function* test_autofilled() {
+  let usernameField = $_("-autofilled", "uname");
+  info("Username and password already filled so don't show autocomplete");
+  let noPopupPromise = promiseNoUnexpectedPopupShown();
+  usernameField.focus();
+  yield noPopupPromise;
+
+  removeFocus();
+  usernameField.value = "testuser";
+  info("Focus when we don't have an exact match");
+  shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+});
+
+add_task(function* test_autofilled_prefilled_un() {
+  let usernameField = $_("-autofilled-prefilled-un", "uname");
+  info("Username and password already filled so don't show autocomplete");
+  let noPopupPromise = promiseNoUnexpectedPopupShown();
+  usernameField.focus();
+  yield noPopupPromise;
+
+  removeFocus();
+  usernameField.value = "testuser";
+  info("Focus when we don't have an exact match");
+  shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+});
+
+add_task(function* test_autofilled_focused_dynamic() {
+  let usernameField = $_("-autofilled-focused-dynamic", "uname");
+  let passwordField = $_("-autofilled-focused-dynamic", "pword");
+  info("Username and password will be filled while username focused");
+  let noPopupPromise = promiseNoUnexpectedPopupShown();
+  usernameField.focus();
+  yield noPopupPromise;
+  info("triggering autofill");
+  noPopupPromise = promiseNoUnexpectedPopupShown();
+  passwordField.type = "password";
+  yield noPopupPromise;
+
+  let popupState = yield getPopupState();
+  is(popupState.open, false, "Check popup is closed");
+
+  removeFocus();
+  passwordField.value = "test";
+  info("Focus when we don't have an exact match");
+  shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+});
+
+// Begin testing forms that have multiple saved logins
+
+add_task(function* test_multiple() {
+  let usernameField = $_("-multiple", "uname");
+  info("Fields not filled due to multiple so autocomplete upon focus");
+  shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+});
+
+add_task(function* test_multiple_dynamic() {
+  let usernameField = $_("-multiple-dynamic", "uname");
+  let passwordField = $_("-multiple-dynamic", "pword");
+  info("Fields not filled but username is focused upon marking so open");
+  let noPopupPromise = promiseNoUnexpectedPopupShown();
+  usernameField.focus();
+  yield noPopupPromise;
+
+  info("triggering _fillForm code");
+  let shownPromise = promiseACShown();
+  passwordField.type = "password";
+  yield shownPromise;
+});
+
+add_task(function* test_multiple_prefilled_un1() {
+  let usernameField = $_("-multiple-prefilled-un1", "uname");
+  info("Username and password already filled so don't show autocomplete");
+  let noPopupPromise = promiseNoUnexpectedPopupShown();
+  usernameField.focus();
+  yield noPopupPromise;
+
+  removeFocus();
+  usernameField.value = "testuser";
+  info("Focus when we don't have an exact match");
+  shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+});
+
+add_task(function* test_multiple_prefilled_un2() {
+  let usernameField = $_("-multiple-prefilled-un2", "uname");
+  info("Username and password already filled so don't show autocomplete");
+  let noPopupPromise = promiseNoUnexpectedPopupShown();
+  usernameField.focus();
+  yield noPopupPromise;
+
+  removeFocus();
+  usernameField.value = "testuser";
+  info("Focus when we don't have an exact match");
+  shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+});
+
+add_task(function* test_multiple_prefilled_focused_dynamic() {
+  let usernameField = $_("-multiple-prefilled-focused-dynamic", "uname");
+  let passwordField = $_("-multiple-prefilled-focused-dynamic", "pword");
+  info("Username and password will be filled while username focused");
+  let noPopupPromise = promiseNoUnexpectedPopupShown();
+  usernameField.focus();
+  yield noPopupPromise;
+  info("triggering autofill");
+  noPopupPromise = promiseNoUnexpectedPopupShown();
+  passwordField.type = "password";
+  yield noPopupPromise;
+
+  let popupState = yield getPopupState();
+  is(popupState.open, false, "Check popup is closed");
+
+  removeFocus();
+  passwordField.value = "test";
+  info("Focus when we don't have an exact match");
+  shownPromise = promiseACShown();
+  usernameField.focus();
+  yield shownPromise;
+});
+
+add_task(function* cleanup() {
+  removeFocus();
+});
+</script>
+</pre>
+</body>
+</html>
--- a/toolkit/components/passwordmgr/test/pwmgr_common.js
+++ b/toolkit/components/passwordmgr/test/pwmgr_common.js
@@ -161,45 +161,48 @@ function commonInit(selfFilling) {
   if (this.sendAsyncMessage) {
     sendAsyncMessage("registerRunTests");
   } else {
     registerRunTests();
   }
 }
 
 function registerRunTests() {
-  // We provide a general mechanism for our tests to know when they can
-  // safely run: we add a final form that we know will be filled in, wait
-  // for the login manager to tell us that it's filled in and then continue
-  // with the rest of the tests.
-  window.addEventListener("DOMContentLoaded", (event) => {
-    var form = document.createElement("form");
-    form.id = "observerforcer";
-    var username = document.createElement("input");
-    username.name = "testuser";
-    form.appendChild(username);
-    var password = document.createElement("input");
-    password.name = "testpass";
-    password.type = "password";
-    form.appendChild(password);
+  return new Promise(resolve => {
+    // We provide a general mechanism for our tests to know when they can
+    // safely run: we add a final form that we know will be filled in, wait
+    // for the login manager to tell us that it's filled in and then continue
+    // with the rest of the tests.
+    window.addEventListener("DOMContentLoaded", (event) => {
+      var form = document.createElement("form");
+      form.id = "observerforcer";
+      var username = document.createElement("input");
+      username.name = "testuser";
+      form.appendChild(username);
+      var password = document.createElement("input");
+      password.name = "testpass";
+      password.type = "password";
+      form.appendChild(password);
 
-    var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
-      var formLikeRoot = subject.QueryInterface(SpecialPowers.Ci.nsIDOMNode);
-      if (formLikeRoot.id !== "observerforcer")
-        return;
-      SpecialPowers.removeObserver(observer, "passwordmgr-processed-form");
-      formLikeRoot.remove();
-      SimpleTest.executeSoon(() => {
-        var runTestEvent = new Event("runTests");
-        window.dispatchEvent(runTestEvent);
+      var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
+        var formLikeRoot = subject.QueryInterface(SpecialPowers.Ci.nsIDOMNode);
+        if (formLikeRoot.id !== "observerforcer")
+          return;
+        SpecialPowers.removeObserver(observer, "passwordmgr-processed-form");
+        formLikeRoot.remove();
+        SimpleTest.executeSoon(() => {
+          var runTestEvent = new Event("runTests");
+          window.dispatchEvent(runTestEvent);
+          resolve();
+        });
       });
+      SpecialPowers.addObserver(observer, "passwordmgr-processed-form", false);
+
+      document.body.appendChild(form);
     });
-    SpecialPowers.addObserver(observer, "passwordmgr-processed-form", false);
-
-    document.body.appendChild(form);
   });
 }
 
 const masterPassword = "omgsecret!";
 
 function enableMasterPassword() {
   setMasterPassword(true);
 }
--- a/toolkit/components/satchel/test/satchel_common.js
+++ b/toolkit/components/satchel/test/satchel_common.js
@@ -224,44 +224,51 @@ function getPopupState(then = null) {
         then(state);
       }
       resolve(state);
     });
   });
 }
 
 function listenForUnexpectedPopupShown() {
-  gChromeScript.addMessageListener("onpopupshown", function onPopupShown() {
+  gPopupShownListener = function onPopupShown() {
     if (!gPopupShownExpected) {
       ok(false, "Unexpected autocomplete popupshown event");
     }
-  });
+  };
+}
+
+function* promiseNoUnexpectedPopupShown() {
+  gPopupShownExpected = false;
+  listenForUnexpectedPopupShown();
+  SimpleTest.requestFlakyTimeout("Giving a chance for an unexpected popupshown to occur");
+  yield new Promise(resolve => setTimeout(resolve, 1000));
 }
 
 /**
  * Resolve at the next popupshown event for the autocomplete popup
  * @return {Promise} with the results
  */
 function promiseACShown() {
   gPopupShownExpected = true;
   return new Promise(resolve => {
-    gChromeScript.addMessageListener("onpopupshown", ({ results }) => {
+    gPopupShownListener = ({ results }) => {
       gPopupShownExpected = false;
       resolve(results);
-    });
+    };
   });
 }
 
 function satchelCommonSetup() {
   var chromeURL = SimpleTest.getTestFileURL("parent_utils.js");
   gChromeScript = SpecialPowers.loadChromeScript(chromeURL);
   gChromeScript.addMessageListener("onpopupshown", ({ results }) => {
     gLastAutoCompleteResults = results;
     if (gPopupShownListener)
-      gPopupShownListener();
+      gPopupShownListener({results});
   });
 
   SimpleTest.registerCleanupFunction(() => {
     gChromeScript.sendAsyncMessage("cleanup");
     gChromeScript.destroy();
   });
 }