summaryrefslogtreecommitdiffstats
path: root/browser/components/aboutlogins/tests/browser/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/aboutlogins/tests/browser/head.js')
-rw-r--r--browser/components/aboutlogins/tests/browser/head.js225
1 files changed, 225 insertions, 0 deletions
diff --git a/browser/components/aboutlogins/tests/browser/head.js b/browser/components/aboutlogins/tests/browser/head.js
new file mode 100644
index 0000000000..2aec0e632a
--- /dev/null
+++ b/browser/components/aboutlogins/tests/browser/head.js
@@ -0,0 +1,225 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let { LoginBreaches } = ChromeUtils.importESModule(
+ "resource:///modules/LoginBreaches.sys.mjs"
+);
+let { RemoteSettings } = ChromeUtils.importESModule(
+ "resource://services-settings/remote-settings.sys.mjs"
+);
+let { _AboutLogins } = ChromeUtils.importESModule(
+ "resource:///actors/AboutLoginsParent.sys.mjs"
+);
+let { OSKeyStoreTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/OSKeyStoreTestUtils.sys.mjs"
+);
+var { LoginTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/LoginTestUtils.sys.mjs"
+);
+
+let nsLoginInfo = new Components.Constructor(
+ "@mozilla.org/login-manager/loginInfo;1",
+ Ci.nsILoginInfo,
+ "init"
+);
+
+let TEST_LOGIN1 = new nsLoginInfo(
+ "https://example.com",
+ "https://example.com",
+ null,
+ "user1",
+ "pass1",
+ "username",
+ "password"
+);
+let TEST_LOGIN2 = new nsLoginInfo(
+ "https://2.example.com",
+ "https://2.example.com",
+ null,
+ "user2",
+ "pass2",
+ "username",
+ "password"
+);
+
+let TEST_LOGIN3 = new nsLoginInfo(
+ "https://breached.example.com",
+ "https://breached.example.com",
+ null,
+ "breachedLogin1",
+ "pass3",
+ "breachedLogin",
+ "password"
+);
+TEST_LOGIN3.QueryInterface(Ci.nsILoginMetaInfo).timePasswordChanged = 123456;
+
+async function addLogin(login) {
+ const result = await Services.logins.addLoginAsync(login);
+ registerCleanupFunction(() => {
+ let matchData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag2
+ );
+ matchData.setPropertyAsAUTF8String("guid", result.guid);
+
+ let logins = Services.logins.searchLogins(matchData);
+ if (!logins.length) {
+ return;
+ }
+ // Use the login that was returned from searchLogins
+ // in case the initial login object was changed by the test code,
+ // since removeLogin makes sure that the login argument exactly
+ // matches the login that it will be removing.
+ Services.logins.removeLogin(logins[0]);
+ });
+ return result;
+}
+
+let EXPECTED_BREACH = null;
+let EXPECTED_ERROR_MESSAGE = null;
+add_setup(async function setup_head() {
+ const db = RemoteSettings(LoginBreaches.REMOTE_SETTINGS_COLLECTION).db;
+ if (EXPECTED_BREACH) {
+ await db.create(EXPECTED_BREACH, {
+ useRecordId: true,
+ });
+ }
+ await db.importChanges({}, Date.now());
+ if (EXPECTED_BREACH) {
+ await RemoteSettings(LoginBreaches.REMOTE_SETTINGS_COLLECTION).emit(
+ "sync",
+ { data: { current: [EXPECTED_BREACH] } }
+ );
+ }
+
+ SpecialPowers.registerConsoleListener(function onConsoleMessage(msg) {
+ if (msg.isWarning || !msg.errorMessage) {
+ // Ignore warnings and non-errors.
+ return;
+ }
+
+ if (msg.errorMessage.includes('Unknown event: ["jsonfile", "load"')) {
+ // Ignore telemetry errors from JSONFile.sys.mjs.
+ return;
+ }
+
+ if (
+ msg.errorMessage == "Refreshing device list failed." ||
+ msg.errorMessage == "Skipping device list refresh; not signed in"
+ ) {
+ // Ignore errors from browser-sync.js.
+ return;
+ }
+ if (
+ msg.errorMessage.includes(
+ "ReferenceError: MigrationWizard is not defined"
+ )
+ ) {
+ // todo(Bug 1587237): Ignore error when loading the Migration Wizard in automation.
+ return;
+ }
+ if (
+ msg.errorMessage.includes("Error detecting Chrome profiles") ||
+ msg.errorMessage.includes(
+ "Library/Application Support/Chromium/Local State (No such file or directory)"
+ ) ||
+ msg.errorMessage.includes(
+ "Library/Application Support/Google/Chrome/Local State (No such file or directory)"
+ )
+ ) {
+ // Ignore errors that can occur when the migrator is looking for a
+ // Chrome/Chromium profile
+ return;
+ }
+ if (msg.errorMessage.includes("Can't find profile directory.")) {
+ // Ignore error messages for no profile found in old XULStore.jsm
+ return;
+ }
+ if (msg.errorMessage.includes("Error reading typed URL history")) {
+ // The Migrator when opened can log this exception if there is no Edge
+ // history on the machine.
+ return;
+ }
+ if (msg.errorMessage.includes(EXPECTED_ERROR_MESSAGE)) {
+ return;
+ }
+ if (msg.errorMessage == "FILE_FORMAT_ERROR") {
+ // Ignore errors handled by the error message dialog.
+ return;
+ }
+ if (
+ msg.errorMessage ==
+ "NotFoundError: No such JSWindowActor 'MarionetteEvents'"
+ ) {
+ // Ignore MarionetteEvents error (Bug 1730837, Bug 1710079).
+ return;
+ }
+ Assert.ok(false, msg.message || msg.errorMessage);
+ });
+
+ registerCleanupFunction(async () => {
+ EXPECTED_ERROR_MESSAGE = null;
+ await db.clear();
+ Services.telemetry.clearEvents();
+ SpecialPowers.postConsoleSentinel();
+ });
+});
+
+/**
+ * Waits for the primary password prompt and performs an action.
+ * @param {string} action Set to "authenticate" to log in or "cancel" to
+ * close the dialog without logging in.
+ */
+function waitForMPDialog(action, aWindow = window) {
+ const BRAND_BUNDLE = Services.strings.createBundle(
+ "chrome://branding/locale/brand.properties"
+ );
+ const BRAND_FULL_NAME = BRAND_BUNDLE.GetStringFromName("brandFullName");
+ let dialogShown = TestUtils.topicObserved("common-dialog-loaded");
+ return dialogShown.then(function ([subject]) {
+ let dialog = subject.Dialog;
+ let expected = "Password Required - " + BRAND_FULL_NAME;
+ Assert.equal(
+ dialog.args.title,
+ expected,
+ "Dialog is the Primary Password dialog"
+ );
+ if (action == "authenticate") {
+ SpecialPowers.wrap(dialog.ui.password1Textbox).setUserInput(
+ LoginTestUtils.primaryPassword.primaryPassword
+ );
+ dialog.ui.button0.click();
+ } else if (action == "cancel") {
+ dialog.ui.button1.click();
+ }
+ return BrowserTestUtils.waitForEvent(aWindow, "DOMModalDialogClosed");
+ });
+}
+
+/**
+ * Allows for tests to reset the MP auth expiration and
+ * return a promise that will resolve after the MP dialog has
+ * been presented.
+ *
+ * @param {string} action Set to "authenticate" to log in or "cancel" to
+ * close the dialog without logging in.
+ * @returns {Promise} Resolves after the MP dialog has been presented and actioned upon
+ */
+function forceAuthTimeoutAndWaitForMPDialog(action, aWindow = window) {
+ const AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes (duplicated from AboutLoginsParent.sys.mjs)
+ _AboutLogins._authExpirationTime -= AUTH_TIMEOUT_MS + 1;
+ return waitForMPDialog(action, aWindow);
+}
+
+/**
+ * Allows for tests to reset the OS auth expiration and
+ * return a promise that will resolve after the OS auth dialog has
+ * been presented.
+ *
+ * @param {bool} loginResult True if the auth prompt should pass, otherwise false will fail
+ * @returns {Promise} Resolves after the OS auth dialog has been presented
+ */
+function forceAuthTimeoutAndWaitForOSKeyStoreLogin({ loginResult }) {
+ const AUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes (duplicated from AboutLoginsParent.sys.mjs)
+ _AboutLogins._authExpirationTime -= AUTH_TIMEOUT_MS + 1;
+ return OSKeyStoreTestUtils.waitForOSKeyStoreLogin(loginResult);
+}