Bug 1325538 - Add mochitest for form autofill feature. r?MattN draft
authorsteveck-chung <schung@mozilla.com>
Mon, 17 Apr 2017 18:14:14 +0800
changeset 570013 3cb44c9e78df188dc64fb7669ca1d683b2e21508
parent 566881 73752931e273091185e1e4b5231c28beed657cc8
child 571311 407ab467a7dae1cb7731213d66c8c5ed8bd7ecdf
push id56362
push userbmo:schung@mozilla.com
push dateFri, 28 Apr 2017 08:30:41 +0000
reviewersMattN
bugs1325538
milestone55.0a1
Bug 1325538 - Add mochitest for form autofill feature. r?MattN MozReview-Commit-ID: HK6dmI4m3co
browser/app/profile/firefox.js
browser/extensions/formautofill/moz.build
browser/extensions/formautofill/test/mochitest/.eslintrc.js
browser/extensions/formautofill/test/mochitest/formautofill_common.js
browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
browser/extensions/formautofill/test/mochitest/mochitest.ini
browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1605,17 +1605,17 @@ pref("browser.crashReports.unsubmittedCh
 // crash report notification across different days and shutdown
 // without a user choice before we suppress the notification for
 // some number of days.
 pref("browser.crashReports.unsubmittedCheck.chancesUntilSuppress", 4);
 pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);
 
 // Preferences for the form autofill system extension
 pref("browser.formautofill.experimental", false);
-pref("browser.formautofill.enabled", false);
+pref("browser.formautofill.enabled", true);
 pref("browser.formautofill.loglevel", "Warn");
 
 // Whether or not to restore a session with lazy-browser tabs.
 pref("browser.sessionstore.restore_tabs_lazily", true);
 
 // Enable safebrowsing v4 tables (suffixed by "-proto") update.
 #ifdef NIGHTLY_BUILD
 pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,goog-malware-proto,goog-unwanted-proto,test-malware-simple,test-unwanted-simple");
--- a/browser/extensions/formautofill/moz.build
+++ b/browser/extensions/formautofill/moz.build
@@ -16,12 +16,14 @@ FINAL_TARGET_FILES.features['formautofil
 FINAL_TARGET_PP_FILES.features['formautofill@mozilla.org'] += [
   'install.rdf.in'
 ]
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
+MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
+
 JAR_MANIFESTS += ['jar.mn']
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Form Manager')
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "plugin:mozilla/mochitest-test"
+  ],
+};
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -0,0 +1,75 @@
+/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SimpleTest.js */
+/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
+/* eslint-disable no-unused-vars */
+
+"use strict";
+
+let formFillChromeScript;
+
+function setInput(selector, value) {
+  let input = document.querySelector("input" + selector);
+  input.value = value;
+  input.focus();
+}
+
+function checkMenuEntries(expectedValues) {
+  let actualValues = getMenuEntries();
+
+  is(actualValues.length, expectedValues.length, " Checking length of expected menu");
+  for (let i = 0; i < expectedValues.length; i++) {
+    is(actualValues[i], expectedValues[i], " Checking menu entry #" + i);
+  }
+}
+
+function addProfile(profile) {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:AddProfile", {profile});
+    formFillChromeScript.addMessageListener("FormAutofillTest:ProfileAdded", function onAdded(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:ProfileAdded", onAdded);
+
+      resolve();
+    });
+  });
+}
+
+function removeProfile(guid) {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:RemoveProfile", {guid});
+    formFillChromeScript.addMessageListener("FormAutofillTest:ProfileRemoved", function onDeleted(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:ProfileRemoved", onDeleted);
+
+      resolve();
+    });
+  });
+}
+
+function updateProfile(guid, profile) {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:UpdateProfile", {profile, guid});
+    formFillChromeScript.addMessageListener("FormAutofillTest:ProfileUpdated", function onUpdated(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:ProfileUpdated", onUpdated);
+
+      resolve();
+    });
+  });
+}
+
+function formAutoFillCommonSetup() {
+  let chromeURL = SimpleTest.getTestFileURL("formautofill_parent_utils.js");
+  formFillChromeScript = SpecialPowers.loadChromeScript(chromeURL);
+  SpecialPowers.setBoolPref("dom.forms.autocomplete.experimental", true);
+  formFillChromeScript.addMessageListener("onpopupshown", ({results}) => {
+    gLastAutoCompleteResults = results;
+    if (gPopupShownListener) {
+      gPopupShownListener({results});
+    }
+  });
+
+  SimpleTest.registerCleanupFunction(() => {
+    SpecialPowers.clearUserPref("dom.forms.autocomplete.experimental");
+    formFillChromeScript.sendAsyncMessage("cleanup");
+    formFillChromeScript.destroy();
+  });
+}
+
+formAutoFillCommonSetup();
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -0,0 +1,63 @@
+// assert is available to chrome scripts loaded via SpecialPowers.loadChromeScript.
+/* global assert */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+var ParentUtils = {
+  cleanUpProfile() {
+    Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
+      Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
+
+      let profiles = result.data;
+      Services.cpmm.sendAsyncMessage("FormAutofill:RemoveProfiles",
+                                     {guids: profiles.map(profile => profile.guid)});
+    });
+
+    Services.cpmm.sendAsyncMessage("FormAutofill:GetProfiles", {searchString: ""});
+  },
+
+  updateProfile(type, chromeMsg, msgData, contentMsg) {
+    Services.cpmm.sendAsyncMessage(chromeMsg, msgData);
+    Services.obs.addObserver(function observer(subject, topic, data) {
+      if (data != type) {
+        return;
+      }
+
+      Services.obs.removeObserver(observer, topic);
+      sendAsyncMessage(contentMsg);
+    }, "formautofill-storage-changed");
+  },
+
+  observe(subject, topic, data) {
+    assert.ok(topic === "formautofill-storage-changed");
+    sendAsyncMessage("formautofill-storage-changed", {subject: null, topic, data});
+  },
+
+  cleanup() {
+    Services.obs.removeObserver(this, "formautofill-storage-changed");
+    this.cleanUpProfile();
+  },
+};
+
+ParentUtils.cleanUpProfile();
+Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
+
+addMessageListener("FormAutofillTest:AddProfile", (msg) => {
+  ParentUtils.updateProfile("add", "FormAutofill:SaveProfile", msg, "FormAutofillTest:ProfileAdded");
+});
+
+addMessageListener("FormAutofillTest:RemoveProfile", (msg) => {
+  ParentUtils.updateProfile("remove", "FormAutofill:Removefile", msg, "FormAutofillTest:ProfileRemoved");
+});
+
+addMessageListener("FormAutofillTest:UpdateProfile", (msg) => {
+  ParentUtils.updateProfile("update", "FormAutofill:SaveProfile", msg, "FormAutofillTest:ProfileUpdated");
+});
+
+addMessageListener("cleanup", () => {
+  ParentUtils.cleanup();
+});
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+  ../../../../../toolkit/components/satchel/test/satchel_common.js
+  ../../../../../toolkit/components/satchel/test/parent_utils.js
+  formautofill_common.js
+  formautofill_parent_utils.js
+
+[test_basic_autocomplete_form.html]
+
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
@@ -0,0 +1,176 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test basic autofill</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="formautofill_common.js"></script>
+  <script type="text/javascript" src="satchel_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Form autofill test: simple form profile autofill
+
+<script>
+/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
+/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
+/* import-globals-from formautofill_common.js */
+
+"use strict";
+
+let expectingPopup = null;
+let MOCK_STORAGE = [{
+  organization: "Sesame Street",
+  "street-address": "123 Sesame Street.",
+  tel: "1-345-345-3456",
+}, {
+  organization: "Mozilla",
+  "street-address": "331 E. Evelyn Avenue",
+  tel: "1-650-903-0800",
+}];
+
+function expectPopup() {
+  info("expecting a popup");
+  return new Promise(resolve => {
+    expectingPopup = resolve;
+  });
+}
+
+function popupShownListener() {
+  info("popup shown for test ");
+  if (expectingPopup) {
+    expectingPopup();
+    expectingPopup = null;
+  }
+}
+
+function checkInputFilled(element, expectedvalue) {
+  return new Promise(resolve => {
+    element.addEventListener("change", function onChange() {
+      is(element.value, expectedvalue, "Checking " + element.name + " field");
+      resolve();
+    }, {once: true});
+  });
+}
+
+function checkAutoCompleteInputFilled(element, expectedvalue) {
+  return new Promise(resolve => {
+    element.addEventListener("DOMAutoComplete", function onChange() {
+      is(element.value, expectedvalue, "Checking " + element.name + " field");
+      resolve();
+    }, {once: true});
+  });
+}
+
+function checkFormFilled(profile) {
+  let promises = [];
+  for (let prop in profile) {
+    let element = document.getElementById(prop);
+    if (document.activeElement == element) {
+      promises.push(checkAutoCompleteInputFilled(element, profile[prop]));
+    } else {
+      promises.push(checkInputFilled(element, profile[prop]));
+    }
+  }
+  doKey("return");
+  return Promise.all(promises);
+}
+
+function* setupProfileStorage() {
+  yield addProfile(MOCK_STORAGE[0]);
+  yield addProfile(MOCK_STORAGE[1]);
+}
+
+function* setupFormHistory() {
+  yield updateFormHistory([
+    {op: "add", fieldname: "tel", value: "1-234-567-890"},
+    {op: "add", fieldname: "country", value: "US"},
+  ]);
+}
+
+// Form with history only.
+add_task(function* history_only_menu_checking() {
+  yield setupFormHistory();
+
+  setInput("#tel", "");
+  doKey("down");
+  yield expectPopup();
+  checkMenuEntries(["1-234-567-890"]);
+});
+
+// Form with both history and profile storage.
+add_task(function* check_menu_when_both_existed() {
+  yield setupProfileStorage();
+
+  setInput("#organization", "");
+  doKey("down");
+  yield expectPopup();
+  checkMenuEntries(MOCK_STORAGE.map(profile =>
+    JSON.stringify({primary: profile.organization, secondary: profile["street-address"]})
+  ));
+
+  setInput("#street-address", "");
+  doKey("down");
+  yield expectPopup();
+  checkMenuEntries(MOCK_STORAGE.map(profile =>
+    JSON.stringify({primary: profile["street-address"], secondary: profile.organization})
+  ));
+
+  setInput("#tel", "");
+  doKey("down");
+  yield expectPopup();
+  checkMenuEntries(MOCK_STORAGE.map(profile =>
+    JSON.stringify({primary: profile.tel, secondary: profile["street-address"]})
+  ));
+});
+
+// Display history search result if no matched data in profiles.
+add_task(function* check_fallback_for_mismatched_field() {
+  setInput("#country", "");
+  doKey("down");
+  yield expectPopup();
+  checkMenuEntries(["US"]);
+});
+
+// Autofill the profile from dropdown menu.
+add_task(function* check_fields_after_form_autofill() {
+  setInput("#organization", "Moz");
+  doKey("down");
+  yield expectPopup();
+  checkMenuEntries(MOCK_STORAGE.map(profile =>
+    JSON.stringify({primary: profile.organization, secondary: profile["street-address"]})
+  ).slice(1));
+  doKey("down");
+  yield checkFormFilled(MOCK_STORAGE[1]);
+});
+
+// Fallback to history search after autofill profile.
+add_task(function* check_fallback_after_form_autofill() {
+  setInput("#tel", "");
+  doKey("down");
+  yield expectPopup();
+  checkMenuEntries(["1-234-567-890"]);
+});
+
+registerPopupShownListener(popupShownListener);
+
+</script>
+
+<p id="display"></p>
+
+<div id="content">
+
+  <form id="form1">
+    <p>This is a basic form.</p>
+    <p><label>organization: <input id="organization" name="organization" autocomplete="organization" type="text"></label></p>
+    <p><label>streetAddress: <input id="street-address" name="street-address" autocomplete="street-address" type="text"></label></p>
+    <p><label>tel: <input id="tel" name="tel" autocomplete="tel" type="text"></label></p>
+    <p><label>country: <input id="country" name="country" autocomplete="country" type="text"></label></p>
+  </form>
+
+</div>
+
+<pre id="test"></pre>
+</body>
+</html>