Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Tests automatic configuration of Thundermail with a net.thunderbird URL.
*/
const { HttpServer } = ChromeUtils.importESModule(
);
const { MessageGenerator } = ChromeUtils.importESModule(
);
const { NetworkTestUtils } = ChromeUtils.importESModule(
);
const { OAuth2TestUtils } = ChromeUtils.importESModule(
);
const { ServerTestUtils } = ChromeUtils.importESModule(
);
const setupService = Cc["@mozilla.org/thundermail-url-handler;1"].getService(
Ci.nsIObserver
);
let oAuth2Server, imapServer;
let account, incomingServer, outgoingServer;
async function configureServers(username, accessToken, refreshToken) {
imapServer?.close();
imapServer = await ServerTestUtils.createServer({
...ServerTestUtils.serverDefs.imap.oAuth,
options: {
username,
password: accessToken,
},
hostname: "external.test",
});
OAuth2TestUtils.stopServer();
oAuth2Server = await OAuth2TestUtils.startServer({
username,
accessToken,
refreshToken,
});
oAuth2Server.grantedScope = "test_mail";
}
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["mail.accounthub.thundermail.hostname", "external.test"],
["mailnews.oauth.useExternalBrowser", false],
],
});
// We get the autoconfig file from the service, not ISPDB.
const xml = await IOUtils.readUTF8(getTestFilePath("xml/external.test"));
const autoConfigServer = new HttpServer();
autoConfigServer.identity.add("http", "autoconfig.external.test", 80);
autoConfigServer.registerPathHandler(
"/mail/config-v1.1.xml",
(request, response) => {
response.setHeader("Content-Type", "text/xml", false);
response.write(xml);
}
);
autoConfigServer.start(-1);
NetworkTestUtils.configureProxy(
"autoconfig.external.test",
80,
autoConfigServer.identity.primaryPort
);
await ServerTestUtils.createServer(ServerTestUtils.serverDefs.smtp.oAuth);
registerCleanupFunction(async () => {
if (account) {
MailServices.accounts.removeAccount(account, false);
}
if (outgoingServer) {
MailServices.outgoingServer.deleteServer(outgoingServer);
}
await Services.logins.removeAllLoginsAsync();
NetworkTestUtils.unconfigureProxy("autoconfig.external.test", 80);
autoConfigServer.stop();
});
});
/**
* Tests adding a Thundermail account where everything is working fine.
*/
add_task(async function testGoodConfig() {
const name = "Roc E. Mail";
const username = "roc@external.test";
const token = "refresh_token";
await configureServers(username, "access_token", token);
await imapServer.addMessages(
"INBOX",
new MessageGenerator().makeMessages({ count: 5 })
);
// Load the URL.
setupService.observe(
null,
"net-thunderbird-url",
`net.thunderbird://thundermail/add?name=${name}&email=${username}&token=${token}`
);
// Check the account hub.
const dialog = await subtest_wait_for_account_hub_dialog("MAIL");
const footer = dialog.querySelector("#emailFooter");
const footerForward = footer.querySelector("#forward");
// Wait for the Thundermail config to be found and displayed, then move on.
const configFoundStep = dialog.querySelector("email-config-found");
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(configFoundStep),
"waiting for Thundermail config to be found"
);
// Click through to the success page. No address books or calendars are
// found because this test is a bit of a hack.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const successStep = dialog.querySelector("email-added-success");
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(successStep),
"waiting for account added success message"
);
// Click to the close the account hub.
const closeEvent = BrowserTestUtils.waitForEvent(dialog, "close");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
await closeEvent;
await TestUtils.waitForCondition(
() => MailServices.accounts.accounts.length === 3,
"waiting for Thundermail account to be added"
);
// Okay, we've finished the account set up. Check the login is saved.
const logins = await Services.logins.getAllLogins();
Assert.equal(logins.length, 1, "there should be a saved password");
Assert.equal(logins[0].origin, "oauth://external.test", "login origin");
Assert.equal(logins[0].formActionOrigin, null, "login formActionOrigin");
Assert.equal(logins[0].httpRealm, "test_mail", "login httpRealm");
Assert.equal(logins[0].username, username, "login username");
Assert.equal(logins[0].password, token, "login password");
// Check the incoming server config was saved.
Assert.equal(MailServices.accounts.accounts.length, 3);
account = MailServices.accounts.accounts.find(
a => !["account1", "account2"].includes(a.key)
);
incomingServer = account.incomingServer;
Assert.equal(incomingServer.type, "imap");
Assert.equal(incomingServer.hostName, "external.test");
Assert.equal(incomingServer.port, 143);
Assert.equal(incomingServer.authMethod, Ci.nsMsgAuthMethod.OAuth2);
Assert.equal(incomingServer.username, username);
// Check an identity was created with the right information.
Assert.equal(account.identities.length, 1);
Assert.equal(account.defaultIdentity.fullName, name);
Assert.equal(account.defaultIdentity.email, username);
// Check the outgoing server config was saved.
Assert.equal(MailServices.outgoingServer.servers.length, 2);
outgoingServer = MailServices.outgoingServer.servers.find(
o => o.key != "smtp1"
);
Assert.equal(outgoingServer.type, "smtp");
Assert.equal(outgoingServer.hostname, "external.test");
Assert.equal(outgoingServer.port, 587);
Assert.equal(outgoingServer.authMethod, Ci.nsMsgAuthMethod.OAuth2);
Assert.equal(outgoingServer.username, username);
Assert.equal(account.defaultIdentity.smtpServerKey, outgoingServer.key);
// Wait for mail to appear in the inbox.
const inbox = incomingServer.rootFolder.getFolderWithFlags(
Ci.nsMsgFolderFlags.Inbox
);
await TestUtils.waitForCondition(
() => inbox.getTotalMessages(false) == 5,
"waiting for mail to be received"
);
incomingServer.QueryInterface(Ci.nsIImapIncomingServer);
await TestUtils.waitForCondition(
() => incomingServer.allConnectionsIdle,
"waiting for IMAP connection to become idle"
);
// Do not clean up.
});
/**
* Tests what happens when trying to add a Thundermail account that already
* exists. Thunderbird should refuse to add a new account and MUST NOT
* override the existing token. Depends on testGoodConfig.
*/
add_task(async function testRepeat() {
const name = "Roc E. Mail";
const username = "roc@external.test";
const token = "refresh_token";
setupService.observe(
null,
"net-thunderbird-url",
`net.thunderbird://thundermail/add?name=${name}&email=${username}&token=other_token`
);
// The account hub dialog should open with an error message.
const dialog = await subtest_wait_for_account_hub_dialog("MAIL");
const subview = dialog.querySelector("#emailAutoConfigSubview");
const header = subview.shadowRoot.querySelector("account-hub-header");
const closeButton = header.shadowRoot.querySelector("#closeButton");
const footer = dialog.querySelector("#emailFooter");
const footerBack = footer.querySelector("#back");
const footerForward = footer.querySelector("#forward");
// TODO There should be an appropriate subview.
// TODO This is the wrong string.
await checkErrorNotification(subview, "account-hub-creation-error-title");
Assert.ok(BrowserTestUtils.isHidden(footerBack));
Assert.ok(footerForward.disabled);
// Close the account hub.
const closeEvent = BrowserTestUtils.waitForEvent(dialog, "close");
EventUtils.synthesizeMouseAtCenter(closeButton, {});
await closeEvent;
// Check the login was not overwritten.
const logins = await Services.logins.getAllLogins();
Assert.equal(logins.length, 1, "there should still be a saved password");
Assert.equal(logins[0].username, username, "login username");
Assert.equal(
logins[0].password,
token,
"login password must not have changed"
);
// Check no new servers were created.
Assert.equal(MailServices.accounts.accounts.length, 3);
Assert.equal(MailServices.outgoingServer.servers.length, 2);
// Do not clean up.
});
/**
* Tests adding a second valid account. This can co-exist with the first account.
* Depends on testGoodConfig.
*/
add_task(async function testSecondConfig() {
const secondName = "Twee T. Bird";
const secondUsername = "twee.t@external.test";
const secondToken = "second_refresh_token";
await configureServers(
secondUsername,
"second_access_token",
"second_refresh_token"
);
// Load the URL.
setupService.observe(
null,
"net-thunderbird-url",
`net.thunderbird://thundermail/add?name=${secondName}&email=${secondUsername}&token=${secondToken}`
);
// Check the account hub.
const dialog = await subtest_wait_for_account_hub_dialog("MAIL");
const footer = dialog.querySelector("#emailFooter");
const footerForward = footer.querySelector("#forward");
// Wait for the Thundermail config to be found and displayed, then move on.
const configFoundStep = dialog.querySelector("email-config-found");
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(configFoundStep),
"waiting for Thundermail config to be found"
);
// Click through to the success page. No address books or calendars are
// found because this test is a bit of a hack.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const successStep = dialog.querySelector("email-added-success");
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(successStep),
"waiting for account added success message"
);
// Click to the close the account hub.
const closeEvent = BrowserTestUtils.waitForEvent(dialog, "close");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
await closeEvent;
await TestUtils.waitForCondition(
() => MailServices.accounts.accounts.length === 4,
"waiting for Thundermail account to be added"
);
// Okay, we've finished the account set up. Check the login is saved.
const logins = await Services.logins.getAllLogins();
Assert.equal(logins.length, 2, "there should be a second saved password");
Assert.equal(logins[1].origin, "oauth://external.test", "login origin");
Assert.equal(logins[1].formActionOrigin, null, "login formActionOrigin");
Assert.equal(logins[1].httpRealm, "test_mail", "login httpRealm");
Assert.equal(logins[1].username, secondUsername, "login username");
Assert.equal(logins[1].password, secondToken, "login password");
// Check the incoming server config was saved.
Assert.equal(MailServices.accounts.accounts.length, 4);
const secondAccount = MailServices.accounts.accounts.find(
a => !["account1", "account2", account.key].includes(a.key)
);
const secondIncomingServer = secondAccount.incomingServer;
Assert.equal(secondIncomingServer.username, secondUsername);
// Check an identity was created with the right information.
Assert.equal(secondAccount.identities.length, 1);
Assert.equal(secondAccount.defaultIdentity.fullName, secondName);
Assert.equal(secondAccount.defaultIdentity.email, secondUsername);
// Check the outgoing server config was saved.
Assert.equal(MailServices.outgoingServer.servers.length, 3);
const secondOutgoingServer = MailServices.outgoingServer.servers.find(
o => !["smtp1", outgoingServer.key].includes(o.key)
);
Assert.equal(secondOutgoingServer.username, secondUsername);
Assert.equal(
secondAccount.defaultIdentity.smtpServerKey,
secondOutgoingServer.key
);
secondIncomingServer.QueryInterface(Ci.nsIImapIncomingServer);
await TestUtils.waitForCondition(
() => secondIncomingServer.allConnectionsIdle,
"waiting for IMAP connection to become idle"
);
// Clean up both accounts.
MailServices.accounts.removeAccount(account, false);
MailServices.outgoingServer.deleteServer(outgoingServer);
account = incomingServer = outgoingServer = null;
MailServices.accounts.removeAccount(secondAccount, false);
MailServices.outgoingServer.deleteServer(secondOutgoingServer);
await Services.logins.removeAllLoginsAsync();
});
async function checkErrorNotification(subview, expectedId, expectedArgs = {}) {
const header = subview.shadowRoot.querySelector("account-hub-header");
const errorNotification = header.shadowRoot.querySelector(
"#emailFormNotification"
);
const errorTitle = header.shadowRoot.querySelector(
"#emailFormNotificationTitle"
);
await BrowserTestUtils.waitForMutationCondition(
errorNotification,
{
attributes: true,
attributeFilter: ["hidden"],
},
() => BrowserTestUtils.isVisible(errorTitle)
);
await TestUtils.waitForTick();
Assert.ok(errorNotification.classList.contains("error"));
Assert.deepEqual(
document.l10n.getAttributes(errorTitle.querySelector(".localized-title")),
{ id: expectedId, args: expectedArgs }
);
}