mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 10:00:54 +00:00
Bug 1812704 - Wire up the new MigrationWizard to MigrationUtils to let it perform migrations. r=NeilDeakin
Differential Revision: https://phabricator.services.mozilla.com/D167998
This commit is contained in:
parent
f50327e5e8
commit
c73afb6eef
@ -605,6 +605,7 @@ let JSWINDOWACTORS = {
|
||||
esModuleURI: "resource:///actors/MigrationWizardChild.sys.mjs",
|
||||
events: {
|
||||
"MigrationWizard:Init": { wantUntrusted: true },
|
||||
"MigrationWizard:BeginMigration": { wantsUntrusted: true },
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
/* 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/. */
|
||||
|
||||
import { MigratorBase } from "resource:///modules/MigratorBase.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
|
||||
});
|
||||
|
||||
/**
|
||||
* A stub of a migrator used for automated testing only.
|
||||
*/
|
||||
export class InternalTestingProfileMigrator extends MigratorBase {
|
||||
static get key() {
|
||||
return "internal-testing";
|
||||
}
|
||||
|
||||
static get displayNameL10nID() {
|
||||
return "Internal Testing Migrator";
|
||||
}
|
||||
|
||||
// We will create a single MigratorResource for each resource type that
|
||||
// just immediately reports a successful migration.
|
||||
getResources() {
|
||||
return Object.values(lazy.MigrationUtils.resourceTypes).map(type => {
|
||||
return {
|
||||
type,
|
||||
migrate: callback => {
|
||||
callback(true /* success */);
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the MigratorResources that are normally cached by the
|
||||
* MigratorBase parent class after a call to getResources. This
|
||||
* allows our automated tests to try different resource availability
|
||||
* scenarios between tests.
|
||||
*/
|
||||
flushResourceCache() {
|
||||
this._resourcesByProfile = null;
|
||||
}
|
||||
}
|
@ -103,6 +103,11 @@ const MIGRATOR_MODULES = Object.freeze({
|
||||
moduleURI: "resource:///modules/ChromeProfileMigrator.sys.mjs",
|
||||
platforms: ["macosx", "win"],
|
||||
},
|
||||
|
||||
InternalTestingProfileMigrator: {
|
||||
moduleURI: "resource:///modules/InternalTestingProfileMigrator.sys.mjs",
|
||||
platforms: ["linux", "macosx", "win"],
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -21,18 +21,48 @@ export class MigrationWizardChild extends JSWindowActorChild {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async handleEvent(event) {
|
||||
if (event.type == "MigrationWizard:Init") {
|
||||
this.#wizardEl = event.target;
|
||||
let migrators = await this.sendQuery("GetAvailableMigrators");
|
||||
switch (event.type) {
|
||||
case "MigrationWizard:Init": {
|
||||
this.#wizardEl = event.target;
|
||||
let migrators = await this.sendQuery("GetAvailableMigrators");
|
||||
this.setComponentState({
|
||||
migrators,
|
||||
page: MigrationWizardConstants.PAGES.SELECTION,
|
||||
});
|
||||
this.#wizardEl.dispatchEvent(
|
||||
new this.contentWindow.CustomEvent("MigrationWizard:Ready", {
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "MigrationWizard:BeginMigration": {
|
||||
await this.sendQuery("Migrate", event.detail);
|
||||
this.#wizardEl.dispatchEvent(
|
||||
new this.contentWindow.CustomEvent("MigrationWizard:DoneMigration", {
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General message handler function for messages received from the
|
||||
* associated MigrationWizardParent JSWindowActor.
|
||||
*
|
||||
* @param {ReceiveMessageArgument} message
|
||||
* The message received from the MigrationWizardParent.
|
||||
*/
|
||||
receiveMessage(message) {
|
||||
if (message.name == "UpdateProgress") {
|
||||
let progress = message.data;
|
||||
this.setComponentState({
|
||||
migrators,
|
||||
page: MigrationWizardConstants.PAGES.SELECTION,
|
||||
page: MigrationWizardConstants.PAGES.PROGRESS,
|
||||
progress,
|
||||
});
|
||||
this.#wizardEl.dispatchEvent(
|
||||
new this.contentWindow.CustomEvent("MigrationWizard:Ready", {
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,12 @@ XPCOMUtils.defineLazyGetter(lazy, "gFluentStrings", function() {
|
||||
]);
|
||||
});
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
InternalTestingProfileMigrator:
|
||||
"resource:///modules/InternalTestingProfileMigrator.sys.mjs",
|
||||
PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs",
|
||||
});
|
||||
|
||||
/**
|
||||
* This class is responsible for communicating with MigrationUtils to do the
|
||||
* actual heavy-lifting of any kinds of migration work, based on messages from
|
||||
@ -44,22 +50,131 @@ export class MigrationWizardParent extends JSWindowActorParent {
|
||||
);
|
||||
}
|
||||
|
||||
if (message.name == "GetAvailableMigrators") {
|
||||
let availableMigrators = [];
|
||||
for (const key of MigrationUtils.availableMigratorKeys) {
|
||||
availableMigrators.push(this.#getMigratorAndProfiles(key));
|
||||
switch (message.name) {
|
||||
case "GetAvailableMigrators": {
|
||||
let availableMigrators = [];
|
||||
for (const key of MigrationUtils.availableMigratorKeys) {
|
||||
availableMigrators.push(this.#getMigratorAndProfiles(key));
|
||||
}
|
||||
// Wait for all getMigrator calls to resolve in parallel
|
||||
let results = await Promise.all(availableMigrators);
|
||||
// Each migrator might give us a single MigratorProfileInstance,
|
||||
// or an Array of them, so we flatten them out and filter out
|
||||
// any that ended up going wrong and returning null from the
|
||||
// #getMigratorAndProfiles call.
|
||||
return results.flat().filter(result => result);
|
||||
}
|
||||
|
||||
case "Migrate": {
|
||||
await this.#doMigration(
|
||||
message.data.key,
|
||||
message.data.resourceTypes,
|
||||
message.data.profile
|
||||
);
|
||||
}
|
||||
// Wait for all getMigrator calls to resolve in parallel
|
||||
let results = await Promise.all(availableMigrators);
|
||||
// Each migrator might give us a single MigratorProfileInstance,
|
||||
// or an Array of them, so we flatten them out and filter out
|
||||
// any that ended up going wrong and returning null from the
|
||||
// #getMigratorAndProfiles call.
|
||||
return results.flat().filter(result => result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls into MigrationUtils to perform a migration given the parameters
|
||||
* sent via the wizard.
|
||||
*
|
||||
* @param {string} migratorKey
|
||||
* The unique identification key for a migrator.
|
||||
* @param {string[]} resourceTypes
|
||||
* An array of strings, where each string represents a resource type
|
||||
* that can be imported for this migrator and profile. The strings
|
||||
* should be one of the key values of
|
||||
* MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.
|
||||
* @param {object|null} profileObj
|
||||
* A description of the user profile that the migrator can import.
|
||||
* @param {string} profileObj.id
|
||||
* A unique ID for the user profile.
|
||||
* @param {string} profileObj.name
|
||||
* The display name for the user profile.
|
||||
* @returns {Promise<undefined>}
|
||||
* Resolves once the Migration:Ended observer notification has fired.
|
||||
*/
|
||||
async #doMigration(migratorKey, resourceTypes, profileObj) {
|
||||
let migrator = await MigrationUtils.getMigrator(migratorKey);
|
||||
let resourceTypesToMigrate = 0;
|
||||
let progress = {};
|
||||
|
||||
for (let resourceType of resourceTypes) {
|
||||
resourceTypesToMigrate |= MigrationUtils.resourceTypes[resourceType];
|
||||
progress[resourceType] = {
|
||||
inProgress: true,
|
||||
message: "",
|
||||
};
|
||||
}
|
||||
|
||||
this.sendAsyncMessage("UpdateProgress", progress);
|
||||
|
||||
let observer = {
|
||||
observe: (subject, topic, resourceType) => {
|
||||
if (topic == "Migration:Ended") {
|
||||
observer.migrationDefer.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unfortunately, MigratorBase hands us the string representation
|
||||
// of the numeric value of the MigrationUtils.resourceType from this
|
||||
// observer. For now, we'll just do a look-up to map it to the right
|
||||
// constant.
|
||||
|
||||
let resourceTypeNum = parseInt(resourceType, 10);
|
||||
let foundResourceTypeName;
|
||||
for (let resourceTypeName in MigrationUtils.resourceTypes) {
|
||||
if (
|
||||
MigrationUtils.resourceTypes[resourceTypeName] == resourceTypeNum
|
||||
) {
|
||||
foundResourceTypeName = resourceTypeName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundResourceTypeName) {
|
||||
console.error(
|
||||
"Could not find a resource type for value: ",
|
||||
resourceType
|
||||
);
|
||||
} else {
|
||||
// For now, we ignore errors in migration, and simply display
|
||||
// the success state.
|
||||
progress[foundResourceTypeName] = {
|
||||
inProgress: false,
|
||||
message: "",
|
||||
};
|
||||
this.sendAsyncMessage("UpdateProgress", progress);
|
||||
}
|
||||
},
|
||||
|
||||
migrationDefer: lazy.PromiseUtils.defer(),
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference,
|
||||
]),
|
||||
};
|
||||
Services.obs.addObserver(observer, "Migration:ItemAfterMigrate", true);
|
||||
Services.obs.addObserver(observer, "Migration:ItemError", true);
|
||||
Services.obs.addObserver(observer, "Migration:Ended", true);
|
||||
|
||||
try {
|
||||
// The MigratorBase API is somewhat awkward - we must wait for an observer
|
||||
// notification with topic Migration:Ended to know when the migration
|
||||
// finishes.
|
||||
migrator.migrate(resourceTypesToMigrate, false, profileObj);
|
||||
await observer.migrationDefer.promise;
|
||||
} finally {
|
||||
Services.obs.removeObserver(observer, "Migration:ItemAfterMigrate");
|
||||
Services.obs.removeObserver(observer, "Migration:ItemError");
|
||||
Services.obs.removeObserver(observer, "Migration:Ended");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} MigratorProfileInstance
|
||||
* An object that describes a single user profile (or the default
|
||||
@ -146,11 +261,22 @@ export class MigrationWizardParent extends JSWindowActorParent {
|
||||
}
|
||||
}
|
||||
|
||||
let displayName;
|
||||
|
||||
if (migrator.constructor.key == lazy.InternalTestingProfileMigrator.key) {
|
||||
// In the case of the InternalTestingProfileMigrator, which is never seen
|
||||
// by users outside of testing, we don't make our localization community
|
||||
// localize it's display name, and just display the ID instead.
|
||||
displayName = migrator.constructor.displayNameL10nID;
|
||||
} else {
|
||||
displayName = await lazy.gFluentStrings.formatValue(
|
||||
migrator.constructor.displayNameL10nID
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
key: migrator.constructor.key,
|
||||
displayName: await lazy.gFluentStrings.formatValue(
|
||||
migrator.constructor.displayNameL10nID
|
||||
),
|
||||
displayName,
|
||||
resourceTypes: availableResourceTypes,
|
||||
profile: profileObj,
|
||||
};
|
||||
|
@ -30,10 +30,17 @@ export const MigrationWizardConstants = Object.freeze({
|
||||
|
||||
// COOKIE resource migration is going to be removed, so we don't include
|
||||
// it here.
|
||||
HISTORY: "history",
|
||||
FORMDATA: "form-autofill",
|
||||
PASSWORDS: "logins-and-passwords",
|
||||
BOOKMARKS: "bookmarks",
|
||||
|
||||
// This is a little silly, but JavaScript doesn't have a notion of
|
||||
// enums. The advantage of this set-up is that these constants values
|
||||
// can be used to access the MigrationUtils.resourceTypes constants,
|
||||
// are reasonably readable as DOM attributes, and easily serialize /
|
||||
// deserialize.
|
||||
HISTORY: "HISTORY",
|
||||
FORMDATA: "FORMDATA",
|
||||
PASSWORDS: "PASSWORDS",
|
||||
BOOKMARKS: "BOOKMARKS",
|
||||
|
||||
// We don't yet show OTHERDATA or SESSION resources.
|
||||
}),
|
||||
});
|
||||
|
@ -19,6 +19,7 @@ export class MigrationWizard extends HTMLElement {
|
||||
#browserProfileSelector = null;
|
||||
#resourceTypeList = null;
|
||||
#shadowRoot = null;
|
||||
#importButton = null;
|
||||
|
||||
static get markup() {
|
||||
return `
|
||||
@ -31,47 +32,47 @@ export class MigrationWizard extends HTMLElement {
|
||||
<select id="browser-profile-selector">
|
||||
</select>
|
||||
<fieldset id="resource-type-list">
|
||||
<label id="bookmarks">
|
||||
<label id="bookmarks" data-resource-type="BOOKMARKS"/>
|
||||
<input type="checkbox"/><span data-l10n-id="migration-bookmarks-option-label"></span>
|
||||
</label>
|
||||
<label id="logins-and-passwords">
|
||||
<label id="logins-and-passwords" data-resource-type="PASSWORDS">
|
||||
<input type="checkbox"/><span data-l10n-id="migration-logins-and-passwords-option-label"></span>
|
||||
</label>
|
||||
<label id="history">
|
||||
<label id="history" data-resource-type="HISTORY">
|
||||
<input type="checkbox"/><span data-l10n-id="migration-history-option-label"></span>
|
||||
</label>
|
||||
<label id="form-autofill">
|
||||
<label id="form-autofill" data-resource-type="FORMDATA">
|
||||
<input type="checkbox"/><span data-l10n-id="migration-form-autofill-option-label"></span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<moz-button-group class="buttons">
|
||||
<button class="cancel-close" data-l10n-id="migration-cancel-button-label"></button>
|
||||
<button class="primary" data-l10n-id="migration-import-button-label"></button>
|
||||
<button id="import" class="primary" data-l10n-id="migration-import-button-label"></button>
|
||||
</moz-button-group>
|
||||
</div>
|
||||
|
||||
<div name="page-progress">
|
||||
<h3 id="progress-header" data-l10n-id="migration-wizard-progress-header"></h3>
|
||||
<div class="resource-progress">
|
||||
<div data-resource-type="bookmarks" class="resource-progress-group">
|
||||
<div data-resource-type="BOOKMARKS" class="resource-progress-group">
|
||||
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
|
||||
<span data-l10n-id="migration-bookmarks-option-label"></span>
|
||||
<span class="success-text"> </span>
|
||||
</div>
|
||||
|
||||
<div data-resource-type="logins-and-passwords" class="resource-progress-group">
|
||||
<div data-resource-type="PASSWORDS" class="resource-progress-group">
|
||||
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
|
||||
<span data-l10n-id="migration-logins-and-passwords-option-label"></span>
|
||||
<span class="success-text"> </span>
|
||||
</div>
|
||||
|
||||
<div data-resource-type="history" class="resource-progress-group">
|
||||
<div data-resource-type="HISTORY" class="resource-progress-group">
|
||||
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
|
||||
<span data-l10n-id="migration-history-option-label"></span>
|
||||
<span class="success-text"> </span>
|
||||
</div>
|
||||
|
||||
<div data-resource-type="form-autofill" class="resource-progress-group">
|
||||
<div data-resource-type="FORMDATA" class="resource-progress-group">
|
||||
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
|
||||
<span data-l10n-id="migration-form-autofill-option-label"></span>
|
||||
<span class="success-text"> </span>
|
||||
@ -129,6 +130,9 @@ export class MigrationWizard extends HTMLElement {
|
||||
button.addEventListener("click", this);
|
||||
}
|
||||
|
||||
this.#importButton = shadow.querySelector("#import");
|
||||
this.#importButton.addEventListener("click", this);
|
||||
|
||||
this.#browserProfileSelector.addEventListener("change", this);
|
||||
this.#resourceTypeList = shadow.querySelector("#resource-type-list");
|
||||
this.#shadowRoot = shadow;
|
||||
@ -180,10 +184,8 @@ export class MigrationWizard extends HTMLElement {
|
||||
}
|
||||
|
||||
for (let resourceType of resourceTypes) {
|
||||
let resourceID =
|
||||
MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES[resourceType];
|
||||
let resourceLabel = this.#resourceTypeList.querySelector(
|
||||
`#${resourceID}`
|
||||
`label[data-resource-type="${resourceType}"]`
|
||||
);
|
||||
if (resourceLabel) {
|
||||
resourceLabel.hidden = false;
|
||||
@ -324,10 +326,45 @@ export class MigrationWizard extends HTMLElement {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the current state of the selections page and bundles them
|
||||
* up into a MigrationWizard:BeginMigration event that can be handled
|
||||
* externally to perform the actual migration.
|
||||
*/
|
||||
#doImport() {
|
||||
let option = this.#browserProfileSelector.options[
|
||||
this.#browserProfileSelector.selectedIndex
|
||||
];
|
||||
let key = option.value;
|
||||
let profile = option.profile;
|
||||
let resourceTypeFields = this.#resourceTypeList.querySelectorAll(
|
||||
"label[data-resource-type]"
|
||||
);
|
||||
let resourceTypes = [];
|
||||
for (let resourceTypeField of resourceTypeFields) {
|
||||
if (resourceTypeField.control.checked) {
|
||||
resourceTypes.push(resourceTypeField.dataset.resourceType);
|
||||
}
|
||||
}
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("MigrationWizard:BeginMigration", {
|
||||
bubbles: true,
|
||||
detail: {
|
||||
key,
|
||||
profile,
|
||||
resourceTypes,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "click": {
|
||||
if (event.target.classList.contains("cancel-close")) {
|
||||
if (event.target == this.#importButton) {
|
||||
this.#doImport();
|
||||
} else if (event.target.classList.contains("cancel-close")) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("MigrationWizard:Close", { bubbles: true })
|
||||
);
|
||||
|
@ -26,6 +26,7 @@ EXTRA_JS_MODULES += [
|
||||
"ChromeMigrationUtils.sys.mjs",
|
||||
"ChromeProfileMigrator.sys.mjs",
|
||||
"FirefoxProfileMigrator.sys.mjs",
|
||||
"InternalTestingProfileMigrator.sys.mjs",
|
||||
"MigrationUtils.sys.mjs",
|
||||
"MigratorBase.sys.mjs",
|
||||
"ProfileMigrator.sys.mjs",
|
||||
|
@ -2,7 +2,9 @@
|
||||
head = head.js
|
||||
prefs =
|
||||
browser.migrate.content-modal.enabled=true
|
||||
browser.migrate.internal-testing.enabled=true
|
||||
|
||||
[browser_dialog_cancel_close.js]
|
||||
[browser_dialog_open.js]
|
||||
[browser_dialog_resize.js]
|
||||
[browser_do_migration.js]
|
||||
|
@ -0,0 +1,174 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm");
|
||||
const { InternalTestingProfileMigrator } = ChromeUtils.importESModule(
|
||||
"resource:///modules/InternalTestingProfileMigrator.sys.mjs"
|
||||
);
|
||||
|
||||
/**
|
||||
* A helper function that prepares the InternalTestingProfileMigrator
|
||||
* with some set of fake available resources, and resolves a Promise
|
||||
* when the InternalTestingProfileMigrator is used for a migration.
|
||||
*
|
||||
* @param {number[]} availableResourceTypes
|
||||
* An array of resource types from MigrationUtils.resourcesTypes.
|
||||
* A single MigrationResource will be created per type, with a
|
||||
* no-op migrate function.
|
||||
* @param {number[]} expectedResourceTypes
|
||||
* An array of resource types from MigrationUtils.resourceTypes.
|
||||
* These are the resource types that are expected to be passed
|
||||
* to the InternalTestingProfileMigrator.migrate function.
|
||||
* @param {object|string} expectedProfile
|
||||
* The profile object or string that is expected to be passed
|
||||
* to the InternalTestingProfileMigrator.migrate function.
|
||||
* @returns {Promise<undefined>}
|
||||
*/
|
||||
async function waitForTestMigration(
|
||||
availableResourceTypes,
|
||||
expectedResourceTypes,
|
||||
expectedProfile
|
||||
) {
|
||||
let sandbox = sinon.createSandbox();
|
||||
|
||||
// Fake out the getResources method of the migrator so that we return
|
||||
// a single fake MigratorResource per availableResourceType.
|
||||
sandbox
|
||||
.stub(InternalTestingProfileMigrator.prototype, "getResources")
|
||||
.callsFake(() => {
|
||||
return Promise.resolve(
|
||||
availableResourceTypes.map(resourceType => {
|
||||
return {
|
||||
type: resourceType,
|
||||
migrate: () => {},
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Fake out the migrate method of the migrator and assert that the
|
||||
// next time it's called, its arguments match our expectations.
|
||||
return new Promise(resolve => {
|
||||
sandbox
|
||||
.stub(InternalTestingProfileMigrator.prototype, "migrate")
|
||||
.callsFake((aResourceTypes, aStartup, aProfile) => {
|
||||
Assert.ok(
|
||||
!aStartup,
|
||||
"Migrator should not have been called as a startup migration."
|
||||
);
|
||||
Assert.deepEqual(
|
||||
aResourceTypes,
|
||||
expectedResourceTypes,
|
||||
"Got the expected resource types"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
aProfile,
|
||||
expectedProfile,
|
||||
"Got the expected profile object"
|
||||
);
|
||||
Services.obs.notifyObservers(null, "Migration:Ended");
|
||||
resolve();
|
||||
});
|
||||
}).finally(async () => {
|
||||
sandbox.restore();
|
||||
|
||||
// MigratorBase caches resources fetched by the getResources method
|
||||
// as a performance optimization. In order to allow different tests
|
||||
// to have different available resources, we call into a special
|
||||
// method of InternalTestingProfileMigrator that clears that
|
||||
// cache.
|
||||
let migrator = await MigrationUtils.getMigrator(
|
||||
InternalTestingProfileMigrator.key
|
||||
);
|
||||
migrator.flushResourceCache();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a MigrationWizard element and chooses the
|
||||
* InternalTestingProfileMigrator as the browser to migrate from. Then, it
|
||||
* checks the checkboxes associated with the selectedResourceTypes and
|
||||
* unchecks the rest before clicking the "Import" button.
|
||||
*
|
||||
* @param {Element} wizard
|
||||
* The MigrationWizard element.
|
||||
* @param {string[]} selectedResourceTypes
|
||||
* An array of resource type strings from
|
||||
* MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.
|
||||
*/
|
||||
function selectResourceTypesAndStartMigration(wizard, selectedResourceTypes) {
|
||||
let shadow = wizard.openOrClosedShadowRoot;
|
||||
|
||||
// First, select the InternalTestingProfileMigrator browser.
|
||||
let selector = shadow.querySelector("#browser-profile-selector");
|
||||
selector.value = InternalTestingProfileMigrator.key;
|
||||
// Apparently we have to dispatch our own "change" events for <select>
|
||||
// dropdowns.
|
||||
selector.dispatchEvent(new CustomEvent("change", { bubbles: true }));
|
||||
|
||||
// And then check the right checkboxes for the resource types.
|
||||
let resourceTypeList = shadow.querySelector("#resource-type-list");
|
||||
for (let resourceType in MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
|
||||
let node = resourceTypeList.querySelector(
|
||||
`label[data-resource-type="${resourceType}"]`
|
||||
);
|
||||
node.control.checked = selectedResourceTypes.includes(resourceType);
|
||||
}
|
||||
|
||||
let importButton = shadow.querySelector("#import");
|
||||
importButton.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the MigrationWizard can be used to successfully migrate
|
||||
* using the InternalTestingProfileMigrator in a few scenarios.
|
||||
*/
|
||||
add_task(async function test_successful_migrations() {
|
||||
// Scenario 1: A single resource type is available.
|
||||
let migration = waitForTestMigration(
|
||||
[MigrationUtils.resourceTypes.BOOKMARKS],
|
||||
[MigrationUtils.resourceTypes.BOOKMARKS],
|
||||
null
|
||||
);
|
||||
|
||||
await withMigrationWizardSubdialog(async subdialogWin => {
|
||||
let dialogBody = subdialogWin.document.body;
|
||||
let wizard = dialogBody.querySelector("#wizard");
|
||||
let wizardDone = BrowserTestUtils.waitForEvent(
|
||||
wizard,
|
||||
"MigrationWizard:DoneMigration"
|
||||
);
|
||||
selectResourceTypesAndStartMigration(wizard, [
|
||||
MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS,
|
||||
]);
|
||||
await migration;
|
||||
await wizardDone;
|
||||
});
|
||||
|
||||
// Scenario 2: Several resource types are available, but only 1
|
||||
// is checked / expected.
|
||||
migration = waitForTestMigration(
|
||||
[
|
||||
MigrationUtils.resourceTypes.BOOKMARKS,
|
||||
MigrationUtils.resourceTypes.PASSWORDS,
|
||||
],
|
||||
[MigrationUtils.resourceTypes.PASSWORDS],
|
||||
null
|
||||
);
|
||||
|
||||
await withMigrationWizardSubdialog(async subdialogWin => {
|
||||
let dialogBody = subdialogWin.document.body;
|
||||
let wizard = dialogBody.querySelector("#wizard");
|
||||
let wizardDone = BrowserTestUtils.waitForEvent(
|
||||
wizard,
|
||||
"MigrationWizard:DoneMigration"
|
||||
);
|
||||
selectResourceTypesAndStartMigration(wizard, [
|
||||
MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS,
|
||||
]);
|
||||
await migration;
|
||||
await wizardDone;
|
||||
});
|
||||
});
|
@ -102,13 +102,12 @@
|
||||
// dropdowns.
|
||||
selector.dispatchEvent(new CustomEvent("change", { "bubbles" : true }));
|
||||
|
||||
for (let displayedResourceType in MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
|
||||
let resourceTypeID = MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES[displayedResourceType];
|
||||
let node = resourceTypeList.querySelector(`#${resourceTypeID}`);
|
||||
if (migratorInstance.resourceTypes.includes(displayedResourceType)) {
|
||||
ok(!isHidden(node), `Selection for ${displayedResourceType} should be shown.`);
|
||||
for (let resourceType in MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
|
||||
let node = resourceTypeList.querySelector(`label[data-resource-type="${resourceType}"]`);
|
||||
if (migratorInstance.resourceTypes.includes(resourceType)) {
|
||||
ok(!isHidden(node), `Selection for ${resourceType} should be shown.`);
|
||||
} else {
|
||||
ok(isHidden(node), `Selection for ${displayedResourceType} should be hidden.`);
|
||||
ok(isHidden(node), `Selection for ${resourceType} should be hidden.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user