diff options
Diffstat (limited to 'comm/mailnews/import/test/unit/resources/import_helper.js')
-rw-r--r-- | comm/mailnews/import/test/unit/resources/import_helper.js | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/comm/mailnews/import/test/unit/resources/import_helper.js b/comm/mailnews/import/test/unit/resources/import_helper.js new file mode 100644 index 0000000000..e689547377 --- /dev/null +++ b/comm/mailnews/import/test/unit/resources/import_helper.js @@ -0,0 +1,665 @@ +var { MailServices } = ChromeUtils.import( + "resource:///modules/MailServices.jsm" +); + +// used by checkProgress to periodically check the progress of the import +var gGenericImportHelper; +/** + * GenericImportHelper + * The parent class of AbImportHelper, MailImportHelper, SettingsImportHelper + * and FiltersImportHelper. + * + * @param aModuleType The type of import module. Should be addressbook or mail. + * @param aModuleSearchString + * The string to search the module names for, such as + * "Text file" to find the import module for comma-separated + * value, LDIF, and tab-delimited files. + * @param aFile An instance of nsIFile to import. + * + * @class + * @class + */ +function GenericImportHelper(aModuleType, aModuleSearchString, aFile) { + gGenericImportHelper = null; + if (!["addressbook", "mail", "settings", "filters"].includes(aModuleType)) { + do_throw("Unexpected type passed to the GenericImportHelper constructor"); + } + this.mModuleType = aModuleType; + this.mModuleSearchString = aModuleSearchString; + this.mInterface = this._findInterface(); + Assert.ok(this.mInterface !== null); + + this.mFile = aFile; // checked in the beginImport method +} + +GenericImportHelper.prototype = { + interfaceType: Ci.nsIImportGeneric, + /** + * GenericImportHelper.beginImport + * Imports the given address book export or mail data and invoke + * checkProgress of child class to check the data, + */ + beginImport() { + Assert.ok(this.mFile instanceof Ci.nsIFile && this.mFile.exists()); + + if (this.mModuleType == "addressbook") { + this.mInterface.SetData("addressLocation", this.mFile); + } else if (this.mModuleType == "mail") { + this.mInterface.SetData("mailLocation", this.mFile); + } + + Assert.ok(this.mInterface.WantsProgress()); + const error = Cc["@mozilla.org/supports-string;1"].createInstance( + Ci.nsISupportsString + ); + Assert.ok(this.mInterface.BeginImport(null, error)); + Assert.equal(error.data, ""); + do_test_pending(); + this.checkProgress(); + }, + /** + * GenericImportHelper.getInterface + * + * @returns An nsIImportGeneric import interface. + */ + getInterface() { + return this.mInterface; + }, + + _findInterface() { + var importService = Cc["@mozilla.org/import/import-service;1"].getService( + Ci.nsIImportService + ); + var count = importService.GetModuleCount(this.mModuleType); + + // Iterate through each import module until the one being searched for is + // found and then return the ImportInterface of that module + for (var i = 0; i < count; i++) { + // Check if the current module fits the search string gets the interface + if ( + importService + .GetModuleName(this.mModuleType, i) + .includes(this.mModuleSearchString) + ) { + return importService + .GetModule(this.mModuleType, i) + .GetImportInterface(this.mModuleType) + .QueryInterface(this.interfaceType); + } + } + return null; // it wasn't found + }, + /** + * GenericImportHelper.checkProgress + * Checks the progress of an import every 200 milliseconds until it is + * complete. Checks the test results if there is an original address book, + * otherwise evaluates the optional command, or calls do_test_finished(). + */ + checkProgress() { + Assert.ok( + this.mInterface && this.mInterface instanceof Ci.nsIImportGeneric + ); + Assert.ok(this.mInterface.ContinueImport()); + // if the import isn't done, check again in 200 milliseconds. + if (this.mInterface.GetProgress() != 100) { + // use the helper object to check the progress of the import after 200 ms + gGenericImportHelper = this; + do_timeout(200, function () { + gGenericImportHelper.checkProgress(); + }); + } else { + // if it is done, check the results or finish the test. + this.checkResults(); + do_test_finished(); + } + }, + + /** + * GenericImportHelper.checkResults + * Checks the results of the import. + * Child class should implement this method. + */ + checkResults() {}, +}; + +/** + * AbImportHelper + * A helper for Address Book imports. To use, supply at least the file and type. + * If you would like the results checked, add a new array in the addressbook + * JSON file in the resources folder and supply aAbName and aJsonName. + * See AB_README for more information. + * + * @param aFile An instance of nsIAbFile to import. + * @param aModuleSearchString + * The string to search the module names for, such as + * "Text file" to find the import module for comma-separated + * value, LDIF, and tab-delimited files. + * Optional parameters: Include if you would like the import checked. + * @param aAbName The name the address book will have (the filename without + * the extension). + * @param aJsonName The name of the array in addressbook.json with the cards + * to compare with the imported cards. + * @class + * @class + */ +function AbImportHelper(aFile, aModuleSearchString, aAbName, aJsonName) { + GenericImportHelper.call(this, "addressbook", aModuleSearchString, aFile); + + this.mAbName = aAbName; + /* Attribute notes: The attributes listed in the declaration below are + * supported by all three text export/import types. + * The following are not supported: anniversaryYear, anniversaryMonth, + * anniversaryDay, popularityIndex, isMailList, mailListURI, lastModifiedDate. + */ + var supportedAttributes = [ + "FirstName", + "LastName", + "DisplayName", + "NickName", + "PrimaryEmail", + "SecondEmail", + "WorkPhone", + "HomePhone", + "FaxNumber", + "PagerNumber", + "CellularNumber", + "HomeAddress", + "HomeAddress2", + "HomeCity", + "HomeState", + "HomeZipCode", + "HomeCountry", + "WorkAddress", + "WorkAddress2", + "WorkCity", + "WorkState", + "WorkZipCode", + "WorkCountry", + "JobTitle", + "Department", + "Company", + "BirthYear", + "BirthMonth", + "BirthDay", + "WebPage1", + "WebPage2", + "Custom1", + "Custom2", + "Custom3", + "Custom4", + "Notes", + "_AimScreenName", + "_vCard", + ]; + + // get the extra attributes supported for the given type of import + if (this.mFile.leafName.toLowerCase().endsWith(".ldif")) { + this.mSupportedAttributes = supportedAttributes; + } else if (this.mFile.leafName.toLowerCase().endsWith(".csv")) { + this.mSupportedAttributes = supportedAttributes; + this.setFieldMap(this.getDefaultFieldMap(true)); + } else if (this.mFile.leafName.toLowerCase().endsWith(".vcf")) { + this.mSupportedAttributes = supportedAttributes; + } + + // get the "cards" from the JSON file, if necessary + if (aJsonName) { + this.mJsonCards = this.getJsonCards(aJsonName); + } +} + +AbImportHelper.prototype = { + __proto__: GenericImportHelper.prototype, + /** + * AbImportHelper.getDefaultFieldMap + * Returns the default field map. + * + * @param aSkipFirstRecord True if the first record of the text file should + * be skipped. + * @returns A default field map. + */ + getDefaultFieldMap(aSkipFirstRecord) { + var importService = Cc["@mozilla.org/import/import-service;1"].getService( + Ci.nsIImportService + ); + var fieldMap = importService.CreateNewFieldMap(); + + fieldMap.DefaultFieldMap(fieldMap.numMozFields); + this.mInterface + .GetData("addressInterface") + .QueryInterface(Ci.nsIImportAddressBooks) + .InitFieldMap(fieldMap); + fieldMap.skipFirstRecord = aSkipFirstRecord; + + return fieldMap; + }, + + /** + * AbImportHelper.setFieldMap + * Set the field map. + * + * @param aFieldMap The field map used for address book import. + */ + setFieldMap(aFieldMap) { + this.mInterface.SetData("fieldMap", aFieldMap); + }, + + /** + * AbImportHelper.setAddressLocation + * Set the the location of the address book. + * + * @param aLocation The location of the source address book. + */ + setAddressBookLocation(aLocation) { + this.mInterface.SetData("addressLocation", aLocation); + }, + + /** + * AbImportHelper.setAddressDestination + * Set the the destination of the address book. + * + * @param aDestination URI of destination address book or null if + * new address books will be created. + */ + setAddressDestination(aDestination) { + this.mInterface.SetData("addressDestination", aDestination); + }, + + /** + * AbImportHelper.checkResults + * Checks the results of the import. + * Ensures the an address book was created, then compares the supported + * attributes of each card with the card(s) in the JSON array. + * Calls do_test_finished() when done + */ + checkResults() { + if (!this.mJsonCards) { + do_throw("The address book must be setup before checking results"); + } + // When do_test_pending() was called and there is an error the test hangs. + // This try/catch block will catch any errors and call do_throw() with the + // error to throw the error and avoid the hang. + try { + // make sure an address book was created + var newAb = this.getAbByName(this.mAbName); + Assert.ok(newAb !== null); + Assert.ok(newAb.QueryInterface(Ci.nsIAbDirectory)); + // get the imported card(s) and check each one + var count = 0; + for (let importedCard of newAb.childCards) { + this.compareCards(this.mJsonCards[count], importedCard); + count++; + } + // make sure there are the same number of cards in the address book and + // the JSON array + Assert.equal(count, this.mJsonCards.length); + do_test_finished(); + } catch (e) { + do_throw(e); + } + }, + /** + * AbImportHelper.getAbByName + * Returns the Address Book (if any) with the given name. + * + * @param aName The name of the Address Book to find. + * @returns An nsIAbDirectory, if found. + * null if the requested Address Book could not be found. + */ + getAbByName(aName) { + Assert.ok(aName && aName.length > 0); + + for (let data of MailServices.ab.directories) { + if (data.dirName == aName) { + return data; + } + } + return null; + }, + /** + * AbImportHelper.compareCards + * Compares a JSON "card" with an imported card and throws an error if the + * values of a supported attribute are different. + * + * @param aJsonCard The object decoded from addressbook.json. + * @param aCard The imported card to compare with. + */ + compareCards(aJsonCard, aCard) { + for (let [key, value] of Object.entries(aJsonCard)) { + if (!this.mSupportedAttributes.includes(key)) { + continue; + } + if (key == "_vCard") { + equal( + aCard + .getProperty(key, "") + .replace( + /UID:[a-f0-9-]{36}/i, + "UID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + ), + `BEGIN:VCARD\r\n${value.join("\r\n")}\r\nEND:VCARD\r\n`, + "_vCard should be correct" + ); + } else { + equal(aCard.getProperty(key, ""), value, `${key} should be correct`); + } + } + }, + /** + * AbImportHelper.getJsonCards + * Gets an array of "cards" from the JSON file addressbook.json located in the + * mailnews/import/test/resources folder. The array should contain objects + * with the expected properties and values of the cards in the imported + * address book. + * See addressbook.json for an example and AB_README for more details. + * + * @param aName The name of the array in addressbook.json. + * @returns An array of "cards". + */ + getJsonCards(aName) { + if (!aName) { + do_throw("Error - getJSONAb requires an address book name"); + } + var file = do_get_file("resources/addressbook.json"); + if (!file || !file.exists() || !file.isFile()) { + do_throw("Unable to get JSON file"); + } + + var fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( + Ci.nsIFileInputStream + ); + fis.init(file, 0x01, 0o444, 0); + var istream = Cc[ + "@mozilla.org/intl/converter-input-stream;1" + ].createInstance(Ci.nsIConverterInputStream); + var replacementChar = + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER; + istream.init(fis, "UTF-8", 1024, replacementChar); + var json = ""; + var str = {}; + // get the entire file into the json string + while (istream.readString(4096, str) != 0) { + json += str.value; + } + // close the input streams + istream.close(); + fis.close(); + // decode the JSON and get the array of cards + var arr = JSON.parse(json)[aName]; + Assert.ok(arr && arr.length > 0); + return arr; + }, + + setSupportedAttributes(attributes) { + this.mSupportedAttributes = attributes; + }, +}; + +/** + * MailImportHelper + * A helper for mail imports. + * + * @param aFile An instance of nsIFile to import. + * @param aModuleSearchString + * The string to search the module names for, such as + * "Outlook Express", etc. + * @param aExpected An instance of nsIFile to compare with the imported + * folders. + * + * @class + * @class + */ +function MailImportHelper(aFile, aModuleSearchString, aExpected) { + GenericImportHelper.call(this, "mail", aModuleSearchString, aFile); + this.mExpected = aExpected; +} + +MailImportHelper.prototype = { + __proto__: GenericImportHelper.prototype, + interfaceType: Ci.nsIImportGeneric, + _checkEqualFolder(expectedFolder, actualFolder) { + Assert.equal(expectedFolder.leafName, actualFolder.name); + + let expectedSubFolders = []; + for (let entry of expectedFolder.directoryEntries) { + if (entry.isDirectory()) { + expectedSubFolders.push(entry); + } + } + let actualSubFolders = actualFolder.subFolders; + Assert.equal(expectedSubFolders.length, actualSubFolders.length); + for (let i = 0; i < expectedSubFolders.length; i++) { + this._checkEqualFolder(expectedSubFolders[i], actualSubFolders[i]); + } + }, + + checkResults() { + let rootFolder = MailServices.accounts.localFoldersServer.rootFolder; + Assert.ok(rootFolder.containsChildNamed(this.mFile.leafName)); + let importedFolder = rootFolder.getChildNamed(this.mFile.leafName); + Assert.notEqual(importedFolder, null); + + this._checkEqualFolder(this.mExpected, importedFolder); + }, +}; + +/** + * SettingsImportHelper + * A helper for settings imports. + * + * @param aFile An instance of nsIFile to import, can be null. + * @param aModuleSearchString + * The string to search the module names for, such as + * "Outlook Express", etc. + * @param aExpected An array of object which has incomingServer, identity + * and smtpSever to compare with imported nsIMsgAccount. + * + * @class + * @class + */ +function SettingsImportHelper(aFile, aModuleSearchString, aExpected) { + GenericImportHelper.call(this, "settings", aModuleSearchString, aFile); + this.mExpected = aExpected; +} + +SettingsImportHelper.prototype = { + __proto__: GenericImportHelper.prototype, + interfaceType: Ci.nsIImportSettings, + /** + * SettingsImportHelper.beginImport + * Imports settings from a specific file or auto-located if the file is null, + * and compare the import results with the expected array. + */ + beginImport() { + this._ensureNoAccounts(); + if (this.mFile) { + this.mInterface.SetLocation(this.mFile); + } else { + Assert.equal(true, this.mInterface.AutoLocate({}, {})); + } + Assert.equal(true, this.mInterface.Import({})); + this.checkResults(); + }, + + _ensureNoAccounts() { + for (let account of MailServices.accounts.accounts) { + MailServices.accounts.removeAccount(account); + } + }, + + _checkSmtpServer(expected, actual) { + Assert.equal(expected.port, actual.port); + Assert.equal(expected.username, actual.username); + Assert.equal(expected.authMethod, actual.authMethod); + Assert.equal(expected.socketType, actual.socketType); + }, + + _checkIdentity(expected, actual) { + Assert.equal(expected.fullName, actual.fullName); + Assert.equal(expected.email, actual.email); + Assert.equal(expected.replyTo, actual.replyTo); + Assert.equal(expected.organization, actual.organization); + }, + + _checkPop3IncomingServer(expected, actual) { + Assert.equal(expected.leaveMessagesOnServer, actual.leaveMessagesOnServer); + Assert.equal( + expected.deleteMailLeftOnServer, + actual.deleteMailLeftOnServer + ); + Assert.equal(expected.deleteByAgeFromServer, actual.deleteByAgeFromServer); + Assert.equal( + expected.numDaysToLeaveOnServer, + actual.numDaysToLeaveOnServer + ); + }, + + _checkIncomingServer(expected, actual) { + Assert.equal(expected.type, actual.type); + Assert.equal(expected.port, actual.port); + Assert.equal(expected.username, actual.username); + Assert.equal(expected.isSecure, actual.isSecure); + Assert.equal(expected.hostName, actual.hostName); + Assert.equal(expected.prettyName, actual.prettyName); + Assert.equal(expected.authMethod, actual.authMethod); + Assert.equal(expected.socketType, actual.socketType); + Assert.equal(expected.doBiff, actual.doBiff); + Assert.equal(expected.biffMinutes, actual.biffMinutes); + + if (expected.type == "pop3") { + this._checkPop3IncomingServer( + expected, + actual.QueryInterface(Ci.nsIPop3IncomingServer) + ); + } + }, + + _checkAccount(expected, actual) { + this._checkIncomingServer(expected.incomingServer, actual.incomingServer); + + Assert.equal(1, actual.identities.length); + let actualIdentity = actual.identities[0]; + this._checkIdentity(expected.identity, actualIdentity); + + if (expected.incomingServer.type != "nntp") { + let actualSmtpServer = MailServices.smtp.getServerByKey( + actualIdentity.smtpServerKey + ); + this._checkSmtpServer(expected.smtpServer, actualSmtpServer); + } + }, + + _isLocalMailAccount(account) { + return ( + account.incomingServer.type == "none" && + account.incomingServer.username == "nobody" && + account.incomingServer.hostName == "Local Folders" + ); + }, + + _findExpectedAccount(account) { + return this.mExpected.filter(function (expectedAccount) { + return ( + expectedAccount.incomingServer.type == account.incomingServer.type && + expectedAccount.incomingServer.username == + account.incomingServer.username && + expectedAccount.incomingServer.hostName == + account.incomingServer.hostName + ); + }); + }, + + checkResults() { + for (let actualAccount of MailServices.accounts.accounts) { + if (this._isLocalMailAccount(actualAccount)) { + continue; + } + let expectedAccounts = this._findExpectedAccount(actualAccount); + Assert.notEqual(null, expectedAccounts); + Assert.equal(1, expectedAccounts.length); + this._checkAccount(expectedAccounts[0], actualAccount); + } + }, +}; + +/** + * FiltersImportHelper + * A helper for filter imports. + * + * @param aFile An instance of nsIFile to import. + * @param aModuleSearchString + * The string to search the module names for, such as + * "Outlook Express", etc. + * @param aExpected The number of filters that should exist after import. + * + * @class + * @class + */ +function FiltersImportHelper(aFile, aModuleSearchString, aExpected) { + GenericImportHelper.call(this, "filters", aModuleSearchString, aFile); + this.mExpected = aExpected; +} + +FiltersImportHelper.prototype = { + __proto__: GenericImportHelper.prototype, + interfaceType: Ci.nsIImportFilters, + + /** + * FiltersImportHelper.beginImport + * Imports filters from a specific file/folder or auto-located if the file is null, + * and compare the import results with the expected array. + */ + beginImport() { + if (this.mFile) { + this.mInterface.SetLocation(this.mFile); + } else { + Assert.equal(true, this.mInterface.AutoLocate({}, {})); + } + Assert.equal(true, this.mInterface.Import({})); + this.checkResults(); + }, + + _loopOverFilters(aFilterList, aCondition) { + let result = 0; + for (let i = 0; i < aFilterList.filterCount; i++) { + let filter = aFilterList.getFilterAt(i); + if (aCondition(filter)) { + result++; + } + } + return result; + }, + + checkResults() { + let expected = this.mExpected; + let server = MailServices.accounts.localFoldersServer; + let filterList = server.getFilterList(null); + if ("count" in expected) { + Assert.equal(filterList.filterCount, expected.count); + } + if ("enabled" in expected) { + Assert.equal( + this._loopOverFilters(filterList, f => f.enabled), + expected.enabled + ); + } + if ("incoming" in expected) { + Assert.equal( + this._loopOverFilters( + filterList, + f => f.filterType & Ci.nsMsgFilterType.InboxRule + ), + expected.incoming + ); + } + if ("outgoing" in expected) { + Assert.equal( + this._loopOverFilters( + filterList, + f => f.filterType & Ci.nsMsgFilterType.PostOutgoing + ), + expected.outgoing + ); + } + }, +}; |