diff options
Diffstat (limited to 'browser/extensions/formautofill/test/unit')
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] |