summaryrefslogtreecommitdiffstats
path: root/browser/components/aboutlogins/tests/browser/browser_createLogin.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/aboutlogins/tests/browser/browser_createLogin.js')
-rw-r--r--browser/components/aboutlogins/tests/browser/browser_createLogin.js535
1 files changed, 535 insertions, 0 deletions
diff --git a/browser/components/aboutlogins/tests/browser/browser_createLogin.js b/browser/components/aboutlogins/tests/browser/browser_createLogin.js
new file mode 100644
index 0000000000..a876320ecf
--- /dev/null
+++ b/browser/components/aboutlogins/tests/browser/browser_createLogin.js
@@ -0,0 +1,535 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_setup(async function () {
+ let aboutLoginsTab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: "about:logins",
+ });
+ registerCleanupFunction(() => {
+ BrowserTestUtils.removeTab(aboutLoginsTab);
+ Services.logins.removeAllUserFacingLogins();
+ });
+});
+
+add_task(async function test_create_login() {
+ let browser = gBrowser.selectedBrowser;
+ await SpecialPowers.spawn(browser, [], async () => {
+ let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+ Assert.ok(
+ !loginList._selectedGuid,
+ "should not be a selected guid by default"
+ );
+ Assert.ok(
+ content.document.documentElement.classList.contains("no-logins"),
+ "Should initially be in no logins view"
+ );
+ Assert.ok(
+ loginList.classList.contains("no-logins"),
+ "login-list should initially be in no logins view"
+ );
+ Assert.equal(
+ loginList._loginGuidsSortedOrder.length,
+ 0,
+ "login list should be empty"
+ );
+ });
+
+ let testCases = [
+ ["ftp://ftp.example.com/", "ftp://ftp.example.com"],
+ ["https://example.com/foo", "https://example.com"],
+ ["http://example.com/", "http://example.com"],
+ [
+ "https://testuser1:testpass1@bugzilla.mozilla.org/show_bug.cgi?id=1556934",
+ "https://bugzilla.mozilla.org",
+ ],
+ ["https://www.example.com/bar", "https://www.example.com"],
+ ];
+
+ for (let i = 0; i < testCases.length; i++) {
+ let originTuple = testCases[i];
+ info("Testcase " + i);
+ let storageChangedPromised = TestUtils.topicObserved(
+ "passwordmgr-storage-changed",
+ (_, data) => data == "addLogin"
+ );
+
+ await SpecialPowers.spawn(
+ browser,
+ [[originTuple, i]],
+ async ([aOriginTuple, index]) => {
+ let loginList = Cu.waiveXrays(
+ content.document.querySelector("login-list")
+ );
+ let createButton = loginList._createLoginButton;
+ Assert.ok(
+ ContentTaskUtils.is_hidden(loginList._blankLoginListItem),
+ "the blank login list item should be hidden initially"
+ );
+ Assert.ok(
+ !createButton.disabled,
+ "Create button should not be disabled initially"
+ );
+
+ let loginItem = Cu.waiveXrays(
+ content.document.querySelector("login-item")
+ );
+ let usernameInput = loginItem.shadowRoot.querySelector(
+ "input[name='username']"
+ );
+ usernameInput.placeholder = "dummy placeholder";
+
+ createButton.click();
+
+ Assert.ok(
+ ContentTaskUtils.is_visible(loginList._blankLoginListItem),
+ "the blank login list item should be visible after clicking on the create button"
+ );
+ Assert.ok(
+ createButton.disabled,
+ "Create button should be disabled after being clicked"
+ );
+
+ let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
+ Assert.ok(
+ ContentTaskUtils.is_visible(cancelButton),
+ "cancel button should be visible in create mode with no logins saved"
+ );
+
+ let originInput = loginItem.shadowRoot.querySelector(
+ "input[name='origin']"
+ );
+ let passwordInput = loginItem.shadowRoot.querySelector(
+ "input[name='password']"
+ );
+
+ Assert.equal(
+ content.document.l10n.getAttributes(usernameInput).id,
+ null,
+ "there should be no placeholder id on the username input in edit mode"
+ );
+ Assert.equal(
+ usernameInput.placeholder,
+ "",
+ "there should be no placeholder on the username input in edit mode"
+ );
+ originInput.value = aOriginTuple[0];
+ usernameInput.value = "testuser1";
+ passwordInput.value = "testpass1";
+
+ let saveChangesButton = loginItem.shadowRoot.querySelector(
+ ".save-changes-button"
+ );
+ saveChangesButton.click();
+ }
+ );
+
+ info("waiting for login to get added to storage");
+ await storageChangedPromised;
+ info("login added to storage");
+
+ let canTestOSKeyStoreLogin = OSKeyStoreTestUtils.canTestOSKeyStoreLogin();
+ if (canTestOSKeyStoreLogin) {
+ storageChangedPromised = TestUtils.topicObserved(
+ "passwordmgr-storage-changed",
+ (_, data) => data == "modifyLogin"
+ );
+ }
+ await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => {
+ await ContentTaskUtils.waitForCondition(() => {
+ return !content.document.documentElement.classList.contains(
+ "no-logins"
+ );
+ }, "waiting for no-logins view to exit");
+ Assert.ok(
+ !content.document.documentElement.classList.contains("no-logins"),
+ "Should no longer be in no logins view"
+ );
+ let loginList = Cu.waiveXrays(
+ content.document.querySelector("login-list")
+ );
+ Assert.ok(
+ !loginList.classList.contains("no-logins"),
+ "login-list should no longer be in no logins view"
+ );
+ Assert.ok(
+ ContentTaskUtils.is_hidden(loginList._blankLoginListItem),
+ "the blank login list item should be hidden after adding new login"
+ );
+ Assert.ok(
+ !loginList._createLoginButton.disabled,
+ "Create button shouldn't be disabled after exiting create login view"
+ );
+
+ let loginGuid = await ContentTaskUtils.waitForCondition(() => {
+ return loginList._loginGuidsSortedOrder.find(
+ guid => loginList._logins[guid].login.origin == aOriginTuple[1]
+ );
+ }, "Waiting for login to be displayed");
+ Assert.ok(loginGuid, "Expected login found in login-list");
+
+ let loginItem = Cu.waiveXrays(
+ content.document.querySelector("login-item")
+ );
+ Assert.equal(loginItem._login.guid, loginGuid, "login-item should match");
+
+ let { login, listItem } = loginList._logins[loginGuid];
+ Assert.ok(
+ listItem.classList.contains("selected"),
+ "list item should be selected"
+ );
+ Assert.equal(
+ login.origin,
+ aOriginTuple[1],
+ "Stored login should only include the origin of the URL provided during creation"
+ );
+ Assert.equal(
+ login.username,
+ "testuser1",
+ "Stored login should have username provided during creation"
+ );
+ Assert.equal(
+ login.password,
+ "testpass1",
+ "Stored login should have password provided during creation"
+ );
+
+ let usernameInput = loginItem.shadowRoot.querySelector(
+ "input[name='username']"
+ );
+ await ContentTaskUtils.waitForCondition(
+ () => usernameInput.placeholder,
+ "waiting for placeholder to get set"
+ );
+ Assert.ok(
+ usernameInput.placeholder,
+ "there should be a placeholder on the username input when not in edit mode"
+ );
+ });
+
+ if (!canTestOSKeyStoreLogin) {
+ continue;
+ }
+
+ let reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({
+ loginResult: true,
+ });
+ await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => {
+ let loginItem = Cu.waiveXrays(
+ content.document.querySelector("login-item")
+ );
+ let editButton = loginItem.shadowRoot.querySelector(".edit-button");
+ info("clicking on edit button");
+ editButton.click();
+ });
+ info("waiting for oskeystore auth");
+ await reauthObserved;
+
+ await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => {
+ let loginItem = Cu.waiveXrays(
+ content.document.querySelector("login-item")
+ );
+ await ContentTaskUtils.waitForCondition(
+ () => loginItem.dataset.editing,
+ "waiting for 'edit' mode"
+ );
+ info("in edit mode");
+
+ let usernameInput = loginItem.shadowRoot.querySelector(
+ "input[name='username']"
+ );
+ let passwordInput = loginItem.shadowRoot.querySelector(
+ "input[type='password']"
+ );
+ passwordInput.focus();
+ passwordInput = loginItem.shadowRoot.querySelector(
+ "input[name='password']"
+ );
+ usernameInput.value = "testuser2";
+ passwordInput.value = "testpass2";
+
+ let saveChangesButton = loginItem.shadowRoot.querySelector(
+ ".save-changes-button"
+ );
+ info("clicking save changes button");
+ saveChangesButton.click();
+ });
+
+ info("waiting for login to get modified in storage");
+ await storageChangedPromised;
+ info("login modified in storage");
+
+ await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => {
+ let loginList = Cu.waiveXrays(
+ content.document.querySelector("login-list")
+ );
+ let login;
+ await ContentTaskUtils.waitForCondition(() => {
+ login = Object.values(loginList._logins).find(
+ obj => obj.login.origin == aOriginTuple[1]
+ ).login;
+ info(`${login.origin} / ${login.username} / ${login.password}`);
+ return (
+ login.origin == aOriginTuple[1] &&
+ login.username == "testuser2" &&
+ login.password == "testpass2"
+ );
+ }, "waiting for the login to get updated");
+ Assert.equal(
+ login.origin,
+ aOriginTuple[1],
+ "Stored login should only include the origin of the URL provided during creation"
+ );
+ Assert.equal(
+ login.username,
+ "testuser2",
+ "Stored login should have modified username"
+ );
+ Assert.equal(
+ login.password,
+ "testpass2",
+ "Stored login should have modified password"
+ );
+ });
+ }
+
+ await SpecialPowers.spawn(
+ browser,
+ [testCases.length],
+ async testCasesLength => {
+ let loginList = Cu.waiveXrays(
+ content.document.querySelector("login-list")
+ );
+ Assert.equal(
+ loginList._loginGuidsSortedOrder.length,
+ 5,
+ "login list should have a login per testcase"
+ );
+ }
+ );
+});
+
+add_task(async function test_cancel_create_login() {
+ let browser = gBrowser.selectedBrowser;
+ await SpecialPowers.spawn(browser, [], async () => {
+ let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+ Assert.ok(
+ loginList._selectedGuid,
+ "there should be a selected guid before create mode"
+ );
+ Assert.ok(
+ ContentTaskUtils.is_hidden(loginList._blankLoginListItem),
+ "the blank login list item should be hidden before create mode"
+ );
+
+ let createButton = content.document
+ .querySelector("login-list")
+ .shadowRoot.querySelector(".create-login-button");
+ createButton.click();
+
+ Assert.ok(
+ !loginList._selectedGuid,
+ "there should be no selected guid when in create mode"
+ );
+ Assert.ok(
+ ContentTaskUtils.is_visible(loginList._blankLoginListItem),
+ "the blank login list item should be visible in create mode"
+ );
+
+ let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
+ let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
+ cancelButton.click();
+
+ Assert.ok(
+ loginList._selectedGuid,
+ "there should be a selected guid after canceling create mode"
+ );
+ Assert.ok(
+ ContentTaskUtils.is_hidden(loginList._blankLoginListItem),
+ "the blank login list item should be hidden after canceling create mode"
+ );
+ });
+});
+
+add_task(
+ async function test_cancel_create_login_with_filter_showing_one_login() {
+ const browser = gBrowser.selectedBrowser;
+ await SpecialPowers.spawn(browser, [], async () => {
+ const loginList = Cu.waiveXrays(
+ content.document.querySelector("login-list")
+ );
+
+ const loginFilter = Cu.waiveXrays(
+ loginList.shadowRoot.querySelector("login-filter")
+ );
+ loginFilter.value = "bugzilla.mozilla.org";
+ Assert.equal(
+ loginList._list.querySelectorAll(
+ ".login-list-item[data-guid]:not([hidden])"
+ ).length,
+ 1,
+ "filter should have one login showing"
+ );
+ let visibleLoginGuid = loginList.shadowRoot.querySelectorAll(
+ ".login-list-item[data-guid]:not([hidden])"
+ )[0].dataset.guid;
+
+ let createButton = loginList._createLoginButton;
+ createButton.click();
+
+ let loginItem = Cu.waiveXrays(
+ content.document.querySelector("login-item")
+ );
+ let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
+ Assert.ok(
+ ContentTaskUtils.is_visible(cancelButton),
+ "cancel button should be visible in create mode with one login showing"
+ );
+ cancelButton.click();
+
+ Assert.equal(
+ loginFilter.value,
+ "bugzilla.mozilla.org",
+ "login-filter should not be cleared if there was a login in the list"
+ );
+ Assert.equal(
+ loginList.shadowRoot.querySelectorAll(
+ ".login-list-item[data-guid]:not([hidden])"
+ )[0].dataset.guid,
+ visibleLoginGuid,
+ "the same login should still be visible"
+ );
+ });
+ }
+);
+
+add_task(async function test_cancel_create_login_with_logins_filtered_out() {
+ const browser = gBrowser.selectedBrowser;
+ await SpecialPowers.spawn(browser, [], async () => {
+ const loginList = Cu.waiveXrays(
+ content.document.querySelector("login-list")
+ );
+ const loginFilter = Cu.waiveXrays(
+ loginList.shadowRoot.querySelector("login-filter")
+ );
+ loginFilter.value = "XXX-no-logins-should-match-this-XXX";
+ await Promise.resolve();
+ Assert.equal(
+ loginList._list.querySelectorAll(
+ ".login-list-item[data-guid]:not([hidden])"
+ ).length,
+ 0,
+ "filter should have no logins showing"
+ );
+
+ let createButton = loginList._createLoginButton;
+ createButton.click();
+
+ let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
+ let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
+ Assert.ok(
+ ContentTaskUtils.is_visible(cancelButton),
+ "cancel button should be visible in create mode with no logins showing"
+ );
+ cancelButton.click();
+ await Promise.resolve();
+
+ Assert.equal(
+ loginFilter.value,
+ "",
+ "login-filter should be cleared if there were no logins in the list"
+ );
+ let visibleLoginItems = loginList.shadowRoot.querySelectorAll(
+ ".login-list-item[data-guid]:not([hidden])"
+ );
+ Assert.equal(
+ visibleLoginItems.length,
+ 5,
+ "all logins should be visible with blank filter"
+ );
+ Assert.equal(
+ loginList._selectedGuid,
+ visibleLoginItems[0].dataset.guid,
+ "the first item in the list should be selected"
+ );
+ });
+});
+
+add_task(async function test_create_duplicate_login() {
+ if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
+ return;
+ }
+
+ let browser = gBrowser.selectedBrowser;
+ EXPECTED_ERROR_MESSAGE = "This login already exists.";
+ await SpecialPowers.spawn(browser, [], async () => {
+ let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+ let createButton = loginList._createLoginButton;
+ createButton.click();
+
+ let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
+ let originInput = loginItem.shadowRoot.querySelector(
+ "input[name='origin']"
+ );
+ let usernameInput = loginItem.shadowRoot.querySelector(
+ "input[name='username']"
+ );
+ let passwordInput = loginItem.shadowRoot.querySelector(
+ "input[name='password']"
+ );
+ const EXISTING_USERNAME = "testuser2";
+ const EXISTING_ORIGIN = "https://example.com";
+ originInput.value = EXISTING_ORIGIN;
+ usernameInput.value = EXISTING_USERNAME;
+ passwordInput.value = "different password value";
+
+ let saveChangesButton = loginItem.shadowRoot.querySelector(
+ ".save-changes-button"
+ );
+ saveChangesButton.click();
+
+ await ContentTaskUtils.waitForCondition(
+ () => !loginItem._errorMessage.hidden,
+ "waiting until the error message is visible"
+ );
+ let duplicatedGuid = Object.values(loginList._logins).find(
+ v =>
+ v.login.origin == EXISTING_ORIGIN &&
+ v.login.username == EXISTING_USERNAME
+ ).login.guid;
+ Assert.equal(
+ loginItem._errorMessageLink.dataset.errorGuid,
+ duplicatedGuid,
+ "Error message has GUID of existing duplicated login set on it"
+ );
+
+ let confirmationDialog = Cu.waiveXrays(
+ content.document.querySelector("confirmation-dialog")
+ );
+ Assert.ok(
+ confirmationDialog.hidden,
+ "the discard-changes dialog should be hidden before clicking the error-message-text"
+ );
+ loginItem._errorMessageLink.querySelector("a").click();
+ Assert.ok(
+ !confirmationDialog.hidden,
+ "the discard-changes dialog should be visible"
+ );
+ let discardChangesButton =
+ confirmationDialog.shadowRoot.querySelector(".confirm-button");
+ discardChangesButton.click();
+
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ Object.keys(loginItem._login).length > 1 &&
+ loginItem._login.guid == duplicatedGuid,
+ "waiting until the existing duplicated login is selected"
+ );
+ Assert.equal(
+ loginList._selectedGuid,
+ duplicatedGuid,
+ "the duplicated login should be selected in the list"
+ );
+ });
+ EXPECTED_ERROR_MESSAGE = null;
+});