Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Tests for the restore-from-backup component</title>
<script
src="chrome://browser/content/backup/restore-from-backup.mjs"
type="module"
></script>
<link rel="localization" href="browser/backupSettings.ftl"/>
<link rel="localization" href="branding/brand.ftl"/>
<script>
const { BrowserTestUtils } = ChromeUtils.importESModule(
);
const { ERRORS } = ChromeUtils.importESModule(
"chrome://browser/content/backup/backup-constants.mjs"
);
/**
* Tests that adding a restore-from-backup element to the DOM causes it to
* fire a BackupUI:InitWidget event.
*/
add_task(async function test_initWidget() {
let restoreFromBackup = document.createElement("restore-from-backup");
let content = document.getElementById("content");
let sawInitWidget = BrowserTestUtils.waitForEvent(content, "BackupUI:InitWidget");
content.appendChild(restoreFromBackup);
await sawInitWidget;
ok(true, "Saw BackupUI:InitWidget");
restoreFromBackup.remove();
});
/**
* Tests that pressing the restore and restart button will dispatch the expected events.
*/
add_task(async function test_restore() {
let restoreFromBackup = document.getElementById("test-restore-from-backup");
let confirmButton = restoreFromBackup.confirmButtonEl;
ok(confirmButton, "Restore button should be found");
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
backupFileToRestore: "/Some/User/Documents/Firefox Backup/backup.html"
};
await restoreFromBackup.updateComplete;
let content = document.getElementById("content");
let promise = BrowserTestUtils.waitForEvent(content, "BackupUI:RestoreFromBackupFile");
confirmButton.click();
await promise;
ok(true, "Detected event after pressing the restore button");
});
/**
* Tests that pressing the cancel button will dispatch the expected events.
*/
add_task(async function test_cancel() {
let restoreFromBackup = document.getElementById("test-restore-from-backup");
let cancelButton = restoreFromBackup.cancelButtonEl;
ok(cancelButton, "Cancel button should be found");
let content = document.getElementById("content");
let promise = BrowserTestUtils.waitForEvent(content, "dialogCancel");
cancelButton.click();
await promise;
ok(true, "Detected event after pressing the cancel button");
});
/**
* Tests that pressing the choose button will dispatch the expected events.
*/
add_task(async function test_choose() {
let restoreFromBackup = document.getElementById("test-restore-from-backup");
ok(restoreFromBackup.chooseButtonEl, "Choose button should be found");
let content = document.getElementById("content");
let promise = BrowserTestUtils.waitForEvent(content, "BackupUI:ShowFilepicker");
restoreFromBackup.chooseButtonEl.click();
await promise;
ok(true, "Detected event after pressing the choose button");
});
/**
* Tests that selecting a backup file from the filepicker will dispatch the expected events.
*/
add_task(async function test_new_file_selected() {
let restoreFromBackup = document.getElementById("test-restore-from-backup");
let content = document.getElementById("content");
let promise = BrowserTestUtils.waitForEvent(content, "BackupUI:GetBackupFileInfo");
let selectEvent = new CustomEvent("BackupUI:SelectNewFilepickerPath", {
detail: {
path: "/Some/User/Documents/Firefox Backup/backup-default.html",
}
});
restoreFromBackup.dispatchEvent(selectEvent);
await promise;
ok(true, "Detected event after new file is selected");
});
/**
* Tests that the password input will shown when a file is encrypted.
*/
add_task(async function test_show_password() {
let restoreFromBackup = document.getElementById("test-restore-from-backup");
ok(!restoreFromBackup.passwordInput, "Password input should not be present");
let date = new Date();
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
backupFileInfo: {
date,
isEncrypted: true,
}
};
await restoreFromBackup.updateComplete;
ok(restoreFromBackup.passwordInput, "Password input should be present");
});
/**
* Tests that incorrect password is shown in a different error format than top level
*/
add_task(async function test_incorrect_password_error_condition() {
let restoreFromBackup = document.getElementById("test-restore-from-backup");
is(restoreFromBackup.backupServiceState.recoveryErrorCode, ERRORS.NONE, "Recovery error code should be 0");
ok(!restoreFromBackup.isIncorrectPassword, "Error message should not be displayed");
ok(!restoreFromBackup.errorMessageEl, "No error message should be displayed");
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
recoveryErrorCode: ERRORS.UNAUTHORIZED
};
await restoreFromBackup.updateComplete;
is(restoreFromBackup.backupServiceState.recoveryErrorCode, ERRORS.UNAUTHORIZED, "Recovery error code should be set");
ok(restoreFromBackup.isIncorrectPassword, "Error message should be displayed");
ok(!restoreFromBackup.errorMessageEl, "No top level error message should be displayed");
});
/**
* Tests that a top level error message is displayed if there is an error restoring from backup.
*/
add_task(async function test_error_condition() {
let restoreFromBackup = document.getElementById("test-restore-from-backup");
ok(!restoreFromBackup.errorMessageEl, "No error message should be displayed");
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
recoveryErrorCode: ERRORS.CORRUPTED_ARCHIVE
};
await restoreFromBackup.updateComplete;
is(restoreFromBackup.backupServiceState.recoveryErrorCode, ERRORS.CORRUPTED_ARCHIVE, "Recovery error code should be set");
ok(restoreFromBackup.errorMessageEl, "Error message should be displayed");
});
/**
* Tests that changes to backupServiceState emits BackupUI:RecoveryProgress,
* with the current progress state and progress is false when an error code is present.
*/
add_task(async function test_recovery_state_updates() {
const content = document.getElementById("content");
let restoreFromBackup = document.getElementById("test-restore-from-backup");
content.appendChild(restoreFromBackup);
// Reset previous state changes
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
recoveryInProgress: false,
recoveryErrorCode: ERRORS.NONE,
};
await restoreFromBackup.updateComplete;
// Helper to dispatch the BackupUI:RecoveryProgress event
async function sendState(testState) {
const promise = BrowserTestUtils.waitForEvent(
restoreFromBackup,
"BackupUI:RecoveryProgress"
);
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
...testState,
};
await restoreFromBackup.updateComplete;
return promise;
}
// Initial state
is(
restoreFromBackup.backupServiceState.recoveryInProgress,
false,
"Initial progress state is false"
);
is(restoreFromBackup.backupServiceState.recoveryErrorCode, ERRORS.NONE, "Initial error code should be 0");
// Backup in progress with no error
let event = await sendState({ recoveryInProgress: true });
is(event.detail?.recoveryInProgress, true, "'recoveryInProgress' is true");
is(restoreFromBackup.backupServiceState.recoveryInProgress, true, "State reflects in-progress");
// Backup not in progress
event = await sendState({ recoveryInProgress: false, recoveryErrorCode: 0 });
is(event.detail?.recoveryInProgress, false, "'recoveryInProgress' is false");
is(
restoreFromBackup.backupServiceState.recoveryInProgress,
false,
"State reflects not in-progress"
);
// Any error should clear progress
for (const code of [ERRORS.CORRUPTED_ARCHIVE, ERRORS.UNAUTHORIZED]) {
info(`Asserting recovery progress clears with error code: ${code}`);
event = await sendState({
recoveryInProgress: true,
recoveryErrorCode: ERRORS.NONE,
});
is(
restoreFromBackup.backupServiceState.recoveryInProgress,
true,
"State reflects in-progress"
);
// Add an error
event = await sendState({
recoveryInProgress: true,
recoveryErrorCode: code,
});
is(
event.detail?.recoveryInProgress,
false,
`Progress cleared for error ${code}`
);
// Clear state
await sendState({ recoveryInProgress: false, recoveryErrorCode: ERRORS.NONE });
}
restoreFromBackup.remove();
});
/**
* Helper function to test that a support link has correct attributes
* and UTM params when used with aboutWelcomeEmbedded
*
* @param {Element} link - The support link element to test
* @param {string} linkName - The name of the link to test
*/
function assertEmbeddedSupportLink(link, linkName) {
ok(link, `${linkName} should be present`);
ok(
!link.hasAttribute("is"),
`${linkName} should not have 'is' attribute`
);
ok(
!link.hasAttribute("support-page"),
`${linkName} should not have support-page attribute`
);
ok(
link.hasAttribute("href"),
`${linkName} should have href attribute`
);
let url = new URL(link.getAttribute("href"));
is(
url.searchParams.get("utm_medium"),
"firefox-desktop",
`${linkName} should have correct utm_medium`
);
is(
url.searchParams.get("utm_source"),
"npo",
`${linkName} should have correct utm_source`
);
is(
url.searchParams.get("utm_campaign"),
"fx-backup-restore",
`${linkName} should have correct utm_campaign`
);
is(
url.searchParams.get("utm_content"),
"restore-error",
`${linkName} should have correct utm_content`
);
is(
link.getAttribute("target"),
"_blank",
`${linkName} should have target='_blank'`
);
}
/**
* Tests that support links have UTM parameters when aboutWelcomeEmbedded is true
*/
add_task(async function test_support_links_with_utm_params() {
let content = document.getElementById("content");
let restoreFromBackup = document.createElement("restore-from-backup");
content.appendChild(restoreFromBackup);
// Set up the support base link for testing, otherwise links will be broken
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
};
restoreFromBackup.aboutWelcomeEmbedded = true;
await restoreFromBackup.updateComplete;
// Test the "no backup file" link
let noBackupFileLink = restoreFromBackup.shadowRoot.querySelector(
"#restore-from-backup-no-backup-file-link"
);
assertEmbeddedSupportLink(noBackupFileLink, "No backup file link");
// Test the incorrect password support link
restoreFromBackup.backupServiceState = {
...restoreFromBackup.backupServiceState,
backupFileInfo: {
date: new Date(),
isEncrypted: true,
},
recoveryErrorCode: ERRORS.UNAUTHORIZED,
};
await restoreFromBackup.updateComplete;
let passwordErrorLink = restoreFromBackup.shadowRoot.querySelector(
"#backup-incorrect-password-support-link"
);
assertEmbeddedSupportLink(passwordErrorLink, "Password error link");
restoreFromBackup.remove();
});
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
<restore-from-backup id="test-restore-from-backup"></restore-from-backup>
</div>
<pre id="test"></pre>
</body>
</html>