Bug 1837367 - Allow file migrators to show an error message if migration failed for some reason. r=kpatenio,desktop-theme-reviewers

The spec calls for a special-case in the event that file migration
ever fails - the wizard should be sent back to the selection page,
have the associated file migrator still set in the dropdown, and
show an error message.

A later patch in this series will add such an error message for
the bookmarks file migrator.

Depends on D180458

Differential Revision: https://phabricator.services.mozilla.com/D180490
This commit is contained in:
Mike Conley
2023-06-13 14:53:00 +00:00
parent 8700386a38
commit 99e87d9a44
7 changed files with 306 additions and 37 deletions

View File

@@ -62,6 +62,18 @@ class DummyFileMigrator extends FileMigratorBase {
}
}
const { MockFilePicker } = SpecialPowers;
add_setup(async () => {
// 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.
MockFilePicker.init(window);
registerCleanupFunction(() => {
MockFilePicker.cleanup();
});
});
/**
* Tests the flow of selecting a file migrator (in this case,
* the DummyFileMigrator), getting the file picker opened for it,
@@ -97,15 +109,6 @@ add_task(async function test_file_migration() {
return SUCCESS_STATE;
});
// 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;
MockFilePicker.init(window);
registerCleanupFunction(() => {
MockFilePicker.cleanup();
});
let dummyFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
dummyFile.initWithPath(TEST_FILE_PATH);
let filePickerShownPromise = new Promise(resolve => {
@@ -182,4 +185,121 @@ add_task(async function test_file_migration() {
}
}
});
sandbox.restore();
});
/**
* Tests that the migration wizard will go back to the selection page and
* show an error message if the migration for a FileMigrator throws an
* exception.
*/
add_task(async function test_file_migration_error() {
let migrator = new DummyFileMigrator();
let sandbox = sinon.createSandbox();
registerCleanupFunction(() => {
sandbox.restore();
});
// First, use Sinon to insert our DummyFileMigrator as the only available
// file migrator.
sandbox.stub(MigrationUtils, "getFileMigrator").callsFake(() => {
return migrator;
});
sandbox.stub(MigrationUtils, "availableFileMigrators").get(() => {
return [migrator];
});
const ERROR_MESSAGE = "This is my error message";
let migrateStub = sandbox.stub(migrator, "migrate").callsFake(() => {
throw new Error(ERROR_MESSAGE);
});
let dummyFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
dummyFile.initWithPath(TEST_FILE_PATH);
let 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 shadow = wizard.openOrClosedShadowRoot;
let wizardDone = BrowserTestUtils.waitForEvent(
wizard,
"MigrationWizard:DoneMigration"
);
// Now select our DummyFileMigrator from the list.
let selector = shadow.querySelector("#browser-profile-selector");
selector.click();
info("Waiting for panel-list shown");
await new Promise(resolve => {
wizard
.querySelector("panel-list")
.addEventListener("shown", resolve, { once: true });
});
info("Panel list shown. Clicking on panel-item");
let panelItem = wizard.querySelector(
`panel-item[key="${DUMMY_FILEMIGRATOR_KEY}"]`
);
panelItem.click();
// Selecting a file migrator from the selector should automatically
// open the file picker, so we await it here. Once the file is
// selected, migration should begin immediately.
info("Waiting for file picker");
await filePickerShownPromise;
await wizardDone;
Assert.ok(migrateStub.called, "Migrate on DummyFileMigrator was called.");
// At this point, with migration having completed, we should be showing
// the SELECTION page again with the ERROR_MESSAGE displayed.
let deck = shadow.querySelector("#wizard-deck");
await BrowserTestUtils.waitForMutationCondition(
deck,
{ attributeFilter: ["selected-view"] },
() => {
return (
deck.getAttribute("selected-view") ==
"page-" + MigrationWizardConstants.PAGES.SELECTION
);
}
);
Assert.equal(
selector.selectedPanelItem.getAttribute("key"),
DUMMY_FILEMIGRATOR_KEY,
"Should have the file migrator selected."
);
let errorMessageContainer = shadow.querySelector(".file-import-error");
// Using BrowserTestUtils.is_visible to check for the visibility of the
// message seems to throw as it works its way up the ancestry and hits
// the shadowRoot. We'll work around this by making sure that the
// boundingClientRect has a width and height.
let errorRect = errorMessageContainer.getBoundingClientRect();
Assert.ok(
errorRect.width && errorRect.height,
"Should be showing the error message container"
);
let fileImportErrorMessage = shadow.querySelector(
"#file-import-error-message"
).textContent;
Assert.equal(fileImportErrorMessage, ERROR_MESSAGE);
});
sandbox.restore();
});

View File

@@ -1139,6 +1139,60 @@
""
);
});
/**
* Tests that the migration wizard can be put into the selection page after
* a file migrator error and show an error message.
*/
add_task(async function test_file_migrator_error() {
const FILE_MIGRATOR_KEY = "some-file-migrator";
const FILE_IMPORT_ERROR_MESSAGE = "This is an error message";
const MIGRATORS = [
{
key: "some-browser-0",
type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER,
displayName: "Some Browser 0",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "person-2", name: "Person 2" },
},
{
key: "some-browser-1",
type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER,
displayName: "Some Browser 1",
resourceTypes: ["HISTORY", "BOOKMARKS"],
profile: null,
},
{
key: FILE_MIGRATOR_KEY,
type: MigrationWizardConstants.MIGRATOR_TYPES.FILE,
displayName: "Some File Migrator",
resourceTypes: [],
},
];
gWiz.setState({
page: MigrationWizardConstants.PAGES.SELECTION,
migrators: MIGRATORS,
showImportAll: true,
migratorKey: "some-file-migrator",
fileImportErrorMessage: FILE_IMPORT_ERROR_MESSAGE,
});
let errorMessageContainer = gShadowRoot.querySelector(".file-import-error");
ok(!isHidden(errorMessageContainer), "Error message should be shown.");
let errorText = gShadowRoot.querySelector("#file-import-error-message").textContent;
is(errorText, FILE_IMPORT_ERROR_MESSAGE);
gWiz.setState({
page: MigrationWizardConstants.PAGES.SELECTION,
migrators: MIGRATORS,
showImportAll: true,
});
ok(isHidden(errorMessageContainer), "Error message should be hidden.");
errorText = gShadowRoot.querySelector("#file-import-error-message").textContent;
is(errorText, "");
});
</script>
</head>
<body>