Bug 1330111 - Test changes for opening username autocomplete. r=johannh draft
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 03 Feb 2017 16:41:48 -0800
changeset 479585 da4b9e070943bff6af98d31797756f3ff5195143
parent 479584 bb4200281706bc5bdc7824eb9a9f0db15be2fbaf
child 544738 fd3a9590a2b735fb118cfe92b3d169aaf895f4b5
push id44309
push usermozilla@noorenberghe.ca
push dateTue, 07 Feb 2017 00:41:56 +0000
reviewersjohannh
bugs1330111
milestone54.0a1
Bug 1330111 - Test changes for opening username autocomplete. r=johannh MozReview-Commit-ID: 2okX1Vfh5Kh
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();
   });
 }