summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/update/tests/browser
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/update/tests/browser')
-rw-r--r--toolkit/mozapps/update/tests/browser/browser.bits.ini81
-rw-r--r--toolkit/mozapps/update/tests/browser/browser.ini122
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_checking.js32
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_and_install.js41
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_failed.js43
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_downloading.js47
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_internal_error.js45
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_no_update.js29
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_ready_for_restart.js27
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_staging.js64
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_swap.js64
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded.js17
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staged.js28
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staging.js55
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_stagingFailure.js31
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading.js76
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_notify.js67
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_staging.js68
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_multiUpdate.js52
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_apply_blocked.js94
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_cantApply.js24
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_malformedXML.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_noUpdate.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_otherInstance.js19
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_unsupported.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto.js62
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto_staging.js46
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn.js44
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn_staging.js52
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_failure.js18
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_offline.js31
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_completeBadSize.js36
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize.js36
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_complete.js38
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js53
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutDialog_internalError.js35
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_backgroundUpdateSetting.js172
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded.js17
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staged.js28
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staging.js59
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_stagingFailure.js29
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading.js63
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading_staging.js72
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_multiUpdate.js52
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_apply_blocked.js96
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_cantApply.js24
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_malformedXML.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_noUpdate.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_otherInstance.js19
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_unsupported.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto.js37
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto_staging.js46
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn.js44
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn_staging.js52
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_failure.js17
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_offline.js31
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_completeBadSize.js36
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize.js36
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_complete.js38
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js53
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_internalError.js35
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_aboutPrefs_settings.js151
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_cantApply.js18
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_malformedXML.js26
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_unsupported.js94
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js35
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js80
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn.js25
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_bgWin.js63
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_staging.js29
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded.js18
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_disableBITS.js31
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate.js93
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate_promptWaitTime.js89
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js33
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js33
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_complete.js18
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js33
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_completeApplyFailure.js23
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete.js22
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js32
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js28
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_elevationDialog.js139
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_memory_allocation_error_fallback.js81
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_downloaded_ready.js68
-rw-r--r--toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_staged_ready.js72
-rw-r--r--toolkit/mozapps/update/tests/browser/downloadPage.html13
-rw-r--r--toolkit/mozapps/update/tests/browser/head.js1347
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/browser.ini18
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateFalse.js43
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateTrue.js43
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateFalse.js55
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateTrue.js55
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_noBackgroundUpdate.js17
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json5
-rw-r--r--toolkit/mozapps/update/tests/browser/manual_app_update_only/head.js9
-rw-r--r--toolkit/mozapps/update/tests/browser/testConstants.js7
99 files changed, 5755 insertions, 0 deletions
diff --git a/toolkit/mozapps/update/tests/browser/browser.bits.ini b/toolkit/mozapps/update/tests/browser/browser.bits.ini
new file mode 100644
index 0000000000..02dde57751
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser.bits.ini
@@ -0,0 +1,81 @@
+[DEFAULT]
+skip-if =
+ os != 'win'
+ msix # Updater is disabled in MSIX builds
+reason = BITS is only available on Windows.
+dupe-manifest =
+tags = appupdate bits
+head = head.js
+support-files =
+ ../data/shared.js
+ ../data/sharedUpdateXML.js
+ ../data/app_update.sjs
+ downloadPage.html
+ testConstants.js
+
+prefs =
+ app.update.BITS.enabled=true
+ app.update.langpack.enabled=true
+
+# BITS Download Tests
+#####################
+
+# About Dialog Application Update Tests
+[browser_aboutDialog_bc_downloaded.js]
+[browser_aboutDialog_bc_downloaded_staged.js]
+[browser_aboutDialog_bc_downloaded_staging.js]
+[browser_aboutDialog_bc_downloaded_stagingFailure.js]
+[browser_aboutDialog_bc_downloading.js]
+[browser_aboutDialog_bc_downloading_notify.js]
+[browser_aboutDialog_bc_downloading_staging.js]
+[browser_aboutDialog_bc_multiUpdate.js]
+[browser_aboutDialog_fc_downloadAuto.js]
+[browser_aboutDialog_fc_downloadAuto_staging.js]
+[browser_aboutDialog_fc_downloadOptIn.js]
+[browser_aboutDialog_fc_downloadOptIn_staging.js]
+[browser_aboutDialog_fc_patch_completeBadSize.js]
+[browser_aboutDialog_fc_patch_partialBadSize.js]
+[browser_aboutDialog_fc_patch_partialBadSize_complete.js]
+[browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js]
+
+# about:preferences Application Update Tests
+[browser_aboutPrefs_bc_downloaded.js]
+[browser_aboutPrefs_bc_downloaded_staged.js]
+[browser_aboutPrefs_bc_downloaded_staging.js]
+[browser_aboutPrefs_bc_downloaded_stagingFailure.js]
+[browser_aboutPrefs_bc_downloading.js]
+[browser_aboutPrefs_bc_downloading_staging.js]
+[browser_aboutPrefs_bc_multiUpdate.js]
+[browser_aboutPrefs_fc_downloadAuto.js]
+[browser_aboutPrefs_fc_downloadAuto_staging.js]
+[browser_aboutPrefs_fc_downloadOptIn.js]
+[browser_aboutPrefs_fc_downloadOptIn_staging.js]
+[browser_aboutPrefs_fc_patch_completeBadSize.js]
+[browser_aboutPrefs_fc_patch_partialBadSize.js]
+[browser_aboutPrefs_fc_patch_partialBadSize_complete.js]
+[browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js]
+
+# Doorhanger Application Update Tests
+[browser_doorhanger_bc_downloadAutoFailures.js]
+[browser_doorhanger_bc_downloadAutoFailures_bgWin.js]
+[browser_doorhanger_bc_downloadOptIn.js]
+[browser_doorhanger_bc_downloadOptIn_bgWin.js]
+[browser_doorhanger_bc_downloadOptIn_staging.js]
+[browser_doorhanger_bc_downloaded.js]
+[browser_doorhanger_bc_downloaded_disableBITS.js]
+[browser_doorhanger_bc_downloaded_staged.js]
+[browser_doorhanger_bc_multiUpdate.js]
+[browser_doorhanger_bc_multiUpdate_promptWaitTime.js]
+[browser_doorhanger_bc_patch_completeBadSize.js]
+[browser_doorhanger_bc_patch_partialBadSize.js]
+[browser_doorhanger_bc_patch_partialBadSize_complete.js]
+[browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js]
+[browser_doorhanger_sp_patch_completeApplyFailure.js]
+[browser_doorhanger_sp_patch_partialApplyFailure.js]
+[browser_doorhanger_sp_patch_partialApplyFailure_complete.js]
+[browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js]
+[browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js]
+
+# Telemetry Update Ping Tests
+[browser_telemetry_updatePing_downloaded_ready.js]
+[browser_telemetry_updatePing_staged_ready.js]
diff --git a/toolkit/mozapps/update/tests/browser/browser.ini b/toolkit/mozapps/update/tests/browser/browser.ini
new file mode 100644
index 0000000000..f2d7313c94
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser.ini
@@ -0,0 +1,122 @@
+[DEFAULT]
+dupe-manifest =
+tags = appupdate internal
+head = head.js
+support-files =
+ ../data/shared.js
+ ../data/sharedUpdateXML.js
+ ../data/app_update.sjs
+ downloadPage.html
+ testConstants.js
+prefs =
+ app.update.BITS.enabled=false
+ app.update.langpack.enabled=true
+skip-if = os == 'win' && msix # Updater is disabled in MSIX builds
+
+# About Dialog Application Update Tests
+[browser_aboutDialog_AppUpdater_stop_checking.js]
+[browser_aboutDialog_AppUpdater_stop_download_and_install.js]
+[browser_aboutDialog_AppUpdater_stop_download_failed.js]
+[browser_aboutDialog_AppUpdater_stop_downloading.js]
+[browser_aboutDialog_AppUpdater_stop_internal_error.js]
+[browser_aboutDialog_AppUpdater_stop_no_update.js]
+[browser_aboutDialog_AppUpdater_stop_ready_for_restart.js]
+[browser_aboutDialog_AppUpdater_stop_staging.js]
+[browser_aboutDialog_AppUpdater_stop_swap.js]
+[browser_aboutDialog_bc_downloaded.js]
+[browser_aboutDialog_bc_downloaded_staged.js]
+[browser_aboutDialog_bc_downloaded_staging.js]
+[browser_aboutDialog_bc_downloaded_stagingFailure.js]
+[browser_aboutDialog_bc_downloading.js]
+[browser_aboutDialog_bc_downloading_notify.js]
+[browser_aboutDialog_bc_downloading_staging.js]
+[browser_aboutDialog_bc_multiUpdate.js]
+[browser_aboutDialog_fc_apply_blocked.js]
+[browser_aboutDialog_fc_check_cantApply.js]
+skip-if = os != 'win'
+reason = test must be able to prevent file deletion.
+[browser_aboutDialog_fc_check_malformedXML.js]
+[browser_aboutDialog_fc_check_noUpdate.js]
+[browser_aboutDialog_fc_check_otherInstance.js]
+skip-if = os != 'win'
+reason = Windows only feature.
+[browser_aboutDialog_fc_check_unsupported.js]
+[browser_aboutDialog_fc_downloadAuto.js]
+skip-if = tsan # Bug 1683730
+[browser_aboutDialog_fc_downloadAuto_staging.js]
+[browser_aboutDialog_fc_downloadOptIn.js]
+[browser_aboutDialog_fc_downloadOptIn_staging.js]
+[browser_aboutDialog_fc_network_failure.js]
+[browser_aboutDialog_fc_network_offline.js]
+[browser_aboutDialog_fc_patch_completeBadSize.js]
+[browser_aboutDialog_fc_patch_partialBadSize.js]
+[browser_aboutDialog_fc_patch_partialBadSize_complete.js]
+[browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js]
+[browser_aboutDialog_internalError.js]
+[browser_aboutPrefs_backgroundUpdateSetting.js]
+[browser_aboutPrefs_bc_downloaded.js]
+[browser_aboutPrefs_bc_downloaded_staged.js]
+[browser_aboutPrefs_bc_downloaded_staging.js]
+[browser_aboutPrefs_bc_downloaded_stagingFailure.js]
+
+# about:preferences Application Update Tests
+[browser_aboutPrefs_bc_downloading.js]
+[browser_aboutPrefs_bc_downloading_staging.js]
+[browser_aboutPrefs_bc_multiUpdate.js]
+[browser_aboutPrefs_fc_apply_blocked.js]
+[browser_aboutPrefs_fc_check_cantApply.js]
+skip-if = os != 'win'
+reason = test must be able to prevent file deletion.
+[browser_aboutPrefs_fc_check_malformedXML.js]
+[browser_aboutPrefs_fc_check_noUpdate.js]
+[browser_aboutPrefs_fc_check_otherInstance.js]
+skip-if = os != 'win'
+reason = Windows only feature.
+[browser_aboutPrefs_fc_check_unsupported.js]
+[browser_aboutPrefs_fc_downloadAuto.js]
+[browser_aboutPrefs_fc_downloadAuto_staging.js]
+[browser_aboutPrefs_fc_downloadOptIn.js]
+[browser_aboutPrefs_fc_downloadOptIn_staging.js]
+[browser_aboutPrefs_fc_network_failure.js]
+[browser_aboutPrefs_fc_network_offline.js]
+[browser_aboutPrefs_fc_patch_completeBadSize.js]
+[browser_aboutPrefs_fc_patch_partialBadSize.js]
+[browser_aboutPrefs_fc_patch_partialBadSize_complete.js]
+[browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js]
+[browser_aboutPrefs_internalError.js]
+[browser_aboutPrefs_settings.js]
+
+# Doorhanger Application Update Tests
+[browser_doorhanger_bc_check_cantApply.js]
+skip-if = os != 'win'
+reason = test must be able to prevent file deletion.
+[browser_doorhanger_bc_check_malformedXML.js]
+[browser_doorhanger_bc_check_unsupported.js]
+[browser_doorhanger_bc_downloadAutoFailures.js]
+[browser_doorhanger_bc_downloadAutoFailures_bgWin.js]
+[browser_doorhanger_bc_downloadOptIn.js]
+[browser_doorhanger_bc_downloadOptIn_bgWin.js]
+[browser_doorhanger_bc_downloadOptIn_staging.js]
+[browser_doorhanger_bc_downloaded.js]
+[browser_doorhanger_bc_downloaded_staged.js]
+[browser_doorhanger_bc_multiUpdate.js]
+[browser_doorhanger_bc_multiUpdate_promptWaitTime.js]
+[browser_doorhanger_bc_patch_completeBadSize.js]
+[browser_doorhanger_bc_patch_partialBadSize.js]
+[browser_doorhanger_bc_patch_partialBadSize_complete.js]
+[browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js]
+[browser_doorhanger_sp_patch_completeApplyFailure.js]
+[browser_doorhanger_sp_patch_partialApplyFailure.js]
+[browser_doorhanger_sp_patch_partialApplyFailure_complete.js]
+[browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js]
+[browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js]
+
+# Elevation Dialog Tests
+[browser_elevationDialog.js]
+
+# Memory Fallback Tests
+[browser_memory_allocation_error_fallback.js]
+
+# Telemetry Update Ping Tests
+[browser_telemetry_updatePing_downloaded_ready.js]
+[browser_telemetry_updatePing_staged_ready.js]
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_checking.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_checking.js
new file mode 100644
index 0000000000..370f658656
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_checking.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` while
+// in the checking state causes the interface to return to the `NEVER_CHECKED`
+// state.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_checking() {
+ let params = { queryString: "&noUpdates=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ // Omit the continue file to keep us in the checking state.
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "checkForUpdates",
+ },
+ ]);
+
+ // Ideally this would go in a cleanup function. But this needs to happen
+ // before any other cleanup functions and for some reason cleanup functions
+ // do not always seem to execute in reverse registration order.
+ dump("Cleanup: Waiting for checking to finish.\n");
+ await continueFileHandler(CONTINUE_CHECK);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_and_install.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_and_install.js
new file mode 100644
index 0000000000..74e179043d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_and_install.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` while
+// in the "download and install" state causes the interface to return to the
+// `NEVER_CHECKED` state.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_download_and_install() {
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ noContinue: true,
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "checkForUpdates",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_failed.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_failed.js
new file mode 100644
index 0000000000..8900765f81
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_download_failed.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` while
+// in the "downloadFailed" state doesn't cause a shift to any other state, such
+// as internal error.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_download_failed() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "complete", bitsResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "complete", internalResult: gBadSizeResult };
+ } else {
+ downloadInfo[0] = { patchType: "complete", internalResult: gBadSizeResult };
+ }
+
+ let params = { queryString: "&completePatchOnly=1&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "downloadFailed",
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "downloadFailed",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_downloading.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_downloading.js
new file mode 100644
index 0000000000..d0de7e03b9
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_downloading.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` during
+// the downloading state causes the interface to return to the `NEVER_CHECKED`
+// state.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_downloading() {
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_DOWNLOADING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ // Omit continue file to keep the UI in the downloading state.
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "checkForUpdates",
+ // The update will still be in the downloading state even though
+ // AppUpdater has stopped because stopping AppUpdater doesn't stop the
+ // Application Update Service from continuing with the update.
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ expectedStateOverride: Ci.nsIApplicationUpdateService.STATE_DOWNLOADING,
+ },
+ ]);
+
+ // Ideally this would go in a cleanup function. But this needs to happen
+ // before any other cleanup functions and for some reason cleanup functions
+ // do not always seem to execute in reverse registration order.
+ dump("Cleanup: Waiting for downloading to finish.\n");
+ await continueFileHandler(CONTINUE_DOWNLOAD);
+ if (gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_DOWNLOADING) {
+ await gAUS.stateTransition;
+ }
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_internal_error.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_internal_error.js
new file mode 100644
index 0000000000..69c13783bf
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_internal_error.js
@@ -0,0 +1,45 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ AppUpdater: "resource://gre/modules/AppUpdater.sys.mjs",
+ sinon: "resource://testing-common/Sinon.sys.mjs",
+});
+
+add_setup(function setup_internalErrorTest() {
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(AppUpdater.prototype, "aus").get(() => {
+ throw new Error("intentional test error");
+ });
+ sandbox.stub(AppUpdater.prototype, "checker").get(() => {
+ throw new Error("intentional test error");
+ });
+ sandbox.stub(AppUpdater.prototype, "um").get(() => {
+ throw new Error("intentional test error");
+ });
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+});
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` while
+// in the "internal error" state doesn't cause a shift to any other state.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_internal_error() {
+ let params = {};
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "internalError",
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "internalError",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_no_update.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_no_update.js
new file mode 100644
index 0000000000..65a52ccc87
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_no_update.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` while
+// in the "noUpdatesFound" state doesn't cause a shift to any other state, such
+// as internal error.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_no_update() {
+ let params = { queryString: "&noUpdates=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "noUpdatesFound",
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "noUpdatesFound",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_ready_for_restart.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_ready_for_restart.js
new file mode 100644
index 0000000000..8c9d1f788f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_ready_for_restart.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` while
+// in the "ready for restart" state doesn't cause a shift to any other state,
+// such as internal error.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_ready_for_restart() {
+ let params = { backgroundUpdate: true, waitForUpdateState: STATE_PENDING };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_staging.js
new file mode 100644
index 0000000000..dd822e6391
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_staging.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that while a download is in-progress, calling `AppUpdater.stop()` while
+// in the staging state causes the interface to return to the `NEVER_CHECKED`
+// state.
+// This is less a test of the About dialog than of AppUpdater, but it's easier
+// to test it via the About dialog just because there is already a testing
+// framework for the About dialog.
+add_task(async function aboutDialog_AppUpdater_stop_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_DOWNLOADING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ // Don't pass a continue file in order to leave us in the staging state.
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "checkForUpdates",
+ // The update will still be in the staging state even though AppUpdater
+ // has stopped because stopping AppUpdater doesn't stop the Application
+ // Update Service from continuing with the update.
+ checkActiveUpdate: { state: STATE_PENDING },
+ expectedStateOverride: Ci.nsIApplicationUpdateService.STATE_STAGING,
+ },
+ ]);
+
+ // Ideally this would go in a cleanup function. But this needs to happen
+ // before any other cleanup functions and for some reason cleanup functions
+ // do not always seem to execute in reverse registration order.
+ dump("Cleanup: Waiting for staging to finish.\n");
+ await continueFileHandler(CONTINUE_STAGING);
+ if (gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_STAGING) {
+ await gAUS.stateTransition;
+ }
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_swap.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_swap.js
new file mode 100644
index 0000000000..02441dea53
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_AppUpdater_stop_swap.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const FIRST_UPDATE_VERSION = "999998.0";
+const SECOND_UPDATE_VERSION = "999999.0";
+
+function prepareToDownloadVersion(version) {
+ setUpdateURL(
+ URL_HTTP_UPDATE_SJS +
+ `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}`
+ );
+}
+
+add_task(async function aboutDialog_backgroundCheck_multiUpdate() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let params = {
+ version: FIRST_UPDATE_VERSION,
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_PENDING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ () => {
+ prepareToDownloadVersion(SECOND_UPDATE_VERSION);
+ gAUS.checkForBackgroundUpdates();
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ // Don't pass a continue file in order to leave us in the staging state.
+ },
+ aboutDialog => {
+ aboutDialog.gAppUpdater._appUpdater.stop();
+ },
+ {
+ panelId: "checkForUpdates",
+ checkActiveUpdate: { state: STATE_PENDING },
+ expectedStateOverride: Ci.nsIApplicationUpdateService.STATE_STAGING,
+ },
+ ]);
+
+ // Ideally this would go in a cleanup function. But this needs to happen
+ // before any other cleanup functions and for some reason cleanup functions
+ // do not always seem to execute in reverse registration order.
+ dump("Cleanup: Waiting for staging to finish.\n");
+ await continueFileHandler(CONTINUE_STAGING);
+ if (gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_STAGING) {
+ await gAUS.stateTransition;
+ }
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded.js
new file mode 100644
index 0000000000..0955750c93
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog background check for updates
+// with the update downloaded when the About Dialog is opened.
+add_task(async function aboutDialog_backgroundCheck_downloaded() {
+ let params = { backgroundUpdate: true, waitForUpdateState: STATE_PENDING };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staged.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staged.js
new file mode 100644
index 0000000000..2d7bd64d05
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staged.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog background check for updates
+// with the update downloaded and staged when the About Dialog is opened.
+add_task(async function aboutDialog_backgroundCheck_downloaded_staged() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ continueFile: CONTINUE_STAGING,
+ waitForUpdateState: STATE_APPLIED,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staging.js
new file mode 100644
index 0000000000..b60a8f128d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_staging.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog background check for updates
+// with the update downloaded and the About Dialog opened during staging.
+add_task(async function aboutDialog_backgroundCheck_downloaded_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let lankpackCall = mockLangpackInstall();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_PENDING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ async aboutDialog => {
+ // Once the state is applied but langpacks aren't complete the about
+ // dialog should still be showing applying.
+ TestUtils.waitForCondition(() => {
+ return readStatusFile() == STATE_APPLIED;
+ });
+
+ is(
+ aboutDialog.gAppUpdater.selectedPanel.id,
+ "applying",
+ "UI should still show as applying."
+ );
+
+ let { appVersion, resolve } = await lankpackCall;
+ is(
+ appVersion,
+ Services.appinfo.version,
+ "Should see the right app version."
+ );
+ resolve();
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_stagingFailure.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_stagingFailure.js
new file mode 100644
index 0000000000..c05b2daa74
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloaded_stagingFailure.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog background check for updates
+// with the update downloaded and staging has failed when the About Dialog is
+// opened.
+add_task(
+ async function aboutDialog_backgroundCheck_downloaded_stagingFailure() {
+ Services.env.set("MOZ_TEST_STAGING_ERROR", "1");
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&completePatchOnly=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_PENDING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+ }
+);
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading.js
new file mode 100644
index 0000000000..6c2a7486a9
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog background check for updates
+// with the About Dialog opened during downloading.
+add_task(async function aboutDialog_backgroundCheck_downloading() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_NOTIFYDURINGDOWNLOAD, false]],
+ });
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ let lankpackCall = mockLangpackInstall();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_DOWNLOADING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ async function aboutDialog_downloading() {
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The window's doorhanger is closed."
+ );
+ ok(
+ !PanelUI.menuButton.hasAttribute("badge-status"),
+ "The window does not have a badge."
+ );
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ async aboutDialog => {
+ // Once the state is pending but langpacks aren't complete the about
+ // dialog should still be showing downloading.
+ TestUtils.waitForCondition(() => {
+ return readStatusFile() == STATE_PENDING;
+ });
+
+ is(
+ aboutDialog.gAppUpdater.selectedPanel.id,
+ "downloading",
+ "UI should still show as downloading."
+ );
+
+ let { appVersion, resolve } = await lankpackCall;
+ is(
+ appVersion,
+ Services.appinfo.version,
+ "Should see the right app version."
+ );
+ resolve();
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_notify.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_notify.js
new file mode 100644
index 0000000000..cf067efe7d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_notify.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog background check for updates with the
+// "notify during download" feature turned on.
+add_task(async function aboutDialog_backgroundCheck_downloading_notify() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_NOTIFYDURINGDOWNLOAD, true]],
+ });
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_DOWNLOADING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ async function aboutDialog_downloading_notification() {
+ await TestUtils.waitForCondition(
+ () => PanelUI.menuButton.hasAttribute("badge-status"),
+ "Waiting for update badge",
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test.
+ logTestInfo(e);
+ });
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The window's doorhanger is closed."
+ );
+ ok(
+ PanelUI.menuButton.hasAttribute("badge-status"),
+ "The window has a badge."
+ );
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-downloading",
+ "The downloading badge is showing for the background window"
+ );
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_staging.js
new file mode 100644
index 0000000000..3f6b476e1b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_downloading_staging.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog background check for updates
+// with the About Dialog opened during downloading and stages the update.
+add_task(async function aboutDialog_backgroundCheck_downloading_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ let lankpackCall = mockLangpackInstall();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_DOWNLOADING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ async aboutDialog => {
+ // Once the state is applied but langpacks aren't complete the about
+ // dialog should still be showing applying.
+ TestUtils.waitForCondition(() => {
+ return readStatusFile() == STATE_APPLIED;
+ });
+
+ is(
+ aboutDialog.gAppUpdater.selectedPanel.id,
+ "applying",
+ "UI should still show as applying."
+ );
+
+ let { appVersion, resolve } = await lankpackCall;
+ is(
+ appVersion,
+ Services.appinfo.version,
+ "Should see the right app version."
+ );
+ resolve();
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_multiUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_multiUpdate.js
new file mode 100644
index 0000000000..b6c893ea15
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_bc_multiUpdate.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const FIRST_UPDATE_VERSION = "999998.0";
+const SECOND_UPDATE_VERSION = "999999.0";
+
+function prepareToDownloadVersion(version) {
+ setUpdateURL(
+ URL_HTTP_UPDATE_SJS +
+ `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}`
+ );
+}
+
+add_task(async function aboutDialog_backgroundCheck_multiUpdate() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let params = {
+ version: FIRST_UPDATE_VERSION,
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_PENDING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ () => {
+ prepareToDownloadVersion(SECOND_UPDATE_VERSION);
+ gAUS.checkForBackgroundUpdates();
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_apply_blocked.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_apply_blocked.js
new file mode 100644
index 0000000000..6cb2b9d10d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_apply_blocked.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { PromptTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromptTestUtils.sys.mjs"
+);
+
+const BUILDER_URL = "https://example.com/document-builder.sjs?html=";
+const PAGE_MARKUP = `
+<html>
+<head>
+ <script>
+ window.onbeforeunload = function() {
+ return true;
+ };
+ </script>
+</head>
+<body>TEST PAGE</body>
+</html>
+`;
+const TEST_URL = BUILDER_URL + encodeURI(PAGE_MARKUP);
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", false]],
+ });
+});
+
+// Test for About Dialog foreground check for updates
+// and apply but restart is blocked by a page.
+add_task(async function aboutDialog_foregroundCheck_apply_blocked() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ let aboutDialog;
+ let handlePromise = (async () => {
+ let dialog = await PromptTestUtils.waitForPrompt(window, {
+ modalType: Ci.nsIPrompt.MODAL_TYPE_CONTENT,
+ promptType: "confirmEx",
+ });
+ Assert.equal(
+ aboutDialog.gAppUpdater.selectedPanel.id,
+ "restarting",
+ "The restarting panel should be displayed"
+ );
+
+ await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 1 });
+ })();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1&promptWaitTime=0" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ async function getAboutDialogHandle(dialog) {
+ aboutDialog = dialog;
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ forceApply: true,
+ },
+ async function ensureDialogHasBeenCanceled() {
+ await handlePromise;
+ },
+ // A final check to ensure that we are back in the apply state.
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+
+ BrowserTestUtils.removeTab(tab, { skipPermitUnload: true });
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_cantApply.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_cantApply.js
new file mode 100644
index 0000000000..cd659ef74b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_cantApply.js
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// without the ability to apply updates.
+add_task(async function aboutDialog_foregroundCheck_cantApply() {
+ lockWriteTestFile();
+
+ let params = {};
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "manualUpdate",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_malformedXML.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_malformedXML.js
new file mode 100644
index 0000000000..529a4c7a63
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_malformedXML.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with a malformed update XML file.
+add_task(async function aboutDialog_foregroundCheck_malformedXML() {
+ let params = { queryString: "&xmlMalformed=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "checkingFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_noUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_noUpdate.js
new file mode 100644
index 0000000000..2bd23cddf0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_noUpdate.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with no update available.
+add_task(async function aboutDialog_foregroundCheck_noUpdate() {
+ let params = { queryString: "&noUpdates=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "noUpdatesFound",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_otherInstance.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_otherInstance.js
new file mode 100644
index 0000000000..fa1110effd
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_otherInstance.js
@@ -0,0 +1,19 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with another application instance handling updates.
+add_task(async function aboutDialog_foregroundCheck_otherInstance() {
+ setOtherInstanceHandlingUpdates();
+
+ let params = {};
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "otherInstanceHandlingUpdates",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_unsupported.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_unsupported.js
new file mode 100644
index 0000000000..22e3425967
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_check_unsupported.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with an unsupported update.
+add_task(async function aboutDialog_foregroundCheck_unsupported() {
+ let params = { queryString: "&unsupported=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "unsupportedSystem",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto.js
new file mode 100644
index 0000000000..a80c9deac8
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with an automatic download.
+add_task(async function aboutDialog_foregroundCheck_downloadAuto() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1&promptWaitTime=0" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ async function aboutDialog_restart_notification() {
+ await TestUtils.waitForCondition(
+ () => PanelUI.menuButton.hasAttribute("badge-status"),
+ "Waiting for update badge",
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test.
+ logTestInfo(e);
+ });
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The window's doorhanger is closed."
+ );
+ ok(
+ PanelUI.menuButton.hasAttribute("badge-status"),
+ "The window has a badge."
+ );
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-restart",
+ "The restart badge is showing for the background window"
+ );
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto_staging.js
new file mode 100644
index 0000000000..e212b0c611
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadAuto_staging.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with an automatic download and update staging.
+add_task(async function aboutDialog_foregroundCheck_downloadAuto_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn.js
new file mode 100644
index 0000000000..5a2ff513ad
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with a manual download.
+add_task(async function aboutDialog_foregroundCheck_downloadOptIn() {
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn_staging.js
new file mode 100644
index 0000000000..3ed331ec64
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_downloadOptIn_staging.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with a manual download and update staging.
+add_task(async function aboutDialog_foregroundCheck_downloadOptIn_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_failure.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_failure.js
new file mode 100644
index 0000000000..8e25b96d7f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_failure.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates which fails, because it is
+// impossible to connect to the update server
+add_task(async function aboutDialog_foregroundCheck_network_failure() {
+ let params = {
+ baseURL: "https://localhost:7777",
+ };
+
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingFailed",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_offline.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_offline.js
new file mode 100644
index 0000000000..9f81573e23
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_network_offline.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates which fails, because the
+// browser is in offline mode and `localhost` cannot be resolved.
+add_task(async function aboutDialog_foregroundCheck_network_offline() {
+ info("[OFFLINE] setting Services.io.offline (do not forget to reset it!)");
+ // avoid that real network connectivity changes influence the test execution
+ Services.io.manageOfflineStatus = false;
+ Services.io.offline = true;
+ registerCleanupFunction(() => {
+ info("[ONLINE] Resetting Services.io.offline");
+ Services.io.offline = false;
+ Services.io.manageOfflineStatus = true;
+ });
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["network.disable-localhost-when-offline", true],
+ ["network.dns.offline-localhost", false],
+ ],
+ });
+
+ await runAboutDialogUpdateTest({}, [
+ {
+ panelId: "checkingFailed",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_completeBadSize.js
new file mode 100644
index 0000000000..fdc7d3407e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_completeBadSize.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with a complete bad size patch.
+add_task(async function aboutDialog_foregroundCheck_completeBadSize() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "complete", bitsResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "complete", internalResult: gBadSizeResult };
+ } else {
+ downloadInfo[0] = { patchType: "complete", internalResult: gBadSizeResult };
+ }
+
+ let params = { queryString: "&completePatchOnly=1&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "downloadFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize.js
new file mode 100644
index 0000000000..411fb25969
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with a partial bad size patch.
+add_task(async function aboutDialog_foregroundCheck_partialBadSize() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult };
+ }
+
+ let params = { queryString: "&partialPatchOnly=1&invalidPartialSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "downloadFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_complete.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_complete.js
new file mode 100644
index 0000000000..396be1e930
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_complete.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with a partial bad size patch and a complete patch.
+add_task(async function aboutDialog_foregroundCheck_partialBadSize_complete() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult };
+ downloadInfo[2] = { patchType: "complete", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "complete", internalResult: "0" };
+ }
+
+ let params = { queryString: "&invalidPartialSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js
new file mode 100644
index 0000000000..fb10250a49
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_fc_patch_partialBadSize_completeBadSize.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for About Dialog foreground check for updates
+// with a partial bad size patch and a complete bad size patch.
+add_task(
+ async function aboutDialog_foregroundCheck_partialBadSize_completeBadSize() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult };
+ downloadInfo[1] = {
+ patchType: "partial",
+ internalResult: gBadSizeResult,
+ };
+ downloadInfo[2] = { patchType: "complete", bitsResult: gBadSizeResult };
+ downloadInfo[3] = {
+ patchType: "complete",
+ internalResult: gBadSizeResult,
+ };
+ } else {
+ downloadInfo[0] = {
+ patchType: "partial",
+ internalResult: gBadSizeResult,
+ };
+ downloadInfo[1] = {
+ patchType: "complete",
+ internalResult: gBadSizeResult,
+ };
+ }
+
+ let params = { queryString: "&invalidPartialSize=1&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "downloadFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+ }
+);
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutDialog_internalError.js b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_internalError.js
new file mode 100644
index 0000000000..d8d20ea05a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutDialog_internalError.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ AppUpdater: "resource://gre/modules/AppUpdater.sys.mjs",
+ sinon: "resource://testing-common/Sinon.sys.mjs",
+});
+
+add_setup(function setup_internalErrorTest() {
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(AppUpdater.prototype, "aus").get(() => {
+ throw new Error("intentional test error");
+ });
+ sandbox.stub(AppUpdater.prototype, "checker").get(() => {
+ throw new Error("intentional test error");
+ });
+ sandbox.stub(AppUpdater.prototype, "um").get(() => {
+ throw new Error("intentional test error");
+ });
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+});
+
+// Test for the About dialog's internal error handling.
+add_task(async function aboutDialog_internalError() {
+ let params = {};
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "internalError",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_backgroundUpdateSetting.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_backgroundUpdateSetting.js
new file mode 100644
index 0000000000..88afa8fdbe
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_backgroundUpdateSetting.js
@@ -0,0 +1,172 @@
+/* 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/.
+ */
+"use strict";
+
+/**
+ * This file tests the background update UI in about:preferences.
+ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
+});
+
+const BACKGROUND_UPDATE_PREF = "app.update.background.enabled";
+
+add_task(async function testBackgroundUpdateSettingUI() {
+ if (!AppConstants.MOZ_UPDATE_AGENT) {
+ // The element that we are testing in about:preferences is #ifdef'ed out of
+ // the file if MOZ_UPDATE_AGENT isn't defined. So there is nothing to
+ // test in that case.
+ logTestInfo(
+ `
+===============================================================================
+WARNING! This test involves background update, but background tasks are
+ disabled. This test will unconditionally pass since the feature it
+ wants to test isn't available.
+===============================================================================
+`
+ );
+ // Some of our testing environments do not consider a test to have passed if
+ // it didn't make any assertions.
+ ok(true, "Unconditionally passing test");
+ return;
+ }
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:preferences"
+ );
+
+ const originalBackgroundUpdateVal = await UpdateUtils.readUpdateConfigSetting(
+ BACKGROUND_UPDATE_PREF
+ );
+ const originalUpdateAutoVal = await UpdateUtils.getAppUpdateAutoEnabled();
+ registerCleanupFunction(async () => {
+ await BrowserTestUtils.removeTab(tab);
+ await UpdateUtils.writeUpdateConfigSetting(
+ BACKGROUND_UPDATE_PREF,
+ originalBackgroundUpdateVal
+ );
+ await UpdateUtils.setAppUpdateAutoEnabled(originalUpdateAutoVal);
+ });
+
+ // If auto update is disabled, the control for background update should be
+ // disabled, since we cannot update in the background if we can't update
+ // automatically.
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED],
+ async perInstallationPrefsSupported => {
+ let backgroundUpdateCheckbox =
+ content.document.getElementById("backgroundUpdate");
+ is(
+ backgroundUpdateCheckbox.hidden,
+ !perInstallationPrefsSupported,
+ `The background update UI should ${
+ perInstallationPrefsSupported ? "not" : ""
+ } be hidden when and perInstallationPrefsSupported is ` +
+ `${perInstallationPrefsSupported}`
+ );
+ if (perInstallationPrefsSupported) {
+ is(
+ backgroundUpdateCheckbox.disabled,
+ true,
+ `The background update UI should be disabled when auto update is ` +
+ `disabled`
+ );
+ }
+ }
+ );
+
+ if (!UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED) {
+ // The remaining tests only make sense on platforms where per-installation
+ // prefs are supported and the UI will ever actually be displayed
+ return;
+ }
+
+ await UpdateUtils.setAppUpdateAutoEnabled(true);
+ await UpdateUtils.writeUpdateConfigSetting(BACKGROUND_UPDATE_PREF, true);
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let backgroundUpdateCheckbox =
+ content.document.getElementById("backgroundUpdate");
+ is(
+ backgroundUpdateCheckbox.disabled,
+ false,
+ `The background update UI should not be disabled when auto update is ` +
+ `enabled`
+ );
+
+ is(
+ backgroundUpdateCheckbox.checked,
+ true,
+ "After enabling background update, the checkbox should be checked"
+ );
+
+ // Note that this action results in asynchronous activity. Normally when
+ // we change the update config, we await on the function to wait for the
+ // value to be written to the disk. We can't easily await on the UI state
+ // though. Luckily, we don't have to because reads/writes of the config file
+ // are serialized. So when we verify the written value by awaiting on
+ // readUpdateConfigSetting(), that will also wait for the value to be
+ // written to disk and for this UI to react to that.
+ backgroundUpdateCheckbox.click();
+ });
+
+ is(
+ await UpdateUtils.readUpdateConfigSetting(BACKGROUND_UPDATE_PREF),
+ false,
+ "Toggling the checkbox should have changed the setting value to false"
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let backgroundUpdateCheckbox =
+ content.document.getElementById("backgroundUpdate");
+ is(
+ backgroundUpdateCheckbox.checked,
+ false,
+ "After toggling the checked checkbox, it should be unchecked."
+ );
+
+ // Like the last call like this one, this initiates asynchronous behavior.
+ backgroundUpdateCheckbox.click();
+ });
+
+ is(
+ await UpdateUtils.readUpdateConfigSetting(BACKGROUND_UPDATE_PREF),
+ true,
+ "Toggling the checkbox should have changed the setting value to true"
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ is(
+ content.document.getElementById("backgroundUpdate").checked,
+ true,
+ "After toggling the unchecked checkbox, it should be checked"
+ );
+ });
+
+ // Test that the UI reacts to observed setting changes properly.
+ await UpdateUtils.writeUpdateConfigSetting(BACKGROUND_UPDATE_PREF, false);
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ is(
+ content.document.getElementById("backgroundUpdate").checked,
+ false,
+ "Externally disabling background update should uncheck the checkbox"
+ );
+ });
+
+ await UpdateUtils.writeUpdateConfigSetting(BACKGROUND_UPDATE_PREF, true);
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ is(
+ content.document.getElementById("backgroundUpdate").checked,
+ true,
+ "Externally enabling background update should check the checkbox"
+ );
+ });
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded.js
new file mode 100644
index 0000000000..6df4ee7ff5
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences background check for updates
+// with the update downloaded when about:preferences is opened.
+add_task(async function aboutPrefs_backgroundCheck_downloaded() {
+ let params = { backgroundUpdate: true, waitForUpdateState: STATE_PENDING };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staged.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staged.js
new file mode 100644
index 0000000000..749a8f0b07
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staged.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences background check for updates
+// with the update downloaded and staged when about:preferences is opened.
+add_task(async function aboutPrefs_backgroundCheck_downloaded_staged() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ continueFile: CONTINUE_STAGING,
+ waitForUpdateState: STATE_APPLIED,
+ };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staging.js
new file mode 100644
index 0000000000..73804cdf7a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_staging.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences background check for updates
+// with the update downloaded and about:preferences opened during staging.
+add_task(async function aboutPrefs_backgroundCheck_downloaded_staged() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let lankpackCall = mockLangpackInstall();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_PENDING,
+ };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ async tab => {
+ // Once the state is pending but langpacks aren't complete the about
+ // dialog should still be showing downloading.
+ TestUtils.waitForCondition(() => {
+ return readStatusFile() == STATE_APPLIED;
+ });
+
+ let updateDeckId = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return content.gAppUpdater.selectedPanel.id;
+ }
+ );
+
+ is(updateDeckId, "applying", "UI should still show as applying.");
+
+ let { appVersion, resolve } = await lankpackCall;
+ is(
+ appVersion,
+ Services.appinfo.version,
+ "Should see the right app version."
+ );
+ resolve();
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_stagingFailure.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_stagingFailure.js
new file mode 100644
index 0000000000..9755fe167b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloaded_stagingFailure.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences background check for updates
+// with the update downloaded and staging has failed when the about:preferences
+// is opened.
+add_task(async function aboutPrefs_backgroundCheck_downloaded_stagingFailure() {
+ Services.env.set("MOZ_TEST_STAGING_ERROR", "1");
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&completePatchOnly=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_PENDING,
+ };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading.js
new file mode 100644
index 0000000000..d9034296b0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences background check for updates
+// with about:preferences opened during downloading.
+add_task(async function aboutPrefs_backgroundCheck_downloading() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ let lankpackCall = mockLangpackInstall();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_DOWNLOADING,
+ };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ async tab => {
+ // Once the state is pending but langpacks aren't complete the about
+ // dialog should still be showing downloading.
+ TestUtils.waitForCondition(() => {
+ return readStatusFile() == STATE_PENDING;
+ });
+
+ let updateDeckId = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return content.gAppUpdater.selectedPanel.id;
+ }
+ );
+
+ is(updateDeckId, "downloading", "UI should still show as downloading.");
+
+ let { appVersion, resolve } = await lankpackCall;
+ is(
+ appVersion,
+ Services.appinfo.version,
+ "Should see the right app version."
+ );
+ resolve();
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading_staging.js
new file mode 100644
index 0000000000..8085ec4c2c
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_downloading_staging.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences background check for updates
+// with about:preferences opened during downloading and stages the update.
+add_task(async function aboutPrefs_backgroundCheck_downloading_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ let lankpackCall = mockLangpackInstall();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&useSlowDownloadMar=1&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_DOWNLOADING,
+ };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ async tab => {
+ // Once the state is pending but langpacks aren't complete the about
+ // dialog should still be showing downloading.
+ TestUtils.waitForCondition(() => {
+ return readStatusFile() == STATE_APPLIED;
+ });
+
+ let updateDeckId = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return content.gAppUpdater.selectedPanel.id;
+ }
+ );
+
+ is(updateDeckId, "applying", "UI should still show as applying.");
+
+ let { appVersion, resolve } = await lankpackCall;
+ is(
+ appVersion,
+ Services.appinfo.version,
+ "Should see the right app version."
+ );
+ resolve();
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_multiUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_multiUpdate.js
new file mode 100644
index 0000000000..9ccf593db5
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_bc_multiUpdate.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const FIRST_UPDATE_VERSION = "999998.0";
+const SECOND_UPDATE_VERSION = "999999.0";
+
+function prepareToDownloadVersion(version) {
+ setUpdateURL(
+ URL_HTTP_UPDATE_SJS +
+ `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}`
+ );
+}
+
+add_task(async function aboutPrefs_backgroundCheck_multiUpdate() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let params = {
+ version: FIRST_UPDATE_VERSION,
+ backgroundUpdate: true,
+ waitForUpdateState: STATE_PENDING,
+ };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ () => {
+ prepareToDownloadVersion(SECOND_UPDATE_VERSION);
+ gAUS.checkForBackgroundUpdates();
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_apply_blocked.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_apply_blocked.js
new file mode 100644
index 0000000000..22daa5e256
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_apply_blocked.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { PromptTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromptTestUtils.sys.mjs"
+);
+
+const BUILDER_URL = "https://example.com/document-builder.sjs?html=";
+const PAGE_MARKUP = `
+<html>
+<head>
+ <script>
+ window.onbeforeunload = function() {
+ return true;
+ };
+ </script>
+</head>
+<body>TEST PAGE</body>
+</html>
+`;
+const TEST_URL = BUILDER_URL + encodeURI(PAGE_MARKUP);
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", false]],
+ });
+});
+
+// Test for About Dialog foreground check for updates
+// and apply but restart is blocked by a page.
+add_task(async function aboutDialog_foregroundCheck_apply_blocked() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ let prefsTab;
+ let handlePromise = (async () => {
+ let dialog = await PromptTestUtils.waitForPrompt(window, {
+ modalType: Ci.nsIPrompt.MODAL_TYPE_CONTENT,
+ promptType: "confirmEx",
+ });
+ await SpecialPowers.spawn(prefsTab.linkedBrowser, [], async () => {
+ Assert.equal(
+ content.gAppUpdater.selectedPanel.id,
+ "restarting",
+ "The restarting panel should be displayed"
+ );
+ });
+
+ await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 1 });
+ })();
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1&promptWaitTime=0" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ async function getPrefsTab(tab) {
+ prefsTab = tab;
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ forceApply: true,
+ },
+ async function ensureDialogHasBeenCanceled() {
+ await handlePromise;
+ },
+ // A final check to ensure that we are back in the apply state.
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+
+ BrowserTestUtils.removeTab(tab, { skipPermitUnload: true });
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_cantApply.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_cantApply.js
new file mode 100644
index 0000000000..aac9b33134
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_cantApply.js
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// without the ability to apply updates.
+add_task(async function aboutPrefs_foregroundCheck_cantApply() {
+ lockWriteTestFile();
+
+ let params = {};
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "manualUpdate",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_malformedXML.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_malformedXML.js
new file mode 100644
index 0000000000..6d99a58e86
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_malformedXML.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with a malformed update XML file.
+add_task(async function aboutPrefs_foregroundCheck_malformedXML() {
+ let params = { queryString: "&xmlMalformed=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "checkingFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_noUpdate.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_noUpdate.js
new file mode 100644
index 0000000000..a8f6c072f7
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_noUpdate.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with no update available.
+add_task(async function aboutPrefs_foregroundCheck_noUpdate() {
+ let params = { queryString: "&noUpdates=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "noUpdatesFound",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_otherInstance.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_otherInstance.js
new file mode 100644
index 0000000000..271c8f2837
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_otherInstance.js
@@ -0,0 +1,19 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with another application instance handling updates.
+add_task(async function aboutPrefs_foregroundCheck_otherInstance() {
+ setOtherInstanceHandlingUpdates();
+
+ let params = {};
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "otherInstanceHandlingUpdates",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_unsupported.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_unsupported.js
new file mode 100644
index 0000000000..e18ce31bc4
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_check_unsupported.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with an unsupported update.
+add_task(async function aboutPrefs_foregroundCheck_unsupported() {
+ let params = { queryString: "&unsupported=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "unsupportedSystem",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto.js
new file mode 100644
index 0000000000..bd5fd18289
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with an automatic download.
+add_task(async function aboutPrefs_foregroundCheck_downloadAuto() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto_staging.js
new file mode 100644
index 0000000000..1d9d082edd
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadAuto_staging.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with an automatic download and update staging.
+add_task(async function aboutPrefs_foregroundCheck_downloadAuto_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn.js
new file mode 100644
index 0000000000..115e875b74
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with a manual download.
+add_task(async function aboutPrefs_foregroundCheck_downloadOptIn() {
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn_staging.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn_staging.js
new file mode 100644
index 0000000000..cff5d74701
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_downloadOptIn_staging.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with a manual download and update staging.
+add_task(async function aboutPrefs_foregroundCheck_downloadOptIn_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: "0" };
+ }
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "applying",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: CONTINUE_STAGING,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_failure.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_failure.js
new file mode 100644
index 0000000000..fbf91c0e54
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_failure.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates which fails,
+// because it is impossible to connect to the update server.
+add_task(async function aboutPrefs_foregroundCheck_network_failure() {
+ let params = {
+ baseURL: "https://localhost:7777",
+ };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingFailed",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_offline.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_offline.js
new file mode 100644
index 0000000000..7b5b6899b3
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_network_offline.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates which fails because
+// the browser is in offline mode and `localhost` cannot be resolved.
+add_task(async function aboutPrefs_foregroundCheck_network_offline() {
+ info("[OFFLINE] Setting Services.io.offline (do not forget to reset it!)");
+ // avoid that real network connectivity changes influence the test execution
+ Services.io.manageOfflineStatus = false;
+ Services.io.offline = true;
+ registerCleanupFunction(() => {
+ info("[ONLINE] Resetting Services.io.offline");
+ Services.io.offline = false;
+ Services.io.manageOfflineStatus = true;
+ });
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["network.disable-localhost-when-offline", true],
+ ["network.dns.offline-localhost", false],
+ ],
+ });
+
+ await runAboutPrefsUpdateTest({}, [
+ {
+ panelId: "checkingFailed",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_completeBadSize.js
new file mode 100644
index 0000000000..a36d3d9807
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_completeBadSize.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with a complete bad size patch.
+add_task(async function aboutPrefs_foregroundCheck_completeBadSize() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "complete", bitsResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "complete", internalResult: gBadSizeResult };
+ } else {
+ downloadInfo[0] = { patchType: "complete", internalResult: gBadSizeResult };
+ }
+
+ let params = { queryString: "&completePatchOnly=1&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "downloadFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize.js
new file mode 100644
index 0000000000..060acd405a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with a partial bad size patch.
+add_task(async function aboutPrefs_foregroundCheck_partialBadSize() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult };
+ }
+
+ let params = { queryString: "&partialPatchOnly=1&invalidPartialSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "downloadFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_complete.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_complete.js
new file mode 100644
index 0000000000..c6e5b5b20b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_complete.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with a partial bad size patch and a complete patch.
+add_task(async function aboutPrefs_foregroundCheck_partialBadSize_complete() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "partial", internalResult: gBadSizeResult };
+ downloadInfo[2] = { patchType: "complete", bitsResult: "0" };
+ } else {
+ downloadInfo[0] = { patchType: "partial", internalResult: gBadSizeResult };
+ downloadInfo[1] = { patchType: "complete", internalResult: "0" };
+ }
+
+ let params = { queryString: "&invalidPartialSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js
new file mode 100644
index 0000000000..62abdc3af5
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_fc_patch_partialBadSize_completeBadSize.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for about:preferences foreground check for updates
+// with a partial bad size patch and a complete bad size patch.
+add_task(
+ async function aboutPrefs_foregroundCheck_partialBadSize_completeBadSize() {
+ let downloadInfo = [];
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_BITS_ENABLED)) {
+ downloadInfo[0] = { patchType: "partial", bitsResult: gBadSizeResult };
+ downloadInfo[1] = {
+ patchType: "partial",
+ internalResult: gBadSizeResult,
+ };
+ downloadInfo[2] = { patchType: "complete", bitsResult: gBadSizeResult };
+ downloadInfo[3] = {
+ patchType: "complete",
+ internalResult: gBadSizeResult,
+ };
+ } else {
+ downloadInfo[0] = {
+ patchType: "partial",
+ internalResult: gBadSizeResult,
+ };
+ downloadInfo[1] = {
+ patchType: "complete",
+ internalResult: gBadSizeResult,
+ };
+ }
+
+ let params = { queryString: "&invalidPartialSize=1&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "downloadFailed",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ ]);
+ }
+);
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_internalError.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_internalError.js
new file mode 100644
index 0000000000..95a7ec1063
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_internalError.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ AppUpdater: "resource://gre/modules/AppUpdater.sys.mjs",
+ sinon: "resource://testing-common/Sinon.sys.mjs",
+});
+
+add_setup(function setup_internalErrorTest() {
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(AppUpdater.prototype, "aus").get(() => {
+ throw new Error("intentional test error");
+ });
+ sandbox.stub(AppUpdater.prototype, "checker").get(() => {
+ throw new Error("intentional test error");
+ });
+ sandbox.stub(AppUpdater.prototype, "um").get(() => {
+ throw new Error("intentional test error");
+ });
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+});
+
+// Test for about:preferences internal error handling.
+add_task(async function aboutPrefs_internalError() {
+ let params = {};
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "internalError",
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_settings.js b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_settings.js
new file mode 100644
index 0000000000..db1b538d14
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_aboutPrefs_settings.js
@@ -0,0 +1,151 @@
+/* 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/.
+ */
+
+// Changes, then verifies the value of app.update.auto via the about:preferences
+// UI. Requires a tab with about:preferences open to be passed in.
+async function changeAndVerifyPref(tab, newConfigValue) {
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [{ newConfigValue }],
+ async function ({ newConfigValue }) {
+ let radioId = newConfigValue ? "autoDesktop" : "manualDesktop";
+ let radioElement = content.document.getElementById(radioId);
+ let updateRadioGroup = radioElement.radioGroup;
+ let promise = ContentTaskUtils.waitForEvent(
+ updateRadioGroup,
+ "ProcessedUpdatePrefChange"
+ );
+ radioElement.click();
+ await promise;
+
+ is(
+ updateRadioGroup.value,
+ `${newConfigValue}`,
+ "Update preference should match expected"
+ );
+ is(
+ updateRadioGroup.disabled,
+ false,
+ "Update preferences should no longer be disabled"
+ );
+ }
+ );
+
+ let configValueRead = await UpdateUtils.getAppUpdateAutoEnabled();
+ is(
+ configValueRead,
+ newConfigValue,
+ "Value returned should have matched the expected value"
+ );
+}
+
+async function changeAndVerifyUpdateWrites({
+ tab,
+ newConfigValue,
+ discardUpdate,
+ expectPrompt,
+ expectRemainingUpdate,
+}) {
+ // A value of 1 will keep the update and a value of 0 will discard the update
+ // when the prompt service is called when the value of app.update.auto is
+ // changed to false.
+ let confirmExReply = discardUpdate ? 0 : 1;
+ let didPrompt = false;
+ let promptService = {
+ QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]),
+ confirmEx(...args) {
+ promptService._confirmExArgs = args;
+ didPrompt = true;
+ return confirmExReply;
+ },
+ };
+ Services.prompt = promptService;
+ await changeAndVerifyPref(tab, newConfigValue);
+ is(
+ didPrompt,
+ expectPrompt,
+ `We should ${expectPrompt ? "" : "not "}be prompted`
+ );
+ is(
+ !!gUpdateManager.readyUpdate,
+ expectRemainingUpdate,
+ `There should ${expectRemainingUpdate ? "" : "not "}be a ready update`
+ );
+}
+
+add_task(async function testUpdateAutoPrefUI() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:preferences"
+ );
+
+ // Hack: make the test run faster:
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ content.gMainPane._minUpdatePrefDisableTime = 10;
+ });
+
+ info("Enable automatic updates and check that works.");
+ await changeAndVerifyPref(tab, true);
+ ok(
+ !gUpdateManager.downloadingUpdate,
+ "There should not be a downloading update"
+ );
+ ok(!gUpdateManager.readyUpdate, "There should not be a ready update");
+
+ info("Disable automatic updates and check that works.");
+ await changeAndVerifyPref(tab, false);
+ ok(
+ !gUpdateManager.downloadingUpdate,
+ "There should not be a downloading update"
+ );
+ ok(!gUpdateManager.readyUpdate, "There should not be a ready update");
+
+ let patchProps = { state: STATE_PENDING };
+ let patches = getLocalPatchString(patchProps);
+ let updateProps = { checkInterval: "1" };
+ let updates = getLocalUpdateString(updateProps, patches);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_PENDING);
+ reloadUpdateManagerData();
+ ok(!!gUpdateManager.readyUpdate, "There should be a ready update");
+
+ let { prompt } = Services;
+ registerCleanupFunction(() => {
+ Services.prompt = prompt;
+ });
+
+ // Setting the value to false will call the prompt service and when we
+ // don't discard the update there should still be an active update afterwards.
+ await changeAndVerifyUpdateWrites({
+ tab,
+ newConfigValue: false,
+ discardUpdate: false,
+ expectPrompt: true,
+ expectRemainingUpdate: true,
+ });
+
+ // Setting the value to true should not call the prompt service so there
+ // should still be an active update, even if we indicate we can discard
+ // the update in a hypothetical prompt.
+ await changeAndVerifyUpdateWrites({
+ tab,
+ newConfigValue: true,
+ discardUpdate: true,
+ expectPrompt: false,
+ expectRemainingUpdate: true,
+ });
+
+ // Setting the value to false will call the prompt service, and we do
+ // discard the update, so there should not be an active update.
+ await changeAndVerifyUpdateWrites({
+ tab,
+ newConfigValue: false,
+ discardUpdate: true,
+ expectPrompt: true,
+ expectRemainingUpdate: false,
+ });
+
+ await BrowserTestUtils.removeTab(tab);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_cantApply.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_cantApply.js
new file mode 100644
index 0000000000..90f4c385cc
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_cantApply.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_check_cantApply() {
+ lockWriteTestFile();
+
+ let params = { checkAttempts: 1, queryString: "&promptWaitTime=0" };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { whatsNew: gDetailsURL, manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_malformedXML.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_malformedXML.js
new file mode 100644
index 0000000000..d83bc70b6f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_malformedXML.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_check_malformedXML() {
+ const maxBackgroundErrors = 10;
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors]],
+ });
+
+ let params = {
+ checkAttempts: maxBackgroundErrors,
+ queryString: "&xmlMalformed=1",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If the update check fails 10 consecutive attempts then the manual
+ // update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { whatsNew: gDetailsURL, manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_unsupported.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_unsupported.js
new file mode 100644
index 0000000000..02aaab1064
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_check_unsupported.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for doorhanger background check for updates
+// with an unsupported update.
+add_task(async function doorhanger_bc_check_unsupported() {
+ let params = { checkAttempts: 1, queryString: "&unsupported=1" };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-unsupported",
+ button: "button",
+ pageURLs: { manual: gDetailsURL },
+ },
+ async function doorhanger_unsupported_persist() {
+ await TestUtils.waitForCondition(
+ () => PanelUI.menuButton.hasAttribute("badge-status"),
+ "Waiting for update badge",
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test.
+ logTestInfo(e);
+ });
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The window's doorhanger is closed."
+ );
+ ok(
+ PanelUI.menuButton.hasAttribute("badge-status"),
+ "The window has a badge."
+ );
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-unsupported",
+ "The correct badge is showing for the background window"
+ );
+
+ // Test persistence of the badge when the client has restarted by
+ // resetting the UpdateListener.
+ UpdateListener.reset();
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The window's doorhanger is closed."
+ );
+ ok(
+ !PanelUI.menuButton.hasAttribute("badge-status"),
+ "The window does not have a badge."
+ );
+ UpdateListener.maybeShowUnsupportedNotification();
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The window's doorhanger is closed."
+ );
+ ok(
+ PanelUI.menuButton.hasAttribute("badge-status"),
+ "The window has a badge."
+ );
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-unsupported",
+ "The correct badge is showing for the background window."
+ );
+ },
+ ]);
+
+ params = {
+ checkAttempts: 1,
+ queryString: "&invalidCompleteSize=1&promptWaitTime=0",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ async function doorhanger_unsupported_removed() {
+ // Test that finding an update removes the app.update.unsupported.url
+ // preference.
+ let unsupportedURL = Services.prefs.getCharPref(
+ PREF_APP_UPDATE_UNSUPPORTED_URL,
+ null
+ );
+ ok(
+ !unsupportedURL,
+ "The " + PREF_APP_UPDATE_UNSUPPORTED_URL + " preference was removed."
+ );
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js
new file mode 100644
index 0000000000..3678a440d2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloadAutoFailures() {
+ const maxBackgroundErrors = 5;
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors]],
+ });
+
+ let params = { checkAttempts: 1, queryString: "&badURL=1" };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If the update download fails maxBackgroundErrors download attempts then
+ // show the update available prompt.
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ // If the update process is unable to install the update show the manual
+ // update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js
new file mode 100644
index 0000000000..a4502d7626
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadAutoFailures_bgWin.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloadAutoFailures_bgWin() {
+ function getBackgroundWindowHandler(destroyWindow) {
+ return async function () {
+ await TestUtils.waitForCondition(
+ () => PanelUI.menuButton.hasAttribute("badge-status"),
+ "Background window has a badge.",
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test.
+ logTestInfo(e);
+ });
+ ok(
+ PanelUI.menuButton.hasAttribute("badge-status"),
+ "PanelUI.menuButton should have a 'badge-status' attribute"
+ );
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The doorhanger is not showing for the background window"
+ );
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-available",
+ "The badge is showing for the background window"
+ );
+
+ let buttonEl = getNotificationButton(
+ extraWindow,
+ "update-available",
+ "button"
+ );
+ buttonEl.click();
+
+ if (destroyWindow) {
+ // The next popup may be shown during closeWindow or promiseFocus
+ // calls.
+ let waitForPopupShown = new Promise(resolve => {
+ window.addEventListener(
+ "popupshown",
+ () => {
+ executeSoon(resolve);
+ },
+ { once: true }
+ );
+ });
+ await BrowserTestUtils.closeWindow(extraWindow);
+ await SimpleTest.promiseFocus(window);
+ await waitForPopupShown;
+ }
+ };
+ }
+
+ const maxBackgroundErrors = 5;
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors]],
+ });
+
+ let extraWindow = await BrowserTestUtils.openNewBrowserWindow();
+ await SimpleTest.promiseFocus(extraWindow);
+
+ let params = { checkAttempts: 1, queryString: "&badURL=1", popupShown: true };
+ await runDoorhangerUpdateTest(params, [
+ getBackgroundWindowHandler(false),
+ getBackgroundWindowHandler(true),
+ {
+ // If the update process is unable to install the update show the manual
+ // update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn.js
new file mode 100644
index 0000000000..2237161863
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloadOptIn() {
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let params = {
+ checkAttempts: 1,
+ queryString: "&invalidCompleteSize=1&promptWaitTime=0",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_bgWin.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_bgWin.js
new file mode 100644
index 0000000000..17dcce57ce
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_bgWin.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloadOptIn_bgWin() {
+ function getBackgroundWindowHandler() {
+ return async function () {
+ await TestUtils.waitForCondition(
+ () => PanelUI.menuButton.hasAttribute("badge-status"),
+ "Background window has a badge.",
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test.
+ logTestInfo(e);
+ });
+ ok(
+ PanelUI.menuButton.hasAttribute("badge-status"),
+ "PanelUI.menuButton should have a 'badge-status' attribute"
+ );
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "The doorhanger is not showing for the background window"
+ );
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-available",
+ "The badge is showing for the background window"
+ );
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ PanelUI.notificationPanel,
+ "popupshown"
+ );
+ await BrowserTestUtils.closeWindow(extraWindow);
+ await SimpleTest.promiseFocus(window);
+ await popupShownPromise;
+
+ let buttonEl = getNotificationButton(
+ window,
+ "update-available",
+ "button"
+ );
+ buttonEl.click();
+ };
+ }
+
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let extraWindow = await BrowserTestUtils.openNewBrowserWindow();
+ await SimpleTest.promiseFocus(extraWindow);
+
+ let params = { checkAttempts: 1, queryString: "&promptWaitTime=0" };
+ await runDoorhangerUpdateTest(params, [
+ getBackgroundWindowHandler(),
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_staging.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_staging.js
new file mode 100644
index 0000000000..7ba2d67964
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloadOptIn_staging.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloadOptIn_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ // Tests the app.update.promptWaitTime pref
+ [PREF_APP_UPDATE_PROMPTWAITTIME, 0],
+ [PREF_APP_UPDATE_STAGING_ENABLED, true],
+ ],
+ });
+ await UpdateUtils.setAppUpdateAutoEnabled(false);
+
+ let params = { checkAttempts: 1, queryString: "&invalidCompleteSize=1" };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded.js
new file mode 100644
index 0000000000..e29dad26fa
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloaded() {
+ let params = {
+ checkAttempts: 1,
+ queryString: "&invalidCompleteSize=1&promptWaitTime=0",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_disableBITS.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_disableBITS.js
new file mode 100644
index 0000000000..5f89c95322
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_disableBITS.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloaded_disableBITS() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_BITS_ENABLED, true]],
+ });
+
+ let params = {
+ checkAttempts: 1,
+ queryString: "&promptWaitTime=0&disableBITS=true",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ ]);
+
+ let patch = getPatchOfType(
+ "partial",
+ gUpdateManager.readyUpdate
+ ).QueryInterface(Ci.nsIWritablePropertyBag);
+ ok(
+ !patch.getProperty("bitsId"),
+ "The selected patch should not have a bitsId property"
+ );
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js
new file mode 100644
index 0000000000..50416608f2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_downloaded_staged.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_downloaded_staged() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let params = {
+ checkAttempts: 1,
+ queryString: "&invalidCompleteSize=1&promptWaitTime=0",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate.js
new file mode 100644
index 0000000000..07e7bf51fa
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * This test downloads 2 updates sequentially, and ensures that the doorhanger
+ * and badge do what they are supposed to do:
+ * First thing after the first download, the doorhanger should be displayed.
+ * Then download the next update.
+ * While that update stages, the badge should be hidden to prevent restarting
+ * to update while the update is staging.
+ * Once the staging completes, the badge should return. The doorhanger should
+ * not be shown at this time, because it has already been shown this
+ * session.
+ */
+
+const FIRST_UPDATE_VERSION = "999998.0";
+const SECOND_UPDATE_VERSION = "999999.0";
+
+function prepareToDownloadVersion(version) {
+ setUpdateURL(
+ URL_HTTP_UPDATE_SJS +
+ `?detailsURL=${gDetailsURL}&promptWaitTime=0&appVersion=${version}`
+ );
+}
+
+add_task(async function doorhanger_bc_multiUpdate() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let params = {
+ checkAttempts: 1,
+ queryString: "&promptWaitTime=0",
+ version: FIRST_UPDATE_VERSION,
+ slowStaging: true,
+ };
+ await runDoorhangerUpdateTest(params, [
+ () => {
+ return continueFileHandler(CONTINUE_STAGING);
+ },
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ },
+ async () => {
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-restart",
+ "Should have restart badge"
+ );
+
+ prepareToDownloadVersion(SECOND_UPDATE_VERSION);
+ let updateSwapped = waitForEvent("update-swap");
+ gAUS.checkForBackgroundUpdates();
+ await updateSwapped;
+ // The badge should be hidden while we swap from one update to the other
+ // to prevent restarting to update while staging is occurring. But since
+ // it will be waiting on the same event we are waiting on, wait an
+ // additional tick to let the other update-swap listeners run.
+ await TestUtils.waitForTick();
+
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "",
+ "Should not have restart badge during staging"
+ );
+
+ await continueFileHandler(CONTINUE_STAGING);
+
+ try {
+ await TestUtils.waitForCondition(
+ () =>
+ PanelUI.menuButton.getAttribute("badge-status") == "update-restart",
+ "Waiting for update restart badge to return after staging"
+ );
+ } catch (ex) {}
+
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-restart",
+ "Restart badge should be restored after staging completes"
+ );
+ is(
+ PanelUI.notificationPanel.state,
+ "closed",
+ "Should not open a second doorhanger"
+ );
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate_promptWaitTime.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate_promptWaitTime.js
new file mode 100644
index 0000000000..00c61bcbb4
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_multiUpdate_promptWaitTime.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * This test downloads 2 updates sequentially, and ensures that the doorhanger
+ * and badge do what they are supposed to do. However, the first update has a
+ * long promptWaitTime, and the second has a short one and the badge wait time
+ * is set to 0. This should result in this behavior:
+ * First thing after the first download, the badge should be displayed, but
+ * not the doorhanger.
+ * Then download the next update.
+ * While that update stages, the badge should be hidden to prevent restarting
+ * to update while the update is staging.
+ * Once the staging completes, the doorhanger should be shown. Despite the
+ * long promptWaitTime of the initial update, this patch's short wait time
+ * means that the doorhanger should be shown soon rather than in a long
+ * time.
+ */
+
+const FIRST_UPDATE_VERSION = "999998.0";
+const SECOND_UPDATE_VERSION = "999999.0";
+const LONG_PROMPT_WAIT_TIME_SEC = 10 * 60 * 60; // 10 hours
+
+function prepareToDownloadVersion(version, promptWaitTime) {
+ setUpdateURL(
+ URL_HTTP_UPDATE_SJS +
+ `?detailsURL=${gDetailsURL}&promptWaitTime=${promptWaitTime}&appVersion=${version}`
+ );
+}
+
+add_task(async function doorhanger_bc_multiUpdate() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_APP_UPDATE_STAGING_ENABLED, true],
+ [PREF_APP_UPDATE_BADGEWAITTIME, 0],
+ ],
+ });
+
+ let params = {
+ checkAttempts: 1,
+ queryString: `&promptWaitTime=${LONG_PROMPT_WAIT_TIME_SEC}`,
+ version: FIRST_UPDATE_VERSION,
+ slowStaging: true,
+ };
+ await runDoorhangerUpdateTest(params, [
+ async () => {
+ await continueFileHandler(CONTINUE_STAGING);
+
+ try {
+ await TestUtils.waitForCondition(
+ () =>
+ PanelUI.menuButton.getAttribute("badge-status") == "update-restart",
+ "Waiting for update restart badge to return after staging"
+ );
+ } catch (ex) {}
+
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "update-restart",
+ "Should have restart badge"
+ );
+
+ prepareToDownloadVersion(SECOND_UPDATE_VERSION, 0);
+ let updateSwapped = waitForEvent("update-swap");
+ gAUS.checkForBackgroundUpdates();
+ await updateSwapped;
+ // The badge should be hidden while we swap from one update to the other
+ // to prevent restarting to update while staging is occurring. But since
+ // it will be waiting on the same event we are waiting on, wait an
+ // additional tick to let the other update-swap listeners run.
+ await TestUtils.waitForTick();
+
+ is(
+ PanelUI.menuButton.getAttribute("badge-status"),
+ "",
+ "Should not have restart badge during staging"
+ );
+
+ await continueFileHandler(CONTINUE_STAGING);
+ },
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js
new file mode 100644
index 0000000000..3c29d6b4b0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_completeBadSize.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_patch_completeBadSize() {
+ let params = {
+ checkAttempts: 1,
+ queryString: "&completePatchOnly=1&invalidCompleteSize=1",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If the update download fails maxBackgroundErrors download attempts then
+ // show the update available prompt.
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ // If the update process is unable to install the update show the manual
+ // update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js
new file mode 100644
index 0000000000..68854b3f26
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_patch_partialBadSize() {
+ let params = {
+ checkAttempts: 1,
+ queryString: "&partialPatchOnly=1&invalidPartialSize=1",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If the update download fails maxBackgroundErrors download attempts then
+ // show the update available prompt.
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ // If the update process is unable to install the update show the manual
+ // update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_complete.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_complete.js
new file mode 100644
index 0000000000..f3c9b1f51f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_complete.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_patch_partialBadSize_complete() {
+ let params = {
+ checkAttempts: 1,
+ queryString: "&invalidPartialSize=1&promptWaitTime=0",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js
new file mode 100644
index 0000000000..a18e2f6444
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_bc_patch_partialBadSize_completeBadSize.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_bc_patch_partialBadSize_completeBadSize() {
+ let params = {
+ checkAttempts: 1,
+ queryString: "&invalidPartialSize=1&invalidCompleteSize=1",
+ };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If the update download fails maxBackgroundErrors download attempts then
+ // show the update available prompt.
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ notificationId: "update-available",
+ button: "button",
+ checkActiveUpdate: null,
+ },
+ {
+ // If the update process is unable to install the update show the manual
+ // update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_completeApplyFailure.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_completeApplyFailure.js
new file mode 100644
index 0000000000..5c7c937d81
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_completeApplyFailure.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_sp_patch_completeApplyFailure() {
+ let patchProps = { state: STATE_PENDING };
+ let patches = getLocalPatchString(patchProps);
+ let updateProps = { checkInterval: "1" };
+ let updates = getLocalUpdateString(updateProps, patches);
+
+ let params = { updates };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If the update process is unable to install the update show the manual
+ // update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure.js
new file mode 100644
index 0000000000..45434c8361
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_sp_patch_partialApplyFailure() {
+ let patchProps = { type: "partial", state: STATE_PENDING };
+ let patches = getLocalPatchString(patchProps);
+ let updateProps = { isCompleteUpdate: "false", checkInterval: "1" };
+ let updates = getLocalUpdateString(updateProps, patches);
+
+ let params = { updates };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If there is only an invalid patch show the manual update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete.js
new file mode 100644
index 0000000000..bf533dab04
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function doorhanger_sp_patch_partialApplyFailure_complete() {
+ let patchProps = { type: "partial", state: STATE_PENDING };
+ let patches = getLocalPatchString(patchProps);
+ patchProps = { selected: "false" };
+ patches += getLocalPatchString(patchProps);
+ let updateProps = { isCompleteUpdate: "false", promptWaitTime: "0" };
+ let updates = getLocalUpdateString(updateProps, patches);
+
+ let params = { updates };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_PENDING },
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js
new file mode 100644
index 0000000000..df17bc1220
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_completeBadSize.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(
+ async function doorhanger_sp_patch_partialApplyFailure_completeBadSize() {
+ // Because of the way the test is simulating failure it has to pretend it has
+ // already retried.
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 0]],
+ });
+
+ let patchProps = { type: "partial", state: STATE_PENDING };
+ let patches = getLocalPatchString(patchProps);
+ patchProps = { size: "1234", selected: "false" };
+ patches += getLocalPatchString(patchProps);
+ let updateProps = { isCompleteUpdate: "false" };
+ let updates = getLocalUpdateString(updateProps, patches);
+
+ let params = { updates };
+ await runDoorhangerUpdateTest(params, [
+ {
+ // If there is only an invalid patch show the manual update doorhanger.
+ notificationId: "update-manual",
+ button: "button",
+ checkActiveUpdate: null,
+ pageURLs: { manual: URL_MANUAL_UPDATE },
+ },
+ ]);
+ }
+);
diff --git a/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js
new file mode 100644
index 0000000000..a99c04b0be
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_doorhanger_sp_patch_partialApplyFailure_complete_staging.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(
+ async function doorhanger_sp_patch_partialApplyFailure_complete_staging() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let patchProps = { type: "partial", state: STATE_PENDING };
+ let patches = getLocalPatchString(patchProps);
+ patchProps = { selected: "false" };
+ patches += getLocalPatchString(patchProps);
+ let updateProps = { isCompleteUpdate: "false", promptWaitTime: "0" };
+ let updates = getLocalUpdateString(updateProps, patches);
+
+ let params = { updates };
+ await runDoorhangerUpdateTest(params, [
+ {
+ notificationId: "update-restart",
+ button: "secondaryButton",
+ checkActiveUpdate: { state: STATE_APPLIED },
+ },
+ ]);
+ }
+);
diff --git a/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js b/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js
new file mode 100644
index 0000000000..6aa32a7fc9
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js
@@ -0,0 +1,139 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function elevation_dialog() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_DISABLEDFORTESTING, false]],
+ });
+
+ // Create a mock of nsIAppStartup's quit method so clicking the restart button
+ // won't restart the application.
+ let { startup } = Services;
+ let appStartup = {
+ QueryInterface: ChromeUtils.generateQI(["nsIAppStartup"]),
+ quit(mode) {
+ if (elevationDialog) {
+ elevationDialog.close();
+ elevationDialog = null;
+ }
+ },
+ };
+ Services.startup = appStartup;
+ registerCleanupFunction(() => {
+ Services.startup = startup;
+ });
+
+ registerCleanupFunction(async () => {
+ let win = Services.wm.getMostRecentWindow("Update:Elevation");
+ if (win) {
+ win.close();
+ await TestUtils.waitForCondition(
+ () => !Services.wm.getMostRecentWindow("Update:Elevation"),
+ "The Update Elevation dialog should have closed"
+ );
+ }
+ });
+
+ // Test clicking the "Restart Later" button
+ let elevationDialog = await waitForElevationDialog();
+ await TestUtils.waitForTick();
+ elevationDialog.document.getElementById("elevateExtra2").click();
+ await TestUtils.waitForCondition(
+ () => !Services.wm.getMostRecentWindow("Update:Elevation"),
+ "The Update Elevation dialog should have closed"
+ );
+ ok(!!gUpdateManager.readyUpdate, "There should be a ready update");
+ is(
+ gUpdateManager.readyUpdate.state,
+ STATE_PENDING_ELEVATE,
+ "The ready update state should equal " + STATE_PENDING_ELEVATE
+ );
+ is(
+ readStatusFile(),
+ STATE_PENDING_ELEVATE,
+ "The status file state should equal " + STATE_PENDING_ELEVATE
+ );
+
+ // Test clicking the "No Thanks" button
+ elevationDialog = await waitForElevationDialog();
+ await TestUtils.waitForTick();
+ elevationDialog.document.getElementById("elevateExtra1").click();
+ await TestUtils.waitForCondition(
+ () => !Services.wm.getMostRecentWindow("Update:Elevation"),
+ "The Update Elevation dialog should have closed"
+ );
+ ok(!gUpdateManager.readyUpdate, "There should not be a ready update");
+ is(
+ readStatusFile(),
+ STATE_NONE,
+ "The status file state should equal " + STATE_NONE
+ );
+
+ // Test clicking the "Restart <brandShortName>" button
+ elevationDialog = await waitForElevationDialog();
+ await TestUtils.waitForTick();
+ elevationDialog.document.getElementById("elevateAccept").click();
+ await TestUtils.waitForCondition(
+ () => !Services.wm.getMostRecentWindow("Update:Elevation"),
+ "The Update Elevation dialog should have closed"
+ );
+ ok(!!gUpdateManager.readyUpdate, "There should be a ready update");
+ is(
+ gUpdateManager.readyUpdate.state,
+ STATE_PENDING_ELEVATE,
+ "The active update state should equal " + STATE_PENDING_ELEVATE
+ );
+ is(
+ readStatusFile(),
+ STATE_PENDING,
+ "The status file state should equal " + STATE_PENDING
+ );
+});
+
+/**
+ * Waits for the Update Elevation Dialog to load.
+ *
+ * @return A promise that returns the domWindow for the Update Elevation Dialog
+ * and resolves when the Update Elevation Dialog loads.
+ */
+function waitForElevationDialog() {
+ return new Promise(resolve => {
+ var listener = {
+ onOpenWindow: aXULWindow => {
+ debugDump("Update Elevation dialog shown...");
+ Services.wm.removeListener(listener);
+
+ async function elevationDialogOnLoad() {
+ domwindow.removeEventListener("load", elevationDialogOnLoad, true);
+ let chromeURI =
+ "chrome://mozapps/content/update/updateElevation.xhtml";
+ is(
+ domwindow.document.location.href,
+ chromeURI,
+ "Update Elevation appeared"
+ );
+ resolve(domwindow);
+ }
+
+ var domwindow = aXULWindow.docShell.domWindow;
+ domwindow.addEventListener("load", elevationDialogOnLoad, true);
+ },
+ onCloseWindow: aXULWindow => {},
+ };
+
+ Services.wm.addListener(listener);
+ // Add the active-update.xml and update.status files used for these tests,
+ // reload the update manager, and then simulate startup so the Update
+ // Elevation Dialog is opened.
+ let patchProps = { state: STATE_PENDING_ELEVATE };
+ let patches = getLocalPatchString(patchProps);
+ let updateProps = { checkInterval: "1" };
+ let updates = getLocalUpdateString(updateProps, patches);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_PENDING_ELEVATE);
+ reloadUpdateManagerData();
+ testPostUpdateProcessing();
+ });
+}
diff --git a/toolkit/mozapps/update/tests/browser/browser_memory_allocation_error_fallback.js b/toolkit/mozapps/update/tests/browser/browser_memory_allocation_error_fallback.js
new file mode 100644
index 0000000000..55ec2e14a1
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_memory_allocation_error_fallback.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * When the updater fails with a memory allocation error, we should fall back to
+ * updating without staging.
+ */
+
+const READ_STRINGS_MEM_ERROR = 10;
+const ARCHIVE_READER_MEM_ERROR = 11;
+const BSPATCH_MEM_ERROR = 12;
+const UPDATER_MEM_ERROR = 13;
+const UPDATER_QUOTED_PATH_MEM_ERROR = 14;
+
+const EXPECTED_STATUS =
+ AppConstants.platform == "win" ? STATE_PENDING_SVC : STATE_PENDING;
+
+add_setup(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_APP_UPDATE_STAGING_ENABLED, true],
+ [PREF_APP_UPDATE_SERVICE_ENABLED, true],
+ ],
+ });
+
+ registerCleanupFunction(() => {
+ Services.env.set("MOZ_FORCE_ERROR_CODE", "");
+ });
+});
+
+async function memAllocErrorFallback(errorCode) {
+ Services.env.set("MOZ_FORCE_ERROR_CODE", errorCode.toString());
+
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = {
+ queryString: "&invalidCompleteSize=1",
+ backgroundUpdate: true,
+ continueFile: CONTINUE_STAGING,
+ waitForUpdateState: EXPECTED_STATUS,
+ };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: EXPECTED_STATUS },
+ continueFile: null,
+ },
+ ]);
+}
+
+function cleanup() {
+ reloadUpdateManagerData(true);
+ removeUpdateFiles(true);
+}
+
+add_task(async function memAllocErrorFallback_READ_STRINGS_MEM_ERROR() {
+ await memAllocErrorFallback(READ_STRINGS_MEM_ERROR);
+ cleanup();
+});
+
+add_task(async function memAllocErrorFallback_ARCHIVE_READER_MEM_ERROR() {
+ await memAllocErrorFallback(ARCHIVE_READER_MEM_ERROR);
+ cleanup();
+});
+
+add_task(async function memAllocErrorFallback_BSPATCH_MEM_ERROR() {
+ await memAllocErrorFallback(BSPATCH_MEM_ERROR);
+ cleanup();
+});
+
+add_task(async function memAllocErrorFallback_UPDATER_MEM_ERROR() {
+ await memAllocErrorFallback(UPDATER_MEM_ERROR);
+ cleanup();
+});
+
+add_task(async function memAllocErrorFallback_UPDATER_QUOTED_PATH_MEM_ERROR() {
+ await memAllocErrorFallback(UPDATER_QUOTED_PATH_MEM_ERROR);
+ cleanup();
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_downloaded_ready.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_downloaded_ready.js
new file mode 100644
index 0000000000..f9045bbdda
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_downloaded_ready.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { TelemetryArchiveTesting } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryArchiveTesting.sys.mjs"
+);
+
+/**
+ * Test that UpdatePing telemetry with a payload reason of ready is sent for a
+ * staged update.
+ *
+ * Please note that this is really a Telemetry test, not an
+ * "update UI" test like the rest of the tests in this directory.
+ * This test does not live in toolkit/components/telemetry/tests to prevent
+ * duplicating the code for all the test dependencies. Unfortunately, due
+ * to a limitation in the build system, we were not able to simply reference
+ * the dependencies as "support-files" in the test manifest.
+ */
+add_task(async function telemetry_updatePing_ready() {
+ let archiveChecker = new TelemetryArchiveTesting.Checker();
+ await archiveChecker.promiseInit();
+
+ let updateParams = "";
+ await runTelemetryUpdateTest(updateParams, "update-downloaded");
+
+ // We cannot control when the ping will be generated/archived after we trigger
+ // an update, so let's make sure to have one before moving on with validation.
+ let updatePing;
+ await TestUtils.waitForCondition(
+ async function () {
+ // Check that the ping made it into the Telemetry archive.
+ // The test data is defined in ../data/sharedUpdateXML.js
+ updatePing = await archiveChecker.promiseFindPing("update", [
+ [["payload", "reason"], "ready"],
+ [["payload", "targetBuildId"], "20080811053724"],
+ ]);
+ return !!updatePing;
+ },
+ "Make sure the ping is generated before trying to validate it.",
+ 500,
+ 100
+ );
+
+ ok(updatePing, "The 'update' ping must be correctly sent.");
+
+ // We don't know the exact value for the other fields, so just check
+ // that they're available.
+ for (let f of ["targetVersion", "targetChannel", "targetDisplayVersion"]) {
+ ok(
+ f in updatePing.payload,
+ `${f} must be available in the update ping payload.`
+ );
+ ok(
+ typeof updatePing.payload[f] == "string",
+ `${f} must have the correct format.`
+ );
+ }
+
+ // Also make sure that the ping contains both a client id and an
+ // environment section.
+ ok("clientId" in updatePing, "The update ping must report a client id.");
+ ok(
+ "environment" in updatePing,
+ "The update ping must report the environment."
+ );
+});
diff --git a/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_staged_ready.js b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_staged_ready.js
new file mode 100644
index 0000000000..42bd0da546
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/browser_telemetry_updatePing_staged_ready.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { TelemetryArchiveTesting } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryArchiveTesting.sys.mjs"
+);
+
+/**
+ * Test that UpdatePing telemetry with a payload reason of ready is sent for a
+ * staged update.
+ *
+ * Please note that this is really a Telemetry test, not an
+ * "update UI" test like the rest of the tests in this directory.
+ * This test does not live in toolkit/components/telemetry/tests to prevent
+ * duplicating the code for all the test dependencies. Unfortunately, due
+ * to a limitation in the build system, we were not able to simply reference
+ * the dependencies as "support-files" in the test manifest.
+ */
+add_task(async function telemetry_updatePing_ready() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]],
+ });
+
+ let archiveChecker = new TelemetryArchiveTesting.Checker();
+ await archiveChecker.promiseInit();
+
+ let updateParams = "";
+ await runTelemetryUpdateTest(updateParams, "update-staged");
+
+ // We cannot control when the ping will be generated/archived after we trigger
+ // an update, so let's make sure to have one before moving on with validation.
+ let updatePing;
+ await TestUtils.waitForCondition(
+ async function () {
+ // Check that the ping made it into the Telemetry archive.
+ // The test data is defined in ../data/sharedUpdateXML.js
+ updatePing = await archiveChecker.promiseFindPing("update", [
+ [["payload", "reason"], "ready"],
+ [["payload", "targetBuildId"], "20080811053724"],
+ ]);
+ return !!updatePing;
+ },
+ "Make sure the ping is generated before trying to validate it.",
+ 500,
+ 100
+ );
+
+ ok(updatePing, "The 'update' ping must be correctly sent.");
+
+ // We don't know the exact value for the other fields, so just check
+ // that they're available.
+ for (let f of ["targetVersion", "targetChannel", "targetDisplayVersion"]) {
+ ok(
+ f in updatePing.payload,
+ `${f} must be available in the update ping payload.`
+ );
+ ok(
+ typeof updatePing.payload[f] == "string",
+ `${f} must have the correct format.`
+ );
+ }
+
+ // Also make sure that the ping contains both a client id and an
+ // environment section.
+ ok("clientId" in updatePing, "The update ping must report a client id.");
+ ok(
+ "environment" in updatePing,
+ "The update ping must report the environment."
+ );
+});
diff --git a/toolkit/mozapps/update/tests/browser/downloadPage.html b/toolkit/mozapps/update/tests/browser/downloadPage.html
new file mode 100644
index 0000000000..4810e2e0d6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/downloadPage.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Download page</title>
+ <meta charset="utf-8">
+</head>
+<body>
+<!-- just use simple.mar since we have it available and it will result in a download dialog -->
+<a id="download-link" href="http://example.com/browser/browser/base/content/test/appUpdate/simple.mar" data-link-type="download">
+ Download
+</a>
+</body>
+</html>
diff --git a/toolkit/mozapps/update/tests/browser/head.js b/toolkit/mozapps/update/tests/browser/head.js
new file mode 100644
index 0000000000..756b2c7ea5
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/head.js
@@ -0,0 +1,1347 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs",
+ DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs",
+ UpdateListener: "resource://gre/modules/UpdateListener.sys.mjs",
+});
+const { XPIInstall } = ChromeUtils.import(
+ "resource://gre/modules/addons/XPIInstall.jsm"
+);
+
+const BIN_SUFFIX = AppConstants.platform == "win" ? ".exe" : "";
+const FILE_UPDATER_BIN =
+ "updater" + (AppConstants.platform == "macosx" ? ".app" : BIN_SUFFIX);
+const FILE_UPDATER_BIN_BAK = FILE_UPDATER_BIN + ".bak";
+
+const LOG_FUNCTION = info;
+
+const MAX_UPDATE_COPY_ATTEMPTS = 10;
+
+const DATA_URI_SPEC =
+ "chrome://mochitests/content/browser/toolkit/mozapps/update/tests/browser/";
+/* import-globals-from testConstants.js */
+Services.scriptloader.loadSubScript(DATA_URI_SPEC + "testConstants.js", this);
+
+var gURLData = URL_HOST + "/" + REL_PATH_DATA;
+const URL_MANUAL_UPDATE = gURLData + "downloadPage.html";
+
+const gBadSizeResult = Cr.NS_ERROR_UNEXPECTED.toString();
+
+/* import-globals-from ../data/shared.js */
+Services.scriptloader.loadSubScript(DATA_URI_SPEC + "shared.js", this);
+
+let gOriginalUpdateAutoValue = null;
+
+// Some elements append a trailing /. After the chrome tests are removed this
+// code can be changed so URL_HOST already has a trailing /.
+const gDetailsURL = URL_HOST + "/";
+
+// Set to true to log additional information for debugging. To log additional
+// information for individual tests set gDebugTest to false here and to true
+// globally in the test.
+gDebugTest = false;
+
+// This is to accommodate the TV task which runs the tests with --verify.
+requestLongerTimeout(10);
+
+/**
+ * Common tasks to perform for all tests before each one has started.
+ */
+add_setup(async function setupTestCommon() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_APP_UPDATE_BADGEWAITTIME, 1800],
+ [PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, 0],
+ [PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 2],
+ [PREF_APP_UPDATE_LOG, gDebugTest],
+ [PREF_APP_UPDATE_PROMPTWAITTIME, 3600],
+ [PREF_APP_UPDATE_SERVICE_ENABLED, false],
+ ],
+ });
+
+ // We need to keep the update sync manager from thinking two instances are
+ // running because of the mochitest parent instance, which means we need to
+ // override the directory service with a fake executable path and then reset
+ // the lock. But leaving the directory service overridden causes problems for
+ // these tests, so we need to restore the real service immediately after.
+ // To form the path, we'll use the real executable path with a token appended
+ // (the path needs to be absolute, but not to point to a real file).
+ // This block is loosely copied from adjustGeneralPaths() in another update
+ // test file, xpcshellUtilsAUS.js, but this is a much more limited version;
+ // it's been copied here both because the full function is overkill and also
+ // because making it general enough to run in both xpcshell and mochitest
+ // would have been unreasonably difficult.
+ let exePath = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile);
+ let dirProvider = {
+ getFile: function AGP_DP_getFile(aProp, aPersistent) {
+ // Set the value of persistent to false so when this directory provider is
+ // unregistered it will revert back to the original provider.
+ aPersistent.value = false;
+ switch (aProp) {
+ case XRE_EXECUTABLE_FILE:
+ exePath.append("browser-test");
+ return exePath;
+ }
+ return null;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]),
+ };
+ let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService);
+ ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE);
+ ds.registerProvider(dirProvider);
+
+ let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
+ Ci.nsIUpdateSyncManager
+ );
+ syncManager.resetLock();
+
+ ds.unregisterProvider(dirProvider);
+
+ setUpdateTimerPrefs();
+ reloadUpdateManagerData(true);
+ removeUpdateFiles(true);
+ UpdateListener.reset();
+ AppMenuNotifications.removeNotification(/.*/);
+ // Most app update mochitest-browser-chrome tests expect auto update to be
+ // enabled. Those that don't will explicitly change this.
+ await setAppUpdateAutoEnabledHelper(true);
+});
+
+/**
+ * Common tasks to perform for all tests after each one has finished.
+ */
+registerCleanupFunction(async () => {
+ AppMenuNotifications.removeNotification(/.*/);
+ Services.env.set("MOZ_TEST_SKIP_UPDATE_STAGE", "");
+ Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "");
+ Services.env.set("MOZ_TEST_STAGING_ERROR", "");
+ UpdateListener.reset();
+ AppMenuNotifications.removeNotification(/.*/);
+ reloadUpdateManagerData(true);
+ // Pass false when the log files are needed for troubleshooting the tests.
+ removeUpdateFiles(true);
+ // Always try to restore the original updater files. If none of the updater
+ // backup files are present then this is just a no-op.
+ await finishTestRestoreUpdaterBackup();
+ // Reset the update lock once again so that we know the lock we're
+ // interested in here will be closed properly (normally that happens during
+ // XPCOM shutdown, but that isn't consistent during tests).
+ let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService(
+ Ci.nsIUpdateSyncManager
+ );
+ syncManager.resetLock();
+});
+
+/**
+ * Overrides the add-ons manager language pack staging with a mocked version.
+ * The returned promise resolves when language pack staging begins returning an
+ * object with the new appVersion and platformVersion and functions to resolve
+ * or reject the install.
+ */
+function mockLangpackInstall() {
+ let original = XPIInstall.stageLangpacksForAppUpdate;
+ registerCleanupFunction(() => {
+ XPIInstall.stageLangpacksForAppUpdate = original;
+ });
+
+ let stagingCall = PromiseUtils.defer();
+ XPIInstall.stageLangpacksForAppUpdate = (appVersion, platformVersion) => {
+ let result = PromiseUtils.defer();
+ stagingCall.resolve({
+ appVersion,
+ platformVersion,
+ resolve: result.resolve,
+ reject: result.reject,
+ });
+
+ return result.promise;
+ };
+
+ return stagingCall.promise;
+}
+
+/**
+ * Creates and locks the app update write test file so it is possible to test
+ * when the user doesn't have write access to update. Since this is only
+ * possible on Windows the function throws when it is called on other platforms.
+ * This uses registerCleanupFunction to remove the lock and the file when the
+ * test completes.
+ *
+ * @throws If the function is called on a platform other than Windows.
+ */
+function lockWriteTestFile() {
+ if (AppConstants.platform != "win") {
+ throw new Error("Windows only test function called");
+ }
+ let file = getUpdateDirFile(FILE_UPDATE_TEST).QueryInterface(
+ Ci.nsILocalFileWin
+ );
+ // Remove the file if it exists just in case.
+ if (file.exists()) {
+ file.readOnly = false;
+ file.remove(false);
+ }
+ file.create(file.NORMAL_FILE_TYPE, 0o444);
+ file.readOnly = true;
+ registerCleanupFunction(() => {
+ file.readOnly = false;
+ file.remove(false);
+ });
+}
+
+/**
+ * Closes the update mutex handle in nsUpdateService.js if it exists and then
+ * creates a new update mutex handle so the update code thinks there is another
+ * instance of the application handling updates.
+ *
+ * @throws If the function is called on a platform other than Windows.
+ */
+function setOtherInstanceHandlingUpdates() {
+ if (AppConstants.platform != "win") {
+ throw new Error("Windows only test function called");
+ }
+ gAUS.observe(null, "test-close-handle-update-mutex", "");
+ let handle = createMutex(getPerInstallationMutexName());
+ registerCleanupFunction(() => {
+ closeHandle(handle);
+ });
+}
+
+/**
+ * Gets the update version info for the update url parameters to send to
+ * app_update.sjs.
+ *
+ * @param aAppVersion (optional)
+ * The application version for the update snippet. If not specified the
+ * current application version will be used.
+ * @return The url parameters for the application and platform version to send
+ * to app_update.sjs.
+ */
+function getVersionParams(aAppVersion) {
+ let appInfo = Services.appinfo;
+ return "&appVersion=" + (aAppVersion ? aAppVersion : appInfo.version);
+}
+
+/**
+ * Prevent nsIUpdateTimerManager from notifying nsIApplicationUpdateService
+ * to check for updates by setting the app update last update time to the
+ * current time minus one minute in seconds and the interval time to 12 hours
+ * in seconds.
+ */
+function setUpdateTimerPrefs() {
+ let now = Math.round(Date.now() / 1000) - 60;
+ Services.prefs.setIntPref(PREF_APP_UPDATE_LASTUPDATETIME, now);
+ Services.prefs.setIntPref(PREF_APP_UPDATE_INTERVAL, 43200);
+}
+
+/*
+ * Sets the value of the App Auto Update setting and sets it back to the
+ * original value at the start of the test when the test finishes.
+ *
+ * @param enabled
+ * The value to set App Auto Update to.
+ */
+async function setAppUpdateAutoEnabledHelper(enabled) {
+ if (gOriginalUpdateAutoValue == null) {
+ gOriginalUpdateAutoValue = await UpdateUtils.getAppUpdateAutoEnabled();
+ registerCleanupFunction(async () => {
+ await UpdateUtils.setAppUpdateAutoEnabled(gOriginalUpdateAutoValue);
+ });
+ }
+ await UpdateUtils.setAppUpdateAutoEnabled(enabled);
+}
+
+/**
+ * Gets the specified button for the notification.
+ *
+ * @param win
+ * The window to get the notification button for.
+ * @param notificationId
+ * The ID of the notification to get the button for.
+ * @param button
+ * The anonid of the button to get.
+ * @return The button element.
+ */
+function getNotificationButton(win, notificationId, button) {
+ let notification = win.document.getElementById(
+ `appMenu-${notificationId}-notification`
+ );
+ ok(!notification.hidden, `${notificationId} notification is showing`);
+ return notification[button];
+}
+
+/**
+ * For staging tests the test updater must be used and this restores the backed
+ * up real updater if it exists and tries again on failure since Windows debug
+ * builds at times leave the file in use. After success moveRealUpdater is
+ * called to continue the setup of the test updater.
+ */
+function setupTestUpdater() {
+ return (async function () {
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED)) {
+ try {
+ restoreUpdaterBackup();
+ } catch (e) {
+ logTestInfo(
+ "Attempt to restore the backed up updater failed... " +
+ "will try again, Exception: " +
+ e
+ );
+ await TestUtils.waitForTick();
+ await setupTestUpdater();
+ return;
+ }
+ await moveRealUpdater();
+ }
+ })();
+}
+
+/**
+ * Backs up the real updater and tries again on failure since Windows debug
+ * builds at times leave the file in use. After success it will call
+ * copyTestUpdater to continue the setup of the test updater.
+ */
+function moveRealUpdater() {
+ return (async function () {
+ try {
+ // Move away the real updater
+ let greBinDir = getGREBinDir();
+ let updater = greBinDir.clone();
+ updater.append(FILE_UPDATER_BIN);
+ updater.moveTo(greBinDir, FILE_UPDATER_BIN_BAK);
+
+ let greDir = getGREDir();
+ let updateSettingsIni = greDir.clone();
+ updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
+ if (updateSettingsIni.exists()) {
+ updateSettingsIni.moveTo(greDir, FILE_UPDATE_SETTINGS_INI_BAK);
+ }
+
+ let precomplete = greDir.clone();
+ precomplete.append(FILE_PRECOMPLETE);
+ if (precomplete.exists()) {
+ precomplete.moveTo(greDir, FILE_PRECOMPLETE_BAK);
+ }
+ } catch (e) {
+ logTestInfo(
+ "Attempt to move the real updater out of the way failed... " +
+ "will try again, Exception: " +
+ e
+ );
+ await TestUtils.waitForTick();
+ await moveRealUpdater();
+ return;
+ }
+
+ await copyTestUpdater();
+ })();
+}
+
+/**
+ * Copies the test updater and tries again on failure since Windows debug builds
+ * at times leave the file in use.
+ */
+function copyTestUpdater(attempt = 0) {
+ return (async function () {
+ try {
+ // Copy the test updater
+ let greBinDir = getGREBinDir();
+ let testUpdaterDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ let relPath = REL_PATH_DATA;
+ let pathParts = relPath.split("/");
+ for (let i = 0; i < pathParts.length; ++i) {
+ testUpdaterDir.append(pathParts[i]);
+ }
+
+ let testUpdater = testUpdaterDir.clone();
+ testUpdater.append(FILE_UPDATER_BIN);
+ testUpdater.copyToFollowingLinks(greBinDir, FILE_UPDATER_BIN);
+
+ let greDir = getGREDir();
+ let updateSettingsIni = greDir.clone();
+ updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
+ writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
+
+ let precomplete = greDir.clone();
+ precomplete.append(FILE_PRECOMPLETE);
+ writeFile(precomplete, PRECOMPLETE_CONTENTS);
+ } catch (e) {
+ if (attempt < MAX_UPDATE_COPY_ATTEMPTS) {
+ logTestInfo(
+ "Attempt to copy the test updater failed... " +
+ "will try again, Exception: " +
+ e
+ );
+ await TestUtils.waitForTick();
+ await copyTestUpdater(attempt++);
+ }
+ }
+ })();
+}
+
+/**
+ * Restores the updater and updater related file that if there a backup exists.
+ * This is called in setupTestUpdater before the backup of the real updater is
+ * done in case the previous test failed to restore the file when a test has
+ * finished. This is also called in finishTestRestoreUpdaterBackup to restore
+ * the files when a test finishes.
+ */
+function restoreUpdaterBackup() {
+ let greBinDir = getGREBinDir();
+ let updater = greBinDir.clone();
+ let updaterBackup = greBinDir.clone();
+ updater.append(FILE_UPDATER_BIN);
+ updaterBackup.append(FILE_UPDATER_BIN_BAK);
+ if (updaterBackup.exists()) {
+ if (updater.exists()) {
+ updater.remove(true);
+ }
+ updaterBackup.moveTo(greBinDir, FILE_UPDATER_BIN);
+ }
+
+ let greDir = getGREDir();
+ let updateSettingsIniBackup = greDir.clone();
+ updateSettingsIniBackup.append(FILE_UPDATE_SETTINGS_INI_BAK);
+ if (updateSettingsIniBackup.exists()) {
+ let updateSettingsIni = greDir.clone();
+ updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
+ if (updateSettingsIni.exists()) {
+ updateSettingsIni.remove(false);
+ }
+ updateSettingsIniBackup.moveTo(greDir, FILE_UPDATE_SETTINGS_INI);
+ }
+
+ let precomplete = greDir.clone();
+ let precompleteBackup = greDir.clone();
+ precomplete.append(FILE_PRECOMPLETE);
+ precompleteBackup.append(FILE_PRECOMPLETE_BAK);
+ if (precompleteBackup.exists()) {
+ if (precomplete.exists()) {
+ precomplete.remove(false);
+ }
+ precompleteBackup.moveTo(greDir, FILE_PRECOMPLETE);
+ } else if (precomplete.exists()) {
+ if (readFile(precomplete) == PRECOMPLETE_CONTENTS) {
+ precomplete.remove(false);
+ }
+ }
+}
+
+/**
+ * When a test finishes this will repeatedly attempt to restore the real updater
+ * and the other files for the updater if a backup of the file exists.
+ */
+function finishTestRestoreUpdaterBackup() {
+ return (async function () {
+ try {
+ // Windows debug builds keep the updater file in use for a short period of
+ // time after the updater process exits.
+ restoreUpdaterBackup();
+ } catch (e) {
+ logTestInfo(
+ "Attempt to restore the backed up updater failed... " +
+ "will try again, Exception: " +
+ e
+ );
+
+ await TestUtils.waitForTick();
+ await finishTestRestoreUpdaterBackup();
+ }
+ })();
+}
+
+/**
+ * Waits for the About Dialog to load.
+ *
+ * @return A promise that returns the domWindow for the About Dialog and
+ * resolves when the About Dialog loads.
+ */
+function waitForAboutDialog() {
+ return new Promise(resolve => {
+ var listener = {
+ onOpenWindow: aXULWindow => {
+ debugDump("About dialog shown...");
+ Services.wm.removeListener(listener);
+
+ async function aboutDialogOnLoad() {
+ domwindow.removeEventListener("load", aboutDialogOnLoad, true);
+ let chromeURI = "chrome://browser/content/aboutDialog.xhtml";
+ is(
+ domwindow.document.location.href,
+ chromeURI,
+ "About dialog appeared"
+ );
+ resolve(domwindow);
+ }
+
+ var domwindow = aXULWindow.docShell.domWindow;
+ domwindow.addEventListener("load", aboutDialogOnLoad, true);
+ },
+ onCloseWindow: aXULWindow => {},
+ };
+
+ Services.wm.addListener(listener);
+ openAboutDialog();
+ });
+}
+
+/**
+ * Return the first UpdatePatch with the given type.
+ *
+ * @param type
+ * The type of the patch ("complete" or "partial")
+ * @param update
+ * The nsIUpdate to select a patch from.
+ * @return A nsIUpdatePatch object matching the type specified
+ */
+function getPatchOfType(type, update) {
+ if (update) {
+ for (let i = 0; i < update.patchCount; ++i) {
+ let patch = update.getPatchAt(i);
+ if (patch && patch.type == type) {
+ return patch;
+ }
+ }
+ }
+ return null;
+}
+
+/**
+ * Runs a Doorhanger update test. This will set various common prefs for
+ * updating and runs the provided list of steps.
+ *
+ * @param params
+ * An object containing parameters used to run the test.
+ * @param steps
+ * An array of test steps to perform. A step will either be an object
+ * containing expected conditions and actions or a function to call.
+ * @return A promise which will resolve once all of the steps have been run.
+ */
+function runDoorhangerUpdateTest(params, steps) {
+ function processDoorhangerStep(step) {
+ if (typeof step == "function") {
+ return step();
+ }
+
+ const {
+ notificationId,
+ button,
+ checkActiveUpdate,
+ pageURLs,
+ expectedStateOverride,
+ } = step;
+ return (async function () {
+ if (!params.popupShown && !PanelUI.isNotificationPanelOpen) {
+ await BrowserTestUtils.waitForEvent(
+ PanelUI.notificationPanel,
+ "popupshown"
+ );
+ }
+ const shownNotificationId = AppMenuNotifications.activeNotification.id;
+ is(
+ shownNotificationId,
+ notificationId,
+ "The right notification showed up."
+ );
+
+ let expectedState = Ci.nsIApplicationUpdateService.STATE_IDLE;
+ if (expectedStateOverride) {
+ expectedState = expectedStateOverride;
+ } else if (notificationId == "update-restart") {
+ expectedState = Ci.nsIApplicationUpdateService.STATE_PENDING;
+ }
+ let actualState = gAUS.currentState;
+ is(
+ actualState,
+ expectedState,
+ `The current update state should be ` +
+ `"${gAUS.getStateName(expectedState)}". Actual: ` +
+ `"${gAUS.getStateName(actualState)}"`
+ );
+
+ if (checkActiveUpdate) {
+ let activeUpdate =
+ checkActiveUpdate.state == STATE_DOWNLOADING
+ ? gUpdateManager.downloadingUpdate
+ : gUpdateManager.readyUpdate;
+ ok(!!activeUpdate, "There should be an active update");
+ is(
+ activeUpdate.state,
+ checkActiveUpdate.state,
+ `The active update state should equal ${checkActiveUpdate.state}`
+ );
+ } else {
+ ok(
+ !gUpdateManager.downloadingUpdate,
+ "There should not be a downloading update"
+ );
+ ok(!gUpdateManager.readyUpdate, "There should not be a ready update");
+ }
+
+ let buttonEl = getNotificationButton(window, notificationId, button);
+ buttonEl.click();
+
+ if (pageURLs && pageURLs.manual !== undefined) {
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ pageURLs.manual,
+ `The page's url should equal ${pageURLs.manual}`
+ );
+ gBrowser.removeTab(gBrowser.selectedTab);
+ }
+ })();
+ }
+
+ return (async function () {
+ if (params.slowStaging) {
+ Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1");
+ } else {
+ Services.env.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1");
+ }
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_APP_UPDATE_DISABLEDFORTESTING, false],
+ [PREF_APP_UPDATE_URL_DETAILS, gDetailsURL],
+ [PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE],
+ ],
+ });
+
+ await setupTestUpdater();
+
+ let baseURL = URL_HTTP_UPDATE_SJS;
+ if (params.baseURL) {
+ baseURL = params.baseURL;
+ }
+ let queryString = params.queryString ? params.queryString : "";
+ let updateURL =
+ baseURL +
+ "?detailsURL=" +
+ gDetailsURL +
+ queryString +
+ getVersionParams(params.version);
+ setUpdateURL(updateURL);
+ if (params.checkAttempts) {
+ // Perform a background check doorhanger test.
+ executeSoon(() => {
+ (async function () {
+ gAUS.checkForBackgroundUpdates();
+ for (var i = 0; i < params.checkAttempts - 1; i++) {
+ await waitForEvent("update-error", "check-attempt-failed");
+ gAUS.checkForBackgroundUpdates();
+ }
+ })();
+ });
+ } else {
+ // Perform a startup processing doorhanger test.
+ writeStatusFile(STATE_FAILED_CRC_ERROR);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(params.updates), true);
+ reloadUpdateManagerData();
+ testPostUpdateProcessing();
+ }
+
+ for (let step of steps) {
+ await processDoorhangerStep(step);
+ }
+ })();
+}
+
+/**
+ * Runs an About Dialog update test. This will set various common prefs for
+ * updating and runs the provided list of steps.
+ *
+ * @param params
+ * An object containing parameters used to run the test.
+ * @param steps
+ * An array of test steps to perform. A step will either be an object
+ * containing expected conditions and actions or a function to call.
+ * @return A promise which will resolve once all of the steps have been run.
+ */
+function runAboutDialogUpdateTest(params, steps) {
+ let aboutDialog;
+ function processAboutDialogStep(step) {
+ if (typeof step == "function") {
+ return step(aboutDialog);
+ }
+
+ const {
+ panelId,
+ checkActiveUpdate,
+ continueFile,
+ downloadInfo,
+ forceApply,
+ noContinue,
+ expectedStateOverride,
+ } = step;
+ return (async function () {
+ await TestUtils.waitForCondition(
+ () =>
+ aboutDialog.gAppUpdater &&
+ aboutDialog.gAppUpdater.selectedPanel?.id == panelId,
+ "Waiting for the expected panel ID: " + panelId,
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test so the panel
+ // ID and the expected panel ID is printed in the log.
+ logTestInfo(e);
+ });
+ let { selectedPanel } = aboutDialog.gAppUpdater;
+ is(selectedPanel.id, panelId, "The panel ID should equal " + panelId);
+ ok(
+ BrowserTestUtils.is_visible(selectedPanel),
+ "The panel should be visible"
+ );
+
+ if (
+ panelId == "downloading" &&
+ gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_IDLE
+ ) {
+ // Now that `AUS.downloadUpdate` is async, we start showing the
+ // downloading panel while `AUS.downloadUpdate` is still resolving.
+ // But the below checks assume that this resolution has already
+ // happened. So we need to wait for things to actually resolve.
+ debugDump("Waiting for downloading state to actually start");
+ await gAUS.stateTransition;
+
+ // Check that the checks that we made above are still valid.
+ selectedPanel = aboutDialog.gAppUpdater.selectedPanel;
+ is(selectedPanel.id, panelId, "The panel ID should equal " + panelId);
+ ok(
+ BrowserTestUtils.is_visible(selectedPanel),
+ "The panel should be visible"
+ );
+ }
+
+ let expectedState = Ci.nsIApplicationUpdateService.STATE_IDLE;
+ if (expectedStateOverride) {
+ expectedState = expectedStateOverride;
+ } else if (panelId == "apply") {
+ expectedState = Ci.nsIApplicationUpdateService.STATE_PENDING;
+ } else if (panelId == "downloading") {
+ expectedState = Ci.nsIApplicationUpdateService.STATE_DOWNLOADING;
+ } else if (panelId == "applying") {
+ expectedState = Ci.nsIApplicationUpdateService.STATE_STAGING;
+ }
+ let actualState = gAUS.currentState;
+ is(
+ actualState,
+ expectedState,
+ `The current update state should be ` +
+ `"${gAUS.getStateName(expectedState)}". Actual: ` +
+ `"${gAUS.getStateName(actualState)}"`
+ );
+
+ if (checkActiveUpdate) {
+ let activeUpdate =
+ checkActiveUpdate.state == STATE_DOWNLOADING
+ ? gUpdateManager.downloadingUpdate
+ : gUpdateManager.readyUpdate;
+ ok(!!activeUpdate, "There should be an active update");
+ is(
+ activeUpdate.state,
+ checkActiveUpdate.state,
+ "The active update state should equal " + checkActiveUpdate.state
+ );
+ } else {
+ ok(
+ !gUpdateManager.downloadingUpdate,
+ "There should not be a downloading update"
+ );
+ ok(!gUpdateManager.readyUpdate, "There should not be a ready update");
+ }
+
+ // Some tests just want to stop at the downloading state. These won't
+ // include a continue file in that state.
+ if (panelId == "downloading" && continueFile) {
+ for (let i = 0; i < downloadInfo.length; ++i) {
+ let data = downloadInfo[i];
+ await continueFileHandler(continueFile);
+ let patch = getPatchOfType(
+ data.patchType,
+ gUpdateManager.downloadingUpdate
+ );
+ // The update is removed early when the last download fails so check
+ // that there is a patch before proceeding.
+ let isLastPatch = i == downloadInfo.length - 1;
+ if (!isLastPatch || patch) {
+ let resultName = data.bitsResult ? "bitsResult" : "internalResult";
+ patch.QueryInterface(Ci.nsIWritablePropertyBag);
+ await TestUtils.waitForCondition(
+ () => patch.getProperty(resultName) == data[resultName],
+ "Waiting for expected patch property " +
+ resultName +
+ " value: " +
+ data[resultName],
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test so the
+ // property value and the expected property value is printed in
+ // the log.
+ logTestInfo(e);
+ });
+ is(
+ "" + patch.getProperty(resultName),
+ data[resultName],
+ "The patch property " +
+ resultName +
+ " value should equal " +
+ data[resultName]
+ );
+
+ // Check the download status text. It should be something like,
+ // "1.4 of 1.4 KB".
+ let expectedText = DownloadUtils.getTransferTotal(
+ data[resultName] == gBadSizeResult ? 0 : patch.size,
+ patch.size
+ );
+ Assert.ok(
+ expectedText,
+ "Sanity check: Expected download status text should be non-empty"
+ );
+ if (aboutDialog.document.hasPendingL10nMutations) {
+ await BrowserTestUtils.waitForEvent(
+ aboutDialog.document,
+ "L10nMutationsFinished"
+ );
+ }
+ Assert.equal(
+ aboutDialog.document.querySelector(
+ `#downloading label[data-l10n-name="download-status"]`
+ ).textContent,
+ expectedText,
+ "Download status text should be correct"
+ );
+ }
+ }
+ } else if (continueFile) {
+ await continueFileHandler(continueFile);
+ }
+
+ let linkPanels = [
+ "downloadFailed",
+ "manualUpdate",
+ "unsupportedSystem",
+ "internalError",
+ ];
+ if (linkPanels.includes(panelId)) {
+ // The unsupportedSystem panel uses the update's detailsURL and the
+ // downloadFailed and manualUpdate panels use the app.update.url.manual
+ // preference.
+ let selector = "label.text-link";
+ if (selectedPanel.ownerDocument.hasPendingL10nMutations) {
+ await BrowserTestUtils.waitForEvent(
+ selectedPanel.ownerDocument,
+ "L10nMutationsFinished"
+ );
+ }
+ let link = selectedPanel.querySelector(selector);
+ is(
+ link.href,
+ gDetailsURL,
+ `The panel's link href should equal ${gDetailsURL}`
+ );
+ const assertNonEmptyText = (node, description) => {
+ let textContent = node.textContent.trim();
+ ok(textContent, `${description}, got "${textContent}"`);
+ };
+ assertNonEmptyText(
+ link,
+ `The panel's link should have non-empty textContent`
+ );
+ let linkWrapperClone = link.parentNode.cloneNode(true);
+ linkWrapperClone.querySelector(selector).remove();
+ assertNonEmptyText(
+ linkWrapperClone,
+ `The panel's link should have text around the link`
+ );
+ }
+
+ // Automatically click the download button unless `noContinue` was passed.
+ let buttonPanels = ["downloadAndInstall", "apply"];
+ if (buttonPanels.includes(panelId) && !noContinue) {
+ let buttonEl = selectedPanel.querySelector("button");
+ await TestUtils.waitForCondition(
+ () => aboutDialog.document.activeElement == buttonEl,
+ "The button should receive focus"
+ );
+ ok(!buttonEl.disabled, "The button should be enabled");
+ // Don't click the button on the apply panel since this will restart the
+ // application.
+ if (panelId != "apply" || forceApply) {
+ buttonEl.click();
+ }
+ }
+ })();
+ }
+
+ return (async function () {
+ Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1");
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_APP_UPDATE_DISABLEDFORTESTING, false],
+ [PREF_APP_UPDATE_URL_MANUAL, gDetailsURL],
+ ],
+ });
+
+ await setupTestUpdater();
+
+ let baseURL = URL_HTTP_UPDATE_SJS;
+ if (params.baseURL) {
+ baseURL = params.baseURL;
+ }
+ let queryString = params.queryString ? params.queryString : "";
+ let updateURL =
+ baseURL +
+ "?detailsURL=" +
+ gDetailsURL +
+ queryString +
+ getVersionParams(params.version);
+ if (params.backgroundUpdate) {
+ setUpdateURL(updateURL);
+ gAUS.checkForBackgroundUpdates();
+ if (params.continueFile) {
+ await continueFileHandler(params.continueFile);
+ }
+ if (params.waitForUpdateState) {
+ let whichUpdate =
+ params.waitForUpdateState == STATE_DOWNLOADING
+ ? "downloadingUpdate"
+ : "readyUpdate";
+ await TestUtils.waitForCondition(
+ () =>
+ gUpdateManager[whichUpdate] &&
+ gUpdateManager[whichUpdate].state == params.waitForUpdateState,
+ "Waiting for update state: " + params.waitForUpdateState,
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test so the panel
+ // ID and the expected panel ID is printed in the log.
+ logTestInfo(e);
+ });
+ // Display the UI after the update state equals the expected value.
+ is(
+ gUpdateManager[whichUpdate].state,
+ params.waitForUpdateState,
+ "The update state value should equal " + params.waitForUpdateState
+ );
+ }
+ } else {
+ updateURL += "&slowUpdateCheck=1&useSlowDownloadMar=1";
+ setUpdateURL(updateURL);
+ }
+
+ aboutDialog = await waitForAboutDialog();
+ registerCleanupFunction(() => {
+ aboutDialog.close();
+ });
+
+ for (let step of steps) {
+ await processAboutDialogStep(step);
+ }
+ })();
+}
+
+/**
+ * Runs an about:preferences update test. This will set various common prefs for
+ * updating and runs the provided list of steps.
+ *
+ * @param params
+ * An object containing parameters used to run the test.
+ * @param steps
+ * An array of test steps to perform. A step will either be an object
+ * containing expected conditions and actions or a function to call.
+ * @return A promise which will resolve once all of the steps have been run.
+ */
+function runAboutPrefsUpdateTest(params, steps) {
+ let tab;
+ function processAboutPrefsStep(step) {
+ if (typeof step == "function") {
+ return step(tab);
+ }
+
+ const {
+ panelId,
+ checkActiveUpdate,
+ continueFile,
+ downloadInfo,
+ forceApply,
+ expectedStateOverride,
+ } = step;
+ return (async function () {
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [{ panelId }],
+ async ({ panelId }) => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.gAppUpdater.selectedPanel?.id == panelId,
+ "Waiting for the expected panel ID: " + panelId,
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test so the panel
+ // ID and the expected panel ID is printed in the log. Use info here
+ // instead of logTestInfo since logTestInfo isn't available in the
+ // content task.
+ info(e);
+ });
+ is(
+ content.gAppUpdater.selectedPanel.id,
+ panelId,
+ "The panel ID should equal " + panelId
+ );
+ }
+ );
+
+ if (
+ panelId == "downloading" &&
+ gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_IDLE
+ ) {
+ // Now that `AUS.downloadUpdate` is async, we start showing the
+ // downloading panel while `AUS.downloadUpdate` is still resolving.
+ // But the below checks assume that this resolution has already
+ // happened. So we need to wait for things to actually resolve.
+ debugDump("Waiting for downloading state to actually start");
+ await gAUS.stateTransition;
+
+ // Check that the checks that we made above are still valid.
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [{ panelId }],
+ ({ panelId }) => {
+ is(
+ content.gAppUpdater.selectedPanel.id,
+ panelId,
+ "The panel ID should equal " + panelId
+ );
+ }
+ );
+ }
+
+ let expectedState = Ci.nsIApplicationUpdateService.STATE_IDLE;
+ if (expectedStateOverride) {
+ expectedState = expectedStateOverride;
+ } else if (panelId == "apply") {
+ expectedState = Ci.nsIApplicationUpdateService.STATE_PENDING;
+ } else if (panelId == "downloading") {
+ expectedState = Ci.nsIApplicationUpdateService.STATE_DOWNLOADING;
+ } else if (panelId == "applying") {
+ expectedState = Ci.nsIApplicationUpdateService.STATE_STAGING;
+ }
+ let actualState = gAUS.currentState;
+ is(
+ actualState,
+ expectedState,
+ `The current update state should be ` +
+ `"${gAUS.getStateName(expectedState)}". Actual: ` +
+ `"${gAUS.getStateName(actualState)}"`
+ );
+
+ if (checkActiveUpdate) {
+ let activeUpdate =
+ checkActiveUpdate.state == STATE_DOWNLOADING
+ ? gUpdateManager.downloadingUpdate
+ : gUpdateManager.readyUpdate;
+ ok(!!activeUpdate, "There should be an active update");
+ is(
+ activeUpdate.state,
+ checkActiveUpdate.state,
+ "The active update state should equal " + checkActiveUpdate.state
+ );
+ } else {
+ ok(
+ !gUpdateManager.downloadingUpdate,
+ "There should not be a downloading update"
+ );
+ ok(!gUpdateManager.readyUpdate, "There should not be a ready update");
+ }
+
+ if (panelId == "downloading") {
+ if (!downloadInfo) {
+ logTestInfo("no downloadinfo, possible error?");
+ }
+ for (let i = 0; i < downloadInfo.length; ++i) {
+ let data = downloadInfo[i];
+ // The About Dialog tests always specify a continue file.
+ await continueFileHandler(continueFile);
+ let patch = getPatchOfType(
+ data.patchType,
+ gUpdateManager.downloadingUpdate
+ );
+ // The update is removed early when the last download fails so check
+ // that there is a patch before proceeding.
+ let isLastPatch = i == downloadInfo.length - 1;
+ if (!isLastPatch || patch) {
+ let resultName = data.bitsResult ? "bitsResult" : "internalResult";
+ patch.QueryInterface(Ci.nsIWritablePropertyBag);
+ await TestUtils.waitForCondition(
+ () => patch.getProperty(resultName) == data[resultName],
+ "Waiting for expected patch property " +
+ resultName +
+ " value: " +
+ data[resultName],
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test so the
+ // property value and the expected property value is printed in
+ // the log.
+ logTestInfo(e);
+ });
+ is(
+ "" + patch.getProperty(resultName),
+ data[resultName],
+ "The patch property " +
+ resultName +
+ " value should equal " +
+ data[resultName]
+ );
+
+ // Check the download status text. It should be something like,
+ // "Downloading update — 1.4 of 1.4 KB". We check only the second
+ // part to make sure that the downloaded size is updated correctly.
+ let actualText = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => {
+ const { document } = content;
+ if (document.hasPendingL10nMutations) {
+ await ContentTaskUtils.waitForEvent(
+ document,
+ "L10nMutationsFinished"
+ );
+ }
+ return document.getElementById("downloading").textContent;
+ }
+ );
+ let expectedSuffix = DownloadUtils.getTransferTotal(
+ data[resultName] == gBadSizeResult ? 0 : patch.size,
+ patch.size
+ );
+ Assert.ok(
+ expectedSuffix,
+ "Sanity check: Expected download status text should be non-empty"
+ );
+ Assert.ok(
+ actualText.endsWith(expectedSuffix),
+ "Download status text should end as expected: " +
+ JSON.stringify({ actualText, expectedSuffix })
+ );
+ }
+ }
+ } else if (continueFile) {
+ await continueFileHandler(continueFile);
+ }
+
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [{ panelId, gDetailsURL, forceApply }],
+ async ({ panelId, gDetailsURL, forceApply }) => {
+ let linkPanels = [
+ "downloadFailed",
+ "manualUpdate",
+ "unsupportedSystem",
+ "internalError",
+ ];
+ if (linkPanels.includes(panelId)) {
+ let { selectedPanel } = content.gAppUpdater;
+ // The unsupportedSystem panel uses the update's detailsURL and the
+ // downloadFailed and manualUpdate panels use the app.update.url.manual
+ // preference.
+ let selector = "label.text-link";
+ // The downloadFailed panel in about:preferences uses an anchor
+ // instead of a label for the link.
+ if (selectedPanel.id == "downloadFailed") {
+ selector = "a.text-link";
+ }
+ // The manualUpdate panel in about:preferences uses
+ // the moz-support-link element which doesn't have
+ // the .text-link class.
+ if (selectedPanel.id == "manualUpdate") {
+ selector = "a.manualLink";
+ }
+ if (selectedPanel.ownerDocument.hasPendingL10nMutations) {
+ await ContentTaskUtils.waitForEvent(
+ selectedPanel.ownerDocument,
+ "L10nMutationsFinished"
+ );
+ }
+ let link = selectedPanel.querySelector(selector);
+ is(
+ link.href,
+ gDetailsURL,
+ `The panel's link href should equal ${gDetailsURL}`
+ );
+ const assertNonEmptyText = (node, description) => {
+ let textContent = node.textContent.trim();
+ ok(textContent, `${description}, got "${textContent}"`);
+ };
+ assertNonEmptyText(
+ link,
+ `The panel's link should have non-empty textContent`
+ );
+ let linkWrapperClone = link.parentNode.cloneNode(true);
+ linkWrapperClone.querySelector(selector).remove();
+ assertNonEmptyText(
+ linkWrapperClone,
+ `The panel's link should have text around the link`
+ );
+ }
+
+ let buttonPanels = ["downloadAndInstall", "apply"];
+ if (buttonPanels.includes(panelId)) {
+ let { selectedPanel } = content.gAppUpdater;
+ let buttonEl = selectedPanel.querySelector("button");
+ // Note: The about:preferences doesn't focus the button like the
+ // About Dialog does.
+ ok(!buttonEl.disabled, "The button should be enabled");
+ // Don't click the button on the apply panel since this will restart
+ // the application.
+ if (selectedPanel.id != "apply" || forceApply) {
+ buttonEl.click();
+ }
+ }
+ }
+ );
+ })();
+ }
+
+ return (async function () {
+ Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1");
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_APP_UPDATE_DISABLEDFORTESTING, false],
+ [PREF_APP_UPDATE_URL_MANUAL, gDetailsURL],
+ ],
+ });
+
+ await setupTestUpdater();
+
+ let baseURL = URL_HTTP_UPDATE_SJS;
+ if (params.baseURL) {
+ baseURL = params.baseURL;
+ }
+ let queryString = params.queryString ? params.queryString : "";
+ let updateURL =
+ baseURL +
+ "?detailsURL=" +
+ gDetailsURL +
+ queryString +
+ getVersionParams(params.version);
+ if (params.backgroundUpdate) {
+ setUpdateURL(updateURL);
+ gAUS.checkForBackgroundUpdates();
+ if (params.continueFile) {
+ await continueFileHandler(params.continueFile);
+ }
+ if (params.waitForUpdateState) {
+ // Wait until the update state equals the expected value before
+ // displaying the UI.
+ let whichUpdate =
+ params.waitForUpdateState == STATE_DOWNLOADING
+ ? "downloadingUpdate"
+ : "readyUpdate";
+ await TestUtils.waitForCondition(
+ () =>
+ gUpdateManager[whichUpdate] &&
+ gUpdateManager[whichUpdate].state == params.waitForUpdateState,
+ "Waiting for update state: " + params.waitForUpdateState,
+ undefined,
+ 200
+ ).catch(e => {
+ // Instead of throwing let the check below fail the test so the panel
+ // ID and the expected panel ID is printed in the log.
+ logTestInfo(e);
+ });
+ is(
+ gUpdateManager[whichUpdate].state,
+ params.waitForUpdateState,
+ "The update state value should equal " + params.waitForUpdateState
+ );
+ }
+ } else {
+ updateURL += "&slowUpdateCheck=1&useSlowDownloadMar=1";
+ setUpdateURL(updateURL);
+ }
+
+ tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:preferences"
+ );
+ registerCleanupFunction(async () => {
+ await BrowserTestUtils.removeTab(tab);
+ });
+
+ // Scroll the UI into view so it is easier to troubleshoot tests.
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.getElementById("updatesCategory").scrollIntoView();
+ });
+
+ for (let step of steps) {
+ await processAboutPrefsStep(step);
+ }
+ })();
+}
+
+/**
+ * Removes the modified update-settings.ini file so the updater will fail to
+ * stage an update.
+ */
+function removeUpdateSettingsIni() {
+ if (Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED)) {
+ let greDir = getGREDir();
+ let updateSettingsIniBak = greDir.clone();
+ updateSettingsIniBak.append(FILE_UPDATE_SETTINGS_INI_BAK);
+ if (updateSettingsIniBak.exists()) {
+ let updateSettingsIni = greDir.clone();
+ updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
+ updateSettingsIni.remove(false);
+ }
+ }
+}
+
+/**
+ * Runs a telemetry update test. This will set various common prefs for
+ * updating, checks for an update, and waits for the specified observer
+ * notification.
+ *
+ * @param updateParams
+ * Params which will be sent to app_update.sjs.
+ * @param event
+ * The observer notification to wait for before proceeding.
+ * @param stageFailure (optional)
+ * Whether to force a staging failure by removing the modified
+ * update-settings.ini file.
+ * @return A promise which will resolve after the .
+ */
+function runTelemetryUpdateTest(updateParams, event, stageFailure = false) {
+ return (async function () {
+ Services.telemetry.clearScalars();
+ Services.env.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1");
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_APP_UPDATE_DISABLEDFORTESTING, false]],
+ });
+
+ await setupTestUpdater();
+
+ if (stageFailure) {
+ removeUpdateSettingsIni();
+ }
+
+ let updateURL =
+ URL_HTTP_UPDATE_SJS +
+ "?detailsURL=" +
+ gDetailsURL +
+ updateParams +
+ getVersionParams();
+ setUpdateURL(updateURL);
+ gAUS.checkForBackgroundUpdates();
+ await waitForEvent(event);
+ })();
+}
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser.ini b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser.ini
new file mode 100644
index 0000000000..b5295f5eac
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser.ini
@@ -0,0 +1,18 @@
+[DEFAULT]
+head = head.js
+prefs =
+ app.update.BITS.enabled=false
+ browser.policies.alternatePath='<test-root>/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json'
+support-files =
+ !/toolkit/mozapps/update/tests/browser/head.js
+ config_manual_app_update_only.json
+ ../../data/shared.js
+ ../../data/app_update.sjs
+ ../testConstants.js
+skip-if = os == 'win' && msix # Updater is disabled in MSIX builds
+
+[browser_aboutDialog_fc_autoUpdateFalse.js]
+[browser_aboutDialog_fc_autoUpdateTrue.js]
+[browser_aboutPrefs_fc_autoUpdateFalse.js]
+[browser_aboutPrefs_fc_autoUpdateTrue.js]
+[browser_noBackgroundUpdate.js]
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateFalse.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateFalse.js
new file mode 100644
index 0000000000..169e66033a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateFalse.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_manual_app_update_policy() {
+ await setAppUpdateAutoEnabledHelper(false);
+
+ is(
+ Services.policies.isAllowed("autoAppUpdateChecking"),
+ false,
+ "autoAppUpdateChecking should be disabled by policy"
+ );
+ is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true");
+
+ let downloadInfo = [{ patchType: "partial", internalResult: "0" }];
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateTrue.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateTrue.js
new file mode 100644
index 0000000000..0a59f59d71
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutDialog_fc_autoUpdateTrue.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_manual_app_update_policy() {
+ await setAppUpdateAutoEnabledHelper(true);
+
+ is(
+ Services.policies.isAllowed("autoAppUpdateChecking"),
+ false,
+ "autoAppUpdateChecking should be disabled by policy"
+ );
+ is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true");
+
+ let downloadInfo = [{ patchType: "partial", internalResult: "0" }];
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutDialogUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateFalse.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateFalse.js
new file mode 100644
index 0000000000..2d9608951e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateFalse.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_manual_app_update_policy() {
+ await setAppUpdateAutoEnabledHelper(false);
+
+ is(
+ Services.policies.isAllowed("autoAppUpdateChecking"),
+ false,
+ "autoAppUpdateChecking should be disabled by policy"
+ );
+ is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true");
+
+ let downloadInfo = [{ patchType: "partial", internalResult: "0" }];
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ async tab => {
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let setting = content.document.getElementById(
+ "updateSettingsContainer"
+ );
+ is(
+ setting.hidden,
+ true,
+ "Update choices should be disabled when manualUpdateOnly"
+ );
+ });
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateTrue.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateTrue.js
new file mode 100644
index 0000000000..b7b0c1027a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_aboutPrefs_fc_autoUpdateTrue.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_manual_app_update_policy() {
+ await setAppUpdateAutoEnabledHelper(true);
+
+ is(
+ Services.policies.isAllowed("autoAppUpdateChecking"),
+ false,
+ "autoAppUpdateChecking should be disabled by policy"
+ );
+ is(gAUS.manualUpdateOnly, true, "gAUS.manualUpdateOnly should be true");
+
+ let downloadInfo = [{ patchType: "partial", internalResult: "0" }];
+ // Since the partial should be successful specify an invalid size for the
+ // complete update.
+ let params = { queryString: "&invalidCompleteSize=1" };
+ await runAboutPrefsUpdateTest(params, [
+ {
+ panelId: "checkingForUpdates",
+ checkActiveUpdate: null,
+ continueFile: CONTINUE_CHECK,
+ },
+ {
+ panelId: "downloadAndInstall",
+ checkActiveUpdate: null,
+ continueFile: null,
+ },
+ {
+ panelId: "downloading",
+ checkActiveUpdate: { state: STATE_DOWNLOADING },
+ continueFile: CONTINUE_DOWNLOAD,
+ downloadInfo,
+ },
+ {
+ panelId: "apply",
+ checkActiveUpdate: { state: STATE_PENDING },
+ continueFile: null,
+ },
+ async tab => {
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let setting = content.document.getElementById(
+ "updateSettingsContainer"
+ );
+ is(
+ setting.hidden,
+ true,
+ "Update choices should be disabled when manualUpdateOnly"
+ );
+ });
+ },
+ ]);
+});
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_noBackgroundUpdate.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_noBackgroundUpdate.js
new file mode 100644
index 0000000000..38b27e31ad
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/browser_noBackgroundUpdate.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_manual_app_update_policy() {
+ // Unfortunately, we can't really test the other background update entry
+ // point, gAUS.notify, because it doesn't return anything and it would be
+ // a bit overkill to edit the nsITimerCallback interface just for this test.
+ // But the two entry points just immediately call the same function, so this
+ // should probably be alright.
+ is(
+ gAUS.checkForBackgroundUpdates(),
+ false,
+ "gAUS.checkForBackgroundUpdates() should not proceed with update check"
+ );
+});
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json b/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json
new file mode 100644
index 0000000000..4e7c785bc1
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/config_manual_app_update_only.json
@@ -0,0 +1,5 @@
+{
+ "policies": {
+ "ManualAppUpdateOnly": true
+ }
+}
diff --git a/toolkit/mozapps/update/tests/browser/manual_app_update_only/head.js b/toolkit/mozapps/update/tests/browser/manual_app_update_only/head.js
new file mode 100644
index 0000000000..2a7576963b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/manual_app_update_only/head.js
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/mozapps/update/tests/browser/head.js",
+ this
+);
diff --git a/toolkit/mozapps/update/tests/browser/testConstants.js b/toolkit/mozapps/update/tests/browser/testConstants.js
new file mode 100644
index 0000000000..915391054b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/browser/testConstants.js
@@ -0,0 +1,7 @@
+const REL_PATH_DATA = "browser/toolkit/mozapps/update/tests/browser/";
+const URL_HOST = "http://127.0.0.1:8888";
+const URL_PATH_UPDATE_XML = "/" + REL_PATH_DATA + "app_update.sjs";
+const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML;
+const CONTINUE_CHECK = "continueCheck";
+const CONTINUE_DOWNLOAD = "continueDownload";
+const CONTINUE_STAGING = "continueStaging";