--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -148,17 +148,17 @@ FormAutofillParent.prototype = {
*
* @returns {boolean} status of form autofill feature
*/
_getStatus() {
if (!Services.prefs.getBoolPref(ENABLED_PREF)) {
return false;
}
- return profileStorage.getAll().length > 0;
+ return profileStorage.addresses.getAll({noComputedFields: true}).length > 0;
},
/**
* Set status and trigger _onStatusChanged.
*
* @param {boolean} newStatus The latest status we want to set for _enabled
*/
_setStatus(newStatus) {
@@ -176,24 +176,24 @@ FormAutofillParent.prototype = {
receiveMessage({name, data, target}) {
switch (name) {
case "FormAutofill:GetAddresses": {
this._getAddresses(data, target);
break;
}
case "FormAutofill:SaveAddress": {
if (data.guid) {
- profileStorage.update(data.guid, data.address);
+ profileStorage.addresses.update(data.guid, data.address);
} else {
- profileStorage.add(data.address);
+ profileStorage.addresses.add(data.address);
}
break;
}
case "FormAutofill:RemoveAddresses": {
- data.guids.forEach(guid => profileStorage.remove(guid));
+ data.guids.forEach(guid => profileStorage.addresses.remove(guid));
break;
}
}
},
/**
* Uninitializes FormAutofillParent. This is for testing only.
*
@@ -220,32 +220,32 @@ FormAutofillParent.prototype = {
* The input autocomplete property's information.
* @param {nsIFrameMessageManager} target
* Content's message manager.
*/
_getAddresses({searchString, info}, target) {
let addresses = [];
if (info && info.fieldName) {
- addresses = profileStorage.getByFilter({searchString, info});
+ addresses = profileStorage.addresses.getByFilter({searchString, info});
} else {
- addresses = profileStorage.getAll();
+ addresses = profileStorage.addresses.getAll();
}
target.sendAsyncMessage("FormAutofill:Addresses", addresses);
},
_updateSavedFieldNames() {
if (!Services.ppmm.initialProcessData.autofillSavedFieldNames) {
Services.ppmm.initialProcessData.autofillSavedFieldNames = new Set();
} else {
Services.ppmm.initialProcessData.autofillSavedFieldNames.clear();
}
- profileStorage.getAll().forEach((address) => {
+ profileStorage.addresses.getAll().forEach((address) => {
Object.keys(address).forEach((fieldName) => {
if (!address[fieldName]) {
return;
}
Services.ppmm.initialProcessData.autofillSavedFieldNames.add(fieldName);
});
});
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -8,46 +8,43 @@
* The data is stored in JSON format, without indentation and the computed
* fields, using UTF-8 encoding. With indentation and computed fields applied,
* the schema would look like this:
*
* {
* version: 1,
* addresses: [
* {
- * guid, // 12 characters
+ * guid, // 12 characters
*
* // address fields
* given-name,
* additional-name,
* family-name,
- * organization, // Company
- * street-address, // (Multiline)
- * address-level2, // City/Town
- * address-level1, // Province (Standardized code if possible)
+ * organization, // Company
+ * street-address, // (Multiline)
+ * address-level2, // City/Town
+ * address-level1, // Province (Standardized code if possible)
* postal-code,
- * country, // ISO 3166
+ * country, // ISO 3166
* tel,
* email,
*
* // computed fields (These fields are not stored in the file as they are
* // generated at runtime.)
* name,
* address-line1,
* address-line2,
* address-line3,
*
* // metadata
- * timeCreated, // in ms
- * timeLastUsed, // in ms
- * timeLastModified, // in ms
+ * timeCreated, // in ms
+ * timeLastUsed, // in ms
+ * timeLastModified, // in ms
* timesUsed
- * },
- * {
- * // ...
* }
* ]
* }
*/
"use strict";
// We expose a singleton from this module. Some tests may import the
@@ -66,46 +63,374 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/JSONFile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillNameUtils",
"resource://formautofill/FormAutofillNameUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
-this.log = null;
-FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
-
const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
const SCHEMA_VERSION = 1;
-const VALID_FIELDS = [
+const VALID_PROFILE_FIELDS = [
"given-name",
"additional-name",
"family-name",
"organization",
"street-address",
"address-level2",
"address-level1",
"postal-code",
"country",
"tel",
"email",
];
+const INTERNAL_FIELDS = [
+ "guid",
+ "timeCreated",
+ "timeLastUsed",
+ "timeLastModified",
+ "timesUsed",
+];
+
+/**
+ * Class that manipulates records in a specified collection.
+ *
+ * Note that it is responsible for converting incoming data to a consistent
+ * format in the storage. For example, computed fields will be transformed to
+ * the original fields.
+ */
+class AutofillRecords {
+ /**
+ * Creates an AutofillRecords.
+ *
+ * @param {JSONFile} store
+ * An instance of JSONFile.
+ * @param {string} collectionName
+ * A key of "store.data".
+ * @param {Array.<string>} validFields
+ * A list containing non-metadata field names.
+ */
+ constructor(store, collectionName, validFields) {
+ FormAutofillUtils.defineLazyLogGetter(this, "AutofillRecords:" + collectionName);
+
+ this.VALID_FIELDS = validFields;
+
+ this._store = store;
+ this._collectionName = collectionName;
+ }
+
+ /**
+ * Gets the schema version number.
+ *
+ * @returns {number}
+ * The current schema version number.
+ */
+ get version() {
+ return SCHEMA_VERSION;
+ }
+
+ /**
+ * Adds a new record.
+ *
+ * @param {Object} record
+ * The new record for saving.
+ */
+ add(record) {
+ this.log.debug("add:", record);
+
+ let recordToSave = this._clone(record);
+ this._normalizeRecord(recordToSave);
+
+ let guid;
+ while (!guid || this._findByGUID(guid)) {
+ guid = gUUIDGenerator.generateUUID().toString()
+ .replace(/[{}-]/g, "").substring(0, 12);
+ }
+ recordToSave.guid = guid;
+
+ // Metadata
+ let now = Date.now();
+ recordToSave.timeCreated = now;
+ recordToSave.timeLastModified = now;
+ recordToSave.timeLastUsed = 0;
+ recordToSave.timesUsed = 0;
+
+ this._store.data[this._collectionName].push(recordToSave);
+ this._store.saveSoon();
+
+ Services.obs.notifyObservers(null, "formautofill-storage-changed", "add");
+ }
+
+ /**
+ * Update the specified record.
+ *
+ * @param {string} guid
+ * Indicates which record to update.
+ * @param {Object} record
+ * The new record used to overwrite the old one.
+ */
+ update(guid, record) {
+ this.log.debug("update:", guid, record);
+
+ let recordFound = this._findByGUID(guid);
+ if (!recordFound) {
+ throw new Error("No matching record.");
+ }
+
+ let recordToUpdate = this._clone(record);
+ this._normalizeRecord(recordToUpdate);
+ for (let field of this.VALID_FIELDS) {
+ if (recordToUpdate[field] !== undefined) {
+ recordFound[field] = recordToUpdate[field];
+ } else {
+ delete recordFound[field];
+ }
+ }
+
+ recordFound.timeLastModified = Date.now();
+
+ this._store.saveSoon();
+
+ Services.obs.notifyObservers(null, "formautofill-storage-changed", "update");
+ }
+
+ /**
+ * Notifies the stroage of the use of the specified record, so we can update
+ * the metadata accordingly.
+ *
+ * @param {string} guid
+ * Indicates which record to be notified.
+ */
+ notifyUsed(guid) {
+ this.log.debug("notifyUsed:", guid);
+
+ let recordFound = this._findByGUID(guid);
+ if (!recordFound) {
+ throw new Error("No matching record.");
+ }
+
+ recordFound.timesUsed++;
+ recordFound.timeLastUsed = Date.now();
+
+ this._store.saveSoon();
+ Services.obs.notifyObservers(null, "formautofill-storage-changed", "notifyUsed");
+ }
+
+ /**
+ * Removes the specified record. No error occurs if the record isn't found.
+ *
+ * @param {string} guid
+ * Indicates which record to remove.
+ */
+ remove(guid) {
+ this.log.debug("remove:", guid);
+
+ this._store.data[this._collectionName] =
+ this._store.data[this._collectionName].filter(record => record.guid != guid);
+ this._store.saveSoon();
+
+ Services.obs.notifyObservers(null, "formautofill-storage-changed", "remove");
+ }
+
+ /**
+ * Returns the record with the specified GUID.
+ *
+ * @param {string} guid
+ * Indicates which record to retrieve.
+ * @returns {Object}
+ * A clone of the record.
+ */
+ get(guid) {
+ this.log.debug("get:", guid);
+
+ let recordFound = this._findByGUID(guid);
+ if (!recordFound) {
+ throw new Error("No matching record.");
+ }
+
+ // The record is cloned to avoid accidental modifications from outside.
+ let clonedRecord = this._clone(recordFound);
+ this._recordReadProcessor(clonedRecord);
+ return clonedRecord;
+ }
+
+ /**
+ * Returns all records.
+ *
+ * @param {Object} config
+ * Specifies how data will be retrieved.
+ * @param {boolean} config.noComputedFields
+ * Returns raw record without those computed fields.
+ * @returns {Array.<Object>}
+ * An array containing clones of all records.
+ */
+ getAll(config = {}) {
+ this.log.debug("getAll", config);
+
+ // Records are cloned to avoid accidental modifications from outside.
+ let clonedRecords = this._store.data[this._collectionName].map(this._clone);
+ clonedRecords.forEach(record => this._recordReadProcessor(record, config));
+ return clonedRecords;
+ }
+
+ /**
+ * Returns the filtered records based on input's information and searchString.
+ *
+ * @returns {Array.<Object>}
+ * An array containing clones of matched record.
+ */
+ getByFilter({info, searchString}) {
+ this.log.debug("getByFilter:", info, searchString);
+
+ let lcSearchString = searchString.toLowerCase();
+ let result = this.getAll().filter(record => {
+ // Return true if string is not provided and field exists.
+ // TODO: We'll need to check if the address is for billing or shipping.
+ // (Bug 1358941)
+ let name = record[info.fieldName];
+
+ if (!searchString) {
+ return !!name;
+ }
+
+ return name && name.toLowerCase().startsWith(lcSearchString);
+ });
+
+ this.log.debug("getByFilter:", "Returning", result.length, "result(s)");
+ return result;
+ }
+
+ _clone(record) {
+ return Object.assign({}, record);
+ }
+
+ _findByGUID(guid) {
+ let found = this._findIndexByGUID(guid);
+ return found < 0 ? undefined : this._store.data[this._collectionName][found];
+ }
+
+ _findIndexByGUID(guid) {
+ return this._store.data[this._collectionName].findIndex(record => record.guid == guid);
+ }
+
+ _normalizeRecord(record) {
+ this._recordWriteProcessor(record);
+
+ for (let key in record) {
+ if (!this.VALID_FIELDS.includes(key)) {
+ throw new Error(`"${key}" is not a valid field.`);
+ }
+ if (typeof record[key] !== "string" &&
+ typeof record[key] !== "number") {
+ throw new Error(`"${key}" contains invalid data type.`);
+ }
+ }
+ }
+
+ // An interface to be inherited.
+ _recordReadProcessor(record, config) {}
+
+ // An interface to be inherited.
+ _recordWriteProcessor(record) {}
+}
+
+class Addresses extends AutofillRecords {
+ constructor(store) {
+ super(store, "addresses", VALID_PROFILE_FIELDS);
+ }
+
+ _recordReadProcessor(profile, {noComputedFields} = {}) {
+ if (noComputedFields) {
+ return;
+ }
+
+ // Compute name
+ let name = FormAutofillNameUtils.joinNameParts({
+ given: profile["given-name"],
+ middle: profile["additional-name"],
+ family: profile["family-name"],
+ });
+ if (name) {
+ profile.name = name;
+ }
+
+ // Compute address
+ if (profile["street-address"]) {
+ let streetAddress = profile["street-address"].split("\n");
+ // TODO: we should prevent the dataloss by concatenating the rest of lines
+ // with a locale-specific character in the future (bug 1360114).
+ for (let i = 0; i < 3; i++) {
+ if (streetAddress[i]) {
+ profile["address-line" + (i + 1)] = streetAddress[i];
+ }
+ }
+ }
+ }
+
+ _recordWriteProcessor(profile) {
+ // Normalize name
+ if (profile.name) {
+ let nameParts = FormAutofillNameUtils.splitName(profile.name);
+ if (!profile["given-name"] && nameParts.given) {
+ profile["given-name"] = nameParts.given;
+ }
+ if (!profile["additional-name"] && nameParts.middle) {
+ profile["additional-name"] = nameParts.middle;
+ }
+ if (!profile["family-name"] && nameParts.family) {
+ profile["family-name"] = nameParts.family;
+ }
+ delete profile.name;
+ }
+
+ // Normalize address
+ if (profile["address-line1"] || profile["address-line2"] ||
+ profile["address-line3"]) {
+ // Treat "street-address" as "address-line1" if it contains only one line
+ // and "address-line1" is omitted.
+ if (!profile["address-line1"] && profile["street-address"] &&
+ !profile["street-address"].includes("\n")) {
+ profile["address-line1"] = profile["street-address"];
+ delete profile["street-address"];
+ }
+
+ // Remove "address-line*" but keep the values.
+ let addressLines = [1, 2, 3].map(i => {
+ let value = profile["address-line" + i];
+ delete profile["address-line" + i];
+ return value;
+ });
+
+ // Concatenate "address-line*" if "street-address" is omitted.
+ if (!profile["street-address"]) {
+ profile["street-address"] = addressLines.join("\n");
+ }
+ }
+ }
+}
+
function ProfileStorage(path) {
this._path = path;
this._initializePromise = null;
+ this.INTERNAL_FIELDS = INTERNAL_FIELDS;
}
ProfileStorage.prototype = {
- // These fields are defined internally for each record.
- INTERNAL_FIELDS:
- ["guid", "timeCreated", "timeLastUsed", "timeLastModified", "timesUsed"],
+ get addresses() {
+ if (!this._addresses) {
+ this._store.ensureDataReady();
+ this._addresses = new Addresses(this._store);
+ }
+ return this._addresses;
+ },
+
/**
* Loads the profile data from file to memory.
*
* @returns {Promise}
* @resolves When the operation finished successfully.
* @rejects JavaScript exception.
*/
initialize() {
@@ -114,270 +439,16 @@ ProfileStorage.prototype = {
path: this._path,
dataPostProcessor: this._dataPostProcessor.bind(this),
});
this._initializePromise = this._store.load();
}
return this._initializePromise;
},
- /**
- * Adds a new address.
- *
- * @param {Address} address
- * The new address for saving.
- */
- add(address) {
- log.debug("add:", address);
- this._store.ensureDataReady();
-
- let addressToSave = this._clone(address);
- this._normalizeAddress(addressToSave);
-
- addressToSave.guid = gUUIDGenerator.generateUUID().toString()
- .replace(/[{}-]/g, "").substring(0, 12);
-
- // Metadata
- let now = Date.now();
- addressToSave.timeCreated = now;
- addressToSave.timeLastModified = now;
- addressToSave.timeLastUsed = 0;
- addressToSave.timesUsed = 0;
-
- this._store.data.addresses.push(addressToSave);
-
- this._store.saveSoon();
- Services.obs.notifyObservers(null, "formautofill-storage-changed", "add");
- },
-
- /**
- * Update the specified address.
- *
- * @param {string} guid
- * Indicates which address to update.
- * @param {Address} address
- * The new address used to overwrite the old one.
- */
- update(guid, address) {
- log.debug("update:", guid, address);
- this._store.ensureDataReady();
-
- let addressFound = this._findByGUID(guid);
- if (!addressFound) {
- throw new Error("No matching record.");
- }
-
- let addressToUpdate = this._clone(address);
- this._normalizeAddress(addressToUpdate);
-
- for (let field of VALID_FIELDS) {
- if (addressToUpdate[field] !== undefined) {
- addressFound[field] = addressToUpdate[field];
- } else {
- delete addressFound[field];
- }
- }
-
- addressFound.timeLastModified = Date.now();
-
- this._store.saveSoon();
- Services.obs.notifyObservers(null, "formautofill-storage-changed", "update");
- },
-
- /**
- * Notifies the stroage of the use of the specified address, so we can update
- * the metadata accordingly.
- *
- * @param {string} guid
- * Indicates which address to be notified.
- */
- notifyUsed(guid) {
- this._store.ensureDataReady();
-
- let addressFound = this._findByGUID(guid);
- if (!addressFound) {
- throw new Error("No matching record.");
- }
-
- addressFound.timesUsed++;
- addressFound.timeLastUsed = Date.now();
-
- this._store.saveSoon();
- Services.obs.notifyObservers(null, "formautofill-storage-changed", "notifyUsed");
- },
-
- /**
- * Removes the specified address. No error occurs if the address isn't found.
- *
- * @param {string} guid
- * Indicates which address to remove.
- */
- remove(guid) {
- log.debug("remove:", guid);
- this._store.ensureDataReady();
-
- this._store.data.addresses =
- this._store.data.addresses.filter(address => address.guid != guid);
- this._store.saveSoon();
- Services.obs.notifyObservers(null, "formautofill-storage-changed", "remove");
- },
-
- /**
- * Returns the address with the specified GUID.
- *
- * @param {string} guid
- * Indicates which address to retrieve.
- * @returns {Address}
- * A clone of the address.
- */
- get(guid) {
- log.debug("get:", guid);
- this._store.ensureDataReady();
-
- let addressFound = this._findByGUID(guid);
- if (!addressFound) {
- throw new Error("No matching record.");
- }
-
- // The record is cloned to avoid accidental modifications from outside.
- let clonedAddress = this._clone(addressFound);
- this._computeFields(clonedAddress);
- return clonedAddress;
- },
-
- /**
- * Returns all addresses.
- *
- * @returns {Array.<Address>}
- * An array containing clones of all addresses.
- */
- getAll() {
- log.debug("getAll");
- this._store.ensureDataReady();
-
- // Records are cloned to avoid accidental modifications from outside.
- let clonedAddresses = this._store.data.addresses.map(this._clone);
- clonedAddresses.forEach(this._computeFields);
- return clonedAddresses;
- },
-
- /**
- * Returns the filtered addresses based on input's information and searchString.
- *
- * @returns {Array.<Address>}
- * An array containing clones of matched addresses.
- */
- getByFilter({info, searchString}) {
- log.debug("getByFilter:", info, searchString);
-
- let lcSearchString = searchString.toLowerCase();
- let result = this.getAll().filter(address => {
- // Return true if string is not provided and field exists.
- // TODO: We'll need to check if the address is for billing or shipping.
- // (Bug 1358941)
- let name = address[info.fieldName];
-
- if (!searchString) {
- return !!name;
- }
-
- return name && name.toLowerCase().startsWith(lcSearchString);
- });
-
- log.debug("getByFilter: Returning", result.length, "result(s)");
- return result;
- },
-
- _clone(record) {
- return Object.assign({}, record);
- },
-
- _findByGUID(guid) {
- return this._store.data.addresses.find(address => address.guid == guid);
- },
-
- _computeFields(address) {
- // Compute name
- address.name = FormAutofillNameUtils.joinNameParts({
- given: address["given-name"],
- middle: address["additional-name"],
- family: address["family-name"],
- });
-
- // Compute address
- if (address["street-address"]) {
- let streetAddress = address["street-address"].split("\n");
- // TODO: we should prevent the dataloss by concatenating the rest of lines
- // with a locale-specific character in the future (bug 1360114).
- for (let i = 0; i < 3; i++) {
- if (streetAddress[i]) {
- address["address-line" + (i + 1)] = streetAddress[i];
- }
- }
- }
- },
-
- _normalizeAddressLines(address) {
- if (address["address-line1"] || address["address-line2"] ||
- address["address-line3"]) {
- // Treat "street-address" as "address-line1" if it contains only one line
- // and "address-line1" is omitted.
- if (!address["address-line1"] && address["street-address"] &&
- !address["street-address"].includes("\n")) {
- address["address-line1"] = address["street-address"];
- delete address["street-address"];
- }
-
- // Remove "address-line*" but keep the values.
- let addressLines = [1, 2, 3].map(i => {
- let value = address["address-line" + i];
- delete address["address-line" + i];
- return value;
- });
-
- // Concatenate "address-line*" if "street-address" is omitted.
- if (!address["street-address"]) {
- address["street-address"] = addressLines.join("\n");
- }
- }
- },
-
- _normalizeName(address) {
- if (!address.name) {
- return;
- }
-
- let nameParts = FormAutofillNameUtils.splitName(address.name);
- if (!address["given-name"] && nameParts.given) {
- address["given-name"] = nameParts.given;
- }
- if (!address["additional-name"] && nameParts.middle) {
- address["additional-name"] = nameParts.middle;
- }
- if (!address["family-name"] && nameParts.family) {
- address["family-name"] = nameParts.family;
- }
- delete address.name;
- },
-
- _normalizeAddress(address) {
- this._normalizeName(address);
- this._normalizeAddressLines(address);
-
- for (let key in address) {
- if (!VALID_FIELDS.includes(key)) {
- throw new Error(`"${key}" is not a valid field.`);
- }
- if (typeof address[key] !== "string" &&
- typeof address[key] !== "number") {
- throw new Error(`"${key}" contains invalid data type.`);
- }
- }
- },
-
_dataPostProcessor(data) {
data.version = SCHEMA_VERSION;
if (!data.addresses) {
data.addresses = [];
}
return data;
},
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_addressRecords.js
@@ -0,0 +1,286 @@
+/**
+ * Tests ProfileStorage object with addresses records.
+ */
+
+"use strict";
+
+const {ProfileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
+
+const TEST_STORE_FILE_NAME = "test-profile.json";
+
+const TEST_ADDRESS_1 = {
+ "given-name": "Timothy",
+ "additional-name": "John",
+ "family-name": "Berners-Lee",
+ organization: "World Wide Web Consortium",
+ "street-address": "32 Vassar Street\nMIT Room 32-G524",
+ "address-level2": "Cambridge",
+ "address-level1": "MA",
+ "postal-code": "02139",
+ country: "US",
+ tel: "+1 617 253 5702",
+ email: "timbl@w3.org",
+};
+
+const TEST_ADDRESS_2 = {
+ "street-address": "Some Address",
+ country: "US",
+};
+
+const TEST_ADDRESS_3 = {
+ "street-address": "Other Address",
+ "postal-code": "12345",
+};
+
+const TEST_ADDRESS_WITH_INVALID_FIELD = {
+ "street-address": "Another Address",
+ invalidField: "INVALID",
+};
+
+let prepareTestRecords = async function(path) {
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
+ (subject, data) => data == "add");
+ profileStorage.addresses.add(TEST_ADDRESS_1);
+ await onChanged;
+ profileStorage.addresses.add(TEST_ADDRESS_2);
+ await profileStorage._saveImmediately();
+};
+
+let do_check_record_matches = (recordWithMeta, record) => {
+ for (let key in record) {
+ do_check_eq(recordWithMeta[key], record[key]);
+ }
+};
+
+add_task(async function test_initialize() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ do_check_eq(profileStorage._store.data.version, 1);
+ do_check_eq(profileStorage._store.data.addresses.length, 0);
+
+ let data = profileStorage._store.data;
+ Assert.deepEqual(data.addresses, []);
+
+ await profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ Assert.deepEqual(profileStorage._store.data, data);
+});
+
+add_task(async function test_getAll() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ await prepareTestRecords(path);
+
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let addresses = profileStorage.addresses.getAll();
+
+ do_check_eq(addresses.length, 2);
+ do_check_record_matches(addresses[0], TEST_ADDRESS_1);
+ do_check_record_matches(addresses[1], TEST_ADDRESS_2);
+
+ // Check computed fields.
+ do_check_eq(addresses[0].name, "Timothy John Berners-Lee");
+ do_check_eq(addresses[0]["address-line1"], "32 Vassar Street");
+ do_check_eq(addresses[0]["address-line2"], "MIT Room 32-G524");
+
+ // Test with noComputedFields set.
+ addresses = profileStorage.addresses.getAll({noComputedFields: true});
+ do_check_eq(addresses[0].name, undefined);
+ do_check_eq(addresses[0]["address-line1"], undefined);
+ do_check_eq(addresses[0]["address-line2"], undefined);
+
+ // Modifying output shouldn't affect the storage.
+ addresses[0].organization = "test";
+ do_check_record_matches(profileStorage.addresses.getAll()[0], TEST_ADDRESS_1);
+});
+
+add_task(async function test_get() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ await prepareTestRecords(path);
+
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let addresses = profileStorage.addresses.getAll();
+ let guid = addresses[0].guid;
+
+ let address = profileStorage.addresses.get(guid);
+ do_check_record_matches(address, TEST_ADDRESS_1);
+
+ // Modifying output shouldn't affect the storage.
+ address.organization = "test";
+ do_check_record_matches(profileStorage.addresses.get(guid), TEST_ADDRESS_1);
+
+ Assert.throws(() => profileStorage.addresses.get("INVALID_GUID"),
+ /No matching record\./);
+});
+
+add_task(async function test_getByFilter() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ await prepareTestRecords(path);
+
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let filter = {info: {fieldName: "street-address"}, searchString: "Some"};
+ let addresses = profileStorage.addresses.getByFilter(filter);
+ do_check_eq(addresses.length, 1);
+ do_check_record_matches(addresses[0], TEST_ADDRESS_2);
+
+ filter = {info: {fieldName: "country"}, searchString: "u"};
+ addresses = profileStorage.addresses.getByFilter(filter);
+ do_check_eq(addresses.length, 2);
+ do_check_record_matches(addresses[0], TEST_ADDRESS_1);
+ do_check_record_matches(addresses[1], TEST_ADDRESS_2);
+
+ filter = {info: {fieldName: "street-address"}, searchString: "test"};
+ addresses = profileStorage.addresses.getByFilter(filter);
+ do_check_eq(addresses.length, 0);
+
+ filter = {info: {fieldName: "street-address"}, searchString: ""};
+ addresses = profileStorage.addresses.getByFilter(filter);
+ do_check_eq(addresses.length, 2);
+
+ // Check if the filtering logic is free from searching special chars.
+ filter = {info: {fieldName: "street-address"}, searchString: ".*"};
+ addresses = profileStorage.addresses.getByFilter(filter);
+ do_check_eq(addresses.length, 0);
+
+ // Prevent broken while searching the property that does not exist.
+ filter = {info: {fieldName: "tel"}, searchString: "1"};
+ addresses = profileStorage.addresses.getByFilter(filter);
+ do_check_eq(addresses.length, 0);
+});
+
+add_task(async function test_add() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ await prepareTestRecords(path);
+
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let addresses = profileStorage.addresses.getAll();
+
+ do_check_eq(addresses.length, 2);
+
+ do_check_record_matches(addresses[0], TEST_ADDRESS_1);
+ do_check_record_matches(addresses[1], TEST_ADDRESS_2);
+
+ do_check_neq(addresses[0].guid, undefined);
+ do_check_neq(addresses[0].timeCreated, undefined);
+ do_check_eq(addresses[0].timeLastModified, addresses[0].timeCreated);
+ do_check_eq(addresses[0].timeLastUsed, 0);
+ do_check_eq(addresses[0].timesUsed, 0);
+
+ Assert.throws(() => profileStorage.addresses.add(TEST_ADDRESS_WITH_INVALID_FIELD),
+ /"invalidField" is not a valid field\./);
+});
+
+add_task(async function test_update() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ await prepareTestRecords(path);
+
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let addresses = profileStorage.addresses.getAll();
+ let guid = addresses[1].guid;
+ let timeLastModified = addresses[1].timeLastModified;
+
+ let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
+ (subject, data) => data == "update");
+
+ do_check_neq(addresses[1].country, undefined);
+
+ profileStorage.addresses.update(guid, TEST_ADDRESS_3);
+ await onChanged;
+ await profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let address = profileStorage.addresses.get(guid);
+
+ do_check_eq(address.country, undefined);
+ do_check_neq(address.timeLastModified, timeLastModified);
+ do_check_record_matches(address, TEST_ADDRESS_3);
+
+ Assert.throws(
+ () => profileStorage.addresses.update("INVALID_GUID", TEST_ADDRESS_3),
+ /No matching record\./
+ );
+
+ Assert.throws(
+ () => profileStorage.addresses.update(guid, TEST_ADDRESS_WITH_INVALID_FIELD),
+ /"invalidField" is not a valid field\./
+ );
+});
+
+add_task(async function test_notifyUsed() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ await prepareTestRecords(path);
+
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let addresses = profileStorage.addresses.getAll();
+ let guid = addresses[1].guid;
+ let timeLastUsed = addresses[1].timeLastUsed;
+ let timesUsed = addresses[1].timesUsed;
+
+ let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
+ (subject, data) => data == "notifyUsed");
+
+ profileStorage.addresses.notifyUsed(guid);
+ await onChanged;
+ await profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let address = profileStorage.addresses.get(guid);
+
+ do_check_eq(address.timesUsed, timesUsed + 1);
+ do_check_neq(address.timeLastUsed, timeLastUsed);
+
+ Assert.throws(() => profileStorage.addresses.notifyUsed("INVALID_GUID"),
+ /No matching record\./);
+});
+
+add_task(async function test_remove() {
+ let path = getTempFile(TEST_STORE_FILE_NAME).path;
+ await prepareTestRecords(path);
+
+ let profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ let addresses = profileStorage.addresses.getAll();
+ let guid = addresses[1].guid;
+
+ let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
+ (subject, data) => data == "remove");
+
+ do_check_eq(addresses.length, 2);
+
+ profileStorage.addresses.remove(guid);
+ await onChanged;
+ await profileStorage._saveImmediately();
+
+ profileStorage = new ProfileStorage(path);
+ await profileStorage.initialize();
+
+ addresses = profileStorage.addresses.getAll();
+
+ do_check_eq(addresses.length, 1);
+
+ Assert.throws(() => profileStorage.addresses.get(guid), /No matching record\./);
+});
--- a/browser/extensions/formautofill/test/unit/test_enabledStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_enabledStatus.js
@@ -59,28 +59,28 @@ add_task(function* test_enabledStatus_ob
});
add_task(function* test_enabledStatus_getStatus() {
let formAutofillParent = new FormAutofillParent();
do_register_cleanup(function cleanup() {
Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
});
- sinon.stub(profileStorage, "getAll");
- profileStorage.getAll.returns([]);
+ sinon.stub(profileStorage.addresses, "getAll");
+ profileStorage.addresses.getAll.returns([]);
// pref is enabled and profile is empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", true);
do_check_eq(formAutofillParent._getStatus(), false);
// pref is disabled and profile is empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
do_check_eq(formAutofillParent._getStatus(), false);
- profileStorage.getAll.returns(["test-profile"]);
+ profileStorage.addresses.getAll.returns(["test-profile"]);
// pref is enabled and profile is not empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", true);
do_check_eq(formAutofillParent._getStatus(), true);
// pref is disabled and profile is not empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
do_check_eq(formAutofillParent._getStatus(), false);
});
deleted file mode 100644
--- a/browser/extensions/formautofill/test/unit/test_profileStorage.js
+++ /dev/null
@@ -1,274 +0,0 @@
-/**
- * Tests ProfileStorage object.
- */
-
-"use strict";
-
-const {ProfileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
-
-const TEST_STORE_FILE_NAME = "test-profile.json";
-
-const TEST_ADDRESS_1 = {
- "given-name": "Timothy",
- "additional-name": "John",
- "family-name": "Berners-Lee",
- organization: "World Wide Web Consortium",
- "street-address": "32 Vassar Street\nMIT Room 32-G524",
- "address-level2": "Cambridge",
- "address-level1": "MA",
- "postal-code": "02139",
- country: "US",
- tel: "+1 617 253 5702",
- email: "timbl@w3.org",
-};
-
-const TEST_ADDRESS_2 = {
- "street-address": "Some Address",
- country: "US",
-};
-
-const TEST_ADDRESS_3 = {
- "street-address": "Other Address",
- "postal-code": "12345",
-};
-
-const TEST_ADDRESS_WITH_INVALID_FIELD = {
- "street-address": "Another Address",
- invalidField: "INVALID",
-};
-
-let prepareTestRecords = async function(path) {
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
- (subject, data) => data == "add");
- profileStorage.add(TEST_ADDRESS_1);
- await onChanged;
- profileStorage.add(TEST_ADDRESS_2);
- await profileStorage._saveImmediately();
-};
-
-let do_check_record_matches = (recordWithMeta, record) => {
- for (let key in record) {
- do_check_eq(recordWithMeta[key], record[key]);
- }
-};
-
-add_task(async function test_initialize() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- do_check_eq(profileStorage._store.data.version, 1);
- do_check_eq(profileStorage._store.data.addresses.length, 0);
-
- let data = profileStorage._store.data;
-
- await profileStorage._saveImmediately();
-
- profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- Assert.deepEqual(profileStorage._store.data, data);
-});
-
-add_task(async function test_getAll() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- await prepareTestRecords(path);
-
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let addresses = profileStorage.getAll();
-
- do_check_eq(addresses.length, 2);
- do_check_record_matches(addresses[0], TEST_ADDRESS_1);
- do_check_record_matches(addresses[1], TEST_ADDRESS_2);
-
- // Modifying output shouldn't affect the storage.
- addresses[0].organization = "test";
- do_check_record_matches(profileStorage.getAll()[0], TEST_ADDRESS_1);
-});
-
-add_task(async function test_get() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- await prepareTestRecords(path);
-
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let addresses = profileStorage.getAll();
- let guid = addresses[0].guid;
-
- let address = profileStorage.get(guid);
- do_check_record_matches(address, TEST_ADDRESS_1);
-
- // Modifying output shouldn't affect the storage.
- address.organization = "test";
- do_check_record_matches(profileStorage.get(guid), TEST_ADDRESS_1);
-
- Assert.throws(() => profileStorage.get("INVALID_GUID"),
- /No matching record\./);
-});
-
-add_task(async function test_getByFilter() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- await prepareTestRecords(path);
-
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let filter = {info: {fieldName: "street-address"}, searchString: "Some"};
- let addresses = profileStorage.getByFilter(filter);
- do_check_eq(addresses.length, 1);
- do_check_record_matches(addresses[0], TEST_ADDRESS_2);
-
- filter = {info: {fieldName: "country"}, searchString: "u"};
- addresses = profileStorage.getByFilter(filter);
- do_check_eq(addresses.length, 2);
- do_check_record_matches(addresses[0], TEST_ADDRESS_1);
- do_check_record_matches(addresses[1], TEST_ADDRESS_2);
-
- filter = {info: {fieldName: "street-address"}, searchString: "test"};
- addresses = profileStorage.getByFilter(filter);
- do_check_eq(addresses.length, 0);
-
- filter = {info: {fieldName: "street-address"}, searchString: ""};
- addresses = profileStorage.getByFilter(filter);
- do_check_eq(addresses.length, 2);
-
- // Check if the filtering logic is free from searching special chars.
- filter = {info: {fieldName: "street-address"}, searchString: ".*"};
- addresses = profileStorage.getByFilter(filter);
- do_check_eq(addresses.length, 0);
-
- // Prevent broken while searching the property that does not exist.
- filter = {info: {fieldName: "tel"}, searchString: "1"};
- addresses = profileStorage.getByFilter(filter);
- do_check_eq(addresses.length, 0);
-});
-
-add_task(async function test_add() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- await prepareTestRecords(path);
-
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let addresses = profileStorage.getAll();
-
- do_check_eq(addresses.length, 2);
-
- do_check_record_matches(addresses[0], TEST_ADDRESS_1);
- do_check_record_matches(addresses[1], TEST_ADDRESS_2);
-
- do_check_neq(addresses[0].guid, undefined);
- do_check_neq(addresses[0].timeCreated, undefined);
- do_check_eq(addresses[0].timeLastModified, addresses[0].timeCreated);
- do_check_eq(addresses[0].timeLastUsed, 0);
- do_check_eq(addresses[0].timesUsed, 0);
-
- Assert.throws(() => profileStorage.add(TEST_ADDRESS_WITH_INVALID_FIELD),
- /"invalidField" is not a valid field\./);
-});
-
-add_task(async function test_update() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- await prepareTestRecords(path);
-
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let addresses = profileStorage.getAll();
- let guid = addresses[1].guid;
- let timeLastModified = addresses[1].timeLastModified;
-
- let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
- (subject, data) => data == "update");
-
- do_check_neq(addresses[1].country, undefined);
-
- profileStorage.update(guid, TEST_ADDRESS_3);
- await onChanged;
- await profileStorage._saveImmediately();
-
- profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let address = profileStorage.get(guid);
-
- do_check_eq(address.country, undefined);
- do_check_neq(address.timeLastModified, timeLastModified);
- do_check_record_matches(address, TEST_ADDRESS_3);
-
- Assert.throws(
- () => profileStorage.update("INVALID_GUID", TEST_ADDRESS_3),
- /No matching record\./
- );
-
- Assert.throws(
- () => profileStorage.update(guid, TEST_ADDRESS_WITH_INVALID_FIELD),
- /"invalidField" is not a valid field\./
- );
-});
-
-add_task(async function test_notifyUsed() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- await prepareTestRecords(path);
-
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let addresses = profileStorage.getAll();
- let guid = addresses[1].guid;
- let timeLastUsed = addresses[1].timeLastUsed;
- let timesUsed = addresses[1].timesUsed;
-
- let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
- (subject, data) => data == "notifyUsed");
-
- profileStorage.notifyUsed(guid);
- await onChanged;
- await profileStorage._saveImmediately();
-
- profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let address = profileStorage.get(guid);
-
- do_check_eq(address.timesUsed, timesUsed + 1);
- do_check_neq(address.timeLastUsed, timeLastUsed);
-
- Assert.throws(() => profileStorage.notifyUsed("INVALID_GUID"),
- /No matching record\./);
-});
-
-add_task(async function test_remove() {
- let path = getTempFile(TEST_STORE_FILE_NAME).path;
- await prepareTestRecords(path);
-
- let profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- let addresses = profileStorage.getAll();
- let guid = addresses[1].guid;
-
- let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
- (subject, data) => data == "remove");
-
- do_check_eq(addresses.length, 2);
-
- profileStorage.remove(guid);
- await onChanged;
- await profileStorage._saveImmediately();
-
- profileStorage = new ProfileStorage(path);
- await profileStorage.initialize();
-
- addresses = profileStorage.getAll();
-
- do_check_eq(addresses.length, 1);
-
- Assert.throws(() => profileStorage.get(guid), /No matching record\./);
-});
--- a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -40,18 +40,18 @@ add_task(async function test_profileSave
add_task(async function test_profileSavedFieldNames_update() {
let formAutofillParent = new FormAutofillParent();
await formAutofillParent.init();
do_register_cleanup(function cleanup() {
Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
});
- sinon.stub(profileStorage, "getAll");
- profileStorage.getAll.returns([]);
+ sinon.stub(profileStorage.addresses, "getAll");
+ profileStorage.addresses.getAll.returns([]);
// The set is empty if there's no profile in the store.
formAutofillParent._updateSavedFieldNames();
do_check_eq(Services.ppmm.initialProcessData.autofillSavedFieldNames.size, 0);
// 2 profiles with 4 valid fields.
let fakeStorage = [{
guid: "test-guid-1",
@@ -69,17 +69,17 @@ add_task(async function test_profileSave
"street-address": "331 E. Evelyn Avenue",
tel: "1-650-903-0800",
country: "US",
timeCreated: 0,
timeLastUsed: 0,
timeLastModified: 0,
timesUsed: 0,
}];
- profileStorage.getAll.returns(fakeStorage);
+ profileStorage.addresses.getAll.returns(fakeStorage);
formAutofillParent._updateSavedFieldNames();
let autofillSavedFieldNames = Services.ppmm.initialProcessData.autofillSavedFieldNames;
do_check_eq(autofillSavedFieldNames.size, 4);
do_check_eq(autofillSavedFieldNames.has("organization"), true);
do_check_eq(autofillSavedFieldNames.has("street-address"), true);
do_check_eq(autofillSavedFieldNames.has("tel"), true);
do_check_eq(autofillSavedFieldNames.has("email"), false);
--- a/browser/extensions/formautofill/test/unit/test_transformFields.js
+++ b/browser/extensions/formautofill/test/unit/test_transformFields.js
@@ -3,17 +3,17 @@
*/
"use strict";
const {ProfileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
const TEST_STORE_FILE_NAME = "test-profile.json";
-const COMPUTE_TESTCASES = [
+const ADDRESS_COMPUTE_TESTCASES = [
// Empty
{
description: "Empty address",
address: {
},
expectedResult: {
},
},
@@ -78,17 +78,17 @@ const COMPUTE_TESTCASES = [
"street-address": "line1\nline2\nline3\nline4",
"address-line1": "line1",
"address-line2": "line2",
"address-line3": "line3",
},
},
];
-const NORMALIZE_TESTCASES = [
+const ADDRESS_NORMALIZE_TESTCASES = [
// Empty
{
description: "Empty address",
address: {
},
expectedResult: {
},
},
@@ -181,47 +181,47 @@ const NORMALIZE_TESTCASES = [
];
let do_check_record_matches = (expectedRecord, record) => {
for (let key in expectedRecord) {
do_check_eq(expectedRecord[key], record[key] || "");
}
};
-add_task(async function test_computeFields() {
+add_task(async function test_computeAddressFields() {
let path = getTempFile(TEST_STORE_FILE_NAME).path;
let profileStorage = new ProfileStorage(path);
await profileStorage.initialize();
- COMPUTE_TESTCASES.forEach(testcase => profileStorage.add(testcase.address));
+ ADDRESS_COMPUTE_TESTCASES.forEach(testcase => profileStorage.addresses.add(testcase.address));
await profileStorage._saveImmediately();
profileStorage = new ProfileStorage(path);
await profileStorage.initialize();
- let addresses = profileStorage.getAll();
+ let addresses = profileStorage.addresses.getAll();
for (let i in addresses) {
- do_print("Verify testcase: " + COMPUTE_TESTCASES[i].description);
- do_check_record_matches(COMPUTE_TESTCASES[i].expectedResult, addresses[i]);
+ do_print("Verify testcase: " + ADDRESS_COMPUTE_TESTCASES[i].description);
+ do_check_record_matches(ADDRESS_COMPUTE_TESTCASES[i].expectedResult, addresses[i]);
}
});
-add_task(async function test_normalizeFields() {
+add_task(async function test_normalizeAddressFields() {
let path = getTempFile(TEST_STORE_FILE_NAME).path;
let profileStorage = new ProfileStorage(path);
await profileStorage.initialize();
- NORMALIZE_TESTCASES.forEach(testcase => profileStorage.add(testcase.address));
+ ADDRESS_NORMALIZE_TESTCASES.forEach(testcase => profileStorage.addresses.add(testcase.address));
await profileStorage._saveImmediately();
profileStorage = new ProfileStorage(path);
await profileStorage.initialize();
- let addresses = profileStorage.getAll();
+ let addresses = profileStorage.addresses.getAll();
for (let i in addresses) {
- do_print("Verify testcase: " + NORMALIZE_TESTCASES[i].description);
- do_check_record_matches(NORMALIZE_TESTCASES[i].expectedResult, addresses[i]);
+ do_print("Verify testcase: " + ADDRESS_NORMALIZE_TESTCASES[i].description);
+ do_check_record_matches(ADDRESS_NORMALIZE_TESTCASES[i].expectedResult, addresses[i]);
}
});
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -11,21 +11,21 @@ support-files =
[heuristics/third_party/test_HomeDepot.js]
[heuristics/third_party/test_Macys.js]
[heuristics/third_party/test_NewEgg.js]
[heuristics/third_party/test_OfficeDepot.js]
[heuristics/third_party/test_QVC.js]
[heuristics/third_party/test_Sears.js]
[heuristics/third_party/test_Staples.js]
[heuristics/third_party/test_Walmart.js]
+[test_addressRecords.js]
[test_autofillFormFields.js]
[test_collectFormFields.js]
[test_enabledStatus.js]
[test_findLabelElements.js]
[test_getFormInputDetails.js]
[test_isCJKName.js]
[test_markAsAutofillField.js]
[test_nameUtils.js]
[test_onFormSubmitted.js]
[test_profileAutocompleteResult.js]
-[test_profileStorage.js]
[test_savedFieldNames.js]
[test_transformFields.js]