Bug 1769146 - Integrate previously saved colorway themes in Colorways section within about:addons. r=dao,rpl

Differential Revision: https://phabricator.services.mozilla.com/D148867
This commit is contained in:
Katherine Patenio 2022-06-21 21:36:28 +00:00
Родитель a523865d98
Коммит 76abd1c793
4 изменённых файлов: 381 добавлений и 131 удалений

Просмотреть файл

@ -842,7 +842,7 @@ section:not(:empty) ~ #empty-addons-message {
font-weight: bolder; font-weight: bolder;
} }
#colorways-section-heading { .colorways-section > .list-section-heading {
margin-bottom: 0; margin-bottom: 0;
} }

Просмотреть файл

@ -210,11 +210,6 @@
<button id="colorways-button" class="primary" action="open-colorways" data-l10n-id="theme-colorways-button"></button> <button id="colorways-button" class="primary" action="open-colorways" data-l10n-id="theme-colorways-button"></button>
</template> </template>
<template name="colorways-list">
<h2 id="colorways-section-heading" class="list-section-heading" data-l10n-id="theme-monochromatic-heading"></h2>
<h3 id="colorways-section-subheading" class="list-section-subheading" data-l10n-id="theme-monochromatic-subheading"></h3>
</template>
<template name="addon-name-container-in-disco-card"> <template name="addon-name-container-in-disco-card">
<div class="disco-card-head"> <div class="disco-card-head">
<h3 class="disco-addon-name"></h3> <h3 class="disco-addon-name"></h3>

Просмотреть файл

@ -3693,6 +3693,12 @@ class AddonCard extends HTMLElement {
customElements.define("addon-card", AddonCard); customElements.define("addon-card", AddonCard);
class ColorwayClosetCard extends HTMLElement { class ColorwayClosetCard extends HTMLElement {
connectedCallback() {
if (this.childElementCount === 0) {
this.render();
}
}
render() { render() {
let card = importTemplate("card").firstElementChild; let card = importTemplate("card").firstElementChild;
let heading = card.querySelector(".addon-name-container"); let heading = card.querySelector(".addon-name-container");
@ -4144,7 +4150,11 @@ class AddonList extends HTMLElement {
} }
createSectionHeading(headingIndex) { createSectionHeading(headingIndex) {
let { headingId, subheadingId } = this.sections[headingIndex]; let {
headingId,
subheadingId,
sectionPreambleCustomElement,
} = this.sections[headingIndex];
let frag = document.createDocumentFragment(); let frag = document.createDocumentFragment();
let heading = document.createElement("h2"); let heading = document.createElement("h2");
heading.classList.add("list-section-heading"); heading.classList.add("list-section-heading");
@ -4154,11 +4164,19 @@ class AddonList extends HTMLElement {
if (subheadingId) { if (subheadingId) {
let subheading = document.createElement("h3"); let subheading = document.createElement("h3");
subheading.classList.add("list-section-subheading"); subheading.classList.add("list-section-subheading");
heading.className = "header-name";
document.l10n.setAttributes(subheading, subheadingId); document.l10n.setAttributes(subheading, subheadingId);
// Preserve the old colorway section header styling
// while the colorway closet section is not yet ready to be enabled
if (!COLORWAY_CLOSET_ENABLED) {
heading.className = "header-name";
}
frag.append(subheading); frag.append(subheading);
} }
if (sectionPreambleCustomElement) {
frag.append(document.createElement(sectionPreambleCustomElement));
}
return frag; return frag;
} }
@ -4190,9 +4208,12 @@ class AddonList extends HTMLElement {
} }
updateSectionIfEmpty(section) { updateSectionIfEmpty(section) {
// The header is added before any add-on cards, so if there's only one // We should empty out the section if there are no more cards to display,
// child then it's the header. In that case we should empty out the section. // (unless the section is configured to stay visible and rendered even when
if (section.children.length == 1) { // there is no addon listed, e.g. the "Colorways Closet" section).
const sectionIndex = parseInt(section.getAttribute("section"));
const { shouldRenderIfEmpty } = this.sections[sectionIndex];
if (!this.getCards(section).length && !shouldRenderIfEmpty) {
section.textContent = ""; section.textContent = "";
} }
} }
@ -4201,8 +4222,12 @@ class AddonList extends HTMLElement {
let section = this.getSection(sectionIndex); let section = this.getSection(sectionIndex);
let sectionCards = this.getCards(section); let sectionCards = this.getCards(section);
// If this is the first card in the section, create the heading. const { shouldRenderIfEmpty } = this.sections[sectionIndex];
if (!sectionCards.length) {
// If this is the first card in the section, and the section
// isn't configure to render the headers even when empty,
// we have to create the section heading first.
if (!shouldRenderIfEmpty && !sectionCards.length) {
section.appendChild(this.createSectionHeading(sectionIndex)); section.appendChild(this.createSectionHeading(sectionIndex));
} }
@ -4392,7 +4417,7 @@ class AddonList extends HTMLElement {
} }
renderSection(addons, index) { renderSection(addons, index) {
const { sectionClass, sectionPreambleCustomElement } = this.sections[index]; const { sectionClass, shouldRenderIfEmpty } = this.sections[index];
let section = document.createElement("section"); let section = document.createElement("section");
section.setAttribute("section", index); section.setAttribute("section", index);
@ -4401,20 +4426,15 @@ class AddonList extends HTMLElement {
} }
// Render the heading and add-ons if there are any. // Render the heading and add-ons if there are any.
if (addons.length) { if (shouldRenderIfEmpty || addons.length) {
if (sectionPreambleCustomElement) {
section.appendChild(
document.createElement(sectionPreambleCustomElement)
);
}
section.appendChild(this.createSectionHeading(index)); section.appendChild(this.createSectionHeading(index));
}
for (let addon of addons) { for (let addon of addons) {
let card = document.createElement("addon-card"); let card = document.createElement("addon-card");
card.setAddon(addon); card.setAddon(addon);
card.render(); card.render();
section.appendChild(card); section.appendChild(card);
}
} }
return section; return section;
@ -4517,22 +4537,6 @@ class AddonList extends HTMLElement {
} }
customElements.define("addon-list", AddonList); customElements.define("addon-list", AddonList);
class ColorwayClosetList extends HTMLElement {
connectedCallback() {
this.appendChild(importTemplate(this.template));
let frag = document.createDocumentFragment();
let card = document.createElement("colorways-card");
card.render();
frag.append(card);
this.append(frag);
}
get template() {
return "colorways-list";
}
}
customElements.define("colorways-list", ColorwayClosetList);
class RecommendedAddonList extends HTMLElement { class RecommendedAddonList extends HTMLElement {
connectedCallback() { connectedCallback() {
if (this.isConnected) { if (this.isConnected) {
@ -4828,15 +4832,12 @@ gViewController.defineView("list", async type => {
return null; return null;
} }
// If monochromatic themes are enabled and any are builtin to Firefox, we
// display those themes together in a separate subsection.
const areColorwayThemesInstalled = async () => const areColorwayThemesInstalled = async () =>
(await AddonManager.getAllAddons()).some( (await AddonManager.getAllAddons()).some(
addon => addon =>
BuiltInThemes.isMonochromaticTheme(addon.id) && BuiltInThemes.isMonochromaticTheme(addon.id) &&
!BuiltInThemes.themeIsExpired(addon.id) !BuiltInThemes.themeIsExpired(addon.id)
); );
let frag = document.createDocumentFragment(); let frag = document.createDocumentFragment();
let list = document.createElement("addon-list"); let list = document.createElement("addon-list");
list.type = type; list.type = type;
@ -4848,43 +4849,68 @@ gViewController.defineView("list", async type => {
filterFn: addon => filterFn: addon =>
!addon.hidden && addon.isActive && !isPending(addon, "uninstall"), !addon.hidden && addon.isActive && !isPending(addon, "uninstall"),
}, },
{ ];
headingId: getL10nIdMapping(`${type}-disabled-heading`),
sectionClass: `${type}-disabled-section`, if (type == "theme" && COLORWAY_CLOSET_ENABLED) {
MozXULElement.insertFTLIfNeeded("preview/colorwaycloset.ftl");
const hasActiveColorways = !!BuiltInThemes.findActiveColorwayCollection?.();
sections.push({
headingId: "theme-monochromatic-heading",
subheadingId: "theme-monochromatic-subheading",
sectionClass: "colorways-section",
// Insert colorway closet card as the first element in the colorways
// section so that it is above any retained colorway themes.
sectionPreambleCustomElement: hasActiveColorways
? "colorways-card"
: null,
// This section should also be rendered when there is no addons that
// match the filterFn, because we still want to show the headers and
// colorways-card. But, we only expect the colorways-card to be visible
// when there is an active colorway collection.
shouldRenderIfEmpty: hasActiveColorways,
filterFn: addon => filterFn: addon =>
!addon.hidden && !addon.hidden &&
!addon.isActive && !addon.isActive &&
!isPending(addon, "uninstall") && !isPending(addon, "uninstall") &&
// For performance related details about this check see the // For performance related details about this check see the
// documentation for themeIsExpired in BuiltInThemeConfig.jsm. // documentation for themeIsExpired in BuiltInThemeConfig.jsm.
(!BuiltInThemes.isMonochromaticTheme(addon.id) || BuiltInThemes.isMonochromaticTheme(addon.id) &&
BuiltInThemes.isRetainedExpiredTheme(addon.id)), BuiltInThemes.isRetainedExpiredTheme(addon.id),
}, });
];
let colorwaysThemeInstalled;
if (type == "theme") {
colorwaysThemeInstalled = await areColorwayThemesInstalled();
if (colorwaysThemeInstalled && COLORWAY_CLOSET_ENABLED) {
// Avoid inserting colorway closet fluent strings in aboutaddons.html,
// considering that there is a dependency on a browser-only resource.
const fluentResourceId = "preview/colorwaycloset.ftl";
if (!document.head.querySelector(`link[href='${fluentResourceId}']`)) {
const fluentLink = document.createElement("link");
fluentLink.setAttribute("rel", "localization");
fluentLink.setAttribute("href", fluentResourceId);
document.head.appendChild(fluentLink);
}
// Insert colorway closet card as the first element so that
// it is positioned between the enabled and disabled sections.
sections[1].sectionPreambleCustomElement = "colorways-list";
}
} }
const disabledAddonsFilterFn = addon =>
!addon.hidden && !addon.isActive && !isPending(addon, "uninstall");
const disabledThemesFilterFn = addon =>
disabledAddonsFilterFn(addon) &&
((BuiltInThemes.isRetainedExpiredTheme(addon.id) &&
!COLORWAY_CLOSET_ENABLED) ||
!BuiltInThemes.isMonochromaticTheme(addon.id));
sections.push({
headingId: getL10nIdMapping(`${type}-disabled-heading`),
sectionClass: `${type}-disabled-section`,
filterFn: addon => {
if (addon.type === "theme") {
return disabledThemesFilterFn(addon);
}
return disabledAddonsFilterFn(addon);
},
});
list.setSections(sections); list.setSections(sections);
frag.appendChild(list); frag.appendChild(list);
if (type == "theme" && colorwaysThemeInstalled) { // Add old colorways section if the new colorway closet is not enabled.
// If monochromatic themes are enabled and any are builtin to Firefox, we
// display those themes together in a separate subsection.
if (
type == "theme" &&
!COLORWAY_CLOSET_ENABLED &&
(await areColorwayThemesInstalled())
) {
let monochromaticList = document.createElement("addon-list"); let monochromaticList = document.createElement("addon-list");
monochromaticList.classList.add("monochromatic-addon-list"); monochromaticList.classList.add("monochromatic-addon-list");
monochromaticList.type = type; monochromaticList.type = type;

Просмотреть файл

@ -11,10 +11,14 @@ const { BuiltInThemeConfig } = ChromeUtils.import(
const { ColorwayClosetOpener } = ChromeUtils.import( const { ColorwayClosetOpener } = ChromeUtils.import(
"resource:///modules/ColorwayClosetOpener.jsm" "resource:///modules/ColorwayClosetOpener.jsm"
); );
const { BuiltInThemes } = ChromeUtils.import(
"resource:///modules/BuiltInThemes.jsm"
);
AddonTestUtils.initMochitest(this); AddonTestUtils.initMochitest(this);
const kTestThemeId = "test-colorway@mozilla.org"; const kTestThemeId = "test-colorway@mozilla.org";
const kTestExpiredThemeId = `expired-${kTestThemeId}`;
// Return a mock expiry date set 1 year ahead from the current date. // Return a mock expiry date set 1 year ahead from the current date.
function getMockExpiry() { function getMockExpiry() {
@ -23,6 +27,66 @@ function getMockExpiry() {
return expireDate; return expireDate;
} }
function getMockThemeXpi(id) {
return AddonTestUtils.createTempWebExtensionFile({
manifest: {
name: "Monochromatic Theme",
applications: { gecko: { id } },
theme: {},
},
});
}
function setMockThemeToExpired(id) {
let yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
yesterday = yesterday.toISOString().split("T")[0];
// Add the test theme to our list of built-in themes so that aboutaddons.js
// will think this theme is expired.
BuiltInThemes.builtInThemeMap.set(id, {
version: "1.0",
expiry: yesterday,
// We use the manifest from Light theme since we know it will be in-tree
// indefinitely.
path: "resource://builtin-themes/light/",
});
}
function setBuiltInThemeConfigMock(...args) {
info("Mocking BuiltInThemeConfig.findActiveColorwaysCollection");
BuiltInThemeConfig.findActiveColorwayCollection = () => {
// Return no active collection
if (!args || !args.length) {
info("Return no active collection");
return null;
}
const { mockExpiry, mockL10nId } = args[0];
info(
`Return mock active colorway collection with expiry set to: ${
mockExpiry.toUTCString().split("T")[0]
}`
);
return {
id: "colorway-test-collection",
expiry: mockExpiry,
l10nId: {
title: mockL10nId,
},
};
};
}
function clearBuiltInThemeConfigMock(originalFindActiveCollection) {
info("Cleaning up BuiltInThemeConfigMock");
if (
BuiltInThemeConfig.findActiveColorwayCollection !==
originalFindActiveCollection
) {
BuiltInThemeConfig.findActiveColorwayCollection = originalFindActiveCollection;
}
}
add_setup(async function() { add_setup(async function() {
info("Register mock fluent locale strings"); info("Register mock fluent locale strings");
@ -89,15 +153,9 @@ add_task(async function testColorwayClosetPrefEnabled() {
// Mock BuiltInThemeConfig.findActiveColorwaysCollection with test colorways. // Mock BuiltInThemeConfig.findActiveColorwaysCollection with test colorways.
const originalFindActiveCollection = const originalFindActiveCollection =
BuiltInThemeConfig.findActiveColorwayCollection; BuiltInThemeConfig.findActiveColorwayCollection;
const clearBuiltInThemeConfigMock = () => { registerCleanupFunction(() => {
if ( clearBuiltInThemeConfigMock(originalFindActiveCollection);
BuiltInThemeConfig.findActiveColorwayCollection !== });
originalFindActiveCollection
) {
BuiltInThemeConfig.findActiveColorwayCollection = originalFindActiveCollection;
}
};
registerCleanupFunction(clearBuiltInThemeConfigMock);
// Mock collection l10n part of the mocked fluent resources. // Mock collection l10n part of the mocked fluent resources.
const mockL10nId = "colorway-collection-test-mock"; const mockL10nId = "colorway-collection-test-mock";
@ -118,27 +176,9 @@ add_task(async function testColorwayClosetPrefEnabled() {
); );
} }
info("Now mocking BuiltInThemeConfig.findActiveColorwayCollection"); setBuiltInThemeConfigMock({ mockExpiry, mockL10nId });
BuiltInThemeConfig.findActiveColorwayCollection = () => {
info(
`Return mock active colorway collection with expiry set to: ${
mockExpiry.toUTCString().split("T")[0]
}`
);
return {
id: "colorway-test-collection",
expiry: mockExpiry,
l10nId: { title: mockL10nId },
};
};
const themeXpi = AddonTestUtils.createTempWebExtensionFile({ const themeXpi = getMockThemeXpi(kTestThemeId);
manifest: {
name: "Monochromatic Theme",
applications: { gecko: { id: kTestThemeId } },
theme: {},
},
});
const { addon } = await AddonTestUtils.promiseInstallFile(themeXpi); const { addon } = await AddonTestUtils.promiseInstallFile(themeXpi);
let win = await loadInitialView("theme"); let win = await loadInitialView("theme");
@ -151,28 +191,27 @@ add_task(async function testColorwayClosetPrefEnabled() {
// Add mocked fluent resources for the mocked active colorway collection. // Add mocked fluent resources for the mocked active colorway collection.
doc.l10n.addResourceIds(["mock-colorwaycloset.ftl"]); doc.l10n.addResourceIds(["mock-colorwaycloset.ftl"]);
let colorwayClosetList = doc.querySelector("colorways-list"); let colorwaySection = getSection(doc, "colorways-section");
ok(colorwaySection, "colorway section was found");
// Make sure fluent strings have all been translated before // Make sure fluent strings have all been translated before
// asserting the expected element to not have empty textContent. // asserting the expected element to not have empty textContent.
await doc.l10n.translateFragment(colorwayClosetList); await doc.l10n.translateFragment(colorwaySection);
info("Verifying colorway closet list contents"); info("Verifying colorway closet list contents");
ok(colorwayClosetList, "colorway closet list was found");
ok( ok(
colorwayClosetList.querySelector("#colorways-section-heading"), colorwaySection.querySelector(".list-section-heading"),
"colorway closet heading was found" "colorway closet heading was found"
); );
ok( ok(
colorwayClosetList.querySelector("#colorways-section-subheading"), colorwaySection.querySelector(".list-section-subheading"),
"colorway closet subheading was found" "colorway closet subheading was found"
); );
let cards = colorwayClosetList.querySelectorAll("colorways-card"); let card = colorwaySection.querySelector("colorways-card");
ok(cards.length, "At least one colorway closet card was found"); ok(card, "colorway closet card was found");
info("Verifying colorway closet card contents"); info("Verifying colorway closet card contents");
let card = cards[0];
ok( ok(
card.querySelector("#colorways-preview-text-container"), card.querySelector("#colorways-preview-text-container"),
"Preview text container found" "Preview text container found"
@ -210,7 +249,8 @@ add_task(async function testColorwayClosetPrefEnabled() {
await closeView(win); await closeView(win);
await addon.uninstall(true); await addon.uninstall(true);
clearBuiltInThemeConfigMock(); await SpecialPowers.popPrefEnv();
clearBuiltInThemeConfigMock(originalFindActiveCollection);
}); });
/** /**
@ -221,23 +261,17 @@ add_task(async function testColorwayClosetSectionPrefDisabled() {
await SpecialPowers.pushPrefEnv({ await SpecialPowers.pushPrefEnv({
set: [["browser.theme.colorway-closet", false]], set: [["browser.theme.colorway-closet", false]],
}); });
const themeXpi = AddonTestUtils.createTempWebExtensionFile({ const themeXpi = getMockThemeXpi(kTestThemeId);
manifest: {
name: "Monochromatic Theme",
applications: { gecko: { id: kTestThemeId } },
theme: {},
},
});
const { addon } = await AddonTestUtils.promiseInstallFile(themeXpi); const { addon } = await AddonTestUtils.promiseInstallFile(themeXpi);
let win = await loadInitialView("theme"); let win = await loadInitialView("theme");
let doc = win.document; let doc = win.document;
let colorwayClosetList = doc.querySelector("colorways-list"); let colorwaySection = getSection(doc, "colorways-section");
ok(!colorwaySection, "colorway section should not be found");
ok(!colorwayClosetList, "colorway closet list should not be found");
await closeView(win); await closeView(win);
await addon.uninstall(true); await addon.uninstall(true);
await SpecialPowers.popPrefEnv();
}); });
/** /**
@ -249,6 +283,20 @@ add_task(async function testButtonOpenModal() {
set: [["browser.theme.colorway-closet", true]], set: [["browser.theme.colorway-closet", true]],
}); });
// Mock BuiltInThemeConfig.findActiveColorwaysCollection with test colorways.
const originalFindActiveCollection =
BuiltInThemeConfig.findActiveColorwayCollection;
registerCleanupFunction(() => {
clearBuiltInThemeConfigMock(originalFindActiveCollection);
});
// Mock collection l10n part of the mocked fluent resources.
const mockL10nId = "colorway-collection-test-mock";
// Mock expiry date string and BuiltInThemeConfig.findActiveColorwayCollection()
const mockExpiry = getMockExpiry();
setBuiltInThemeConfigMock({ mockExpiry, mockL10nId });
let originalOpenModal = ColorwayClosetOpener.openModal; let originalOpenModal = ColorwayClosetOpener.openModal;
const clearOpenModalMock = () => { const clearOpenModalMock = () => {
if (originalOpenModal) { if (originalOpenModal) {
@ -258,25 +306,19 @@ add_task(async function testButtonOpenModal() {
}; };
registerCleanupFunction(clearOpenModalMock); registerCleanupFunction(clearOpenModalMock);
const themeXpi = AddonTestUtils.createTempWebExtensionFile({ const themeXpi = getMockThemeXpi(kTestThemeId);
manifest: {
name: "Monochromatic Theme",
applications: { gecko: { id: kTestThemeId } },
theme: {},
},
});
const { addon } = await AddonTestUtils.promiseInstallFile(themeXpi); const { addon } = await AddonTestUtils.promiseInstallFile(themeXpi);
let win = await loadInitialView("theme"); let win = await loadInitialView("theme");
let doc = win.document; let doc = win.document;
let colorwayClosetList = doc.querySelector("colorways-list"); let colorwaySection = getSection(doc, "colorways-section");
ok(colorwayClosetList, "colorway closet list was found"); ok(colorwaySection, "colorway section was found");
let cards = colorwayClosetList.querySelectorAll("colorways-card"); let card = colorwaySection.querySelector("colorways-card");
ok(cards.length, "At least one colorway closet card was found"); ok(card, "colorway closet card was found");
let colorwaysButton = cards[0].querySelector("#colorways-button"); let colorwaysButton = card.querySelector("#colorways-button");
ok(colorwaysButton, "colorway collection button found"); ok(colorwaysButton, "colorway collection button found");
let colorwayOpenerPromise = new Promise(resolve => { let colorwayOpenerPromise = new Promise(resolve => {
ColorwayClosetOpener.openModal = () => { ColorwayClosetOpener.openModal = () => {
@ -291,4 +333,191 @@ add_task(async function testButtonOpenModal() {
await closeView(win); await closeView(win);
await addon.uninstall(true); await addon.uninstall(true);
clearOpenModalMock(); clearOpenModalMock();
await SpecialPowers.popPrefEnv();
clearBuiltInThemeConfigMock(originalFindActiveCollection);
});
/**
* Tests that disabled retained expired colorways appear in the list of retained
* colorway themes, while disabled unexpired ones do not.
*/
add_task(async function testColorwayClosetSectionOneRetainedOneUnexpired() {
await SpecialPowers.pushPrefEnv({
set: [["browser.theme.colorway-closet", true]],
});
// Mock BuiltInThemeConfig.findActiveColorwaysCollection with test colorways.
const originalFindActiveCollection =
BuiltInThemeConfig.findActiveColorwayCollection;
registerCleanupFunction(() => {
clearBuiltInThemeConfigMock(originalFindActiveCollection);
});
// Mock collection l10n part of the mocked fluent resources.
const mockL10nId = "colorway-collection-test-mock";
// Mock expiry date string and BuiltInThemeConfig.findActiveColorwayCollection()
const mockExpiry = getMockExpiry();
setBuiltInThemeConfigMock({ mockExpiry, mockL10nId });
// Set expired theme as a retained colorway theme
const retainedThemePrefName = "browser.theme.retainedExpiredThemes";
await SpecialPowers.pushPrefEnv({
set: [[retainedThemePrefName, JSON.stringify([kTestExpiredThemeId])]],
});
const themeXpiExpiredAddon = getMockThemeXpi(kTestExpiredThemeId);
const expiredAddon = (
await AddonTestUtils.promiseInstallFile(themeXpiExpiredAddon)
).addon;
// Set up a valid addon that acts as a colorway theme that is not yet expired
const validThemeId = `valid-${kTestThemeId}`;
const themeXpiValidAddon = getMockThemeXpi(validThemeId);
const validAddon = (
await AddonTestUtils.promiseInstallFile(themeXpiValidAddon)
).addon;
await expiredAddon.disable();
await validAddon.disable();
// Make the test theme appear expired.
setMockThemeToExpired(kTestExpiredThemeId);
registerCleanupFunction(() => {
BuiltInThemes.builtInThemeMap.delete(kTestExpiredThemeId);
});
let win = await loadInitialView("theme");
let doc = win.document;
let colorwaySection = getSection(doc, "colorways-section");
info("Verifying colorway section order of elements");
ok(
colorwaySection.children.length,
"colorway section should have at least 1 element"
);
is(
colorwaySection.children[0].classList[0],
"list-section-heading",
"colorway section header should be first"
);
is(
colorwaySection.children[1].classList[0],
"list-section-subheading",
"colorway section subheader should be second"
);
is(
colorwaySection.children[2].tagName.toLowerCase(),
"colorways-card",
"colorway closet list should be third"
);
is(
colorwaySection.children[3].tagName.toLowerCase(),
"addon-card",
"addon theme card should be fourth"
);
info("Verifying cards in list of retained colorway themes");
let expiredAddonCard = colorwaySection.querySelector(
`addon-card[addon-id='${kTestExpiredThemeId}']`
);
ok(
colorwaySection.contains(expiredAddonCard),
"Colorways section contains the expired theme."
);
let disabledSection = getSection(doc, "theme-disabled-section");
expiredAddonCard = disabledSection.querySelector(
`addon-card[addon-id='${kTestExpiredThemeId}']`
);
ok(
!disabledSection.contains(expiredAddonCard),
"The regular, non-Colorways 'Disabled' section does not contain the expired theme."
);
let validAddonCard = colorwaySection.querySelector(
`addon-card[addon-id='${validThemeId}']`
);
ok(
!colorwaySection.contains(validAddonCard),
"Colorways section does not contain valid theme."
);
await closeView(win);
await expiredAddon.uninstall(true);
await validAddon.uninstall(true);
await SpecialPowers.popPrefEnv();
await SpecialPowers.popPrefEnv();
clearBuiltInThemeConfigMock(originalFindActiveCollection);
});
/**
* Tests that the Colorway Closet does not appear when there is no active
* collection, and that retained themes are still visible.
*/
add_task(async function testColorwayNoActiveCollection() {
await SpecialPowers.pushPrefEnv({
set: [["browser.theme.colorway-closet", true]],
});
// Mock BuiltInThemeConfig.findActiveColorwaysCollection with test colorways.
const originalFindActiveCollection =
BuiltInThemeConfig.findActiveColorwayCollection;
registerCleanupFunction(() => {
clearBuiltInThemeConfigMock(originalFindActiveCollection);
});
setBuiltInThemeConfigMock();
// Set expired theme as a retained colorway theme
const retainedThemePrefName = "browser.theme.retainedExpiredThemes";
await SpecialPowers.pushPrefEnv({
set: [[retainedThemePrefName, JSON.stringify([kTestExpiredThemeId])]],
});
const themeXpiExpiredAddon = getMockThemeXpi(kTestExpiredThemeId);
const expiredAddon = (
await AddonTestUtils.promiseInstallFile(themeXpiExpiredAddon)
).addon;
await expiredAddon.disable();
// Make the test theme appear expired.
setMockThemeToExpired(kTestExpiredThemeId);
registerCleanupFunction(() => {
BuiltInThemes.builtInThemeMap.delete(kTestExpiredThemeId);
});
let win = await loadInitialView("theme");
let doc = win.document;
let colorwaySection = getSection(doc, "colorways-section");
ok(colorwaySection, "colorway section was found");
ok(
!colorwaySection.querySelector("colorways-card"),
"colorway closet card was not found"
);
info("Verifying that header and subheader are still visible");
is(
colorwaySection.children[0].classList[0],
"list-section-heading",
"colorway section header should be first"
);
is(
colorwaySection.children[1].classList[0],
"list-section-subheading",
"colorway section subheader should be second"
);
let expiredAddonCard = colorwaySection.querySelector(
`addon-card[addon-id='${kTestExpiredThemeId}']`
);
ok(
colorwaySection.contains(expiredAddonCard),
"Colorways section contains the expired theme."
);
await closeView(win);
await expiredAddon.uninstall(true);
await SpecialPowers.popPrefEnv();
await SpecialPowers.popPrefEnv();
clearBuiltInThemeConfigMock(originalFindActiveCollection);
}); });