commit 12b4adb7efa5bbcd4eff9522044c2fef91d84af8
parent 34b94c2ba9563b11b24f89a825b0254e3a0f64aa
Author: Mark Striemer <mstriemer@mozilla.com>
Date: Fri, 14 Nov 2025 19:28:54 +0000
Bug 1975762 - Tests for setting-pane sub pages r=desktop-theme-reviewers,mconley,tgiles
Differential Revision: https://phabricator.services.mozilla.com/D270940
Diffstat:
8 files changed, 262 insertions(+), 37 deletions(-)
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
@@ -1127,8 +1127,7 @@ Preferences.addSetting({
},
});
-/** @type {Record<string, SettingGroupConfig>} */
-let SETTINGS_CONFIG = {
+SettingGroupManager.registerGroups({
containers: {
// This section is marked as in progress for testing purposes
inProgress: true,
@@ -1879,7 +1878,7 @@ let SETTINGS_CONFIG = {
},
],
},
-};
+});
/**
* @param {string} id - ID of {@link SettingGroup} custom element.
@@ -1887,7 +1886,7 @@ let SETTINGS_CONFIG = {
function initSettingGroup(id) {
/** @type {SettingGroup} */
let group = document.querySelector(`setting-group[groupid=${id}]`);
- const config = SETTINGS_CONFIG[id];
+ const config = SettingGroupManager.get(id);
if (group && config) {
if (config.inProgress && !srdSectionEnabled(id)) {
group.remove();
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js
@@ -176,14 +176,102 @@ function srdSectionEnabled(section) {
return srdSectionPrefs.all || srdSectionPrefs[section];
}
-/** @type {Record<string, SettingPaneConfig>} */
-const CONFIG_PANES = {
+var SettingPaneManager = {
+ /** @type {Map<string, SettingPaneConfig>} */
+ _data: new Map(),
+
+ /**
+ * @param {string} id
+ */
+ get(id) {
+ if (!this._data.has(id)) {
+ throw new Error(`Setting pane "${id}" not found`);
+ }
+ return this._data.get(id);
+ },
+
+ /**
+ * @param {string} id
+ * @param {SettingPaneConfig} config
+ */
+ registerPane(id, config) {
+ if (this._data.has(id)) {
+ throw new Error(`Setting pane "${id}" already registered`);
+ }
+ this._data.set(id, config);
+ let subPane = friendlyPrefCategoryNameToInternalName(id);
+ let settingPane = /** @type {SettingPane} */ (
+ document.createElement("setting-pane")
+ );
+ settingPane.name = subPane;
+ settingPane.config = config;
+ settingPane.isSubPane = !!config.parent;
+ document.getElementById("mainPrefPane").append(settingPane);
+ register_module(subPane, {
+ init() {
+ settingPane.init();
+ },
+ });
+ },
+
+ /**
+ * @param {Record<string, SettingPaneConfig>} paneConfigs
+ */
+ registerPanes(paneConfigs) {
+ for (let id in paneConfigs) {
+ this.registerPane(id, paneConfigs[id]);
+ }
+ },
+};
+
+var SettingGroupManager = {
+ /** @type {Map<string, SettingGroupConfig>} */
+ _data: new Map(),
+
+ /**
+ * @param {string} id
+ */
+ get(id) {
+ if (!this._data.has(id)) {
+ throw new Error(`Setting group "${id}" not found`);
+ }
+ return this._data.get(id);
+ },
+
+ /**
+ * @param {string} id
+ * @param {SettingGroupConfig} config
+ */
+ registerGroup(id, config) {
+ if (this._data.has(id)) {
+ throw new Error(`Setting group "${id}" already registered`);
+ }
+ this._data.set(id, config);
+ },
+
+ /**
+ * @param {Record<string, SettingGroupConfig>} groupConfigs
+ */
+ registerGroups(groupConfigs) {
+ for (let id in groupConfigs) {
+ this.registerGroup(id, groupConfigs[id]);
+ }
+ },
+};
+
+/**
+ * Register initial config-based setting panes here. If you need to register a
+ * pane elsewhere, use {@link SettingPaneManager['registerPane']}.
+ *
+ * @type {Record<string, SettingPaneConfig>}
+ */
+const CONFIG_PANES = Object.freeze({
containers2: {
parent: "general",
l10nId: "containers-section-header",
groupIds: ["containers"],
},
-};
+});
var gLastCategory = { category: undefined, subcategory: undefined };
const gXULDOMParser = new DOMParser();
@@ -237,20 +325,8 @@ function init_all() {
register_module("panePrivacy", gPrivacyPane);
register_module("paneContainers", gContainersPane);
- for (let [subPane, config] of Object.entries(CONFIG_PANES)) {
- subPane = friendlyPrefCategoryNameToInternalName(subPane);
- let settingPane = /** @type {SettingPane} */ (
- document.createElement("setting-pane")
- );
- settingPane.name = subPane;
- settingPane.config = config;
- settingPane.isSubPane = !!config.parent;
- document.getElementById("mainPrefPane").append(settingPane);
- register_module(subPane, {
- init() {
- settingPane.init();
- },
- });
+ for (let [id, config] of Object.entries(CONFIG_PANES)) {
+ SettingPaneManager.registerPane(id, config);
}
if (Services.prefs.getBoolPref("browser.translations.newSettingsUI.enable")) {
diff --git a/browser/components/preferences/tests/browser.toml b/browser/components/preferences/tests/browser.toml
@@ -20,7 +20,9 @@ tags = "os_integration"
["browser_about_settings.js"]
["browser_advanced_update.js"]
-run-if = ["updater"]
+run-if = [
+ "updater",
+]
["browser_application_xml_handle_internally.js"]
@@ -31,7 +33,9 @@ run-if = ["updater"]
["browser_basic_rebuild_fonts_test.js"]
["browser_browser_languages_subdialog.js"]
-skip-if = ["tsan"]
+skip-if = [
+ "tsan",
+]
["browser_bug410900.js"]
@@ -53,7 +57,9 @@ support-files = ["browser_bug1184989_prevent_scrolling_when_preferences_flipped.
["browser_cert_export.js"]
["browser_change_app_handler.js"]
-run-if = ["os == 'win'"] # Windows-specific handler application selection dialog
+run-if = [
+ "os == 'win'", # Windows-specific handler application selection dialog
+]
["browser_checkspelling.js"]
@@ -68,7 +74,9 @@ run-if = ["os == 'win'"] # Windows-specific handler application selection dialog
["browser_connection_bug1505330.js"]
["browser_connection_system_wpad.js"]
-run-if = ["os == 'win'"]
+run-if = [
+ "os == 'win'",
+]
["browser_connection_telemetry.js"]
@@ -100,10 +108,10 @@ run-if = ["os == 'win'"]
["browser_experimental_features_filter.js"]
skip-if = [
- "os == 'linux' && os_version == '24.04' && processor == 'x86_64'", # Bug 1969933
- "os == 'mac' && os_version == '10.15' && processor == 'x86_64' && opt", # Bug 1969933
- "os == 'win' && os_version == '11.26100' && processor == 'x86_64'", # Bug 1969933
- "os == 'win' && os_version == '11.26100' && processor == 'x86'", # Bug 1969933
+ "os == 'linux' && os_version == '24.04' && arch == 'x86_64'", # Bug 1969933
+ "os == 'mac' && os_version == '10.15' && arch == 'x86_64' && opt", # Bug 1969933
+ "os == 'win' && os_version == '11.26100' && arch == 'x86'", # Bug 1969933
+ "os == 'win' && os_version == '11.26100' && arch == 'x86_64'", # Bug 1969933
]
["browser_experimental_features_hidden_when_not_public.js"]
@@ -139,14 +147,18 @@ https_first_disabled = true
["browser_ignore_invalid_capability.js"]
["browser_keyboardfocus.js"]
-run-if = ["os == 'mac'"]
+run-if = [
+ "os == 'mac'",
+]
["browser_languages_subdialog.js"]
["browser_layersacceleration.js"]
["browser_localSearchShortcuts.js"]
-fail-if = ["a11y_checks"] # Bug 1854636 clicked treechildren#engineChildren may not be focusable
+fail-if = [
+ "a11y_checks", # Bug 1854636 clicked treechildren#engineChildren may not be focusable
+]
["browser_moreFromMozilla.js"]
@@ -172,7 +184,9 @@ support-files = ["empty_pdf_file.pdf"]
["browser_performance_e10srollout.js"]
["browser_performance_non_e10s.js"]
-skip-if = ["true"]
+skip-if = [
+ "true",
+]
["browser_permissions_checkPermissionsWereAdded.js"]
@@ -187,7 +201,9 @@ skip-if = ["true"]
["browser_primaryPassword.js"]
["browser_privacy_allowListPreference.js"]
-skip-if = ["os == 'linux' && os_version == '24.04' && processor == 'x86_64' && tsan"] # Bug 1984525
+skip-if = [
+ "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && tsan", # Bug 1984525
+]
["browser_privacy_allowListPreference_dialog.js"]
@@ -239,7 +255,9 @@ support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
["browser_searchShowSuggestionsFirst.js"]
["browser_search_engineList.js"]
-fail-if = ["a11y_checks"] # Bug 1854636 clicked treechildren#engineChildren may not be focusable
+fail-if = [
+ "a11y_checks", # Bug 1854636 clicked treechildren#engineChildren may not be focusable
+]
["browser_search_firefoxSuggest.js"]
@@ -252,7 +270,9 @@ fail-if = ["a11y_checks"] # Bug 1854636 clicked treechildren#engineChildren may
["browser_search_subdialog_tooltip_saved_addresses.js"]
["browser_search_subdialogs_within_preferences_1.js"]
-skip-if = ["tsan"] # Bug 1678829
+skip-if = [
+ "tsan", # Bug 1678829
+]
["browser_search_subdialogs_within_preferences_2.js"]
@@ -290,6 +310,8 @@ skip-if = ["tsan"] # Bug 1678829
["browser_setting_group_in_progress.js"]
+["browser_setting_pane_sub_pane.js"]
+
["browser_site_login_exceptions.js"]
["browser_site_login_exceptions_policy.js"]
@@ -323,7 +345,11 @@ support-files = [
["browser_warning_permanent_private_browsing.js"]
["browser_windows_launch_on_login.js"]
-run-if = ["os == 'win' && !msix"]
+run-if = [
+ "os == 'win' && !msix",
+]
["browser_windows_launch_on_login_msix.js"]
-run-if = ["os == 'win' && msix"]
+run-if = [
+ "os == 'win' && msix",
+]
diff --git a/browser/components/preferences/tests/browser_setting_pane_sub_pane.js b/browser/components/preferences/tests/browser_setting_pane_sub_pane.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+describe("setting-pane", () => {
+ let doc, win, testSubPaneButton;
+
+ beforeEach(async function setup() {
+ await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });
+ doc = gBrowser.selectedBrowser.contentDocument;
+ win = doc.ownerGlobal;
+ testSubPaneButton = doc.createElement("moz-button");
+ testSubPaneButton.hidden = true;
+ testSubPaneButton.setAttribute("data-category", "panePrivacy");
+ testSubPaneButton.textContent = "Go to test pane";
+ testSubPaneButton.addEventListener("click", () =>
+ win.gotoPref("paneTestSubPane")
+ );
+ let privacyHeading = doc.getElementById("browserPrivacyCategory");
+ privacyHeading.insertAdjacentElement("afterend", testSubPaneButton);
+ win.Preferences.addSetting({
+ id: "testSetting",
+ get: () => true,
+ });
+ win.SettingGroupManager.registerGroup("testGroup", {
+ l10nId: "downloads-header-2",
+ headingLevel: 2,
+ items: [
+ {
+ id: "testSetting",
+ controlAttrs: {
+ label: "Test setting",
+ },
+ },
+ ],
+ });
+ win.SettingPaneManager.registerPane("testSubPane", {
+ parent: "privacy",
+ l10nId: "containers-section-header",
+ groupIds: ["testGroup"],
+ });
+ });
+
+ afterEach(() => BrowserTestUtils.removeTab(gBrowser.selectedTab));
+
+ it("can load/go back sub-pane", async () => {
+ let pane = doc.querySelector(
+ 'setting-pane[data-category="paneTestSubPane"]'
+ );
+ is_element_hidden(pane, "Sub pane is initially hidden");
+
+ // Load the privacy pane
+ let paneLoaded = waitForPaneChange("privacy");
+ EventUtils.synthesizeMouseAtCenter(
+ doc.getElementById("category-privacy"),
+ {},
+ win
+ );
+ await paneLoaded;
+
+ // Load the sub pane
+ paneLoaded = waitForPaneChange("testSubPane");
+ EventUtils.synthesizeMouseAtCenter(testSubPaneButton, {}, win);
+ await paneLoaded;
+
+ is_element_visible(pane, "Page header is visible");
+ ok(pane, "There is a setting pane");
+ await pane.updateComplete;
+ let pageHeader = pane.pageHeaderEl;
+ ok(pageHeader, "There is a page header");
+ is(
+ pageHeader.dataset.l10nId,
+ "containers-section-header",
+ "Page header has its l10nId"
+ );
+ let heading = pageHeader.headingEl;
+ ok(heading, "There is a heading in the header");
+ ok(heading.innerText, "The heading has text");
+ is(heading.innerText, pageHeader.heading, "The text is localized");
+ let backButton = pageHeader.backButtonEl;
+ ok(backButton, "There is a back button");
+
+ // Go back
+ paneLoaded = waitForPaneChange("privacy");
+ EventUtils.synthesizeMouseAtCenter(backButton, {}, win);
+ await paneLoaded;
+ is_element_hidden(pane, "Sub pane is hidden again");
+ });
+});
diff --git a/browser/components/preferences/tests/head.js b/browser/components/preferences/tests/head.js
@@ -704,3 +704,17 @@ async function updateCheckBox(win, id, value) {
// Toggle the state.
await EventUtils.synthesizeMouseAtCenter(checkbox, {}, checkbox.ownerGlobal);
}
+
+/**
+ * Wait for the current setting pane to change.
+ *
+ * @param {string} paneId
+ */
+async function waitForPaneChange(paneId) {
+ let doc = gBrowser.selectedBrowser.contentDocument;
+ let event = await BrowserTestUtils.waitForEvent(doc, "paneshown");
+ let expectId = paneId.startsWith("pane")
+ ? paneId
+ : `pane${paneId[0].toUpperCase()}${paneId.substring(1)}`;
+ is(event.detail.category, expectId, "Loaded the correct pane");
+}
diff --git a/browser/components/preferences/widgets/setting-pane/setting-pane.mjs b/browser/components/preferences/widgets/setting-pane/setting-pane.mjs
@@ -19,6 +19,10 @@ export class SettingPane extends MozLitElement {
config: { type: Object },
};
+ static queries = {
+ pageHeaderEl: "moz-page-header",
+ };
+
constructor() {
super();
/** @type {string} */
@@ -33,6 +37,13 @@ export class SettingPane extends MozLitElement {
return this;
}
+ async getUpdateComplete() {
+ let result = await super.getUpdateComplete();
+ // @ts-ignore bug 1997478
+ await this.pageHeaderEl.updateComplete;
+ return result;
+ }
+
goBack() {
window.gotoPref(this.config.parent);
}
diff --git a/toolkit/content/widgets/moz-button/moz-button.css b/toolkit/content/widgets/moz-button/moz-button.css
@@ -2,6 +2,10 @@
* 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/. */
+:host([hidden]) {
+ display: none !important;
+}
+
:host {
display: inline-block;
height: fit-content;
diff --git a/toolkit/content/widgets/moz-page-header/moz-page-header.mjs b/toolkit/content/widgets/moz-page-header/moz-page-header.mjs
@@ -29,6 +29,11 @@ export default class MozPageHeader extends MozLitElement {
backButton: { type: Boolean },
};
+ static queries = {
+ headingEl: "h1",
+ backButtonEl: "moz-button",
+ };
+
constructor() {
super();
this.heading = "";