Bug 1330349 - Part 5 - add tests for new theme type WebExtensions uninstall, enable and disable behavior. r?Mossop
MozReview-Commit-ID: 7FI9rFYtn4D
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -1049,16 +1049,40 @@ function completeAllInstalls(aInstalls,
function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
promiseInstallAllFiles(aFiles, aIgnoreIncompatible).then(aCallback);
}
const EXTENSIONS_DB = "extensions.json";
var gExtensionsJSON = gProfD.clone();
gExtensionsJSON.append(EXTENSIONS_DB);
+function promiseWebExtensionStartup() {
+ const {Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+ return new Promise(resolve => {
+ let listener = (evt, extension) => {
+ Management.off("ready", listener);
+ resolve(extension);
+ };
+
+ Management.on("ready", listener);
+ });
+}
+
+function promiseInstallWebExtension(aData) {
+ let addonFile = createTempWebExtensionFile(aData);
+
+ return promiseInstallAllFiles([addonFile]).then(installs => {
+ Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
+ // Since themes are disabled by default, it won't start up.
+ if ("theme" in aData.manifest)
+ return installs[0].addon;
+ return promiseWebExtensionStartup();
+ });
+}
// By default use strict compatibility
Services.prefs.setBoolPref("extensions.strictCompatibility", true);
// By default, set min compatible versions to 0
Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0");
Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, "0");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_undothemeuninstall.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_undothemeuninstall.js
@@ -346,16 +346,83 @@ add_task(function* canUndoUninstallDisab
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
t1.uninstall();
yield promiseRestartManager();
});
+add_task(function* uninstallWebExtensionOffersUndo() {
+ let { id: addonId } = yield promiseInstallWebExtension({
+ manifest: {
+ "author": "Some author",
+ manifest_version: 2,
+ name: "Web Extension Name",
+ version: "1.0",
+ theme: { images: { headerURL: "https://example.com/example.png" } },
+ }
+ });
+
+ let [ t1, d ] = yield promiseAddonsByIDs([addonId, "default@tests.mozilla.org"]);
+
+ Assert.ok(t1, "Addon should be there");
+ Assert.ok(!t1.isActive);
+ Assert.ok(t1.userDisabled);
+ Assert.equal(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.ok(d, "Addon should be there");
+ Assert.ok(d.isActive);
+ Assert.ok(!d.userDisabled);
+ Assert.equal(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.equal(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({ [addonId]: [ "onUninstalling" ] });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ Assert.ok(!t1.isActive);
+ Assert.ok(t1.userDisabled);
+ Assert.ok(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ Assert.equal(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ [addonId]: [
+ "onOperationCancelled"
+ ]
+ });
+ t1.cancelUninstall();
+ ensure_test_completed();
+
+ Assert.ok(!t1.isActive);
+ Assert.ok(t1.userDisabled);
+ Assert.equal(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs([addonId, "default@tests.mozilla.org"]);
+
+ Assert.ok(d);
+ Assert.ok(d.isActive);
+ Assert.ok(!d.userDisabled);
+ Assert.equal(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.ok(t1);
+ Assert.ok(!t1.isActive);
+ Assert.ok(t1.userDisabled);
+ Assert.equal(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.equal(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ t1.uninstall();
+ yield promiseRestartManager();
+});
+
// Tests that uninstalling an enabled lightweight theme offers the option to undo
add_task(function* uninstallLWTOffersUndo() {
// skipped since lightweight themes don't support undoable uninstall yet
/*
LightweightThemeManager.currentTheme = dummyLWTheme("theme1");
let [ t1, d ] = yield promiseAddonsByIDs(["theme1@personas.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
@@ -9,47 +9,24 @@ const ID = "webextension1@tests.mozilla.
const PREF_SELECTED_LOCALE = "general.useragent.locale";
const profileDir = gProfD.clone();
profileDir.append("extensions");
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
startupManager();
-const { GlobalManager, Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
-
-function promiseAddonStartup() {
- return new Promise(resolve => {
- let listener = (evt, extension) => {
- Management.off("ready", listener);
- resolve(extension);
- };
-
- Management.on("ready", listener);
- });
-}
-
-function promiseInstallWebExtension(aData) {
- let addonFile = createTempWebExtensionFile(aData);
-
- return promiseInstallAllFiles([addonFile]).then(installs => {
- Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
- // Since themes are disabled by default, it won't start up.
- if ("theme" in aData.manifest)
- return installs[0].addon;
- return promiseAddonStartup();
- });
-}
+const { GlobalManager } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
add_task(function*() {
equal(GlobalManager.extensionMap.size, 0);
yield Promise.all([
promiseInstallAllFiles([do_get_addon("webextension_1")], true),
- promiseAddonStartup()
+ promiseWebExtensionStartup()
]);
equal(GlobalManager.extensionMap.size, 1);
ok(GlobalManager.extensionMap.has(ID));
let chromeReg = AM_Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(AM_Ci.nsIChromeRegistry);
try {
@@ -77,17 +54,17 @@ add_task(function*() {
do_check_eq(addon.icon64URL, uri + "icon64.png");
// Should persist through a restart
yield promiseShutdownManager();
equal(GlobalManager.extensionMap.size, 0);
startupManager();
- yield promiseAddonStartup();
+ yield promiseWebExtensionStartup();
equal(GlobalManager.extensionMap.size, 1);
ok(GlobalManager.extensionMap.has(ID));
addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
do_check_eq(addon.version, "1.0");
do_check_eq(addon.name, "Web Extension Name");
@@ -106,17 +83,17 @@ add_task(function*() {
do_check_eq(addon.iconURL, uri + "icon48.png");
do_check_eq(addon.icon64URL, uri + "icon64.png");
addon.userDisabled = true;
equal(GlobalManager.extensionMap.size, 0);
addon.userDisabled = false;
- yield promiseAddonStartup();
+ yield promiseWebExtensionStartup();
equal(GlobalManager.extensionMap.size, 1);
ok(GlobalManager.extensionMap.has(ID));
addon.uninstall();
equal(GlobalManager.extensionMap.size, 0);
do_check_false(GlobalManager.extensionMap.has(ID));
@@ -133,17 +110,17 @@ add_task(function*() {
applications: {
gecko: {
id: ID
}
}
}, profileDir);
startupManager();
- yield promiseAddonStartup();
+ yield promiseWebExtensionStartup();
let addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
do_check_eq(addon.version, "1.0");
do_check_eq(addon.name, "Web Extension Name");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
@@ -158,17 +135,17 @@ add_task(function*() {
yield promiseRestartManager();
});
add_task(function* test_manifest_localization() {
const extensionId = "webextension3@tests.mozilla.org";
yield promiseInstallAllFiles([do_get_addon("webextension_3")], true);
- yield promiseAddonStartup();
+ yield promiseWebExtensionStartup();
let addon = yield promiseAddonByID(extensionId);
addon.userDisabled = true;
equal(addon.name, "Web Extensiøn foo ☹");
equal(addon.description, "Descriptïon bar ☹ of add-on");
Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
@@ -0,0 +1,236 @@
+"use strict";
+
+/**
+ * This file contains test for 'theme' type WebExtension addons. Tests focus mostly
+ * on interoperability between the different theme formats (XUL and LWT) and
+ * Addon Manager integration.
+ *
+ * Coverage may overlap with other tests in this folder.
+ */
+
+const {LightweightThemeManager} = AM_Cu.import("resource://gre/modules/LightweightThemeManager.jsm", {});
+const THEME_IDS = ["theme1@tests.mozilla.org", "theme3@tests.mozilla.org",
+ "theme2@personas.mozilla.org", "default@tests.mozilla.org"];
+const REQUIRE_RESTART = { [THEME_IDS[0]]: 1 };
+const DEFAULT_THEME = THEME_IDS[3];
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// We remember the last/ currently active theme for tracking events.
+var gActiveTheme = null;
+
+add_task(function* setup_to_default_browserish_state() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: THEME_IDS[0],
+ version: "1.0",
+ name: "Test 1",
+ type: 4,
+ skinnable: true,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ yield promiseWriteWebManifestForExtension({
+ author: "Some author",
+ manifest_version: 2,
+ name: "Web Extension Name",
+ version: "1.0",
+ theme: { images: { headerURL: "https://example.com/example.png" } },
+ applications: {
+ gecko: {
+ id: THEME_IDS[1]
+ }
+ }
+ }, profileDir);
+
+ // We need a default theme for some of these things to work but we have hidden
+ // the one in the application directory.
+ writeInstallRDFForExtension({
+ id: DEFAULT_THEME,
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ // We can add an LWT only after the Addon Manager was started.
+ LightweightThemeManager.currentTheme = {
+ id: THEME_IDS[2].substr(0, THEME_IDS[2].indexOf("@")),
+ version: "1",
+ name: "Bling",
+ description: "SO MUCH BLING!",
+ author: "Pixel Pusher",
+ homepageURL: "http://mochi.test:8888/data/index.html",
+ headerURL: "http://mochi.test:8888/data/header.png",
+ previewURL: "http://mochi.test:8888/data/preview.png",
+ iconURL: "http://mochi.test:8888/data/icon.png",
+ textcolor: Math.random().toString(),
+ accentcolor: Math.random().toString()
+ };
+
+ let [ t1, t2, t3, d ] = yield promiseAddonsByIDs(THEME_IDS);
+ Assert.ok(t1, "Theme addon should exist");
+ Assert.ok(t2, "Theme addon should exist");
+ Assert.ok(t3, "Theme addon should exist");
+ Assert.ok(d, "Theme addon should exist");
+
+ t1.userDisabled = t2.userDisabled = t3.userDisabled = true;
+ Assert.ok(!t1.isActive, "Theme should be disabled");
+ Assert.ok(!t2.isActive, "Theme should be disabled");
+ Assert.ok(!t3.isActive, "Theme should be disabled");
+ Assert.ok(d.isActive, "Default theme should be active");
+
+ yield promiseRestartManager();
+
+ [ t1, t2, t3, d ] = yield promiseAddonsByIDs(THEME_IDS);
+ Assert.ok(!t1.isActive, "Theme should still be disabled");
+ Assert.ok(!t2.isActive, "Theme should still be disabled");
+ Assert.ok(!t3.isActive, "Theme should still be disabled");
+ Assert.ok(d.isActive, "Default theme should still be active");
+
+ gActiveTheme = d.id;
+});
+
+/**
+ * Set the `userDisabled` property of one specific theme and check if the theme
+ * switching works as expected by checking the state of all installed themes.
+ *
+ * @param {String} which ID of the addon to set the `userDisabled` property on
+ * @param {Boolean} disabled Flag value to switch to
+ */
+function* setDisabledStateAndCheck(which, disabled = false) {
+ if (disabled)
+ Assert.equal(which, gActiveTheme, "Only the active theme can be disabled");
+
+ let themeToDisable = disabled ? which : gActiveTheme;
+ let themeToEnable = disabled ? DEFAULT_THEME : which;
+ let expectRestart = !!(REQUIRE_RESTART[themeToDisable] || REQUIRE_RESTART[themeToEnable]);
+
+ let expectedStates = {
+ [themeToDisable]: true,
+ [themeToEnable]: false
+ };
+ let expectedEvents = {
+ [themeToDisable]: [ [ "onDisabling", expectRestart ] ],
+ [themeToEnable]: [ [ "onEnabling", expectRestart ] ]
+ };
+ if (!expectRestart) {
+ expectedEvents[themeToDisable].push([ "onDisabled", false ]);
+ expectedEvents[themeToEnable].push([ "onEnabled", false ]);
+ }
+
+ // Set the state of the theme to change.
+ let theme = yield promiseAddonByID(which);
+ prepare_test(expectedEvents);
+ theme.userDisabled = disabled;
+
+ let isDisabled;
+ for (theme of yield promiseAddonsByIDs(THEME_IDS)) {
+ isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
+ Assert.equal(theme.userDisabled, isDisabled,
+ `Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
+ // Some themes need a restart to get their act together.
+ if (expectRestart && (theme.id == themeToEnable || theme.id == themeToDisable)) {
+ let expectedFlag = theme.id == themeToEnable ? AddonManager.PENDING_ENABLE : AddonManager.PENDING_DISABLE;
+ Assert.ok(hasFlag(theme.pendingOperations, expectedFlag),
+ "When expecting a restart, the pending operation flags should match");
+ } else {
+ Assert.equal(theme.pendingOperations, AddonManager.PENDING_NONE,
+ "There should be no pending operations when no restart is expected");
+ Assert.equal(theme.isActive, !isDisabled,
+ `Theme '${theme.id} should be ${isDisabled ? "in" : ""}active`);
+ }
+ }
+
+ yield promiseRestartManager();
+
+ // All should still be good after a restart of the Addon Manager.
+ for (theme of yield promiseAddonsByIDs(THEME_IDS)) {
+ isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
+ Assert.equal(theme.userDisabled, isDisabled,
+ `Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
+ Assert.equal(theme.isActive, !isDisabled,
+ `Theme '${theme.id}' should be ${isDisabled ? "in" : ""}active`);
+ Assert.equal(theme.pendingOperations, AddonManager.PENDING_NONE,
+ "There should be no pending operations left");
+ if (!isDisabled)
+ gActiveTheme = theme.id;
+ }
+
+ ensure_test_completed();
+}
+
+add_task(function* test_dss_themes() {
+ // Enable the complete theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[0]);
+
+ // Disabling the complete theme should revert to the default theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[0], true);
+
+ // Enable it again.
+ yield* setDisabledStateAndCheck(THEME_IDS[0]);
+
+ // Enabling a WebExtension theme should disable the active theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[1]);
+
+ // Switching back should disable the WebExtension theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[0]);
+});
+
+add_task(function* test_WebExtension_themes() {
+ // Enable the WebExtension theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[1]);
+
+ // Disabling WebExtension should revert to the default theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[1], true);
+
+ // Enable it again.
+ yield* setDisabledStateAndCheck(THEME_IDS[1]);
+
+ // Enabling an LWT should disable the active theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[2]);
+
+ // Switching back should disable the LWT.
+ yield* setDisabledStateAndCheck(THEME_IDS[1]);
+});
+
+add_task(function* test_LWTs() {
+ // Start with enabling an LWT.
+ yield* setDisabledStateAndCheck(THEME_IDS[2]);
+
+ // Disabling LWT should revert to the default theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[2], true);
+
+ // Enable it again.
+ yield* setDisabledStateAndCheck(THEME_IDS[2]);
+
+ // Enabling a WebExtension theme should disable the active theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[1]);
+
+ // Switching back should disable the LWT.
+ yield* setDisabledStateAndCheck(THEME_IDS[2]);
+});
+
+add_task(function* test_default_theme() {
+ // Explicitly enable the default theme.
+ yield* setDisabledStateAndCheck(DEFAULT_THEME);
+
+ // Swith to the WebExtension theme.
+ yield* setDisabledStateAndCheck(THEME_IDS[1]);
+
+ // Enable it again.
+ yield* setDisabledStateAndCheck(DEFAULT_THEME);
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini
@@ -3,10 +3,12 @@ head = head_addons.js head_unpack.js
tail =
firefox-appdir = browser
skip-if = toolkit == 'android'
dupe-manifest =
tags = addons
[test_webextension_paths.js]
tags = webextensions
+[test_webextension_theme.js]
+tags = webextensions
[include:xpcshell-shared.ini]