From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../test/mochitest/creditCard/mochitest.ini | 26 ++ .../test_basic_creditcard_autocomplete_form.html | 251 +++++++++++ .../test/mochitest/creditCard/test_clear_form.html | 205 +++++++++ .../test_clear_form_expiry_select_elements.html | 211 +++++++++ .../test_creditcard_autocomplete_off.html | 96 +++++ ...w_highlight_with_multiple_cc_number_fields.html | 174 ++++++++ .../test_preview_highlight_with_site_prefill.html | 110 +++++ .../test/mochitest/formautofill_common.js | 478 +++++++++++++++++++++ .../test/mochitest/formautofill_parent_utils.js | 304 +++++++++++++ .../formautofill/test/mochitest/mochitest.ini | 23 + .../mochitest/test_address_level_1_submission.html | 102 +++++ .../mochitest/test_autofill_and_ordinal_forms.html | 116 +++++ .../test/mochitest/test_autofocus_form.html | 69 +++ .../mochitest/test_basic_autocomplete_form.html | 220 ++++++++++ .../test/mochitest/test_form_changes.html | 128 ++++++ .../test_formautofill_preview_highlight.html | 121 ++++++ .../test_multi_locale_CA_address_form.html | 273 ++++++++++++ .../test/mochitest/test_multiple_forms.html | 67 +++ .../test/mochitest/test_on_address_submission.html | 121 ++++++ 19 files changed, 3095 insertions(+) create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html create mode 100644 browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html create mode 100644 browser/extensions/formautofill/test/mochitest/formautofill_common.js create mode 100644 browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js create mode 100644 browser/extensions/formautofill/test/mochitest/mochitest.ini create mode 100644 browser/extensions/formautofill/test/mochitest/test_address_level_1_submission.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_autofill_and_ordinal_forms.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_autofocus_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_form_changes.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_formautofill_preview_highlight.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_multiple_forms.html create mode 100644 browser/extensions/formautofill/test/mochitest/test_on_address_submission.html (limited to 'browser/extensions/formautofill/test/mochitest') diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini b/browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini new file mode 100644 index 0000000000..d7a7ea2fd8 --- /dev/null +++ b/browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini @@ -0,0 +1,26 @@ +[DEFAULT] +prefs = + extensions.formautofill.creditCards.supported=on + extensions.formautofill.creditCards.enabled=true + extensions.formautofill.reauth.enabled=true +support-files = + !/toolkit/components/satchel/test/satchel_common.js + ../../../../../../toolkit/components/satchel/test/parent_utils.js + !/toolkit/components/satchel/test/parent_utils.js + !/browser/extensions/formautofill/test/mochitest/formautofill_common.js + !/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js +skip-if = xorigin + toolkit == 'android' # bug 1730213 + +[test_basic_creditcard_autocomplete_form.html] +scheme=https +[test_clear_form.html] +scheme=https +[test_clear_form_expiry_select_elements.html] +scheme=https +[test_creditcard_autocomplete_off.html] +scheme=https +[test_preview_highlight_with_multiple_cc_number_fields.html] +scheme=https +[test_preview_highlight_with_site_prefill.html] +scheme=https diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html new file mode 100644 index 0000000000..0764bef0eb --- /dev/null +++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html @@ -0,0 +1,251 @@ + + + + + Test basic autofill + + + + + + + +Form autofill test: simple form credit card autofill + + + +

+ +
+ +
+

This is a basic form.

+

+

+

+

+

+

+
+
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html
new file mode 100644
index 0000000000..3d8049f053
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html
@@ -0,0 +1,205 @@
+
+
+
+  
+  Test form autofill - clear form button
+  
+  
+  
+  
+  
+
+
+Form autofill test: clear form button
+
+
+
+

+ +
+ +
+

This is a basic form.

+

+

+

+

+ +

+

+

+

+

+
+ +
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html
new file mode 100644
index 0000000000..4fc989a36e
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html
@@ -0,0 +1,211 @@
+
+
+
+  
+  Test form autofill - clear form button with select elements
+  
+  
+  
+  
+  
+
+
+Form autofill test: clear form button with select elements.
+
+
+
+

+ +
+ +
+

This is a basic form.

+

+

+

+

+ +

+

+ +

+ +

+

+
+ +
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html
new file mode 100644
index 0000000000..225d828ccb
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html
@@ -0,0 +1,96 @@
+
+
+
+  
+  Test basic autofill
+  
+  
+  
+  
+  
+
+
+Form autofill test: simple form credit card autofill
+
+
+
+

+ +
+
+

This is a Credit Card form with autocomplete="off" cc-name field.

+

+

+
+
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html
new file mode 100644
index 0000000000..c39877d1b7
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html
@@ -0,0 +1,174 @@
+
+
+
+  
+  Test form autofill - preview and highlight with multiple cc number fields
+  
+  
+  
+  
+  
+
+
+Form autofill test: preview and highlight multiple cc number fields
+
+
+

+
+ +
+

This is a basic credit card form.

+

card number subsection 1:

+

card number subsection 2:

+

card number subsection 3:

+

card number subsection 4:

+

cardholder name:

+

expiration month:

+

expiration year:

+
+
+

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html
new file mode 100644
index 0000000000..090eb9290e
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html
@@ -0,0 +1,110 @@
+
+
+
+  
+  Test form autofill - preview and highlight with site prefill
+  
+  
+  
+  
+  
+
+
+Form autofill test: preview and highlight field that has been filled by site
+
+
+

+
+ +
+

This is a basic credit card form.

+

card number:

+

cardholder name:

+

expiration month:

+

expiration year:

+
+
+

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/formautofill_common.js b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
new file mode 100644
index 0000000000..d65f0beb49
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -0,0 +1,478 @@
+/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SimpleTest.js */
+/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/EventUtils.js */
+/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
+/* eslint-disable no-unused-vars */
+
+"use strict";
+
+let formFillChromeScript;
+let defaultTextColor;
+let defaultDisabledTextColor;
+let expectingPopup = null;
+
+const { FormAutofillUtils } = SpecialPowers.ChromeUtils.importESModule(
+  "resource://gre/modules/shared/FormAutofillUtils.sys.mjs"
+);
+
+async function sleep(ms = 500, reason = "Intentionally wait for UI ready") {
+  SimpleTest.requestFlakyTimeout(reason);
+  await new Promise(resolve => setTimeout(resolve, ms));
+}
+
+async function focusAndWaitForFieldsIdentified(
+  input,
+  mustBeIdentified = false
+) {
+  info("expecting the target input being focused and indentified");
+  if (typeof input === "string") {
+    input = document.querySelector(input);
+  }
+  const rootElement = input.form || input.ownerDocument.documentElement;
+  const previouslyFocused = input != document.activeElement;
+
+  input.focus();
+
+  if (mustBeIdentified) {
+    rootElement.removeAttribute("test-formautofill-identified");
+  }
+  if (rootElement.hasAttribute("test-formautofill-identified")) {
+    return;
+  }
+  if (!previouslyFocused) {
+    await new Promise(resolve => {
+      formFillChromeScript.addMessageListener(
+        "FormAutofillTest:FieldsIdentified",
+        function onIdentified() {
+          formFillChromeScript.removeMessageListener(
+            "FormAutofillTest:FieldsIdentified",
+            onIdentified
+          );
+          resolve();
+        }
+      );
+    });
+  }
+  // In order to ensure that "markAsAutofillField" is fully executed, a short period
+  // of timeout is still required.
+  await sleep(300, "Guarantee asynchronous identifyAutofillFields is invoked");
+  rootElement.setAttribute("test-formautofill-identified", "true");
+}
+
+async function setInput(selector, value, userInput = false) {
+  const input = document.querySelector("input" + selector);
+  if (userInput) {
+    SpecialPowers.wrap(input).setUserInput(value);
+  } else {
+    input.value = value;
+  }
+  await focusAndWaitForFieldsIdentified(input);
+
+  return input;
+}
+
+function clickOnElement(selector) {
+  let element = document.querySelector(selector);
+
+  if (!element) {
+    throw new Error("Can not find the element");
+  }
+
+  SimpleTest.executeSoon(() => element.click());
+}
+
+// The equivalent helper function to getAdaptedProfiles in FormAutofillHandler.jsm that
+// transforms the given profile to expected filled profile.
+function _getAdaptedProfile(profile) {
+  const adaptedProfile = Object.assign({}, profile);
+
+  if (profile["street-address"]) {
+    adaptedProfile["street-address"] = FormAutofillUtils.toOneLineAddress(
+      profile["street-address"]
+    );
+  }
+
+  return adaptedProfile;
+}
+
+async function checkFieldHighlighted(elem, expectedValue) {
+  let isHighlightApplied;
+  await SimpleTest.promiseWaitForCondition(function checkHighlight() {
+    isHighlightApplied = elem.matches(":autofill");
+    return isHighlightApplied === expectedValue;
+  }, `Checking #${elem.id} highlight style`);
+
+  is(isHighlightApplied, expectedValue, `Checking #${elem.id} highlight style`);
+}
+
+async function checkFieldPreview(elem, expectedValue) {
+  is(
+    SpecialPowers.wrap(elem).previewValue,
+    expectedValue,
+    `Checking #${elem.id} previewValue`
+  );
+  let isTextColorApplied;
+  await SimpleTest.promiseWaitForCondition(function checkPreview() {
+    const computedStyle = window.getComputedStyle(elem);
+    const actualColor = computedStyle.getPropertyValue("color");
+    if (elem.disabled) {
+      isTextColorApplied = actualColor !== defaultDisabledTextColor;
+    } else {
+      isTextColorApplied = actualColor !== defaultTextColor;
+    }
+    return isTextColorApplied === !!expectedValue;
+  }, `Checking #${elem.id} preview style`);
+
+  is(isTextColorApplied, !!expectedValue, `Checking #${elem.id} preview style`);
+}
+
+async function checkFormFieldsStyle(profile, isPreviewing = true) {
+  const elems = document.querySelectorAll("input, select");
+
+  for (const elem of elems) {
+    let fillableValue;
+    let previewValue;
+    let isElementEligible =
+      FormAutofillUtils.isCreditCardOrAddressFieldType(elem) &&
+      FormAutofillUtils.isFieldAutofillable(elem);
+    if (!isElementEligible) {
+      fillableValue = "";
+      previewValue = "";
+    } else {
+      fillableValue = profile && profile[elem.id];
+      previewValue = (isPreviewing && fillableValue) || "";
+    }
+    await checkFieldHighlighted(elem, !!fillableValue);
+    await checkFieldPreview(elem, previewValue);
+  }
+}
+
+function checkFieldValue(elem, expectedValue) {
+  if (typeof elem === "string") {
+    elem = document.querySelector(elem);
+  }
+  is(elem.value, String(expectedValue), "Checking " + elem.id + " field");
+}
+
+async function triggerAutofillAndCheckProfile(profile) {
+  let adaptedProfile = _getAdaptedProfile(profile);
+  const promises = [];
+  for (const [fieldName, value] of Object.entries(adaptedProfile)) {
+    info(`triggerAutofillAndCheckProfile: ${fieldName}`);
+    const element = document.getElementById(fieldName);
+    const expectingEvent =
+      document.activeElement == element ? "input" : "change";
+    const checkFieldAutofilled = Promise.all([
+      new Promise(resolve => {
+        let beforeInputFired = false;
+        let hadEditor = SpecialPowers.wrap(element).hasEditor;
+        element.addEventListener(
+          "beforeinput",
+          event => {
+            beforeInputFired = true;
+            is(
+              event.inputType,
+              "insertReplacementText",
+              'inputType value should be "insertReplacementText"'
+            );
+            is(
+              event.data,
+              String(value),
+              `data value of "beforeinput" should be "${value}"`
+            );
+            is(
+              event.dataTransfer,
+              null,
+              'dataTransfer of "beforeinput" should be null'
+            );
+            is(
+              event.getTargetRanges().length,
+              0,
+              'getTargetRanges() of "beforeinput" should return empty array'
+            );
+            is(
+              event.cancelable,
+              SpecialPowers.getBoolPref(
+                "dom.input_event.allow_to_cancel_set_user_input"
+              ),
+              `"beforeinput" event should be cancelable on ${element.tagName} unless it's suppressed by the pref`
+            );
+            is(
+              event.bubbles,
+              true,
+              `"beforeinput" event should always bubble on ${element.tagName}`
+            );
+            resolve();
+          },
+          { once: true }
+        );
+        element.addEventListener(
+          "input",
+          event => {
+            if (element.tagName == "INPUT" && element.type == "text") {
+              if (hadEditor) {
+                ok(
+                  beforeInputFired,
+                  `"beforeinput" event should've been fired before "input" event on ${element.tagName}`
+                );
+              } else {
+                ok(
+                  beforeInputFired,
+                  `"beforeinput" event should've been fired before "input" event on ${element.tagName}`
+                );
+              }
+              ok(
+                event instanceof InputEvent,
+                `"input" event should be dispatched with InputEvent interface on ${element.tagName}`
+              );
+              is(
+                event.inputType,
+                "insertReplacementText",
+                'inputType value should be "insertReplacementText"'
+              );
+              is(event.data, String(value), `data value should be "${value}"`);
+              is(event.dataTransfer, null, "dataTransfer should be null");
+              is(
+                event.getTargetRanges().length,
+                0,
+                "getTargetRanges() should return empty array"
+              );
+            } else {
+              ok(
+                !beforeInputFired,
+                `"beforeinput" event shouldn't be fired on ${element.tagName}`
+              );
+              ok(
+                event instanceof Event && !(event instanceof UIEvent),
+                `"input" event should be dispatched with Event interface on ${element.tagName}`
+              );
+            }
+            is(
+              event.cancelable,
+              false,
+              `"input" event should be never cancelable on ${element.tagName}`
+            );
+            is(
+              event.bubbles,
+              true,
+              `"input" event should always bubble on ${element.tagName}`
+            );
+            resolve();
+          },
+          { once: true }
+        );
+      }),
+      new Promise(resolve =>
+        element.addEventListener(expectingEvent, resolve, { once: true })
+      ),
+    ]).then(() => checkFieldValue(element, value));
+
+    promises.push(checkFieldAutofilled);
+  }
+  // Press Enter key and trigger form autofill.
+  synthesizeKey("KEY_Enter");
+
+  return Promise.all(promises);
+}
+
+async function onStorageChanged(type) {
+  info(`expecting the storage changed: ${type}`);
+  return new Promise(resolve => {
+    formFillChromeScript.addMessageListener(
+      "formautofill-storage-changed",
+      function onChanged(data) {
+        formFillChromeScript.removeMessageListener(
+          "formautofill-storage-changed",
+          onChanged
+        );
+        is(data.data, type, `Receive ${type} storage changed event`);
+        resolve();
+      }
+    );
+  });
+}
+
+function checkMenuEntries(expectedValues, isFormAutofillResult = true) {
+  let actualValues = getMenuEntries();
+  // Expect one more item would appear at the bottom as the footer if the result is from form autofill.
+  let expectedLength = isFormAutofillResult
+    ? expectedValues.length + 1
+    : expectedValues.length;
+
+  is(actualValues.length, expectedLength, " Checking length of expected menu");
+  for (let i = 0; i < expectedValues.length; i++) {
+    is(actualValues[i], expectedValues[i], " Checking menu entry #" + i);
+  }
+}
+
+function invokeAsyncChromeTask(message, payload = {}) {
+  info(`expecting the chrome task finished: ${message}`);
+  return formFillChromeScript.sendQuery(message, payload);
+}
+
+async function addAddress(address) {
+  await invokeAsyncChromeTask("FormAutofillTest:AddAddress", { address });
+  await sleep();
+}
+
+async function removeAddress(guid) {
+  return invokeAsyncChromeTask("FormAutofillTest:RemoveAddress", { guid });
+}
+
+async function updateAddress(guid, address) {
+  return invokeAsyncChromeTask("FormAutofillTest:UpdateAddress", {
+    address,
+    guid,
+  });
+}
+
+async function checkAddresses(expectedAddresses) {
+  return invokeAsyncChromeTask("FormAutofillTest:CheckAddresses", {
+    expectedAddresses,
+  });
+}
+
+async function cleanUpAddresses() {
+  return invokeAsyncChromeTask("FormAutofillTest:CleanUpAddresses");
+}
+
+async function addCreditCard(creditcard) {
+  await invokeAsyncChromeTask("FormAutofillTest:AddCreditCard", { creditcard });
+  await sleep();
+}
+
+async function removeCreditCard(guid) {
+  return invokeAsyncChromeTask("FormAutofillTest:RemoveCreditCard", { guid });
+}
+
+async function checkCreditCards(expectedCreditCards) {
+  return invokeAsyncChromeTask("FormAutofillTest:CheckCreditCards", {
+    expectedCreditCards,
+  });
+}
+
+async function cleanUpCreditCards() {
+  return invokeAsyncChromeTask("FormAutofillTest:CleanUpCreditCards");
+}
+
+async function cleanUpStorage() {
+  await cleanUpAddresses();
+  await cleanUpCreditCards();
+}
+
+async function canTestOSKeyStoreLogin() {
+  let { canTest } = await invokeAsyncChromeTask(
+    "FormAutofillTest:CanTestOSKeyStoreLogin"
+  );
+  return canTest;
+}
+
+async function waitForOSKeyStoreLogin(login = false) {
+  await invokeAsyncChromeTask("FormAutofillTest:OSKeyStoreLogin", { login });
+}
+
+function patchRecordCCNumber(record) {
+  const number = record["cc-number"];
+  const ccNumberFmt = {
+    affix: "****",
+    label: number.substr(-4),
+  };
+
+  return Object.assign({}, record, { ccNumberFmt });
+}
+
+// Utils for registerPopupShownListener(in satchel_common.js) that handles dropdown popup
+// Please call "initPopupListener()" in your test and "await expectPopup()"
+// if you want to wait for dropdown menu displayed.
+function expectPopup() {
+  info("expecting a popup");
+  return new Promise(resolve => {
+    expectingPopup = resolve;
+  });
+}
+
+function notExpectPopup(ms = 500) {
+  info("not expecting a popup");
+  return new Promise((resolve, reject) => {
+    expectingPopup = reject.bind(this, "Unexpected Popup");
+    // TODO: We don't have an event to notify no popup showing, so wait for 500
+    // ms (in default) to predict any unexpected popup showing.
+    setTimeout(resolve, ms);
+  });
+}
+
+function popupShownListener() {
+  info("popup shown for test ");
+  if (expectingPopup) {
+    expectingPopup();
+    expectingPopup = null;
+  }
+}
+
+function initPopupListener() {
+  registerPopupShownListener(popupShownListener);
+}
+
+async function triggerPopupAndHoverItem(fieldSelector, selectIndex) {
+  await focusAndWaitForFieldsIdentified(fieldSelector);
+  synthesizeKey("KEY_ArrowDown");
+  await expectPopup();
+  for (let i = 0; i <= selectIndex; i++) {
+    synthesizeKey("KEY_ArrowDown");
+  }
+  await notifySelectedIndex(selectIndex);
+}
+
+function formAutoFillCommonSetup() {
+  // Remove the /creditCard path segement when referenced from the 'creditCard' subdirectory.
+  let chromeURL = SimpleTest.getTestFileURL(
+    "formautofill_parent_utils.js"
+  ).replace(/\/creditCard/, "");
+  formFillChromeScript = SpecialPowers.loadChromeScript(chromeURL);
+  formFillChromeScript.addMessageListener("onpopupshown", ({ results }) => {
+    gLastAutoCompleteResults = results;
+    if (gPopupShownListener) {
+      gPopupShownListener({ results });
+    }
+  });
+
+  add_setup(async () => {
+    info(`expecting the storage setup`);
+    await formFillChromeScript.sendQuery("setup");
+  });
+
+  SimpleTest.registerCleanupFunction(async () => {
+    info(`expecting the storage cleanup`);
+    await formFillChromeScript.sendQuery("cleanup");
+
+    formFillChromeScript.destroy();
+    expectingPopup = null;
+  });
+
+  document.addEventListener(
+    "DOMContentLoaded",
+    function () {
+      defaultTextColor = window
+        .getComputedStyle(document.querySelector("input"))
+        .getPropertyValue("color");
+
+      // This is needed for test_formautofill_preview_highlight.html to work properly
+      let disabledInput = document.querySelector(`input[disabled]`);
+      if (disabledInput) {
+        defaultDisabledTextColor = window
+          .getComputedStyle(disabledInput)
+          .getPropertyValue("color");
+      }
+    },
+    { once: true }
+  );
+}
+
+/*
+ * Extremely over-simplified detection of card type from card number just for
+ * our tests. This is needed to test the aria-label of credit card menu entries.
+ */
+function getCCTypeName(creditCard) {
+  return creditCard["cc-number"][0] == "4" ? "Visa" : "MasterCard";
+}
+
+formAutoFillCommonSetup();
diff --git a/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
new file mode 100644
index 0000000000..0d9af772b5
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -0,0 +1,304 @@
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { FormAutofill } = ChromeUtils.importESModule(
+  "resource://autofill/FormAutofill.sys.mjs"
+);
+const { FormAutofillUtils } = ChromeUtils.importESModule(
+  "resource://gre/modules/shared/FormAutofillUtils.sys.mjs"
+);
+const { OSKeyStoreTestUtils } = ChromeUtils.importESModule(
+  "resource://testing-common/OSKeyStoreTestUtils.sys.mjs"
+);
+
+let { formAutofillStorage } = ChromeUtils.importESModule(
+  "resource://autofill/FormAutofillStorage.sys.mjs"
+);
+
+const { ADDRESSES_COLLECTION_NAME, CREDITCARDS_COLLECTION_NAME } =
+  FormAutofillUtils;
+
+let destroyed = false;
+
+var ParentUtils = {
+  getFormAutofillActor() {
+    let win = Services.wm.getMostRecentWindow("navigator:browser");
+    let selectedBrowser = win.gBrowser.selectedBrowser;
+    return selectedBrowser.browsingContext.currentWindowGlobal.getActor(
+      "FormAutofill"
+    );
+  },
+
+  _getRecords(collectionName) {
+    return this.getFormAutofillActor().receiveMessage({
+      name: "FormAutofill:GetRecords",
+      data: {
+        searchString: "",
+        collectionName,
+      },
+    });
+  },
+
+  async _storageChangeObserved({
+    topic = "formautofill-storage-changed",
+    type,
+    times = 1,
+  }) {
+    let count = times;
+
+    return new Promise(resolve => {
+      Services.obs.addObserver(function observer(subject, obsTopic, data) {
+        if ((type && data != type) || !!--count) {
+          return;
+        }
+
+        // every notification type should have the collection name.
+        // We're not allowed to trigger assertions during mochitest
+        // cleanup functions.
+        if (!destroyed) {
+          let allowedNames = [
+            ADDRESSES_COLLECTION_NAME,
+            CREDITCARDS_COLLECTION_NAME,
+          ];
+          assert.ok(
+            allowedNames.includes(subject.wrappedJSObject.collectionName),
+            "should include the collection name"
+          );
+          // every notification except removeAll should have a guid.
+          if (data != "removeAll") {
+            assert.ok(subject.wrappedJSObject.guid, "should have a guid");
+          }
+        }
+        Services.obs.removeObserver(observer, obsTopic);
+        resolve();
+      }, topic);
+    });
+  },
+
+  async _operateRecord(collectionName, type, msgData) {
+    let msgName, times, topic;
+
+    if (collectionName == ADDRESSES_COLLECTION_NAME) {
+      switch (type) {
+        case "add": {
+          msgName = "FormAutofill:SaveAddress";
+          break;
+        }
+        case "update": {
+          msgName = "FormAutofill:SaveAddress";
+          break;
+        }
+        case "remove": {
+          msgName = "FormAutofill:RemoveAddresses";
+          times = msgData.guids.length;
+          break;
+        }
+        default:
+          return;
+      }
+    } else {
+      switch (type) {
+        case "add": {
+          msgData = Object.assign({}, msgData);
+          msgName = "FormAutofill:SaveCreditCard";
+          break;
+        }
+        case "remove": {
+          msgName = "FormAutofill:RemoveCreditCards";
+          times = msgData.guids.length;
+          break;
+        }
+        default:
+          return;
+      }
+    }
+
+    let storageChangePromise = this._storageChangeObserved({
+      type,
+      times,
+      topic,
+    });
+    this.getFormAutofillActor().receiveMessage({
+      name: msgName,
+      data: msgData,
+    });
+    await storageChangePromise;
+  },
+
+  async operateAddress(type, msgData) {
+    await this._operateRecord(ADDRESSES_COLLECTION_NAME, ...arguments);
+  },
+
+  async operateCreditCard(type, msgData) {
+    await this._operateRecord(CREDITCARDS_COLLECTION_NAME, ...arguments);
+  },
+
+  async cleanUpAddresses() {
+    const guids = (await this._getRecords(ADDRESSES_COLLECTION_NAME)).map(
+      record => record.guid
+    );
+
+    if (!guids.length) {
+      return;
+    }
+
+    await this.operateAddress(
+      "remove",
+      { guids },
+      "FormAutofillTest:AddressesCleanedUp"
+    );
+  },
+
+  async cleanUpCreditCards() {
+    if (!FormAutofill.isAutofillCreditCardsAvailable) {
+      return;
+    }
+    const guids = (await this._getRecords(CREDITCARDS_COLLECTION_NAME)).map(
+      record => record.guid
+    );
+
+    if (!guids.length) {
+      return;
+    }
+
+    await this.operateCreditCard(
+      "remove",
+      { guids },
+      "FormAutofillTest:CreditCardsCleanedUp"
+    );
+  },
+
+  setup() {
+    OSKeyStoreTestUtils.setup();
+  },
+
+  async cleanup() {
+    await this.cleanUpAddresses();
+    await this.cleanUpCreditCards();
+    await OSKeyStoreTestUtils.cleanup();
+
+    Services.obs.removeObserver(this, "formautofill-storage-changed");
+  },
+
+  _areRecordsMatching(recordA, recordB, collectionName) {
+    for (let field of formAutofillStorage[collectionName].VALID_FIELDS) {
+      if (recordA[field] !== recordB[field]) {
+        return false;
+      }
+    }
+    // Check the internal field if both addresses have valid value.
+    for (let field of formAutofillStorage.INTERNAL_FIELDS) {
+      if (
+        field in recordA &&
+        field in recordB &&
+        recordA[field] !== recordB[field]
+      ) {
+        return false;
+      }
+    }
+    return true;
+  },
+
+  async _checkRecords(collectionName, expectedRecords) {
+    const records = await this._getRecords(collectionName);
+
+    if (records.length !== expectedRecords.length) {
+      return false;
+    }
+
+    for (let record of records) {
+      let matching = expectedRecords.some(expectedRecord => {
+        return ParentUtils._areRecordsMatching(
+          record,
+          expectedRecord,
+          collectionName
+        );
+      });
+
+      if (!matching) {
+        return false;
+      }
+    }
+
+    return true;
+  },
+
+  async checkAddresses({ expectedAddresses }) {
+    return this._checkRecords(ADDRESSES_COLLECTION_NAME, expectedAddresses);
+  },
+
+  async checkCreditCards({ expectedCreditCards }) {
+    return this._checkRecords(CREDITCARDS_COLLECTION_NAME, expectedCreditCards);
+  },
+
+  observe(subject, topic, data) {
+    if (!destroyed) {
+      assert.ok(topic === "formautofill-storage-changed");
+    }
+    sendAsyncMessage("formautofill-storage-changed", {
+      subject: null,
+      topic,
+      data,
+    });
+  },
+};
+
+Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
+
+Services.mm.addMessageListener("FormAutofill:FieldsIdentified", () => {
+  return null;
+});
+
+addMessageListener("FormAutofillTest:AddAddress", msg => {
+  return ParentUtils.operateAddress("add", msg);
+});
+
+addMessageListener("FormAutofillTest:RemoveAddress", msg => {
+  return ParentUtils.operateAddress("remove", msg);
+});
+
+addMessageListener("FormAutofillTest:UpdateAddress", msg => {
+  return ParentUtils.operateAddress("update", msg);
+});
+
+addMessageListener("FormAutofillTest:CheckAddresses", msg => {
+  return ParentUtils.checkAddresses(msg);
+});
+
+addMessageListener("FormAutofillTest:CleanUpAddresses", msg => {
+  return ParentUtils.cleanUpAddresses();
+});
+
+addMessageListener("FormAutofillTest:AddCreditCard", msg => {
+  return ParentUtils.operateCreditCard("add", msg);
+});
+
+addMessageListener("FormAutofillTest:RemoveCreditCard", msg => {
+  return ParentUtils.operateCreditCard("remove", msg);
+});
+
+addMessageListener("FormAutofillTest:CheckCreditCards", msg => {
+  return ParentUtils.checkCreditCards(msg);
+});
+
+addMessageListener("FormAutofillTest:CleanUpCreditCards", msg => {
+  return ParentUtils.cleanUpCreditCards();
+});
+
+addMessageListener("FormAutofillTest:CanTestOSKeyStoreLogin", msg => {
+  return { canTest: OSKeyStoreTestUtils.canTestOSKeyStoreLogin() };
+});
+
+addMessageListener("FormAutofillTest:OSKeyStoreLogin", async msg => {
+  await OSKeyStoreTestUtils.waitForOSKeyStoreLogin(msg.login);
+});
+
+addMessageListener("setup", async () => {
+  ParentUtils.setup();
+});
+
+addMessageListener("cleanup", async () => {
+  destroyed = true;
+  await ParentUtils.cleanup();
+});
diff --git a/browser/extensions/formautofill/test/mochitest/mochitest.ini b/browser/extensions/formautofill/test/mochitest/mochitest.ini
new file mode 100644
index 0000000000..2964fd5b13
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+prefs =
+  extensions.formautofill.creditCards.supported=on
+  extensions.formautofill.creditCards.enabled=true
+  extensions.formautofill.addresses.supported=on
+  extensions.formautofill.addresses.enabled=true
+skip-if = toolkit == 'android' # bug 1730213
+support-files =
+  ../../../../../toolkit/components/satchel/test/satchel_common.js
+  ../../../../../toolkit/components/satchel/test/parent_utils.js
+  formautofill_common.js
+  formautofill_parent_utils.js
+
+[test_address_level_1_submission.html]
+[test_autofill_and_ordinal_forms.html]
+[test_autofocus_form.html]
+[test_basic_autocomplete_form.html]
+[test_form_changes.html]
+[test_formautofill_preview_highlight.html]
+skip-if = verify
+[test_multi_locale_CA_address_form.html]
+[test_multiple_forms.html]
+[test_on_address_submission.html]
diff --git a/browser/extensions/formautofill/test/mochitest/test_address_level_1_submission.html b/browser/extensions/formautofill/test/mochitest/test_address_level_1_submission.html
new file mode 100644
index 0000000000..9d6ad1f7ea
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_address_level_1_submission.html
@@ -0,0 +1,102 @@
+
+
+
+  
+  Test autofill submission for a country without address-level1
+  
+  
+  
+  
+  
+
+
+Form autofill test: Test autofill submission for a country without address-level1
+
+
+
+
+ + +
+

+

+

+

+

+
+ +
+ + diff --git a/browser/extensions/formautofill/test/mochitest/test_autofill_and_ordinal_forms.html b/browser/extensions/formautofill/test/mochitest/test_autofill_and_ordinal_forms.html new file mode 100644 index 0000000000..5c143c3f4a --- /dev/null +++ b/browser/extensions/formautofill/test/mochitest/test_autofill_and_ordinal_forms.html @@ -0,0 +1,116 @@ + + + + + Test autofill submit + + + + + + + + + + +
+ +

Address form

+
+ + + + + + + + + + +

+ + +

+
+ +

Ordinal form

+
+ +

+
+ +
+ + diff --git a/browser/extensions/formautofill/test/mochitest/test_autofocus_form.html b/browser/extensions/formautofill/test/mochitest/test_autofocus_form.html new file mode 100644 index 0000000000..e2240474c8 --- /dev/null +++ b/browser/extensions/formautofill/test/mochitest/test_autofocus_form.html @@ -0,0 +1,69 @@ + + + + + Test basic autofill + + + + + + + +Form autofill test: autocomplete on an autofocus form + + + +

+ +
+ +
+

This is a basic form.

+

+ +

+

+

+
+ +
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
new file mode 100644
index 0000000000..a642b2abca
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
@@ -0,0 +1,220 @@
+
+
+
+  
+  Test basic autofill
+  
+  
+  
+  
+  
+
+
+Form autofill test: simple form address autofill
+
+
+
+

+ +
+ +
+

This is a basic form.

+

+

+

+

+

+

+

+
+ +
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/test_form_changes.html b/browser/extensions/formautofill/test/mochitest/test_form_changes.html
new file mode 100644
index 0000000000..2eace91a53
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_form_changes.html
@@ -0,0 +1,128 @@
+
+
+
+  
+  Test basic autofill
+  
+  
+  
+  
+  
+
+
+Form autofill test: autocomplete on an autofocus form
+
+
+
+

+
+
+

+

+

+
+
+

+

+

+
+
+

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/test_formautofill_preview_highlight.html b/browser/extensions/formautofill/test/mochitest/test_formautofill_preview_highlight.html
new file mode 100644
index 0000000000..b32b036c9c
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_formautofill_preview_highlight.html
@@ -0,0 +1,121 @@
+
+
+
+  
+  Test form autofill - preview and highlight
+  
+  
+  
+  
+  
+
+
+Form autofill test: preview and highlight
+
+
+
+

+ +
+ +
+

This is a basic form.

+

+

+

+

+

+

+

+
+ +
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html b/browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html
new file mode 100644
index 0000000000..48e0caa785
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html
@@ -0,0 +1,273 @@
+
+
+
+  
+  Test basic autofill
+  
+  
+  
+  
+  
+
+
+Form autofill test: simple form address autofill
+
+
+
+

+ +
+ +
+

This is a basic CA form with en address level 1 select.

+

+

+

+

+

+

+

+
+ +
+

This is a basic CA form with fr address level 1 select.

+

+

+

+

+

+

+

+
+ +
+ +

+
+
diff --git a/browser/extensions/formautofill/test/mochitest/test_multiple_forms.html b/browser/extensions/formautofill/test/mochitest/test_multiple_forms.html
new file mode 100644
index 0000000000..feea55aae6
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_multiple_forms.html
@@ -0,0 +1,67 @@
+
+
+
+  
+  Test autofill submit
+  
+  
+  
+  
+  
+
+
+
+
+
+
+ +
+ + + +
+ +
+ + + +
+ +
+ + diff --git a/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html b/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html new file mode 100644 index 0000000000..79da48c77b --- /dev/null +++ b/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html @@ -0,0 +1,121 @@ + + + + + Test autofill submit + + + + + + + +Form autofill test: check if address is saved/updated correctly + + + +
+ +
+

This is a basic form for submitting test.

+

+

+

+

+

+
+ +
+ + -- cgit v1.2.3