diff options
Diffstat (limited to 'browser/extensions/formautofill/test/unit/head.js')
-rw-r--r-- | browser/extensions/formautofill/test/unit/head.js | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/browser/extensions/formautofill/test/unit/head.js b/browser/extensions/formautofill/test/unit/head.js new file mode 100644 index 0000000000..9761bea5bd --- /dev/null +++ b/browser/extensions/formautofill/test/unit/head.js @@ -0,0 +1,358 @@ +/** + * Provides infrastructure for automated formautofill components tests. + */ + +"use strict"; + +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); +var { ObjectUtils } = ChromeUtils.import( + "resource://gre/modules/ObjectUtils.jsm" +); +var { FormLikeFactory } = ChromeUtils.importESModule( + "resource://gre/modules/FormLikeFactory.sys.mjs" +); +var { AddonTestUtils, MockAsyncShutdown } = ChromeUtils.import( + "resource://testing-common/AddonTestUtils.jsm" +); +var { ExtensionTestUtils } = ChromeUtils.import( + "resource://testing-common/ExtensionXPCShellUtils.jsm" +); +var { FileTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/FileTestUtils.sys.mjs" +); +var { MockDocument } = ChromeUtils.importESModule( + "resource://testing-common/MockDocument.sys.mjs" +); +var { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm"); +var { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +ChromeUtils.defineModuleGetter( + this, + "AddonManager", + "resource://gre/modules/AddonManager.jsm" +); +ChromeUtils.defineModuleGetter( + this, + "AddonManagerPrivate", + "resource://gre/modules/AddonManager.jsm" +); +ChromeUtils.defineESModuleGetters(this, { + FileUtils: "resource://gre/modules/FileUtils.sys.mjs", +}); + +ChromeUtils.defineModuleGetter( + this, + "ExtensionParent", + "resource://gre/modules/ExtensionParent.jsm" +); + +{ + // We're going to register a mock file source + // with region names based on en-US. This is + // necessary for tests that expect to match + // on region code display names. + const fs = [ + { + path: "toolkit/intl/regionNames.ftl", + source: ` +region-name-us = United States +region-name-nz = New Zealand +region-name-au = Australia +region-name-ca = Canada +region-name-tw = Taiwan + `, + }, + ]; + + let locales = Services.locale.packagedLocales; + const mockSource = L10nFileSource.createMock( + "mock", + "app", + locales, + "resource://mock_path", + fs + ); + L10nRegistry.getInstance().registerSources([mockSource]); +} + +do_get_profile(); + +const EXTENSION_ID = "formautofill@mozilla.org"; + +AddonTestUtils.init(this); +AddonTestUtils.overrideCertDB(); + +function SetPref(name, value) { + switch (typeof value) { + case "string": + Services.prefs.setCharPref(name, value); + break; + case "number": + Services.prefs.setIntPref(name, value); + break; + case "boolean": + Services.prefs.setBoolPref(name, value); + break; + default: + throw new Error("Unknown type"); + } +} + +async function loadExtension() { + AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "1.9.2" + ); + await AddonTestUtils.promiseStartupManager(); + + let extensionPath = Services.dirsvc.get("GreD", Ci.nsIFile); + extensionPath.append("browser"); + extensionPath.append("features"); + extensionPath.append(EXTENSION_ID); + + if (!extensionPath.exists()) { + extensionPath.leafName = `${EXTENSION_ID}.xpi`; + } + + let startupPromise = new Promise(resolve => { + const { apiManager } = ExtensionParent; + function onReady(event, extension) { + if (extension.id == EXTENSION_ID) { + apiManager.off("ready", onReady); + resolve(); + } + } + + apiManager.on("ready", onReady); + }); + + await AddonManager.installTemporaryAddon(extensionPath); + await startupPromise; +} + +// Returns a reference to a temporary file that is guaranteed not to exist and +// is cleaned up later. See FileTestUtils.getTempFile for details. +function getTempFile(leafName) { + return FileTestUtils.getTempFile(leafName); +} + +async function initProfileStorage( + fileName, + records, + collectionName = "addresses" +) { + let { FormAutofillStorage } = ChromeUtils.import( + "resource://autofill/FormAutofillStorage.jsm" + ); + let path = getTempFile(fileName).path; + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + // AddonTestUtils inserts its own directory provider that manages TmpD. + // It removes that directory at shutdown, which races with shutdown + // handing in JSONFile/DeferredTask (which is used by FormAutofillStorage). + // Avoid the race by explicitly finalizing any formautofill JSONFile + // instances created manually by individual tests when the test finishes. + registerCleanupFunction(function finalizeAutofillStorage() { + return profileStorage._finalize(); + }); + + if (!records || !Array.isArray(records)) { + return profileStorage; + } + + let onChanged = TestUtils.topicObserved( + "formautofill-storage-changed", + (subject, data) => + data == "add" && subject.wrappedJSObject.collectionName == collectionName + ); + for (let record of records) { + Assert.ok(await profileStorage[collectionName].add(record)); + await onChanged; + } + await profileStorage._saveImmediately(); + return profileStorage; +} + +function verifySectionFieldDetails(sections, expectedResults) { + Assert.equal( + sections.length, + expectedResults.length, + "Expected section count." + ); + sections.forEach((sectionInfo, sectionIndex) => { + let expectedSectionInfo = expectedResults[sectionIndex]; + info("FieldName Prediction Results: " + sectionInfo.map(i => i.fieldName)); + info( + "FieldName Expected Results: " + + expectedSectionInfo.map(i => i.fieldName) + ); + Assert.equal( + sectionInfo.length, + expectedSectionInfo.length, + "Expected field count." + ); + + sectionInfo.forEach((field, fieldIndex) => { + let expectedField = expectedSectionInfo[fieldIndex]; + delete field._reason; + delete field.elementWeakRef; + delete field.confidence; + Assert.deepEqual(field, expectedField); + }); + }); +} + +var FormAutofillHeuristics, LabelUtils; +var AddressDataLoader, FormAutofillUtils; + +async function runHeuristicsTest(patterns, fixturePathPrefix) { + add_setup(async () => { + ({ FormAutofillHeuristics } = ChromeUtils.import( + "resource://autofill/FormAutofillHeuristics.jsm" + )); + ({ AddressDataLoader, FormAutofillUtils, LabelUtils } = ChromeUtils.import( + "resource://autofill/FormAutofillUtils.jsm" + )); + }); + + patterns.forEach(testPattern => { + add_task(async function() { + info("Starting test fixture: " + testPattern.fixturePath); + let file = do_get_file(fixturePathPrefix + testPattern.fixturePath); + let doc = MockDocument.createTestDocumentFromFile( + "http://localhost:8080/test/", + file + ); + + let forms = []; + + for (let field of FormAutofillUtils.autofillFieldSelector(doc)) { + let formLike = FormLikeFactory.createFromField(field); + if (!forms.some(form => form.rootElement === formLike.rootElement)) { + forms.push(formLike); + } + } + + Assert.equal( + forms.length, + testPattern.expectedResult.length, + "Expected form count." + ); + + forms.forEach((form, formIndex) => { + let sections = FormAutofillHeuristics.getFormInfo(form); + verifySectionFieldDetails( + sections.map(section => section.fieldDetails), + testPattern.expectedResult[formIndex] + ); + }); + }); + }); +} + +/** + * Returns the Sync change counter for a profile storage record. Synced records + * store additional metadata for tracking changes and resolving merge conflicts. + * Deleting a synced record replaces the record with a tombstone. + * + * @param {AutofillRecords} records + * The `AutofillRecords` instance to query. + * @param {string} guid + * The GUID of the record or tombstone. + * @returns {number} + * The change counter, or -1 if the record doesn't exist or hasn't + * been synced yet. + */ +function getSyncChangeCounter(records, guid) { + let record = records._findByGUID(guid, { includeDeleted: true }); + if (!record) { + return -1; + } + let sync = records._getSyncMetaData(record); + if (!sync) { + return -1; + } + return sync.changeCounter; +} + +/** + * Performs a partial deep equality check to determine if an object contains + * the given fields. + * + * @param {object} object + * The object to check. Unlike `ObjectUtils.deepEqual`, properties in + * `object` that are not in `fields` will be ignored. + * @param {object} fields + * The fields to match. + * @returns {boolean} + * Does `object` contain `fields` with matching values? + */ +function objectMatches(object, fields) { + let actual = {}; + for (let key in fields) { + if (!object.hasOwnProperty(key)) { + return false; + } + actual[key] = object[key]; + } + return ObjectUtils.deepEqual(actual, fields); +} + +add_setup(async function head_initialize() { + Services.prefs.setBoolPref("extensions.experiments.enabled", true); + Services.prefs.setBoolPref( + "extensions.formautofill.heuristics.enabled", + true + ); + Services.prefs.setBoolPref("extensions.formautofill.section.enabled", true); + Services.prefs.setBoolPref("dom.forms.autocomplete.formautofill", true); + + Services.prefs.setCharPref( + "extensions.formautofill.addresses.supported", + "on" + ); + Services.prefs.setCharPref( + "extensions.formautofill.creditCards.supported", + "on" + ); + Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", true); + Services.prefs.setBoolPref( + "extensions.formautofill.creditCards.enabled", + true + ); + + // Clean up after every test. + registerCleanupFunction(function head_cleanup() { + Services.prefs.clearUserPref("extensions.experiments.enabled"); + Services.prefs.clearUserPref( + "extensions.formautofill.creditCards.supported" + ); + Services.prefs.clearUserPref("extensions.formautofill.addresses.supported"); + Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled"); + Services.prefs.clearUserPref("extensions.formautofill.heuristics.enabled"); + Services.prefs.clearUserPref("extensions.formautofill.section.enabled"); + Services.prefs.clearUserPref("dom.forms.autocomplete.formautofill"); + Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled"); + Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled"); + }); + + await loadExtension(); +}); + +let OSKeyStoreTestUtils; +add_setup(async function os_key_store_setup() { + ({ OSKeyStoreTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/OSKeyStoreTestUtils.sys.mjs" + )); + OSKeyStoreTestUtils.setup(); + registerCleanupFunction(async function cleanup() { + await OSKeyStoreTestUtils.cleanup(); + }); +}); |