From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../tests/browser/browser_safari_passwords.js | 401 +++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 browser/components/migration/tests/browser/browser_safari_passwords.js (limited to 'browser/components/migration/tests/browser/browser_safari_passwords.js') diff --git a/browser/components/migration/tests/browser/browser_safari_passwords.js b/browser/components/migration/tests/browser/browser_safari_passwords.js new file mode 100644 index 0000000000..299695f9e3 --- /dev/null +++ b/browser/components/migration/tests/browser/browser_safari_passwords.js @@ -0,0 +1,401 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { SafariProfileMigrator } = ChromeUtils.importESModule( + "resource:///modules/SafariProfileMigrator.sys.mjs" +); +const { LoginCSVImport } = ChromeUtils.importESModule( + "resource://gre/modules/LoginCSVImport.sys.mjs" +); + +const TEST_FILE_PATH = getTestFilePath("dummy_file.csv"); + +// We use MockFilePicker to simulate a native file picker, and prepare it +// to return a dummy file pointed at TEST_FILE_PATH. The file at +// TEST_FILE_PATH is not required (nor expected) to exist. +const { MockFilePicker } = SpecialPowers; + +add_setup(async function () { + MockFilePicker.init(window); + registerCleanupFunction(() => { + MockFilePicker.cleanup(); + }); + await SpecialPowers.pushPrefEnv({ + set: [["signon.management.page.fileImport.enabled", true]], + }); +}); + +/** + * A helper function that does most of the heavy lifting for the tests in + * this file. Specfically, it takes care of: + * + * 1. Stubbing out the various hunks of the SafariProfileMigrator in order + * to simulate a migration without actually performing one, since the + * migrator itself isn't being tested here. + * 2. Stubbing out parts of MigrationUtils and LoginCSVImport to have a + * consistent reporting on how many things are imported. + * 3. Setting up the MockFilePicker if expectsFilePicker is true to return + * the TEST_FILE_PATH. + * 4. Opens up the migration wizard, and chooses to import both BOOKMARKS + * and PASSWORDS, and then clicks "Import". + * 5. Waits for the migration wizard to show the Safari password import + * instructions. + * 6. Runs taskFn + * 7. Closes the migration dialog. + * + * @param {boolean} expectsFilePicker + * True if the MockFilePicker should be set up to return TEST_FILE_PATH. + * @param {boolean} migrateBookmarks + * True if bookmarks should be migrated alongside passwords. If not, only + * passwords will be migrated. + * @param {Function} taskFn + * An asynchronous function that takes the following parameters in this + * order: + * + * {Element} wizard + * The opened migration wizard + * {Promise} filePickerShownPromise + * A Promise that resolves once the MockFilePicker has closed. This is + * undefined if expectsFilePicker was false. + * {object} importFromCSVStub + * The Sinon stub object for LoginCSVImport.importFromCSV. This can be + * used to check to see whether it was called. + * {Promise} didMigration + * A Promise that resolves to true once the migration completes. + * {Promise} wizardDone + * A Promise that resolves once the migration wizard reports that a + * migration has completed. + * @returns {Promise} + */ +async function testSafariPasswordHelper( + expectsFilePicker, + migrateBookmarks, + taskFn +) { + let sandbox = sinon.createSandbox(); + registerCleanupFunction(() => { + sandbox.restore(); + }); + + let safariMigrator = new SafariProfileMigrator(); + sandbox.stub(MigrationUtils, "getMigrator").resolves(safariMigrator); + + // We're not testing the permission flow here, so let's pretend that we + // always have permission to read resources from the disk. + sandbox + .stub(SafariProfileMigrator.prototype, "hasPermissions") + .resolves(true); + + // Have the migrator claim that only BOOKMARKS are only available. + sandbox + .stub(SafariProfileMigrator.prototype, "getMigrateData") + .resolves(MigrationUtils.resourceTypes.BOOKMARKS); + + let migrateStub; + let didMigration = new Promise(resolve => { + migrateStub = sandbox + .stub(SafariProfileMigrator.prototype, "migrate") + .callsFake((aResourceTypes, aStartup, aProfile, aProgressCallback) => { + if (!migrateBookmarks) { + Assert.ok( + false, + "Should not have called migrate when only migrating Safari passwords." + ); + } + + Assert.ok( + !aStartup, + "Migrator should not have been called as a startup migration." + ); + Assert.ok( + aResourceTypes & MigrationUtils.resourceTypes.BOOKMARKS, + "Should have requested to migrate the BOOKMARKS resource." + ); + Assert.ok( + !(aResourceTypes & MigrationUtils.resourceTypes.PASSWORDS), + "Should not have requested to migrate the PASSWORDS resource." + ); + + aProgressCallback(MigrationUtils.resourceTypes.BOOKMARKS); + Services.obs.notifyObservers(null, "Migration:Ended"); + resolve(); + }); + }); + + // We'll pretend we added EXPECTED_QUANTITY passwords from the Safari + // password file. + let results = []; + for (let i = 0; i < EXPECTED_QUANTITY; ++i) { + results.push({ result: "added" }); + } + let importFromCSVStub = sandbox + .stub(LoginCSVImport, "importFromCSV") + .resolves(results); + + sandbox.stub(MigrationUtils, "_importQuantities").value({ + bookmarks: EXPECTED_QUANTITY, + }); + + let filePickerShownPromise; + + if (expectsFilePicker) { + MockFilePicker.reset(); + + let dummyFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + dummyFile.initWithPath(TEST_FILE_PATH); + filePickerShownPromise = new Promise(resolve => { + MockFilePicker.showCallback = () => { + Assert.ok(true, "Filepicker shown."); + MockFilePicker.setFiles([dummyFile]); + resolve(); + }; + }); + MockFilePicker.returnValue = MockFilePicker.returnOK; + } + + await withMigrationWizardDialog(async prefsWin => { + let dialogBody = prefsWin.document.body; + let wizard = dialogBody.querySelector("migration-wizard"); + let wizardDone = BrowserTestUtils.waitForEvent( + wizard, + "MigrationWizard:DoneMigration" + ); + + let shadow = wizard.openOrClosedShadowRoot; + + info("Choosing Safari"); + let panelItem = wizard.querySelector( + `panel-item[key="${SafariProfileMigrator.key}"]` + ); + panelItem.click(); + + let resourceTypeList = shadow.querySelector("#resource-type-list"); + + // Let's choose whether to import BOOKMARKS first. + let bookmarksNode = resourceTypeList.querySelector( + `label[data-resource-type="${MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS}"]` + ); + bookmarksNode.control.checked = migrateBookmarks; + + // Let's make sure that PASSWORDS is displayed despite the migrator only + // (currently) returning BOOKMARKS as an available resource to migrate. + let passwordsNode = resourceTypeList.querySelector( + `label[data-resource-type="${MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS}"]` + ); + Assert.ok( + !passwordsNode.hidden, + "PASSWORDS should be available to import from." + ); + passwordsNode.control.checked = true; + + let deck = shadow.querySelector("#wizard-deck"); + let switchedToSafariPermissionPage = + BrowserTestUtils.waitForMutationCondition( + deck, + { attributeFilter: ["selected-view"] }, + () => { + return ( + deck.getAttribute("selected-view") == + "page-" + MigrationWizardConstants.PAGES.SAFARI_PASSWORD_PERMISSION + ); + } + ); + + let importButton = shadow.querySelector("#import"); + importButton.click(); + await switchedToSafariPermissionPage; + Assert.ok(true, "Went to Safari permission page after attempting import."); + + await taskFn( + wizard, + filePickerShownPromise, + importFromCSVStub, + didMigration, + migrateStub, + wizardDone + ); + + let dialog = prefsWin.document.querySelector("#migrationWizardDialog"); + let doneButton = shadow.querySelector( + "div[name='page-progress'] .done-button" + ); + let dialogClosed = BrowserTestUtils.waitForEvent(dialog, "close"); + + doneButton.click(); + await dialogClosed; + }); + + sandbox.restore(); + MockFilePicker.reset(); +} + +/** + * Tests the flow of importing passwords from Safari via an + * exported CSV file. + */ +add_task(async function test_safari_password_do_import() { + await testSafariPasswordHelper( + true, + true, + async ( + wizard, + filePickerShownPromise, + importFromCSVStub, + didMigration, + migrateStub, + wizardDone + ) => { + let shadow = wizard.openOrClosedShadowRoot; + let safariPasswordImportSelect = shadow.querySelector( + "#safari-password-import-select" + ); + safariPasswordImportSelect.click(); + await filePickerShownPromise; + Assert.ok(true, "File picker was shown."); + + await didMigration; + Assert.ok(importFromCSVStub.called, "Importing from CSV was called."); + + await wizardDone; + + assertQuantitiesShown(wizard, [ + MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS, + MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS, + ]); + } + ); +}); + +/** + * Tests that only passwords get imported if the user only opts + * to import passwords, and that nothing else gets imported. + */ +add_task(async function test_safari_password_only_do_import() { + await testSafariPasswordHelper( + true, + false, + async ( + wizard, + filePickerShownPromise, + importFromCSVStub, + didMigration, + migrateStub, + wizardDone + ) => { + let shadow = wizard.openOrClosedShadowRoot; + let safariPasswordImportSelect = shadow.querySelector( + "#safari-password-import-select" + ); + safariPasswordImportSelect.click(); + await filePickerShownPromise; + Assert.ok(true, "File picker was shown."); + + await wizardDone; + + assertQuantitiesShown(wizard, [ + MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS, + ]); + + Assert.ok(importFromCSVStub.called, "Importing from CSV was called."); + Assert.ok( + !migrateStub.called, + "SafariProfileMigrator.migrate was not called." + ); + } + ); +}); + +/** + * Tests that the user can skip importing passwords from Safari. + */ +add_task(async function test_safari_password_skip() { + await testSafariPasswordHelper( + false, + true, + async ( + wizard, + filePickerShownPromise, + importFromCSVStub, + didMigration, + migrateStub, + wizardDone + ) => { + let shadow = wizard.openOrClosedShadowRoot; + let safariPasswordImportSkip = shadow.querySelector( + "#safari-password-import-skip" + ); + safariPasswordImportSkip.click(); + + await didMigration; + Assert.ok(!MockFilePicker.shown, "Never showed the file picker."); + Assert.ok( + !importFromCSVStub.called, + "Importing from CSV was never called." + ); + + await wizardDone; + + assertQuantitiesShown(wizard, [ + MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS, + ]); + } + ); +}); + +/** + * Tests that importing from passwords for Safari doesn't exist if + * signon.management.page.fileImport.enabled is false. + */ +add_task(async function test_safari_password_disabled() { + await SpecialPowers.pushPrefEnv({ + set: [["signon.management.page.fileImport.enabled", false]], + }); + + let sandbox = sinon.createSandbox(); + registerCleanupFunction(() => { + sandbox.restore(); + }); + + let safariMigrator = new SafariProfileMigrator(); + sandbox.stub(MigrationUtils, "getMigrator").resolves(safariMigrator); + + // We're not testing the permission flow here, so let's pretend that we + // always have permission to read resources from the disk. + sandbox + .stub(SafariProfileMigrator.prototype, "hasPermissions") + .resolves(true); + + // Have the migrator claim that only BOOKMARKS are only available. + sandbox + .stub(SafariProfileMigrator.prototype, "getMigrateData") + .resolves(MigrationUtils.resourceTypes.BOOKMARKS); + + await withMigrationWizardDialog(async prefsWin => { + let dialogBody = prefsWin.document.body; + let wizard = dialogBody.querySelector("migration-wizard"); + + let shadow = wizard.openOrClosedShadowRoot; + + info("Choosing Safari"); + let panelItem = wizard.querySelector( + `panel-item[key="${SafariProfileMigrator.key}"]` + ); + panelItem.click(); + + let resourceTypeList = shadow.querySelector("#resource-type-list"); + + // Let's make sure that PASSWORDS is displayed despite the migrator only + // (currently) returning BOOKMARKS as an available resource to migrate. + let passwordsNode = resourceTypeList.querySelector( + `label[data-resource-type="${MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS}"]` + ); + Assert.ok( + passwordsNode.hidden, + "PASSWORDS should not be available to import from." + ); + }); + + await SpecialPowers.popPrefEnv(); +}); -- cgit v1.2.3