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/. */
"use strict";
const { sinon } = ChromeUtils.importESModule(
);
const { ConfigVerifier } = ChromeUtils.importESModule(
);
const { GuessConfig } = ChromeUtils.importESModule(
);
const { ServerTestUtils } = ChromeUtils.importESModule(
);
const PREF_NAME = "mailnews.auto_config_url";
const GSSAPI_TEST_EMAIL = "badtest@example.localhost";
const EXCHANGE_TEST_EMAIL = "testExchange@exchange.test";
const EXCHANGE_TEST_PASSWORD = "hunter2";
const EXCHANGE_TEST_URL = "http://exchange.test/EWS/Exchange.asmx"; // eslint-disable-line sdl/no-insecure-url
let gssapiSandbox;
let gssapiDialog;
let gssapiCurrentStep;
const MANUAL_CONFIG_PREF = "mail.accounthub.manualconfig.enabled";
async function waitForRenderingUpdate() {
await new Promise(resolve =>
requestAnimationFrame(() => Services.tm.dispatchToMainThread(resolve))
);
}
async function disableManualConfig() {
await SpecialPowers.pushPrefEnv({
set: [[MANUAL_CONFIG_PREF, false]],
});
await waitForRenderingUpdate();
}
async function restoreManualConfig() {
await SpecialPowers.popPrefEnv();
await waitForRenderingUpdate();
}
// The guessConfig requests make this test take a long time, so we need a
// longer timeout.
// TODO: Split up this test so longer timeout isn't required.
requestLongerTimeout(2);
add_setup(async function () {
// Set the pref to load a local autoconfig file.
const url =
await SpecialPowers.pushPrefEnv({
set: [[PREF_NAME, url]],
});
});
registerCleanupFunction(async function () {
await cleanupGssapiTest();
});
add_task(async function test_account_email_advanced_setup_incoming() {
await disableManualConfig();
// Fill in email auto form and click continue, waiting for config found
// view to be shown.
const dialog = await subtest_open_account_hub_dialog();
const emailUser = {
name: "John Doe",
email: "john.doe@momo.invalid",
password: "abc12345",
incomingHost: "mail.momo.invalid",
incomingPort: 123,
outgoingHost: "mail.momo.invalid",
outgoingPort: 465,
};
await subtest_fill_initial_config_fields(dialog, emailUser);
const configFoundTemplate = dialog.querySelector("email-config-found");
await TestUtils.waitForCondition(
() =>
BrowserTestUtils.isVisible(configFoundTemplate.querySelector("#imap")),
"The IMAP config option should be visible"
);
// Edit configuration button should lead to incoming config template.
EventUtils.synthesizeMouseAtCenter(
configFoundTemplate.querySelector("#editConfiguration"),
{}
);
const incomingConfigTemplate = dialog.querySelector(
"#emailIncomingConfigSubview"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view"
);
// Update the port number and click advanced config to confirm that the
// account being created takes the updated incoming field.
const incomingPort = incomingConfigTemplate.querySelector("#incomingPort");
EventUtils.synthesizeMouseAtCenter(incomingPort, {});
incomingPort.value = "";
const inputEvent = BrowserTestUtils.waitForEvent(
incomingPort,
"input",
false,
event => event.target.value === "123"
);
EventUtils.sendString("123", window);
await inputEvent;
const advancedConfigButton = incomingConfigTemplate.querySelector(
"#advancedConfigurationIncoming"
);
EventUtils.synthesizeMouseAtCenter(advancedConfigButton, {});
const tabmail = document.getElementById("tabmail");
const oldTab = tabmail.selectedTab;
await BrowserTestUtils.promiseAlertDialog("accept");
// The dialog should automatically close after clicking advanced config
await BrowserTestUtils.waitForEvent(dialog, "close");
await TestUtils.waitForCondition(
() => tabmail.selectedTab != oldTab,
"The tab should change to the account settings tab"
);
await subtest_verify_account_hub_account(
tabmail.selectedTab,
emailUser,
"pop"
);
await subtest_clear_status_bar();
tabmail.closeTab(tabmail.currentTabInfo);
// Confirm that the folder pane is visible.
Assert.ok(BrowserTestUtils.isVisible(tabmail.currentAbout3Pane.folderTree));
await restoreManualConfig();
});
add_task(async function test_account_email_advanced_setup_outgoing() {
await disableManualConfig();
// Fill in email auto form and click continue, waiting for config found
// view to be shown.
const dialog = await subtest_open_account_hub_dialog();
const emailUser = {
name: "John Doe",
email: "john.doe@momo.invalid",
password: "abc12345",
incomingHost: "mail.momo.invalid",
outgoingHost: "mail.momo.invalid",
outgoingPort: 465,
};
await subtest_fill_initial_config_fields(dialog, emailUser);
const footerForward = dialog
.querySelector("account-hub-footer")
.querySelector("#forward");
const configFoundTemplate = dialog.querySelector("email-config-found");
// Edit configuration button should lead to incoming config template.
EventUtils.synthesizeMouseAtCenter(
configFoundTemplate.querySelector("#editConfiguration"),
{}
);
const incomingConfigTemplate = dialog.querySelector(
"#emailIncomingConfigSubview"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view"
);
// Update the port numbers for both incoming and outgoing and click
// advanced config to confirm that the account being created takes the
// updated incoming and outgoing fields.
const incomingPort = incomingConfigTemplate.querySelector("#incomingPort");
EventUtils.synthesizeMouseAtCenter(incomingPort, {});
incomingPort.value = "";
let inputEvent = BrowserTestUtils.waitForEvent(
incomingPort,
"input",
false,
event => event.target.value === "123"
);
EventUtils.sendString("123", window);
await inputEvent;
emailUser.incomingPort = 123;
const outgoingConfigTemplate = dialog.querySelector(
"#emailOutgoingConfigSubview"
);
const isOutgoingVisible = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(outgoingConfigTemplate),
"The outgoing config template should be in view"
);
// Click continue and wait for outgoing config template to be in view.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
await isOutgoingVisible;
const outgoingPort = outgoingConfigTemplate.querySelector("#outgoingPort");
EventUtils.synthesizeMouseAtCenter(outgoingPort, {});
outgoingPort.value = "";
inputEvent = BrowserTestUtils.waitForEvent(
outgoingPort,
"input",
false,
event => event.target.value === "321"
);
EventUtils.sendString("321", window);
emailUser.outgoingPort = 321;
await inputEvent;
const advancedConfigButton = outgoingConfigTemplate.querySelector(
"#advancedConfigurationOutgoing"
);
EventUtils.synthesizeMouseAtCenter(advancedConfigButton, {});
const tabmail = document.getElementById("tabmail");
const oldTab = tabmail.selectedTab;
await BrowserTestUtils.promiseAlertDialog("accept");
// The dialog should automatically close after clicking advanced config
await BrowserTestUtils.waitForEvent(dialog, "close");
await TestUtils.waitForCondition(
() => tabmail.selectedTab != oldTab,
"The tab should change to the account settings tab"
);
await subtest_verify_account_hub_account(
tabmail.selectedTab,
emailUser,
"pop"
);
tabmail.closeTab(tabmail.currentTabInfo);
// Confirm that the folder pane is visible.
Assert.ok(BrowserTestUtils.isVisible(tabmail.currentAbout3Pane.folderTree));
await restoreManualConfig();
});
add_task(async function test_account_email_manual_form() {
await disableManualConfig();
// Fill in email auto form and click continue, waiting for config found
// view to be shown.
const dialog = await subtest_open_account_hub_dialog();
const emailUser = {
name: "John Doe",
email: "john.doe@momo.invalid",
password: "abc12345",
incomingHost: "mail.momo.invalid",
incomingPort: 123,
outgoingHost: "mail.momo.invalid",
outgoingPort: 465,
};
await subtest_fill_initial_config_fields(dialog, emailUser);
const footer = dialog.querySelector("account-hub-footer");
const footerForward = footer.querySelector("#forward");
const footerBack = footer.querySelector("#back");
const footerCustom = footer.querySelector("#custom");
const configFoundTemplate = dialog.querySelector("email-config-found");
await TestUtils.waitForCondition(
() =>
BrowserTestUtils.isVisible(configFoundTemplate.querySelector("#imap")),
"The IMAP config option should be visible"
);
// Edit configuration button should lead to incoming config template.
EventUtils.synthesizeMouseAtCenter(
configFoundTemplate.querySelector("#editConfiguration"),
{},
window
);
const incomingConfigTemplate = dialog.querySelector(
"#emailIncomingConfigSubview"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view"
);
// We should check that the EWS changes to the config form aren't here, as
// we're not editing an EWS config.
Assert.ok(
BrowserTestUtils.isVisible(
incomingConfigTemplate.querySelector("#incomingProtocol")
),
"Default protocol dropdown should be visible"
);
Assert.ok(
BrowserTestUtils.isVisible(
incomingConfigTemplate.querySelector("#incomingConnectionSecurity")
),
"Incoming connection security dropdown should be visible"
);
Assert.ok(
BrowserTestUtils.isVisible(
incomingConfigTemplate.querySelector("#incomingPort")
),
"Incoming port input should be visible"
);
Assert.ok(
BrowserTestUtils.isHidden(
incomingConfigTemplate.querySelector("#incomingExchangeUrl")
),
"EWS URL input should be hidden"
);
let outgoingConfigTemplate = dialog.querySelector(
"#emailOutgoingConfigSubview"
);
let isOutgoingVisible = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(outgoingConfigTemplate),
"The outgoing config template should be in view"
);
// Continuing from incoming with a found config should keep the continue
// button on the outgoing config page enabled.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
await isOutgoingVisible;
Assert.ok(!footerForward.disabled, "Continue button is enabled");
// Go back and update the incoming hostname to have an invalid character,
// which should disable the continue button.
EventUtils.synthesizeMouseAtCenter(footerBack, {});
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view"
);
const incomingHostname =
incomingConfigTemplate.querySelector("#incomingHostname");
EventUtils.synthesizeMouseAtCenter(incomingHostname, {});
const inputEvent = BrowserTestUtils.waitForEvent(
incomingHostname,
"input",
false,
event => event.target.value === `pop.${emailUser.incomingHost}-`
);
// Ensure we move to the end of the input.
EventUtils.synthesizeKey("KEY_End", {});
EventUtils.sendString("-", window);
await inputEvent;
Assert.ok(footerForward.disabled, "Continue button should be disabled");
// Delete the invalid character should re-enable the continue button.
const deleteEvent = BrowserTestUtils.waitForEvent(
incomingHostname,
"input",
false,
event => event.target.value === `pop.${emailUser.incomingHost}`
);
EventUtils.synthesizeKey("KEY_Backspace", {}, window);
await deleteEvent;
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
outgoingConfigTemplate = dialog.querySelector("#emailOutgoingConfigSubview");
isOutgoingVisible = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(outgoingConfigTemplate),
"The outgoing config template should be in view"
);
// Continuing on to outgoing should reveal a disabled continue button and an
// enabled test button.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
await isOutgoingVisible;
Assert.ok(footerForward.disabled, "Continue button should be disabled");
Assert.ok(!footerCustom.disabled, "Test button should be enabled");
await subtest_close_account_hub_dialog(dialog, outgoingConfigTemplate);
await restoreManualConfig();
});
add_task(async function test_pop3_manual_config_flow() {
await disableManualConfig();
// Fill in email auto form and click continue, waiting for config found
// view to be shown.
const dialog = await subtest_open_account_hub_dialog();
const emailUser = {
name: "John Doe",
email: "john.doe@momo.invalid",
password: "abc12345",
incomingHost: "mail.momo.invalid",
incomingPort: 123,
outgoingHost: "mail.momo.invalid",
outgoingPort: 465,
};
await subtest_fill_initial_config_fields(dialog, emailUser);
const configFoundTemplate = dialog.querySelector("email-config-found");
await TestUtils.waitForCondition(
() =>
BrowserTestUtils.isVisible(configFoundTemplate.querySelector("#pop3")),
"The POP3 config option should be visible"
);
EventUtils.synthesizeMouseAtCenter(
configFoundTemplate.querySelector("#pop3"),
{}
);
// POP3 should be the selected config.
Assert.ok(
configFoundTemplate.querySelector("#pop3").classList.contains("selected"),
"POP3 should be the selected config option"
);
// Edit configuration button should lead to incoming config template.
EventUtils.synthesizeMouseAtCenter(
configFoundTemplate.querySelector("#editConfiguration"),
{}
);
const incomingConfigTemplate = dialog.querySelector(
"#emailIncomingConfigSubview"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view"
);
Assert.equal(
incomingConfigTemplate.querySelector("#incomingProtocol").value,
"2",
"The incoming protocol should be POP3"
);
await subtest_close_account_hub_dialog(dialog, incomingConfigTemplate);
await restoreManualConfig();
});
add_task(async function test_invalid_manual_config_flow() {
await disableManualConfig();
const dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
const footerForward = dialog.querySelector("#emailFooter #forward");
const footerCustom = dialog.querySelector("#emailFooter #custom");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
await fillInvalidUserInfo(nameInput, emailInput);
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
// Click continue and wait for incoming config view.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const incomingConfigTemplate = dialog.querySelector(
"#emailIncomingConfigSubview"
);
let incomingConfigTemplatePromise = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view",
500,
500
);
await incomingConfigTemplatePromise;
// The continue button should be enabled if you go back to the email form.
const footerBack = dialog.querySelector("#emailFooter #back");
EventUtils.synthesizeMouseAtCenter(footerBack, {});
const emailFormTemplatePromise = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(emailTemplate),
"The email form template should be in view"
);
await emailFormTemplatePromise;
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
// Go directly back to the incoming form with the the invalid config.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
incomingConfigTemplatePromise = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view",
500,
500
);
await incomingConfigTemplatePromise;
// The continue button should be disabled as the user needs to update the
// incorrect config.
Assert.ok(footerForward.disabled, "Continue button should be disabled");
// The invalid configuration should have an invalid hostname, so the invalid
// hostname input should be marked as invalid.
const incomingHostname = incomingConfigTemplate.querySelector(
"#incomingHostname:invalid"
);
Assert.ok(incomingHostname, "The incoming hostname should be invalid.");
Assert.ok(
BrowserTestUtils.isVisible(
incomingConfigTemplate.querySelector("#incomingHostnameErrorMessage")
),
"The incoming hostname error message should be visible"
);
// Fixing the hostname should enable the continue button.
EventUtils.synthesizeMouseAtCenter(incomingHostname, {});
EventUtils.synthesizeKey("KEY_Home", { shiftKey: true });
EventUtils.synthesizeKey("KEY_ArrowLeft", {});
EventUtils.synthesizeKey("KEY_ArrowRight", {});
let deleteEvent = BrowserTestUtils.waitForEvent(
incomingHostname,
"input",
false,
event => event.target.value === "example.localhost"
);
EventUtils.synthesizeKey("KEY_Backspace", {}, window);
await deleteEvent;
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
Assert.ok(
BrowserTestUtils.isHidden(
incomingConfigTemplate.querySelector("#incomingHostnameErrorMessage")
),
"The incoming hostname error message should be hidden"
);
// Changing the connection security to "None" should show a warning message
// but keep the continue button enabled.
const incomingConnectionSecurity = incomingConfigTemplate.querySelector(
"#incomingConnectionSecurity"
);
const incomingConnectionPromise =
BrowserTestUtils.waitForSelectPopupShown(window);
await EventUtils.synthesizeMouseAtCenter(incomingConnectionSecurity, {});
const incomingConnectionPopup = await incomingConnectionPromise;
const incomingConnectionItems =
incomingConnectionPopup.querySelectorAll("menuitem");
// #incomingConnectionSecurityNoEncryption
incomingConnectionPopup.activateItem(incomingConnectionItems[1]);
await BrowserTestUtils.waitForPopupEvent(incomingConnectionPopup, "hidden");
const securityWarning = incomingConfigTemplate
.querySelector("#incomingConnectionSecurity")
.shadowRoot.querySelector("#securityWarning");
await BrowserTestUtils.waitForAttributeRemoval("hidden", securityWarning);
Assert.ok(
BrowserTestUtils.isVisible(securityWarning),
"Should show security warning"
);
await BrowserTestUtils.waitForAttributeRemoval("disabled", footerForward);
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
// Clicking continue should lead to the outgoing view, with an invalid
// hostname again, with the continue button disabled.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const outgoingConfigTemplate = dialog.querySelector(
"#emailOutgoingConfigSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
outgoingConfigTemplate
);
Assert.ok(footerForward.disabled, "Continue button should be disabled");
const outgoingHostname = outgoingConfigTemplate.querySelector(
"#outgoingHostname:invalid"
);
Assert.ok(outgoingHostname, "The outgoing hostname should be invalid.");
Assert.ok(
BrowserTestUtils.isVisible(
outgoingConfigTemplate.querySelector("#outgoingHostnameErrorMessage")
),
"The outgoing hostname error message should be visible"
);
// Updating the hostname to be valid should not enable the continue button
// as the incoming config was edited.
EventUtils.synthesizeMouseAtCenter(outgoingHostname, {});
EventUtils.synthesizeKey("KEY_Home", { shiftKey: true });
EventUtils.synthesizeKey("KEY_ArrowLeft", {});
EventUtils.synthesizeKey("KEY_ArrowRight", {});
deleteEvent = BrowserTestUtils.waitForEvent(
outgoingHostname,
"input",
false,
event => event.target.value === "example.localhost"
);
EventUtils.synthesizeKey("KEY_Backspace", {}, window);
await deleteEvent;
Assert.ok(footerForward.disabled, "Continue button should be disabled");
Assert.ok(
BrowserTestUtils.isHidden(
incomingConfigTemplate.querySelector("#incomingHostnameErrorMessage")
),
"The outgoing hostname error message should be hidden"
);
// Hitting the test footer button should change the back button to cancel, to
// cancel finding the config.
let backTextPromise = BrowserTestUtils.waitForMutationCondition(
footerBack,
{ attributes: true },
() =>
footerBack.getAttribute("data-l10n-id") ===
"account-hub-email-cancel-button"
);
EventUtils.synthesizeMouseAtCenter(footerCustom, {});
await backTextPromise;
// Hitting cancel should change the back button text to "back" and keep the
// form as the outgoing form.
EventUtils.synthesizeMouseAtCenter(footerBack, {});
backTextPromise = BrowserTestUtils.waitForMutationCondition(
footerBack,
{ attributes: true },
() =>
footerBack.getAttribute("data-l10n-id") ===
"account-hub-email-back-button"
);
Assert.ok(
BrowserTestUtils.isVisible(outgoingConfigTemplate),
"The outgoing form should still be visible"
);
// We still have a config that can't be found because of the testing domain,
// so hitting the test button should lead back to the incoming config, with
// an error notification.
EventUtils.synthesizeMouseAtCenter(footerCustom, {});
incomingConfigTemplatePromise = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view"
);
await incomingConfigTemplatePromise;
const header =
incomingConfigTemplate.shadowRoot.querySelector("account-hub-header");
await TestUtils.waitForCondition(
() =>
header.shadowRoot
.querySelector("#emailFormNotification")
.classList.contains("error"),
"The notification should be present"
);
// The continue button should still be enabled, but going to outgoing the
// continue button should be disabled.
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
outgoingConfigTemplate
);
Assert.ok(footerForward.disabled, "Continue button should be disabled");
await subtest_close_account_hub_dialog(dialog, outgoingConfigTemplate);
await restoreManualConfig();
});
add_task(async function test_account_email_manual_to_ews() {
await disableManualConfig();
const dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
const footerForward = dialog.querySelector("#emailFooter #forward");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
await fillInvalidUserInfo(nameInput, emailInput);
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
// Click continue and wait for incoming config view.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const incomingConfigSubview = dialog.querySelector(
"#emailIncomingConfigSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
incomingConfigSubview
);
info("Now that we're in the incoming config view, switch to EWS");
const protocolSelector =
incomingConfigSubview.querySelector("#incomingProtocol");
const protocolSelectorPromise =
BrowserTestUtils.waitForSelectPopupShown(window);
await EventUtils.synthesizeMouseAtCenter(protocolSelector, {});
const protocolSelectorPopup = await protocolSelectorPromise;
const protocolSelectorItems =
protocolSelectorPopup.querySelectorAll("menuitem");
// #incomingProtocolEWS
protocolSelectorPopup.activateItem(protocolSelectorItems[2]);
await BrowserTestUtils.waitForPopupEvent(protocolSelectorPopup, "hidden");
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
incomingConfigSubview.querySelector("#incomingExchangeUrlFormGroup")
);
const ewsURLInput = incomingConfigSubview.querySelector(
"#incomingExchangeUrl"
);
const focusEvent = BrowserTestUtils.waitForEvent(ewsURLInput, "focus");
EventUtils.synthesizeMouseAtCenter(ewsURLInput, {});
await focusEvent;
const configUpdatedEvent = BrowserTestUtils.waitForEvent(
incomingConfigSubview,
"config-updated",
false,
() => ewsURLInput.value == "https://example.com/"
);
EventUtils.sendString("https://example.com/");
const { detail: configState } = await configUpdatedEvent;
Assert.ok(configState.completed, "Should have a complete EWS config");
Assert.ok(!footerForward.disabled, "Forward button should be enabled");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const passwordSubview = dialog.querySelector("email-password-form");
await BrowserTestUtils.waitForAttributeRemoval("hidden", passwordSubview);
Assert.ok(
BrowserTestUtils.isVisible(passwordSubview),
"Should go to password subview next"
);
await subtest_close_account_hub_dialog(dialog, passwordSubview);
await restoreManualConfig();
});
add_task(async function test_direct_to_manual_config() {
await disableManualConfig();
const dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
const manualConfigButton = emailTemplate.querySelector(
"#manualConfiguration"
);
Assert.ok(
BrowserTestUtils.isHidden(manualConfigButton),
"Manual config button should be hidden"
);
await fillInvalidUserInfo(nameInput, emailInput);
Assert.ok(
BrowserTestUtils.isVisible(manualConfigButton),
"Manual config button should be visible"
);
// Clicking the manual config button should lead to the incoming config form
// with some prefilled data.
manualConfigButton.click();
const incomingConfigTemplate = dialog.querySelector(
"#emailIncomingConfigSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
incomingConfigTemplate
);
Assert.equal(
incomingConfigTemplate.querySelector("#incomingHostname").value,
".example.localhost"
);
Assert.equal(
incomingConfigTemplate.querySelector("#incomingUsername").value,
"badtest@example.localhost"
);
await subtest_close_account_hub_dialog(dialog, incomingConfigTemplate);
await restoreManualConfig();
});
add_task(async function test_account_invalid_email_advanced_setup_incoming() {
await disableManualConfig();
Services.fog.testResetFOG();
// Fill in email auto form and click continue, incoming config step to show
// a base invalid configuration.
const dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
const footerForward = dialog.querySelector("#emailFooter #forward");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
await fillInvalidUserInfo(nameInput, emailInput);
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
info("Clicking next to incoming config");
// Click continue and wait for incoming config view.
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const incomingConfigTemplate = dialog.querySelector(
"#emailIncomingConfigSubview"
);
const incomingConfigTemplatePromise = TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(incomingConfigTemplate),
"The incoming config template should be in view",
500,
500
);
await incomingConfigTemplatePromise;
info("At incoming config view");
const advancedConfigButton = incomingConfigTemplate.querySelector(
"#advancedConfigurationIncoming"
);
EventUtils.synthesizeMouseAtCenter(advancedConfigButton, {});
const tabmail = document.getElementById("tabmail");
const oldTab = tabmail.selectedTab;
await BrowserTestUtils.promiseAlertDialog("accept");
// The dialog should automatically close after clicking advanced config
await BrowserTestUtils.waitForEvent(dialog, "close");
const accountTab = tabmail.selectedTab;
await TestUtils.waitForCondition(
() => accountTab != oldTab,
"The tab should change to the account settings tab"
);
await TestUtils.waitForCondition(
() => !!accountTab.browser.contentWindow.currentAccount,
"The new account should have been created"
);
Assert.equal(
Glean.mail.successfulEmailAccountSetup["advanced-config"].testGetValue(),
1,
"should have recorded advanced-config"
);
const account = accountTab.browser.contentWindow.currentAccount;
const identity = account.defaultIdentity;
const incoming = account.incomingServer;
const outgoing = MailServices.outgoingServer.getServerByKey(
identity.smtpServerKey
);
const config = {
"incoming server username": {
actual: incoming.username,
expected: "badtest@example.localhost",
},
"outgoing server username": {
actual: outgoing.username,
expected: "badtest@example.localhost",
},
"incoming server hostname": {
actual: incoming.hostname,
expected: ".example.localhost",
},
"outgoing server hostname": {
actual: outgoing.serverURI.host,
expected: ".example.localhost",
},
"user real name": { actual: identity.fullName, expected: "Test User" },
"user email address": {
actual: identity.email,
expected: "badtest@example.localhost",
},
"incoming port": {
actual: incoming.port,
expected: 143,
},
"outgoing port": {
actual: outgoing.port,
expected: 0,
},
};
for (const detail in config) {
Assert.equal(
config[detail].actual,
config[detail].expected,
`Configured ${detail} is ${config[detail].actual}. It should be ${config[detail].expected}`
);
}
removeAccountInternal(accountTab, account);
await subtest_clear_status_bar();
tabmail.closeTab(tabmail.currentTabInfo);
// Confirm that the folder pane is visible.
Assert.ok(BrowserTestUtils.isVisible(tabmail.currentAbout3Pane.folderTree));
await restoreManualConfig();
});
add_task(async function test_direct_to_manual_gssapi_skips_password_step() {
await disableManualConfig();
gssapiSandbox = sinon.createSandbox();
const guessConfigStub = gssapiSandbox
.stub(GuessConfig, "guessConfig")
.callsFake((_domain, _progressCallback, initialConfig) =>
Promise.resolve(initialConfig.copy())
);
const verifyConfigStub = gssapiSandbox
.stub(ConfigVerifier.prototype, "verifyConfig")
.callsFake(config => Promise.resolve(config));
gssapiDialog = await subtest_open_account_hub_dialog();
const emailTemplate = gssapiDialog.querySelector("email-auto-form");
gssapiCurrentStep = emailTemplate;
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
nameInput.value = "";
emailInput.value = "";
await fillInvalidUserInfo(nameInput, emailInput);
const manualConfigButton = emailTemplate.querySelector(
"#manualConfiguration"
);
manualConfigButton.click();
const incomingConfigTemplate = gssapiDialog.querySelector(
"#emailIncomingConfigSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
incomingConfigTemplate
);
gssapiCurrentStep = incomingConfigTemplate;
const incomingHostname =
incomingConfigTemplate.querySelector("#incomingHostname");
EventUtils.synthesizeMouseAtCenter(incomingHostname, {});
incomingHostname.select();
let inputEvent = BrowserTestUtils.waitForEvent(
incomingHostname,
"input",
false,
event => event.target.value === "imap.gssapi.test"
);
EventUtils.synthesizeKey("KEY_Backspace", {}, window);
EventUtils.sendString("imap.gssapi.test", window);
await inputEvent;
const incomingPort = incomingConfigTemplate.querySelector("#incomingPort");
EventUtils.synthesizeMouseAtCenter(incomingPort, {});
incomingPort.select();
inputEvent = BrowserTestUtils.waitForEvent(
incomingPort,
"input",
false,
event => event.target.value === "993"
);
EventUtils.synthesizeKey("KEY_Backspace", {}, window);
EventUtils.sendString("993", window);
await inputEvent;
const incomingConnectionSecurity = incomingConfigTemplate.querySelector(
"#incomingConnectionSecurity"
);
let selectPopupShown = BrowserTestUtils.waitForSelectPopupShown(window);
await EventUtils.synthesizeMouseAtCenter(incomingConnectionSecurity, {});
let selectPopup = await selectPopupShown;
let selectItems = selectPopup.querySelectorAll("menuitem");
// #incomingConnectionSecuritySslTls
selectPopup.activateItem(selectItems[3]);
await BrowserTestUtils.waitForPopupEvent(selectPopup, "hidden");
const incomingAuthMethod = incomingConfigTemplate.querySelector(
"#incomingAuthMethod"
);
selectPopupShown = BrowserTestUtils.waitForSelectPopupShown(window);
await EventUtils.synthesizeMouseAtCenter(incomingAuthMethod, {});
selectPopup = await selectPopupShown;
selectItems = selectPopup.querySelectorAll("menuitem");
// #incomingAuthMethodKerbos
selectPopup.activateItem(selectItems[3]);
await BrowserTestUtils.waitForPopupEvent(selectPopup, "hidden");
const footer = gssapiDialog.querySelector("account-hub-footer");
const footerForward = footer.querySelector("#forward");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const outgoingConfigTemplate = gssapiDialog.querySelector(
"#emailOutgoingConfigSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
outgoingConfigTemplate
);
gssapiCurrentStep = outgoingConfigTemplate;
const outgoingHostname =
outgoingConfigTemplate.querySelector("#outgoingHostname");
EventUtils.synthesizeMouseAtCenter(outgoingHostname, {});
outgoingHostname.select();
inputEvent = BrowserTestUtils.waitForEvent(
outgoingHostname,
"input",
false,
event => event.target.value === "smtp.gssapi.test"
);
EventUtils.synthesizeKey("KEY_Backspace", {}, window);
EventUtils.sendString("smtp.gssapi.test", window);
await inputEvent;
const outgoingPort = outgoingConfigTemplate.querySelector("#outgoingPort");
EventUtils.synthesizeMouseAtCenter(outgoingPort, {});
outgoingPort.select();
inputEvent = BrowserTestUtils.waitForEvent(
outgoingPort,
"input",
false,
event => event.target.value === "587"
);
EventUtils.synthesizeKey("KEY_Backspace", {}, window);
EventUtils.sendString("587", window);
await inputEvent;
const outgoingConnectionSecurity = outgoingConfigTemplate.querySelector(
"#outgoingConnectionSecurity"
);
selectPopupShown = BrowserTestUtils.waitForSelectPopupShown(window);
await EventUtils.synthesizeMouseAtCenter(outgoingConnectionSecurity, {});
selectPopup = await selectPopupShown;
selectItems = selectPopup.querySelectorAll("menuitem");
// #outgoingConnectionSecurityStartTtls
selectPopup.activateItem(selectItems[2]);
await BrowserTestUtils.waitForPopupEvent(selectPopup, "hidden");
const outgoingAuthMethod = outgoingConfigTemplate.querySelector(
"#outgoingAuthMethod"
);
selectPopupShown = BrowserTestUtils.waitForSelectPopupShown(window);
await EventUtils.synthesizeMouseAtCenter(outgoingAuthMethod, {});
selectPopup = await selectPopupShown;
selectItems = selectPopup.querySelectorAll("menuitem");
// #outgoingAuthMethodKerbos
selectPopup.activateItem(selectItems[4]);
await BrowserTestUtils.waitForPopupEvent(selectPopup, "hidden");
const footerCustom = footer.querySelector("#custom");
Assert.ok(!footerCustom.disabled, "Test button should be enabled");
EventUtils.synthesizeMouseAtCenter(footerCustom, {});
await BrowserTestUtils.waitForAttributeRemoval("disabled", footerForward);
EventUtils.synthesizeMouseAtCenter(footerForward, {});
Assert.ok(
BrowserTestUtils.isHidden(
gssapiDialog.querySelector("email-password-form")
),
"The email password form should not be visible."
);
const successStep = gssapiDialog.querySelector("email-added-success");
await BrowserTestUtils.waitForAttributeRemoval("hidden", successStep);
gssapiCurrentStep = successStep;
Assert.ok(guessConfigStub.calledOnce, "Should test the manual config.");
Assert.ok(
verifyConfigStub.calledOnce,
"Should verify the GSSAPI config once."
);
const imapAccount = MailServices.accounts.accounts.find(
account => account.defaultIdentity?.email === GSSAPI_TEST_EMAIL
);
Assert.ok(imapAccount, "IMAP account should be created.");
Assert.equal(
imapAccount.incomingServer.authMethod,
Ci.nsMsgAuthMethod.GSSAPI,
"Incoming server should use GSSAPI."
);
const smtpServer = MailServices.outgoingServer.getServerByKey(
imapAccount.defaultIdentity.smtpServerKey
);
Assert.equal(
smtpServer.authMethod,
Ci.nsMsgAuthMethod.GSSAPI,
"Outgoing server should use GSSAPI."
);
await cleanupGssapiTest();
await restoreManualConfig();
});
add_task(async function test_config_found_manual_config_pref_enabled() {
const dialog = await subtest_open_account_hub_dialog();
const emailUser = {
name: "John Doe",
email: "john.doe@momo.invalid",
password: "abc12345",
incomingHost: "mail.momo.invalid",
incomingPort: 123,
outgoingHost: "mail.momo.invalid",
outgoingPort: 465,
};
await subtest_fill_initial_config_fields(dialog, emailUser);
const configFoundTemplate = dialog.querySelector("email-config-found");
await TestUtils.waitForCondition(
() =>
BrowserTestUtils.isVisible(configFoundTemplate.querySelector("#imap")),
"The IMAP config option should be visible"
);
EventUtils.synthesizeMouseAtCenter(
configFoundTemplate.querySelector("#editConfiguration"),
{}
);
const protocolSelectTemplate = dialog.querySelector(
"#emailProtocolSelectSubview"
);
const manualConfigTemplate = dialog.querySelector(
"#emailManualConfigSubview"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(manualConfigTemplate),
"The manual config template should be in view"
);
Assert.ok(
BrowserTestUtils.isHidden(protocolSelectTemplate),
"The protocol select template should stay hidden"
);
// Going back should go back to the config found step.
const footerBack = dialog.querySelector("#emailFooter #back");
EventUtils.synthesizeMouseAtCenter(footerBack, {});
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(configFoundTemplate),
"The config found template should be in view"
);
Assert.ok(
BrowserTestUtils.isHidden(manualConfigTemplate),
"The manual config template should be hidden"
);
// Going back to the email step and clicking manual config and selecting a
// protocol and going back should go back to the protocol step.
EventUtils.synthesizeMouseAtCenter(footerBack, {});
const emailTemplate = dialog.querySelector("#emailAutoConfigSubview");
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(emailTemplate),
"The email template should be in view"
);
emailTemplate.querySelector("#manualConfiguration").click();
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(protocolSelectTemplate),
"The protocol select template should be in view"
);
await subtest_select_protocol_and_continue(dialog, "imap");
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(manualConfigTemplate),
"The manual config template should be in view"
);
Assert.ok(
BrowserTestUtils.isHidden(protocolSelectTemplate),
"The protocol select template should be hidden"
);
EventUtils.synthesizeMouseAtCenter(footerBack, {});
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(protocolSelectTemplate),
"The protocol select template should be in view"
);
await subtest_close_account_hub_dialog(dialog, protocolSelectTemplate);
});
add_task(async function test_direct_to_manual_config_pref_enabled() {
const dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
await fillInvalidUserInfo(nameInput, emailInput);
const manualConfigButton = emailTemplate.querySelector(
"#manualConfiguration"
);
Assert.ok(
BrowserTestUtils.isVisible(manualConfigButton),
"Manual config button should be visible"
);
manualConfigButton.click();
const protocolSelectTemplate = dialog.querySelector(
"#emailProtocolSelectSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
protocolSelectTemplate
);
subtest_assert_protocol_select_chrome(dialog, protocolSelectTemplate);
Assert.ok(
protocolSelectTemplate.querySelector(
`input[name="protocol-select"][value="imap"]`
).checked,
"The direct manual config flow should default to IMAP"
);
await subtest_select_protocol_and_continue(dialog, "imap");
const manualConfigTemplate = dialog.querySelector(
"#emailManualConfigSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
manualConfigTemplate
);
Assert.equal(
manualConfigTemplate.captureState().incoming.type,
"imap",
"The manual config form should use the selected IMAP protocol"
);
subtest_assert_manual_config_hostnames_empty(manualConfigTemplate, "IMAP");
EventUtils.synthesizeMouseAtCenter(
dialog.querySelector("#emailFooter #back"),
{}
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
protocolSelectTemplate
);
await subtest_select_protocol_and_continue(dialog, "microsoft");
const exchangeSettingsSubview = dialog.querySelector(
"#emailExchangeSettingsSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeSettingsSubview
);
Assert.ok(
BrowserTestUtils.isHidden(
dialog.querySelector("#emailManualConfigSubview")
),
"The new manual config form should stay hidden for Microsoft"
);
const exchangeTypeSubview = dialog.querySelector("#emailExchangeTypeSubview");
Assert.ok(
BrowserTestUtils.isHidden(exchangeTypeSubview),
"The Exchange type subview should stay hidden before Exchange settings are complete"
);
EventUtils.synthesizeMouseAtCenter(
dialog.querySelector("#emailFooter #back"),
{}
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
protocolSelectTemplate
);
Assert.ok(
protocolSelectTemplate.querySelector(
`input[name="protocol-select"][value="microsoft"]`
).checked,
"Back navigation should return to protocol select with Microsoft selected"
);
await subtest_select_protocol_and_continue(dialog, "pop3");
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
manualConfigTemplate
);
Assert.ok(
BrowserTestUtils.isHidden(exchangeTypeSubview),
"The Exchange type subview should stay hidden for POP3"
);
Assert.equal(
manualConfigTemplate.captureState().incoming.type,
"pop3",
"The manual config form should use the selected POP3 protocol"
);
subtest_assert_manual_config_hostnames_empty(manualConfigTemplate, "POP3");
await subtest_close_account_hub_dialog(dialog, manualConfigTemplate);
});
add_task(async function test_exchange_type_submission_pref_enabled() {
const dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
await fillInvalidUserInfo(nameInput, emailInput);
const manualConfigButton = emailTemplate.querySelector(
"#manualConfiguration"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(manualConfigButton),
"Manual config button should be visible"
);
manualConfigButton.click();
const protocolSelectTemplate = dialog.querySelector(
"#emailProtocolSelectSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
protocolSelectTemplate
);
await subtest_select_protocol_and_continue(dialog, "microsoft");
const exchangeSettingsSubview = dialog.querySelector(
"#emailExchangeSettingsSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeSettingsSubview
);
const serviceURL = exchangeSettingsSubview.querySelector("#serviceURL");
const serviceURLInput = serviceURL.querySelector("input");
const exchangeSettingsCompleted = BrowserTestUtils.waitForEvent(
exchangeSettingsSubview,
"config-updated",
false,
event => event.detail.completed
);
EventUtils.synthesizeMouseAtCenter(serviceURLInput, {});
EventUtils.sendString(
window
);
await exchangeSettingsCompleted;
const footerForward = dialog.querySelector("#emailFooter #forward");
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
const exchangeTypeSubview = dialog.querySelector("#emailExchangeTypeSubview");
const exchangeTypeShown = BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeTypeSubview
);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await exchangeTypeShown;
Assert.equal(
exchangeTypeSubview.querySelector("#exchangeTypeUsername").value,
"badtest@example.localhost",
"The Exchange type form should use the configured email as the username"
);
const ewsCard = exchangeTypeSubview.querySelector(
'account-hub-radio-card-large[value="ews"]'
);
const authenticationSelect = exchangeTypeSubview.querySelector(
"#exchangeTypeAuthentication"
);
Assert.ok(footerForward.disabled, "Connect button should be disabled");
Assert.equal(
authenticationSelect.value,
"",
"An authentication method should not be selected"
);
ewsCard.focus();
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await TestUtils.waitForTick();
Assert.ok(
BrowserTestUtils.isVisible(exchangeTypeSubview),
"Enter should not submit the Exchange type form while Connect is disabled"
);
Assert.ok(
BrowserTestUtils.isHidden(dialog.querySelector("#emailPasswordSubview")),
"The password step should remain hidden while Connect is disabled"
);
EventUtils.synthesizeMouseAtCenter(ewsCard, {});
authenticationSelect.value = String(Ci.nsMsgAuthMethod.passwordCleartext);
authenticationSelect.select.dispatchEvent(
new Event("change", { bubbles: true })
);
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
EventUtils.synthesizeKey(
"KEY_Enter",
{ repeat: true, type: "keydown" },
window
);
await TestUtils.waitForTick();
Assert.ok(
BrowserTestUtils.isVisible(exchangeTypeSubview),
"A repeated Enter keydown should not submit the Exchange type form"
);
const passwordSubview = dialog.querySelector("#emailPasswordSubview");
const passwordShown = BrowserTestUtils.waitForAttributeRemoval(
"hidden",
passwordSubview
);
ewsCard.focus();
Assert.equal(
document.querySelector("account-hub-container").shadowRoot.activeElement,
ewsCard,
"The selected Exchange type card should be focused before submitting"
);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
await passwordShown;
Assert.ok(
BrowserTestUtils.isHidden(exchangeTypeSubview),
"Submitting the Exchange type form should move to the password step"
);
await subtest_close_account_hub_dialog(dialog, passwordSubview);
});
add_task(
async function test_exchange_type_full_account_creation_pref_enabled() {
const existingOutgoingServerKeys = new Set(
MailServices.outgoingServer.servers.map(server => server.key)
);
const ewsServer = await ServerTestUtils.createServer({
type: "ews",
options: {
username: EXCHANGE_TEST_EMAIL,
password: EXCHANGE_TEST_PASSWORD,
},
hostname: "exchange.test",
port: 80,
});
let account;
let currentStep;
let dialog;
try {
dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
await fillUserInfo(
nameInput,
emailInput,
"Test User",
EXCHANGE_TEST_EMAIL
);
const manualConfigButton = emailTemplate.querySelector(
"#manualConfiguration"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(manualConfigButton),
"Manual config button should be visible"
);
manualConfigButton.click();
const protocolSelectTemplate = dialog.querySelector(
"#emailProtocolSelectSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
protocolSelectTemplate
);
currentStep = protocolSelectTemplate;
await subtest_select_protocol_and_continue(dialog, "microsoft");
const exchangeSettingsSubview = dialog.querySelector(
"#emailExchangeSettingsSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeSettingsSubview
);
currentStep = exchangeSettingsSubview;
const serviceURL = exchangeSettingsSubview.querySelector("#serviceURL");
const serviceURLInput = serviceURL.querySelector("input");
const exchangeSettingsCompleted = BrowserTestUtils.waitForEvent(
exchangeSettingsSubview,
"config-updated",
false,
event => event.detail.completed
);
EventUtils.synthesizeMouseAtCenter(serviceURLInput, {});
EventUtils.sendString(EXCHANGE_TEST_URL, window);
await exchangeSettingsCompleted;
const footerForward = dialog.querySelector("#emailFooter #forward");
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const exchangeTypeSubview = dialog.querySelector(
"#emailExchangeTypeSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeTypeSubview
);
currentStep = exchangeTypeSubview;
Assert.equal(
exchangeTypeSubview.querySelector("#exchangeTypeUsername").value,
EXCHANGE_TEST_EMAIL,
"The Exchange type form should use the configured email as the username"
);
const ewsCard = exchangeTypeSubview.querySelector(
'account-hub-radio-card-large[value="ews"]'
);
EventUtils.synthesizeMouseAtCenter(ewsCard, {});
const authenticationSelect = exchangeTypeSubview.querySelector(
"#exchangeTypeAuthentication"
);
authenticationSelect.value = String(Ci.nsMsgAuthMethod.passwordCleartext);
authenticationSelect.select.dispatchEvent(
new Event("change", { bubbles: true })
);
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const passwordSubview = dialog.querySelector("#emailPasswordSubview");
await BrowserTestUtils.waitForAttributeRemoval("hidden", passwordSubview);
currentStep = passwordSubview;
await fillPasswordInput(passwordSubview, EXCHANGE_TEST_PASSWORD);
const accountPromise = waitForAccountByEmail(EXCHANGE_TEST_EMAIL);
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
account = await accountPromise;
// Creating an account with no address books and calendars should lead to
// the success view.
const successStep = dialog.querySelector("email-added-success");
await BrowserTestUtils.waitForAttributeRemoval("hidden", successStep);
currentStep = successStep;
const identity = account.defaultIdentity;
const incoming = account.incomingServer;
Assert.equal(incoming.type, "ews", "Should create an EWS account");
Assert.equal(
incoming.username,
EXCHANGE_TEST_EMAIL,
"Should save the Exchange username"
);
Assert.equal(
incoming.authMethod,
Ci.nsMsgAuthMethod.passwordCleartext,
"Should save the selected Exchange authentication method"
);
Assert.equal(
incoming.getStringValue("ews_url"),
EXCHANGE_TEST_URL,
"Should save the manually configured Exchange URL"
);
Assert.equal(
identity.fullName,
"Test User",
"Should save the configured real name"
);
Assert.equal(
identity.email,
EXCHANGE_TEST_EMAIL,
"Should save the configured email address"
);
} finally {
if (account) {
MailServices.accounts.removeAccount(account);
}
for (const outgoingServer of Array.from(
MailServices.outgoingServer.servers
)) {
if (!existingOutgoingServerKeys.has(outgoingServer.key)) {
MailServices.outgoingServer.deleteServer(outgoingServer);
}
}
ewsServer.stop();
await Services.logins.removeAllLoginsAsync();
if (dialog?.open && currentStep) {
await subtest_close_account_hub_dialog(dialog, currentStep);
}
await subtest_clear_status_bar();
}
}
);
add_task(
async function test_exchange_type_advanced_configuration_pref_enabled() {
const existingOutgoingServerKeys = new Set(
MailServices.outgoingServer.servers.map(server => server.key)
);
const tabmail = document.getElementById("tabmail");
let account;
let accountTab;
try {
const dialog = await subtest_open_account_hub_dialog();
const emailTemplate = dialog.querySelector("email-auto-form");
const nameInput = emailTemplate.querySelector("#realName");
const emailInput = emailTemplate.querySelector("#email");
// Ensure fields are empty.
nameInput.value = "";
emailInput.value = "";
await fillInvalidUserInfo(nameInput, emailInput);
const manualConfigButton = emailTemplate.querySelector(
"#manualConfiguration"
);
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(manualConfigButton),
"Manual config button should be visible"
);
manualConfigButton.click();
const protocolSelectTemplate = dialog.querySelector(
"#emailProtocolSelectSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
protocolSelectTemplate
);
await subtest_select_protocol_and_continue(dialog, "microsoft");
const exchangeSettingsSubview = dialog.querySelector(
"#emailExchangeSettingsSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeSettingsSubview
);
const serviceURL = exchangeSettingsSubview.querySelector("#serviceURL");
const serviceURLInput = serviceURL.querySelector("input");
const exchangeSettingsCompleted = BrowserTestUtils.waitForEvent(
exchangeSettingsSubview,
"config-updated",
false,
event => event.detail.completed
);
EventUtils.synthesizeMouseAtCenter(serviceURLInput, {});
EventUtils.sendString(
window
);
await exchangeSettingsCompleted;
const footerForward = dialog.querySelector("#emailFooter #forward");
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
EventUtils.synthesizeMouseAtCenter(footerForward, {});
const exchangeTypeSubview = dialog.querySelector(
"#emailExchangeTypeSubview"
);
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeTypeSubview
);
const ewsCard = exchangeTypeSubview.querySelector(
'account-hub-radio-card-large[value="ews"]'
);
EventUtils.synthesizeMouseAtCenter(ewsCard, {});
const authenticationSelect = exchangeTypeSubview.querySelector(
"#exchangeTypeAuthentication"
);
authenticationSelect.value = String(Ci.nsMsgAuthMethod.OAuth2);
authenticationSelect.select.dispatchEvent(
new Event("change", { bubbles: true })
);
const defaultOauthInput = exchangeTypeSubview.querySelector(
"#exchangeTypeDefaultOauth"
);
Assert.ok(
defaultOauthInput.checked,
"Should currently be using default OAuth config"
);
EventUtils.synthesizeMouseAtCenter(defaultOauthInput, {});
await BrowserTestUtils.waitForAttributeRemoval(
"hidden",
exchangeTypeSubview.querySelector("#exchangeTypeOauthCustom")
);
const oldTab = tabmail.selectedTab;
const promptPromise = BrowserTestUtils.promiseAlertDialog("accept");
const dialogClosedPromise = BrowserTestUtils.waitForEvent(
dialog,
"close"
);
exchangeTypeSubview
.querySelector("#advancedConfigurationExchange")
.click();
await promptPromise;
await dialogClosedPromise;
accountTab = tabmail.selectedTab;
await TestUtils.waitForCondition(
() => accountTab != oldTab,
"The tab should change to the account settings tab"
);
await TestUtils.waitForCondition(
() => !!accountTab.browser.contentWindow.currentAccount,
"The new account should have been created"
);
account = accountTab.browser.contentWindow.currentAccount;
const incoming = account.incomingServer;
Assert.equal(incoming.type, "ews", "Should create an EWS account");
Assert.equal(
incoming.username,
"badtest@example.localhost",
"Should save the Exchange username"
);
Assert.equal(
incoming.authMethod,
Ci.nsMsgAuthMethod.OAuth2,
"Should save the selected Exchange authentication method"
);
Assert.equal(
incoming.getStringValue("ews_url"),
"Should save the Exchange URL"
);
Assert.ok(
!incoming.exchangeOverrideOAuthDetails,
"Should not have any OAuth configuration overrides"
);
Assert.ok(
!incoming.exchangeApplicationId,
"Should not have an application ID for OAuth set"
);
Assert.ok(
!incoming.exchangeTenantId,
"Should not have a tenant ID for OAuth set"
);
} finally {
if (accountTab && tabmail.tabInfo.includes(accountTab)) {
tabmail.closeTab(accountTab);
}
if (account) {
MailServices.accounts.removeAccount(account);
}
for (const outgoingServer of Array.from(
MailServices.outgoingServer.servers
)) {
if (!existingOutgoingServerKeys.has(outgoingServer.key)) {
MailServices.outgoingServer.deleteServer(outgoingServer);
}
}
await subtest_clear_status_bar();
}
}
);
async function fillInvalidUserInfo(nameInput, emailInput) {
await fillUserInfo(nameInput, emailInput, "Test User", GSSAPI_TEST_EMAIL);
}
async function fillUserInfo(nameInput, emailInput, name, email) {
EventUtils.synthesizeMouseAtCenter(nameInput, {});
let inputEvent = BrowserTestUtils.waitForEvent(
nameInput,
"input",
false,
event => event.target.value === name
);
EventUtils.sendString(name, window);
await inputEvent;
const focusEvent = BrowserTestUtils.waitForEvent(emailInput, "focus");
EventUtils.synthesizeMouseAtCenter(emailInput, {});
await focusEvent;
inputEvent = BrowserTestUtils.waitForEvent(
emailInput,
"input",
false,
event => event.target.value === email
);
EventUtils.sendString(email, window);
await inputEvent;
}
async function fillPasswordInput(passwordStep, password) {
const passwordInput = passwordStep.querySelector("#password");
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(passwordInput),
"The password form input should be visible."
);
EventUtils.synthesizeMouseAtCenter(passwordInput, {});
const inputEvent = BrowserTestUtils.waitForEvent(
passwordInput,
"input",
true,
event => event.target.value === password
);
EventUtils.sendString(password, window);
await inputEvent;
}
function waitForAccountByEmail(email) {
return new Promise(resolve => {
const listener = {
onServerLoaded() {
const matchingAccount = MailServices.accounts.accounts.find(
account => account.defaultIdentity?.email === email
);
if (matchingAccount) {
MailServices.accounts.removeIncomingServerListener(listener);
resolve(matchingAccount);
}
},
onServerUnloaded() {},
onServerChanged() {},
};
MailServices.accounts.addIncomingServerListener(listener);
listener.onServerLoaded();
});
}
async function cleanupGssapiTest() {
gssapiSandbox?.restore();
gssapiSandbox = null;
const imapAccount = MailServices.accounts.accounts.find(
account => account.defaultIdentity?.email === GSSAPI_TEST_EMAIL
);
if (imapAccount) {
const smtpServer = MailServices.outgoingServer.getServerByKey(
imapAccount.defaultIdentity.smtpServerKey
);
MailServices.accounts.removeAccount(imapAccount);
MailServices.outgoingServer.deleteServer(smtpServer);
}
await Services.logins.removeAllLoginsAsync();
if (gssapiDialog?.open && gssapiCurrentStep) {
await subtest_close_account_hub_dialog(gssapiDialog, gssapiCurrentStep);
}
gssapiDialog = null;
gssapiCurrentStep = null;
}
async function subtest_select_protocol_and_continue(dialog, protocol) {
await subtest_select_protocol(dialog, protocol);
EventUtils.synthesizeKey("KEY_Enter", {}, window);
}
async function subtest_select_protocol(dialog, protocol) {
const protocolSelectTemplate = dialog.querySelector(
"#emailProtocolSelectSubview"
);
const protocolInput = protocolSelectTemplate.querySelector(
`input[name="protocol-select"][value="${protocol}"]`
);
EventUtils.synthesizeMouseAtCenter(protocolInput, {});
Assert.ok(protocolInput.checked, `${protocol} should be selected`);
const footerForward = dialog.querySelector("#emailFooter #forward");
Assert.ok(!footerForward.disabled, "Continue button should be enabled");
protocolInput.focus();
Assert.equal(
document.querySelector("account-hub-container").shadowRoot.activeElement,
protocolInput,
`${protocol} input should be focused before submitting`
);
}
function subtest_assert_protocol_select_chrome(dialog, protocolSelectTemplate) {
const footerForward = dialog.querySelector("#emailFooter #forward");
Assert.equal(
document.l10n.getAttributes(footerForward).id,
"account-hub-email-set-up-account-button",
"The protocol select screen should use the set up account forward button"
);
const protocolSelectHeader =
protocolSelectTemplate.shadowRoot.querySelector("account-hub-header");
const notification = protocolSelectHeader.shadowRoot.querySelector(
"#emailFormNotification"
);
const notificationTitle = protocolSelectHeader.shadowRoot.querySelector(
"#emailFormNotificationTitle .localized-title"
);
Assert.equal(
document.l10n.getAttributes(notificationTitle).id,
"account-hub-email-protocol-select-additional-info",
"The protocol select screen should show the required information notification"
);
Assert.ok(
BrowserTestUtils.isVisible(notification),
"The protocol select screen should show the notification bar"
);
}
function subtest_assert_manual_config_hostnames_empty(
manualConfigTemplate,
protocol
) {
Assert.equal(
manualConfigTemplate.querySelector("#manualIncomingHostname").value,
"",
`${protocol} manual config should not populate the incoming hostname`
);
Assert.equal(
manualConfigTemplate.querySelector("#manualOutgoingHostname").value,
"",
`${protocol} manual config should not populate the outgoing hostname`
);
}