Bug 1449055 Update formautofill tests draft
authorAndrew Swan <aswan@mozilla.com>
Sat, 28 Jul 2018 14:06:07 -0700
changeset 828845 ce6ce90b726a202cb2b2b269e1e4e3223ac25f7d
parent 828840 fe40d079ce18a1f1564f2468bd9e038b9c1d45c5
child 828846 31693a4f1367987872ea67181cc5863f036f4e44
push id118719
push useraswan@mozilla.com
push dateMon, 13 Aug 2018 22:55:53 +0000
bugs1449055
milestone63.0a1
Bug 1449055 Update formautofill tests I intend to squash this with the other patch before landing but keeping this separate for now for easier reviewing... MozReview-Commit-ID: HYPUYmZ6Dwx
browser/extensions/formautofill/FormAutofillStorage.jsm
browser/extensions/formautofill/test/unit/head.js
browser/extensions/formautofill/test/unit/test_activeStatus.js
browser/extensions/formautofill/test/unit/test_addressDataLoader.js
browser/extensions/formautofill/test/unit/test_autofillFormFields.js
browser/extensions/formautofill/test/unit/test_collectFormFields.js
browser/extensions/formautofill/test/unit/test_createRecords.js
browser/extensions/formautofill/test/unit/test_creditCardRecords.js
browser/extensions/formautofill/test/unit/test_extractLabelStrings.js
browser/extensions/formautofill/test/unit/test_findLabelElements.js
browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
browser/extensions/formautofill/test/unit/test_getCategoriesFromFieldNames.js
browser/extensions/formautofill/test/unit/test_getFormInputDetails.js
browser/extensions/formautofill/test/unit/test_getInfo.js
browser/extensions/formautofill/test/unit/test_getRecords.js
browser/extensions/formautofill/test/unit/test_isAvailable.js
browser/extensions/formautofill/test/unit/test_isCJKName.js
browser/extensions/formautofill/test/unit/test_isFieldEligibleForAutofill.js
browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
browser/extensions/formautofill/test/unit/test_masterPassword.js
browser/extensions/formautofill/test/unit/test_migrateRecords.js
browser/extensions/formautofill/test/unit/test_nameUtils.js
browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
browser/extensions/formautofill/test/unit/test_parseAddressFormat.js
browser/extensions/formautofill/test/unit/test_phoneNumber.js
browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
browser/extensions/formautofill/test/unit/test_savedFieldNames.js
browser/extensions/formautofill/test/unit/test_storage_remove.js
browser/extensions/formautofill/test/unit/test_storage_tombstones.js
browser/extensions/formautofill/test/unit/test_sync.js
browser/extensions/formautofill/test/unit/test_toOneLineAddress.js
browser/extensions/formautofill/test/unit/test_transformFields.js
testing/modules/FileTestUtils.jsm
--- a/browser/extensions/formautofill/FormAutofillStorage.jsm
+++ b/browser/extensions/formautofill/FormAutofillStorage.jsm
@@ -1785,13 +1785,17 @@ FormAutofillStorage.prototype = {
     }
     return data;
   },
 
   // For test only.
   _saveImmediately() {
     return this._store._save();
   },
+
+  _finalize() {
+    return this._store.finalize();
+  },
 };
 
 // The singleton exposed by this module.
 this.formAutofillStorage = new FormAutofillStorage(
   OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME));
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -3,72 +3,93 @@
  */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
 ChromeUtils.import("resource://gre/modules/FormLikeFactory.jsm");
+ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
+ChromeUtils.import("resource://testing-common/ExtensionXPCShellUtils.jsm");
 ChromeUtils.import("resource://testing-common/FileTestUtils.jsm");
 ChromeUtils.import("resource://testing-common/MockDocument.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "DownloadPaths",
                                "resource://gre/modules/DownloadPaths.jsm");
 ChromeUtils.defineModuleGetter(this, "FileUtils",
                                "resource://gre/modules/FileUtils.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "resProto",
-                                   "@mozilla.org/network/protocol;1?name=resource",
-                                   "nsISubstitutingProtocolHandler");
+ChromeUtils.defineModuleGetter(this, "ExtensionParent",
+                               "resource://gre/modules/ExtensionParent.jsm");
 
 do_get_profile();
 
 // ================================================
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/releases/v2.3.2/
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
 /* globals sinon */
 // ================================================
 
-// Load our bootstrap extension manifest so we can access our chrome/resource URIs.
 const EXTENSION_ID = "formautofill@mozilla.org";
-let extensionDir = Services.dirsvc.get("GreD", Ci.nsIFile);
-extensionDir.append("browser");
-extensionDir.append("features");
-extensionDir.append(EXTENSION_ID);
-let bootstrapFile = extensionDir.clone();
-bootstrapFile.append("bootstrap.js");
-let bootstrapURI = Services.io.newFileURI(bootstrapFile).spec;
-// If the unpacked extension doesn't exist, use the packed version.
-if (!extensionDir.exists()) {
-  extensionDir = extensionDir.parent;
-  extensionDir.append(EXTENSION_ID + ".xpi");
-  let jarURI = Services.io.newFileURI(extensionDir);
-  bootstrapURI = "jar:" + jarURI.spec + "!/bootstrap.js";
+
+ExtensionTestUtils.init(this);
+
+async function loadExtension() {
+  ExtensionTestUtils.startAddonManager();
+
+  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;
 }
-Components.manager.addBootstrappedManifestLocation(extensionDir);
-
-let resURI = Services.io.newURI("chrome/res/", null, Services.io.newURI(bootstrapURI));
-resProto.setSubstitution("formautofill", resURI);
 
 // 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://formautofill/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" &&
@@ -94,19 +115,21 @@ function verifySectionFieldDetails(secti
       let expectedField = expectedSectionInfo[fieldIndex];
       delete field._reason;
       delete field.elementWeakRef;
       Assert.deepEqual(field, expectedField);
     });
   });
 }
 
-function runHeuristicsTest(patterns, fixturePathPrefix) {
-  ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
-  ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+async function runHeuristicsTest(patterns, fixturePathPrefix) {
+  add_task(async function setup() {
+    ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
+    ChromeUtils.import("resource://formautofill/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 = [];
@@ -189,9 +212,11 @@ add_task(async function head_initialize(
   // Clean up after every test.
   registerCleanupFunction(function head_cleanup() {
     Services.prefs.clearUserPref("extensions.formautofill.available");
     Services.prefs.clearUserPref("extensions.formautofill.creditCards.available");
     Services.prefs.clearUserPref("extensions.formautofill.heuristics.enabled");
     Services.prefs.clearUserPref("extensions.formautofill.section.enabled");
     Services.prefs.clearUserPref("dom.forms.autocomplete.formautofill");
   });
+
+  await loadExtension();
 });
--- a/browser/extensions/formautofill/test/unit/test_activeStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_activeStatus.js
@@ -1,15 +1,19 @@
 /*
  * Test for status handling in Form Autofill Parent.
  */
 
 "use strict";
 
-let {FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {});
+let FormAutofillParent;
+
+add_task(async function setup() {
+  ({FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {}));
+});
 
 add_task(async function test_activeStatus_init() {
   let formAutofillParent = new FormAutofillParent();
   sinon.spy(formAutofillParent, "_updateStatus");
 
   // Default status is null before initialization
   Assert.equal(formAutofillParent._active, null);
   Assert.equal(Services.ppmm.initialProcessData.autofillEnabled, undefined);
--- a/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
+++ b/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
@@ -1,27 +1,29 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
-
 const SUPPORT_COUNTRIES_TESTCASES = [
   {
     country: "US",
     properties: ["languages", "alternative_names", "sub_keys", "sub_names"],
   },
   {
     country: "CA",
     properties: ["languages", "name", "sub_keys", "sub_names"],
   },
   {
     country: "DE",
     properties: ["name"],
   },
 ];
 
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+});
+
 add_task(async function test_initalState() {
   // addressData should not exist
   Assert.equal(AddressDataLoader._addressData, undefined);
   // Verify _dataLoaded state
   Assert.equal(AddressDataLoader._dataLoaded.country, false);
   Assert.equal(AddressDataLoader._dataLoaded.level1.size, 0);
 });
 
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -1,17 +1,20 @@
 /*
  * Test for form auto fill content helper fill all inputs function.
  */
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
-let {MasterPassword} = ChromeUtils.import("resource://formautofill/MasterPassword.jsm", {});
+let MasterPassword;
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
+  ({MasterPassword} = ChromeUtils.import("resource://formautofill/MasterPassword.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"><select id="country"></select>
                <input id='email'><input id="tel"></form>`,
     focusedInputId: "given-name",
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -1,15 +1,17 @@
 /*
  * Test for form auto fill content helper collectFormFields functions.
  */
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillHandler.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"><select id="country"></select>
                <input id='email'><input id="phone"></form>`,
     sections: [
--- a/browser/extensions/formautofill/test/unit/test_createRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_createRecords.js
@@ -1,15 +1,17 @@
 /*
  * Test for the normalization of records created by FormAutofillHandler.
  */
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
+add_task(async function seutp() {
+  ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
+});
 
 const TESTCASES = [
   {
     description: "Don't contain a field whose length of value is greater than 200",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="address-level1" autocomplete="address-level1">
--- a/browser/extensions/formautofill/test/unit/test_creditCardRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js
@@ -1,19 +1,23 @@
 /**
  * Tests FormAutofillStorage object with creditCards records.
  */
 
 "use strict";
 
-const {FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
 ChromeUtils.defineModuleGetter(this, "Preferences",
                                "resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
 
+let FormAutofillStorage;
+add_task(async function setup() {
+  ({FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {}));
+});
+
 const TEST_STORE_FILE_NAME = "test-credit-card.json";
 const COLLECTION_NAME = "creditCards";
 
 const TEST_CREDIT_CARD_1 = {
   "cc-name": "John Doe",
   "cc-number": "4929001587121045",
   "cc-exp-month": 4,
   "cc-exp-year": 2017,
--- a/browser/extensions/formautofill/test/unit/test_extractLabelStrings.js
+++ b/browser/extensions/formautofill/test/unit/test_extractLabelStrings.js
@@ -1,11 +1,13 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
+add_task(async function() {
+  ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
+});
 
 const TESTCASES = [
   {
     description: "A label element contains one input element.",
     document: `<label id="typeA"> label type A
                  <!-- This comment should not be extracted. -->
                  <input type="text">
                  <script>FOO</script>
--- a/browser/extensions/formautofill/test/unit/test_findLabelElements.js
+++ b/browser/extensions/formautofill/test/unit/test_findLabelElements.js
@@ -1,11 +1,13 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
+add_task(async function() {
+  ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
+});
 
 const TESTCASES = [
   {
     description: "Input contains in a label element.",
     document: `<form>
                  <label id="labelA"> label type A
                    <input id="typeA" type="text">
                  </label>
--- a/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
+++ b/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
@@ -1,15 +1,17 @@
 /*
  * Test for form auto fill content helper fill all inputs function.
  */
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
+add_task(async function() {
+  ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
+});
 
 const DEFAULT_ADDRESS_RECORD = {
   "guid": "123",
   "street-address": "2 Harrison St\nline2\nline3",
   "address-line1": "2 Harrison St",
   "address-line2": "line2",
   "address-line3": "line3",
   "address-level1": "CA",
--- a/browser/extensions/formautofill/test/unit/test_getCategoriesFromFieldNames.js
+++ b/browser/extensions/formautofill/test/unit/test_getCategoriesFromFieldNames.js
@@ -1,12 +1,14 @@
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+add_task(async function() {
+  ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+});
 
 add_task(async function test_isAddressField_isCreditCardField() {
   const TEST_CASES = {
     "given-name": {
       isAddressField: true,
       isCreditCardField: false,
     },
     "organization": {
--- a/browser/extensions/formautofill/test/unit/test_getFormInputDetails.js
+++ b/browser/extensions/formautofill/test/unit/test_getFormInputDetails.js
@@ -1,11 +1,13 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
+add_task(async function() {
+  ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
+});
 
 const TESTCASES = [
   {
     description: "Form containing 5 fields with autocomplete attribute.",
     document: `<form id="form1">
                  <input id="street-addr" autocomplete="street-address">
                  <input id="city" autocomplete="address-level2">
                  <select id="country" autocomplete="country"></select>
--- a/browser/extensions/formautofill/test/unit/test_getInfo.js
+++ b/browser/extensions/formautofill/test/unit/test_getInfo.js
@@ -1,11 +1,13 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
+add_task(async function() {
+  ChromeUtils.import("resource://formautofill/FormAutofillHeuristics.jsm");
+});
 
 const TESTCASES = [
   {
     description: "Input element in a label element",
     document: `<form>
                  <label> E-Mail
                    <input id="targetElement" type="text">
                  </label>
--- a/browser/extensions/formautofill/test/unit/test_getRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_getRecords.js
@@ -1,18 +1,22 @@
 /*
  * Test for make sure getRecords can retrieve right collection from storage.
  */
 
 "use strict";
 
-let {FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {});
-ChromeUtils.import("resource://formautofill/MasterPassword.jsm");
 ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
 
+let FormAutofillParent;
+add_task(async function setup() {
+  ({FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {}));
+  ChromeUtils.import("resource://formautofill/MasterPassword.jsm");
+});
+
 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",
--- a/browser/extensions/formautofill/test/unit/test_isAvailable.js
+++ b/browser/extensions/formautofill/test/unit/test_isAvailable.js
@@ -1,32 +1,37 @@
 /**
  * Test enabling the feature in specific locales and regions.
  */
 
 "use strict";
 
-// Load bootstrap.js into a sandbox to be able to test `isAvailable`
-let sandbox = {};
-Services.scriptloader.loadSubScript(bootstrapURI, sandbox, "utf-8");
-info("bootstrapURI: " + bootstrapURI);
+const DOM_ENABLED_PREF = "dom.forms.autocomplete.formautofill";
 
 add_task(async function test_defaultTestEnvironment() {
-  Assert.ok(sandbox.isAvailable());
+  Assert.ok(Services.prefs.getBoolPref(DOM_ENABLED_PREF));
 });
 
 add_task(async function test_unsupportedRegion() {
   Services.prefs.setCharPref("extensions.formautofill.available", "detect");
   Services.prefs.setCharPref("browser.search.region", "ZZ");
   registerCleanupFunction(function cleanupRegion() {
     Services.prefs.clearUserPref("browser.search.region");
   });
-  Assert.ok(!sandbox.isAvailable());
+
+  let addon = await AddonManager.getAddonByID(EXTENSION_ID);
+  await addon.reload();
+
+  Assert.ok(!Services.prefs.getBoolPref(DOM_ENABLED_PREF));
 });
 
 add_task(async function test_supportedRegion() {
   Services.prefs.setCharPref("extensions.formautofill.available", "detect");
   Services.prefs.setCharPref("browser.search.region", "US");
   registerCleanupFunction(function cleanupRegion() {
     Services.prefs.clearUserPref("browser.search.region");
   });
-  Assert.ok(sandbox.isAvailable());
+
+  let addon = await AddonManager.getAddonByID(EXTENSION_ID);
+  await addon.reload();
+
+  Assert.ok(Services.prefs.getBoolPref(DOM_ENABLED_PREF));
 });
--- a/browser/extensions/formautofill/test/unit/test_isCJKName.js
+++ b/browser/extensions/formautofill/test/unit/test_isCJKName.js
@@ -1,15 +1,17 @@
 /**
  * Tests the "isCJKName" function of FormAutofillNameUtils object.
  */
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillNameUtils.jsm");
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillNameUtils.jsm");
+});
 
 // Test cases is initially copied from
 // https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util_unittest.cc
 const TESTCASES = [
   {
     // Non-CJK language with only ASCII characters.
     fullName: "Homer Jay Simpson",
     expectedResult: false,
--- a/browser/extensions/formautofill/test/unit/test_isFieldEligibleForAutofill.js
+++ b/browser/extensions/formautofill/test/unit/test_isFieldEligibleForAutofill.js
@@ -1,11 +1,13 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+add_task(async function seutp() {
+  ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+});
 
 const TESTCASES = [
   {
     document: `<input id="targetElement" type="text">`,
     fieldId: "targetElement",
     expectedResult: true,
   },
   {
--- a/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
+++ b/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
@@ -1,12 +1,10 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
-
 const TESTCASES = [
   {
     description: "Form containing 8 fields with autocomplete attribute.",
     document: `<form>
                  <input id="given-name" autocomplete="given-name">
                  <input id="additional-name" autocomplete="additional-name">
                  <input id="family-name" autocomplete="family-name">
                  <input id="street-addr" autocomplete="street-address">
@@ -56,19 +54,24 @@ const TESTCASES = [
       "country",
       "email",
       "tel",
     ],
   },
 ];
 
 let markedFieldId = [];
-FormAutofillContent._markAsAutofillField = function(field) {
-  markedFieldId.push(field.id);
-};
+
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
+
+  FormAutofillContent._markAsAutofillField = function(field) {
+    markedFieldId.push(field.id);
+  };
+});
 
 TESTCASES.forEach(testcase => {
   add_task(async function() {
     info("Starting testcase: " + testcase.description);
 
     markedFieldId = [];
 
     let doc = MockDocument.createTestDocument(
--- a/browser/extensions/formautofill/test/unit/test_masterPassword.js
+++ b/browser/extensions/formautofill/test/unit/test_masterPassword.js
@@ -1,16 +1,20 @@
 /**
  * Tests of MasterPassword.jsm
  */
 
 "use strict";
 const {MockRegistrar} =
   ChromeUtils.import("resource://testing-common/MockRegistrar.jsm", {});
-let {MasterPassword} = ChromeUtils.import("resource://formautofill/MasterPassword.jsm", {});
+
+let MasterPassword;
+add_task(async function setup() {
+  ({MasterPassword} = ChromeUtils.import("resource://formautofill/MasterPassword.jsm", {}));
+});
 
 const TESTCASES = [{
   description: "With master password set",
   masterPassword: "fakemp",
   mpEnabled: true,
 },
 {
   description: "Without master password set",
@@ -62,20 +66,20 @@ do_get_profile();
 let windowWatcherCID =
   MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
                          gWindowWatcher);
 registerCleanupFunction(() => {
   MockRegistrar.unregister(windowWatcherCID);
 });
 
 TESTCASES.forEach(testcase => {
-  let token = MasterPassword._token;
-
   add_task(async function test_encrypt_decrypt() {
     info("Starting testcase: " + testcase.description);
+
+    let token = MasterPassword._token;
     token.initPassword(testcase.masterPassword);
 
     // Test only: Force the token login without asking for master password
     token.login(/* force */ false);
     Assert.equal(testcase.mpEnabled, token.isLoggedIn(), "Token should now be logged into");
     Assert.equal(MasterPassword.isEnabled, testcase.mpEnabled);
 
     let testText = "test string";
--- a/browser/extensions/formautofill/test/unit/test_migrateRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_migrateRecords.js
@@ -1,15 +1,19 @@
 /**
  * Tests the migration algorithm in profileStorage.
  */
 
 "use strict";
 
-const {FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
+let FormAutofillStorage;
+
+add_task(async function setup() {
+  ({FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {}));
+});
 
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const ADDRESS_SCHEMA_VERSION = 1;
 const CREDIT_CARD_SCHEMA_VERSION = 1;
 
 const ADDRESS_TESTCASES = [
   {
--- a/browser/extensions/formautofill/test/unit/test_nameUtils.js
+++ b/browser/extensions/formautofill/test/unit/test_nameUtils.js
@@ -1,15 +1,17 @@
 /**
  * Tests FormAutofillNameUtils object.
  */
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillNameUtils.jsm");
+add_task(async function() {
+  ChromeUtils.import("resource://formautofill/FormAutofillNameUtils.jsm");
+});
 
 // Test cases initially copied from
 // https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util_unittest.cc
 const TESTCASES = [
   {
     description: "Full name including given, middle and family names",
     fullName: "Homer Jay Simpson",
     nameParts: {
--- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
+++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
@@ -1,11 +1,13 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
+});
 
 const MOCK_DOC = MockDocument.createTestDocument("http://localhost:8080/test/",
                    `<form id="form1">
                       <input id="street-addr" autocomplete="street-address">
                       <select id="address-level1" autocomplete="address-level1">
                         <option value=""></option>
                         <option value="AL">Alabama</option>
                         <option value="AK">Alaska</option>
--- a/browser/extensions/formautofill/test/unit/test_parseAddressFormat.js
+++ b/browser/extensions/formautofill/test/unit/test_parseAddressFormat.js
@@ -1,11 +1,13 @@
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+});
 
 add_task(async function test_parseAddressFormat() {
   const TEST_CASES = [
     {
       fmt: "%N%n%O%n%A%n%C, %S %Z", // US
       parsed: [
         {fieldId: "name", newLine: true},
         {fieldId: "organization", newLine: true},
--- a/browser/extensions/formautofill/test/unit/test_phoneNumber.js
+++ b/browser/extensions/formautofill/test/unit/test_phoneNumber.js
@@ -1,16 +1,18 @@
 /**
  * Tests PhoneNumber.jsm and PhoneNumberNormalizer.jsm.
  */
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/phonenumberutils/PhoneNumber.jsm");
-ChromeUtils.import("resource://formautofill/phonenumberutils/PhoneNumberNormalizer.jsm");
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/phonenumberutils/PhoneNumber.jsm");
+  ChromeUtils.import("resource://formautofill/phonenumberutils/PhoneNumberNormalizer.jsm");
+});
 
 function IsPlain(dial, expected) {
   let result = PhoneNumber.IsPlain(dial);
   Assert.equal(result, expected);
 }
 
 function Normalize(dial, expected) {
   let result = PhoneNumberNormalizer.Normalize(dial);
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
+++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
@@ -1,13 +1,15 @@
 "use strict";
 
 /* global AddressResult, CreditCardResult */
-// eslint-disable-next-line no-unused-vars
-ChromeUtils.import("resource://formautofill/ProfileAutoCompleteResult.jsm");
+add_task(async function setup() {
+  // eslint-disable-next-line no-unused-vars
+  ChromeUtils.import("resource://formautofill/ProfileAutoCompleteResult.jsm");
+});
 
 let matchingProfiles = [{
   guid: "test-guid-1",
   "given-name": "Timothy",
   "family-name": "Berners-Lee",
   name: "Timothy Berners-Lee",
   organization: "Sesame Street",
   "street-address": "123 Sesame Street.",
@@ -336,25 +338,25 @@ let creditCardTestCases = [{
   fieldName: "",
   expected: {
     searchResult: Ci.nsIAutoCompleteResult.RESULT_FAILURE,
     defaultIndex: 0,
     items: [],
   },
 }];
 
-let testSets = [{
-  collectionConstructor: AddressResult,
-  testCases: addressTestCases,
-}, {
-  collectionConstructor: CreditCardResult,
-  testCases: creditCardTestCases,
-}];
+add_task(async function test_all_patterns() {
+  let testSets = [{
+    collectionConstructor: AddressResult,
+    testCases: addressTestCases,
+  }, {
+    collectionConstructor: CreditCardResult,
+    testCases: creditCardTestCases,
+  }];
 
-add_task(async function test_all_patterns() {
   testSets.forEach(({collectionConstructor, testCases}) => {
     testCases.forEach(testCase => {
       info("Starting testcase: " + testCase.description);
       let actual = new collectionConstructor(testCase.searchString,
                                              testCase.fieldName,
                                              testCase.allFieldNames,
                                              testCase.matchingProfiles,
                                              testCase.options);
--- a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -1,15 +1,19 @@
 /*
  * Test for keeping the valid fields information in initialProcessData.
  */
 
 "use strict";
 
-let {FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {});
+let FormAutofillParent;
+
+add_task(async function setup() {
+  ({FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {}));
+});
 
 add_task(async function test_profileSavedFieldNames_init() {
   let formAutofillParent = new FormAutofillParent();
   sinon.stub(formAutofillParent, "_updateSavedFieldNames");
 
   await formAutofillParent.init();
   await formAutofillParent.formAutofillStorage.initialize();
   Assert.equal(formAutofillParent._updateSavedFieldNames.called, true);
--- a/browser/extensions/formautofill/test/unit/test_storage_remove.js
+++ b/browser/extensions/formautofill/test/unit/test_storage_remove.js
@@ -1,15 +1,18 @@
 /**
  * Tests removing all address/creditcard records.
  */
 
 "use strict";
 
-const {FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
+let FormAutofillStorage;
+add_task(async function setup() {
+  ({FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {}));
+});
 
 const TEST_STORE_FILE_NAME = "test-tombstones.json";
 
 const TEST_ADDRESS_1 = {
   "given-name": "Timothy",
   "additional-name": "John",
   "family-name": "Berners-Lee",
   organization: "World Wide Web Consortium",
--- a/browser/extensions/formautofill/test/unit/test_storage_tombstones.js
+++ b/browser/extensions/formautofill/test/unit/test_storage_tombstones.js
@@ -1,15 +1,18 @@
 /**
  * Tests tombstones in address/creditcard records.
  */
 
 "use strict";
 
-const {FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
+let FormAutofillStorage;
+add_task(async function setup() {
+  ({FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {}));
+});
 
 const TEST_STORE_FILE_NAME = "test-tombstones.json";
 
 const TEST_ADDRESS_1 = {
   "given-name": "Timothy",
   "additional-name": "John",
   "family-name": "Berners-Lee",
   organization: "World Wide Web Consortium",
--- a/browser/extensions/formautofill/test/unit/test_sync.js
+++ b/browser/extensions/formautofill/test/unit/test_sync.js
@@ -8,18 +8,21 @@
 /* import-globals-from ../../../../../services/sync/tests/unit/head_http_server.js */
 
 "use strict";
 
 ChromeUtils.import("resource://services-sync/service.js");
 ChromeUtils.import("resource://services-sync/constants.js");
 ChromeUtils.import("resource://testing-common/services/sync/utils.js");
 
-let {sanitizeStorageObject, AutofillRecord, AddressesEngine} =
-  ChromeUtils.import("resource://formautofill/FormAutofillSync.jsm", {});
+let sanitizeStorageObject, AutofillRecord, AddressesEngine;
+add_task(async function() {
+  ({sanitizeStorageObject, AutofillRecord, AddressesEngine} =
+    ChromeUtils.import("resource://formautofill/FormAutofillSync.jsm", {}));
+});
 
 
 Services.prefs.setCharPref("extensions.formautofill.loglevel", "Trace");
 initTestLogging("Trace");
 
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const TEST_PROFILE_1 = {
--- a/browser/extensions/formautofill/test/unit/test_toOneLineAddress.js
+++ b/browser/extensions/formautofill/test/unit/test_toOneLineAddress.js
@@ -1,12 +1,14 @@
 
 "use strict";
 
-ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+add_task(async function setup() {
+  ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
+});
 
 add_task(async function test_getCategoriesFromFieldNames() {
   const TEST_CASES = [
     {
       strings: ["A", "B", "C", "D"],
       expectedValue: "A B C D",
     },
     {
--- a/browser/extensions/formautofill/test/unit/test_transformFields.js
+++ b/browser/extensions/formautofill/test/unit/test_transformFields.js
@@ -1,15 +1,18 @@
 /**
  * Tests the transform algorithm in profileStorage.
  */
 
 "use strict";
 
-const {FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
+let FormAutofillStorage;
+add_task(async function setup() {
+  ({FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {}));
+});
 
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const ADDRESS_COMPUTE_TESTCASES = [
   // Name
   {
     description: "Has split names",
     address: {
@@ -896,16 +899,18 @@ add_task(async function test_computeAddr
     info("Verify testcase: " + testcase.description);
 
     let guid = profileStorage.addresses.add(testcase.address);
     let address = profileStorage.addresses.get(guid);
     do_check_record_matches(testcase.expectedResult, address);
 
     profileStorage.addresses.remove(guid);
   });
+
+  await profileStorage._finalize();
 });
 
 add_task(async function test_normalizeAddressFields() {
   let path = getTempFile(TEST_STORE_FILE_NAME).path;
 
   let profileStorage = new FormAutofillStorage(path);
   await profileStorage.initialize();
 
@@ -913,16 +918,18 @@ add_task(async function test_normalizeAd
     info("Verify testcase: " + testcase.description);
 
     let guid = profileStorage.addresses.add(testcase.address);
     let address = profileStorage.addresses.get(guid);
     do_check_record_matches(testcase.expectedResult, address);
 
     profileStorage.addresses.remove(guid);
   });
+
+  await profileStorage._finalize();
 });
 
 add_task(async function test_computeCreditCardFields() {
   let path = getTempFile(TEST_STORE_FILE_NAME).path;
 
   let profileStorage = new FormAutofillStorage(path);
   await profileStorage.initialize();
 
@@ -930,16 +937,18 @@ add_task(async function test_computeCred
     info("Verify testcase: " + testcase.description);
 
     let guid = profileStorage.creditCards.add(testcase.creditCard);
     let creditCard = profileStorage.creditCards.get(guid);
     do_check_record_matches(testcase.expectedResult, creditCard);
 
     profileStorage.creditCards.remove(guid);
   });
+
+  await profileStorage._finalize();
 });
 
 add_task(async function test_normalizeCreditCardFields() {
   let path = getTempFile(TEST_STORE_FILE_NAME).path;
 
   let profileStorage = new FormAutofillStorage(path);
   await profileStorage.initialize();
 
@@ -947,9 +956,11 @@ add_task(async function test_normalizeCr
     info("Verify testcase: " + testcase.description);
 
     let guid = profileStorage.creditCards.add(testcase.creditCard);
     let creditCard = profileStorage.creditCards.get(guid, {rawData: true});
     do_check_record_matches(testcase.expectedResult, creditCard);
 
     profileStorage.creditCards.remove(guid);
   });
+
+  await profileStorage._finalize();
 });
--- a/testing/modules/FileTestUtils.jsm
+++ b/testing/modules/FileTestUtils.jsm
@@ -113,16 +113,21 @@ XPCOMUtils.defineLazyGetter(FileTestUtil
     let dir = FileUtils.getFile("TmpD", ["testdir-" + randomNumber]);
     dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
     AsyncShutdown.profileBeforeChange.addBlocker("Removing test files",
       async () => {
         // Remove the files we know about first.
         for (let path of gPathsToRemove) {
           await this.tolerantRemove(path);
         }
+
+        if (!(await OS.File.exists(dir.path))) {
+          return;
+        }
+
         // Detect any extra files, like the ".part" files of downloads.
         let iterator = new OS.File.DirectoryIterator(dir.path);
         try {
           await iterator.forEach(entry => this.tolerantRemove(entry.path,
                                                               entry.isDir));
         } finally {
           iterator.close();
         }