summaryrefslogtreecommitdiffstats
path: root/browser/extensions/formautofill/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'browser/extensions/formautofill/test/unit')
-rw-r--r--browser/extensions/formautofill/test/unit/.eslintrc5
-rw-r--r--browser/extensions/formautofill/test/unit/head.js81
-rw-r--r--browser/extensions/formautofill/test/unit/test_autofillFormFields.js200
-rw-r--r--browser/extensions/formautofill/test/unit/test_collectFormFields.js122
-rw-r--r--browser/extensions/formautofill/test/unit/test_populateFieldValues.js106
-rw-r--r--browser/extensions/formautofill/test/unit/test_profileStorage.js222
-rw-r--r--browser/extensions/formautofill/test/unit/xpcshell.ini12
7 files changed, 748 insertions, 0 deletions
diff --git a/browser/extensions/formautofill/test/unit/.eslintrc b/browser/extensions/formautofill/test/unit/.eslintrc
new file mode 100644
index 000000000..8e33fb0c6
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/.eslintrc
@@ -0,0 +1,5 @@
+{
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ],
+}
diff --git a/browser/extensions/formautofill/test/unit/head.js b/browser/extensions/formautofill/test/unit/head.js
new file mode 100644
index 000000000..67e3bd60b
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -0,0 +1,81 @@
+/**
+ * Provides infrastructure for automated login components tests.
+ */
+
+ /* exported importAutofillModule, getTempFile */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://testing-common/MockDocument.jsm");
+
+// Redirect the path of the resouce in addon to the exact file path.
+let defineLazyModuleGetter = XPCOMUtils.defineLazyModuleGetter;
+XPCOMUtils.defineLazyModuleGetter = function() {
+ let result = /^resource\:\/\/formautofill\/(.+)$/.exec(arguments[2]);
+ if (result) {
+ arguments[2] = Services.io.newFileURI(do_get_file(result[1])).spec;
+ }
+ return defineLazyModuleGetter.apply(this, arguments);
+};
+
+// Load the module by Service newFileURI API for running extension's XPCShell test
+function importAutofillModule(module) {
+ return Cu.import(Services.io.newFileURI(do_get_file(module)).spec);
+}
+
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
+ "resource://gre/modules/DownloadPaths.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+
+// While the previous test file should have deleted all the temporary files it
+// used, on Windows these might still be pending deletion on the physical file
+// system. Thus, start from a new base number every time, to make a collision
+// with a file that is still pending deletion highly unlikely.
+let gFileCounter = Math.floor(Math.random() * 1000000);
+
+/**
+ * Returns a reference to a temporary file, that is guaranteed not to exist, and
+ * to have never been created before.
+ *
+ * @param {string} leafName
+ * Suggested leaf name for the file to be created.
+ *
+ * @returns {nsIFile} pointing to a non-existent file in a temporary directory.
+ *
+ * @note It is not enough to delete the file if it exists, or to delete the file
+ * after calling nsIFile.createUnique, because on Windows the delete
+ * operation in the file system may still be pending, preventing a new
+ * file with the same name to be created.
+ */
+function getTempFile(leafName) {
+ // Prepend a serial number to the extension in the suggested leaf name.
+ let [base, ext] = DownloadPaths.splitBaseNameAndExtension(leafName);
+ let finalLeafName = base + "-" + gFileCounter + ext;
+ gFileCounter++;
+
+ // Get a file reference under the temporary directory for this test file.
+ let file = FileUtils.getFile("TmpD", [finalLeafName]);
+ do_check_false(file.exists());
+
+ do_register_cleanup(function() {
+ if (file.exists()) {
+ file.remove(false);
+ }
+ });
+
+ return file;
+}
+
+add_task(function* test_common_initialize() {
+ Services.prefs.setBoolPref("dom.forms.autocomplete.experimental", true);
+
+ // Clean up after every test.
+ do_register_cleanup(() => {
+ Services.prefs.setBoolPref("dom.forms.autocomplete.experimental", false);
+ });
+});
diff --git a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
new file mode 100644
index 000000000..a842e84e8
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -0,0 +1,200 @@
+/*
+ * Test for form auto fill content helper fill all inputs function.
+ */
+
+"use strict";
+
+let {FormAutofillHandler} = importAutofillModule("FormAutofillContent.jsm");
+
+const TESTCASES = [
+ {
+ description: "Form without autocomplete property",
+ document: `<form><input id="given-name"><input id="family-name">
+ <input id="street-addr"><input id="city"><input id="country">
+ <input id='email'><input id="tel"></form>`,
+ fieldDetails: [],
+ profileData: [],
+ expectedResult: {
+ "given-name": "",
+ "family-name": "",
+ "street-addr": "",
+ "city": "",
+ "country": "",
+ "email": "",
+ "tel": "",
+ },
+ },
+ {
+ description: "Form with autocomplete properties and 1 token",
+ document: `<form><input id="given-name" autocomplete="given-name">
+ <input id="family-name" autocomplete="family-name">
+ <input id="street-addr" autocomplete="street-address">
+ <input id="city" autocomplete="address-level2">
+ <input id="country" autocomplete="country">
+ <input id="email" autocomplete="email">
+ <input id="tel" autocomplete="tel"></form>`,
+ fieldDetails: [
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "country", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "email", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "tel", "element": {}},
+ ],
+ profileData: [
+ {"section": "", "addressType": "", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
+ {"section": "", "addressType": "", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
+ {"section": "", "addressType": "", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
+ {"section": "", "addressType": "", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
+ {"section": "", "addressType": "", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
+ {"section": "", "addressType": "", "fieldName": "email", "contactType": "", "index": 5, "value": "foo@mozilla.com"},
+ {"section": "", "addressType": "", "fieldName": "tel", "contactType": "", "index": 6, "value": "1234567"},
+ ],
+ expectedResult: {
+ "given-name": "foo",
+ "family-name": "bar",
+ "street-addr": "2 Harrison St",
+ "city": "San Francisco",
+ "country": "US",
+ "email": "foo@mozilla.com",
+ "tel": "1234567",
+ },
+ },
+ {
+ description: "Form with autocomplete properties and 2 tokens",
+ document: `<form><input id="given-name" autocomplete="shipping given-name">
+ <input id="family-name" autocomplete="shipping family-name">
+ <input id="street-addr" autocomplete="shipping street-address">
+ <input id="city" autocomplete="shipping address-level2">
+ <input id="country" autocomplete="shipping country">
+ <input id='email' autocomplete="shipping email">
+ <input id="tel" autocomplete="shipping tel"></form>`,
+ fieldDetails: [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
+ ],
+ profileData: [
+ {"section": "", "addressType": "shipping", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
+ {"section": "", "addressType": "shipping", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
+ {"section": "", "addressType": "shipping", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
+ {"section": "", "addressType": "shipping", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
+ {"section": "", "addressType": "shipping", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
+ {"section": "", "addressType": "shipping", "fieldName": "email", "contactType": "", "index": 5, "value": "foo@mozilla.com"},
+ {"section": "", "addressType": "shipping", "fieldName": "tel", "contactType": "", "index": 6, "value": "1234567"},
+ ],
+ expectedResult: {
+ "given-name": "foo",
+ "family-name": "bar",
+ "street-addr": "2 Harrison St",
+ "city": "San Francisco",
+ "country": "US",
+ "email": "foo@mozilla.com",
+ "tel": "1234567",
+ },
+ },
+ {
+ description: "Form with autocomplete properties and profile is partly matched",
+ document: `<form><input id="given-name" autocomplete="shipping given-name">
+ <input id="family-name" autocomplete="shipping family-name">
+ <input id="street-addr" autocomplete="shipping street-address">
+ <input id="city" autocomplete="shipping address-level2">
+ <input id="country" autocomplete="shipping country">
+ <input id='email' autocomplete="shipping email">
+ <input id="tel" autocomplete="shipping tel"></form>`,
+ fieldDetails: [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
+ ],
+ profileData: [
+ {"section": "", "addressType": "shipping", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
+ {"section": "", "addressType": "shipping", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
+ {"section": "", "addressType": "shipping", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
+ {"section": "", "addressType": "shipping", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
+ {"section": "", "addressType": "shipping", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
+ {"section": "", "addressType": "shipping", "fieldName": "email", "contactType": "", "index": 5},
+ {"section": "", "addressType": "shipping", "fieldName": "tel", "contactType": "", "index": 6},
+ ],
+ expectedResult: {
+ "given-name": "foo",
+ "family-name": "bar",
+ "street-addr": "2 Harrison St",
+ "city": "San Francisco",
+ "country": "US",
+ "email": "",
+ "tel": "",
+ },
+ },
+ {
+ description: "Form with autocomplete properties but mismatched",
+ document: `<form><input id="given-name" autocomplete="shipping given-name">
+ <input id="family-name" autocomplete="shipping family-name">
+ <input id="street-addr" autocomplete="billing street-address">
+ <input id="city" autocomplete="billing address-level2">
+ <input id="country" autocomplete="billing country">
+ <input id='email' autocomplete="shipping email">
+ <input id="tel" autocomplete="shipping tel"></form>`,
+ fieldDetails: [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
+ ],
+ profileData: [
+ {"section": "", "addressType": "shipping", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
+ {"section": "", "addressType": "shipping", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
+ {"section": "", "addressType": "shipping", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
+ {"section": "", "addressType": "shipping", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
+ {"section": "", "addressType": "shipping", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
+ {"section": "", "addressType": "shipping", "fieldName": "email", "contactType": "", "index": 5, "value": "foo@mozilla.com"},
+ {"section": "", "addressType": "shipping", "fieldName": "tel", "contactType": "", "index": 6, "value": "1234567"},
+ ],
+ expectedResult: {
+ "given-name": "foo",
+ "family-name": "bar",
+ "street-addr": "",
+ "city": "",
+ "country": "",
+ "email": "foo@mozilla.com",
+ "tel": "1234567",
+ },
+ },
+];
+
+for (let tc of TESTCASES) {
+ (function() {
+ let testcase = tc;
+ add_task(function* () {
+ do_print("Starting testcase: " + testcase.description);
+
+ let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
+ testcase.document);
+ let form = doc.querySelector("form");
+ let handler = new FormAutofillHandler(form);
+
+ handler.fieldDetails = testcase.fieldDetails;
+ handler.fieldDetails.forEach((field, index) => {
+ field.element = doc.querySelectorAll("input")[index];
+ });
+
+ handler.autofillFormFields(testcase.profileData);
+ for (let id in testcase.expectedResult) {
+ Assert.equal(doc.getElementById(id).value, testcase.expectedResult[id],
+ "Check the " + id + " fields were filled with correct data");
+ }
+ });
+ })();
+}
diff --git a/browser/extensions/formautofill/test/unit/test_collectFormFields.js b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
new file mode 100644
index 000000000..52549b746
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -0,0 +1,122 @@
+/*
+ * Test for form auto fill content helper collectFormFields functions.
+ */
+
+"use strict";
+
+let {FormAutofillHandler} = importAutofillModule("FormAutofillContent.jsm");
+
+const TESTCASES = [
+ {
+ description: "Form without autocomplete property",
+ document: `<form><input id="given-name"><input id="family-name">
+ <input id="street-addr"><input id="city"><input id="country">
+ <input id='email'><input id="tel"></form>`,
+ returnedFormat: [],
+ fieldDetails: [],
+ },
+ {
+ description: "Form with autocomplete properties and 1 token",
+ document: `<form><input id="given-name" autocomplete="given-name">
+ <input id="family-name" autocomplete="family-name">
+ <input id="street-addr" autocomplete="street-address">
+ <input id="city" autocomplete="address-level2">
+ <input id="country" autocomplete="country">
+ <input id="email" autocomplete="email">
+ <input id="tel" autocomplete="tel"></form>`,
+ returnedFormat: [
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name", "index": 0},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name", "index": 1},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address", "index": 2},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2", "index": 3},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "country", "index": 4},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "email", "index": 5},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "tel", "index": 6},
+ ],
+ fieldDetails: [
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "country", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "email", "element": {}},
+ {"section": "", "addressType": "", "contactType": "", "fieldName": "tel", "element": {}},
+ ],
+ },
+ {
+ description: "Form with autocomplete properties and 2 tokens",
+ document: `<form><input id="given-name" autocomplete="shipping given-name">
+ <input id="family-name" autocomplete="shipping family-name">
+ <input id="street-addr" autocomplete="shipping street-address">
+ <input id="city" autocomplete="shipping address-level2">
+ <input id="country" autocomplete="shipping country">
+ <input id='email' autocomplete="shipping email">
+ <input id="tel" autocomplete="shipping tel"></form>`,
+ returnedFormat: [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "index": 0},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "index": 1},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "index": 2},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "index": 3},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "index": 4},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "index": 5},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "index": 6},
+ ],
+ fieldDetails: [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
+ ],
+ },
+ {
+ description: "Form with autocomplete properties and profile is partly matched",
+ document: `<form><input id="given-name" autocomplete="shipping given-name">
+ <input id="family-name" autocomplete="shipping family-name">
+ <input id="street-addr" autocomplete="shipping street-address">
+ <input id="city" autocomplete="shipping address-level2">
+ <input id="country" autocomplete="shipping country">
+ <input id='email' autocomplete="shipping email">
+ <input id="tel" autocomplete="shipping tel"></form>`,
+ returnedFormat: [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "index": 0},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "index": 1},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "index": 2},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "index": 3},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "index": 4},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "index": 5},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "index": 6},
+ ],
+ fieldDetails: [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
+ ],
+ },
+];
+
+for (let tc of TESTCASES) {
+ (function() {
+ let testcase = tc;
+ add_task(function* () {
+ do_print("Starting testcase: " + testcase.description);
+
+ let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
+ testcase.document);
+ let form = doc.querySelector("form");
+ let handler = new FormAutofillHandler(form);
+
+ Assert.deepEqual(handler.collectFormFields(), testcase.returnedFormat,
+ "Check the format of form autofill were returned correctly");
+
+ Assert.deepEqual(handler.fieldDetails, testcase.fieldDetails,
+ "Check the fieldDetails were set correctly");
+ });
+ })();
+}
diff --git a/browser/extensions/formautofill/test/unit/test_populateFieldValues.js b/browser/extensions/formautofill/test/unit/test_populateFieldValues.js
new file mode 100644
index 000000000..1215cbd16
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_populateFieldValues.js
@@ -0,0 +1,106 @@
+/*
+ * Test for populating field values in Form Autofill Parent.
+ */
+
+/* global FormAutofillParent */
+
+"use strict";
+
+importAutofillModule("FormAutofillParent.jsm");
+
+do_get_profile();
+
+const TEST_FIELDS = [
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "organization"},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level1"},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "postal-code"},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
+ {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email"},
+];
+
+const TEST_GUID = "test-guid";
+
+const TEST_PROFILE = {
+ guid: TEST_GUID,
+ organization: "World Wide Web Consortium",
+ streetAddress: "32 Vassar Street\nMIT Room 32-G524",
+ addressLevel2: "Cambridge",
+ addressLevel1: "MA",
+ postalCode: "02139",
+ country: "US",
+ tel: "+1 617 253 5702",
+ email: "timbl@w3.org",
+};
+
+function camelCase(str) {
+ return str.toLowerCase().replace(/-([a-z])/g, s => s[1].toUpperCase());
+}
+
+add_task(function* test_populateFieldValues() {
+ FormAutofillParent.init();
+
+ let store = FormAutofillParent.getProfileStore();
+ do_check_neq(store, null);
+
+ store.get = function(guid) {
+ do_check_eq(guid, TEST_GUID);
+ return store._clone(TEST_PROFILE);
+ };
+
+ let notifyUsedCalledCount = 0;
+ store.notifyUsed = function(guid) {
+ do_check_eq(guid, TEST_GUID);
+ notifyUsedCalledCount++;
+ };
+
+ yield new Promise((resolve) => {
+ FormAutofillParent.receiveMessage({
+ name: "FormAutofill:PopulateFieldValues",
+ data: {
+ guid: TEST_GUID,
+ fields: TEST_FIELDS,
+ },
+ target: {
+ sendAsyncMessage: function(name, data) {
+ do_check_eq(name, "FormAutofill:fillForm");
+
+ let fields = data.fields;
+ do_check_eq(fields.length, TEST_FIELDS.length);
+
+ for (let i = 0; i < fields.length; i++) {
+ do_check_eq(fields[i].fieldName, TEST_FIELDS[i].fieldName);
+ do_check_eq(fields[i].value,
+ TEST_PROFILE[camelCase(fields[i].fieldName)]);
+ }
+
+ resolve();
+ },
+ },
+ });
+ });
+
+ do_check_eq(notifyUsedCalledCount, 1);
+
+ FormAutofillParent._uninit();
+ do_check_null(FormAutofillParent.getProfileStore());
+});
+
+add_task(function* test_populateFieldValues_with_invalid_guid() {
+ FormAutofillParent.init();
+
+ Assert.throws(() => {
+ FormAutofillParent.receiveMessage({
+ name: "FormAutofill:PopulateFieldValues",
+ data: {
+ guid: "invalid-guid",
+ fields: TEST_FIELDS,
+ },
+ target: {},
+ });
+ }, /No matching profile\./);
+
+ FormAutofillParent._uninit();
+});
diff --git a/browser/extensions/formautofill/test/unit/test_profileStorage.js b/browser/extensions/formautofill/test/unit/test_profileStorage.js
new file mode 100644
index 000000000..018adedb8
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_profileStorage.js
@@ -0,0 +1,222 @@
+/**
+ * Tests ProfileStorage object.
+ */
+
+/* global ProfileStorage */
+
+"use strict";
+
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import(Services.io.newFileURI(do_get_file("ProfileStorage.jsm")).spec);
+
+const TEST_STORE_FILE_NAME = "test-profile.json";
+
+const TEST_PROFILE_1 = {
+ organization: "World Wide Web Consortium",
+ streetAddress: "32 Vassar Street\nMIT Room 32-G524",
+ addressLevel2: "Cambridge",
+ addressLevel1: "MA",
+ postalCode: "02139",
+ country: "US",
+ tel: "+1 617 253 5702",
+ email: "timbl@w3.org",
+};
+
+const TEST_PROFILE_2 = {
+ streetAddress: "Some Address",
+ country: "US",
+};
+
+const TEST_PROFILE_3 = {
+ streetAddress: "Other Address",
+ postalCode: "12345",
+};
+
+const TEST_PROFILE_WITH_INVALID_FIELD = {
+ streetAddress: "Another Address",
+ invalidField: "INVALID",
+};
+
+let prepareTestProfiles = Task.async(function* (path) {
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ profileStorage.add(TEST_PROFILE_1);
+ profileStorage.add(TEST_PROFILE_2);
+ yield profileStorage._saveImmediately();
+});
+
+let do_check_profile_matches = (profileWithMeta, profile) => {
+ for (let key in profile) {
+ do_check_eq(profileWithMeta[key], profile[key]);
+ }
+};
+
+add_task(function* test_initialize() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ do_check_eq(profileStorage._store.data.version, 1);
+ do_check_eq(profileStorage._store.data.profiles.length, 0);
+
+ let data = profileStorage._store.data;
+
+ yield profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ Assert.deepEqual(profileStorage._store.data, data);
+});
+
+add_task(function* test_getAll() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ yield prepareTestProfiles(path);
+
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profiles = profileStorage.getAll();
+
+ do_check_eq(profiles.length, 2);
+ do_check_profile_matches(profiles[0], TEST_PROFILE_1);
+ do_check_profile_matches(profiles[1], TEST_PROFILE_2);
+
+ // Modifying output shouldn't affect the storage.
+ profiles[0].organization = "test";
+ do_check_profile_matches(profileStorage.getAll()[0], TEST_PROFILE_1);
+});
+
+add_task(function* test_get() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ yield prepareTestProfiles(path);
+
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profiles = profileStorage.getAll();
+ let guid = profiles[0].guid;
+
+ let profile = profileStorage.get(guid);
+ do_check_profile_matches(profile, TEST_PROFILE_1);
+
+ // Modifying output shouldn't affect the storage.
+ profile.organization = "test";
+ do_check_profile_matches(profileStorage.get(guid), TEST_PROFILE_1);
+
+ Assert.throws(() => profileStorage.get("INVALID_GUID"),
+ /No matching profile\./);
+});
+
+add_task(function* test_add() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ yield prepareTestProfiles(path);
+
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profiles = profileStorage.getAll();
+
+ do_check_eq(profiles.length, 2);
+
+ do_check_profile_matches(profiles[0], TEST_PROFILE_1);
+ do_check_profile_matches(profiles[1], TEST_PROFILE_2);
+
+ do_check_neq(profiles[0].guid, undefined);
+ do_check_neq(profiles[0].timeCreated, undefined);
+ do_check_eq(profiles[0].timeLastModified, profiles[0].timeCreated);
+ do_check_eq(profiles[0].timeLastUsed, 0);
+ do_check_eq(profiles[0].timesUsed, 0);
+
+ Assert.throws(() => profileStorage.add(TEST_PROFILE_WITH_INVALID_FIELD),
+ /"invalidField" is not a valid field\./);
+});
+
+add_task(function* test_update() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ yield prepareTestProfiles(path);
+
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profiles = profileStorage.getAll();
+ let guid = profiles[1].guid;
+ let timeLastModified = profiles[1].timeLastModified;
+
+ do_check_neq(profiles[1].country, undefined);
+
+ profileStorage.update(guid, TEST_PROFILE_3);
+ yield profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profile = profileStorage.get(guid);
+
+ do_check_eq(profile.country, undefined);
+ do_check_neq(profile.timeLastModified, timeLastModified);
+ do_check_profile_matches(profile, TEST_PROFILE_3);
+
+ Assert.throws(
+ () => profileStorage.update("INVALID_GUID", TEST_PROFILE_3),
+ /No matching profile\./
+ );
+
+ Assert.throws(
+ () => profileStorage.update(guid, TEST_PROFILE_WITH_INVALID_FIELD),
+ /"invalidField" is not a valid field\./
+ );
+});
+
+add_task(function* test_notifyUsed() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ yield prepareTestProfiles(path);
+
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profiles = profileStorage.getAll();
+ let guid = profiles[1].guid;
+ let timeLastUsed = profiles[1].timeLastUsed;
+ let timesUsed = profiles[1].timesUsed;
+
+ profileStorage.notifyUsed(guid);
+ yield profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profile = profileStorage.get(guid);
+
+ do_check_eq(profile.timesUsed, timesUsed + 1);
+ do_check_neq(profile.timeLastUsed, timeLastUsed);
+
+ Assert.throws(() => profileStorage.notifyUsed("INVALID_GUID"),
+ /No matching profile\./);
+});
+
+add_task(function* test_remove() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ yield prepareTestProfiles(path);
+
+ let profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ let profiles = profileStorage.getAll();
+ let guid = profiles[1].guid;
+
+ do_check_eq(profiles.length, 2);
+
+ profileStorage.remove(guid);
+ yield profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ yield profileStorage.initialize();
+
+ profiles = profileStorage.getAll();
+
+ do_check_eq(profiles.length, 1);
+
+ Assert.throws(() => profileStorage.get(guid), /No matching profile\./);
+});
diff --git a/browser/extensions/formautofill/test/unit/xpcshell.ini b/browser/extensions/formautofill/test/unit/xpcshell.ini
new file mode 100644
index 000000000..2c5763681
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+head = head.js
+tail =
+support-files =
+ ../../content/FormAutofillContent.jsm
+ ../../content/FormAutofillParent.jsm
+ ../../content/ProfileStorage.jsm
+
+[test_autofillFormFields.js]
+[test_collectFormFields.js]
+[test_populateFieldValues.js]
+[test_profileStorage.js]