Bug 1325538 - Add mochitest for form autofill feature. r?MattN
MozReview-Commit-ID: HK6dmI4m3co
--- 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>