summaryrefslogtreecommitdiffstats
path: root/toolkit/components/aboutconfig/test/browser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/aboutconfig/test/browser
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/aboutconfig/test/browser')
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser.toml27
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_accessibility.js39
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_basic.js52
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_clipboard.js141
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_edit.js430
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_locked.js54
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_observe.js163
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_search.js177
-rw-r--r--toolkit/components/aboutconfig/test/browser/browser_warning.js41
-rw-r--r--toolkit/components/aboutconfig/test/browser/head.js173
10 files changed, 1297 insertions, 0 deletions
diff --git a/toolkit/components/aboutconfig/test/browser/browser.toml b/toolkit/components/aboutconfig/test/browser/browser.toml
new file mode 100644
index 0000000000..acccf1f8f0
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser.toml
@@ -0,0 +1,27 @@
+[DEFAULT]
+skip-if = [
+ "debug", # Bug 1507747
+ "asan", # Bug 1520398
+ "tsan"
+]
+support-files = ["head.js"]
+
+["browser_accessibility.js"]
+
+["browser_basic.js"]
+
+["browser_clipboard.js"]
+fail-if = ["a11y_checks"] # Bug 1854447 th, td.cell-value, #prefs may not be focusable; #show-all may be unlabeled
+
+["browser_edit.js"]
+fail-if = ["a11y_checks"] # Bug 1854447 span, th, td.cell-value may not be focusable; #show-all, input, button-add/delete/reset ghost-buttons may not be labeled
+skip-if = ["os == 'linux' && ccov"] # Bug 1613515, the test consistently times out on Linux coverage builds.
+
+["browser_locked.js"]
+
+["browser_observe.js"]
+skip-if = ["os == 'linux' && ccov"] # Bug 1614978, the test consistently times out on Linux coverage builds.
+
+["browser_search.js"]
+
+["browser_warning.js"]
diff --git a/toolkit/components/aboutconfig/test/browser/browser_accessibility.js b/toolkit/components/aboutconfig/test/browser/browser_accessibility.js
new file mode 100644
index 0000000000..9310e40186
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_accessibility.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const MAX_PLACEABLE_LENGTH = 2500;
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["test.aboutconfig.added", "=".repeat(MAX_PLACEABLE_LENGTH)],
+ ["test.aboutconfig.long", "=".repeat(MAX_PLACEABLE_LENGTH + 1)],
+ ],
+ });
+});
+
+add_task(async function test_accessible_value() {
+ await AboutConfigTest.withNewTab(async function () {
+ for (let [name, expectHasUserValue] of [
+ [PREF_BOOLEAN_DEFAULT_TRUE, false],
+ [PREF_BOOLEAN_USERVALUE_TRUE, true],
+ ["test.aboutconfig.added", true],
+ ]) {
+ let span = this.getRow(name).valueCell.querySelector("span");
+ let expectedL10nId = expectHasUserValue
+ ? "about-config-pref-accessible-value-custom"
+ : "about-config-pref-accessible-value-default";
+ Assert.equal(span.getAttribute("data-l10n-id"), expectedL10nId);
+ }
+
+ // If the value is too long for localization, the state is not included.
+ let span = this.getRow("test.aboutconfig.long").valueCell.querySelector(
+ "span"
+ );
+ Assert.ok(!span.hasAttribute("data-l10n-id"));
+ Assert.equal(
+ span.getAttribute("aria-label"),
+ Preferences.get("test.aboutconfig.long")
+ );
+ });
+});
diff --git a/toolkit/components/aboutconfig/test/browser/browser_basic.js b/toolkit/components/aboutconfig/test/browser/browser_basic.js
new file mode 100644
index 0000000000..014a98df97
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_basic.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [
+ "test.aboutconfig.userValueLikeLocalized",
+ "chrome://test/locale/testing.properties",
+ ],
+ ],
+ });
+});
+
+add_task(async function test_load_title() {
+ await AboutConfigTest.withNewTab(async function () {
+ Assert.equal(this.document.title, "Advanced Preferences");
+ });
+});
+
+add_task(async function test_load_settings() {
+ await AboutConfigTest.withNewTab(async function () {
+ // Test if page contains elements.
+ Assert.equal(this.getRow(PREF_NUMBER_DEFAULT_ZERO).value, 0);
+ Assert.equal(this.getRow(PREF_STRING_DEFAULT_EMPTY).value, "");
+
+ // Test if the modified state is displayed for the right prefs.
+ Assert.ok(
+ !this.getRow(PREF_BOOLEAN_DEFAULT_TRUE).hasClass("has-user-value")
+ );
+ Assert.ok(
+ this.getRow(PREF_BOOLEAN_USERVALUE_TRUE).hasClass("has-user-value")
+ );
+
+ // Test to see if values are localized, sampling from different files. If
+ // any of these are removed or their value changes, just update the value
+ // here or point to a different preference in the same file.
+ Assert.equal(this.getRow("font.language.group").value, "x-western");
+ Assert.equal(this.getRow("intl.ellipsis").value, "\u2026");
+
+ // Test to see if user created value is not empty string when it matches
+ // /^chrome:\/\/.+\/locale\/.+\.properties/.
+ Assert.equal(
+ this.getRow("test.aboutconfig.userValueLikeLocalized").value,
+ "chrome://test/locale/testing.properties"
+ );
+
+ // Test to see if empty string when value matches
+ // /^chrome:\/\/.+\/locale\/.+\.properties/ and an exception is thrown.
+ Assert.equal(this.getRow(PREF_STRING_LOCALIZED_MISSING).value, "");
+ });
+});
diff --git a/toolkit/components/aboutconfig/test/browser/browser_clipboard.js b/toolkit/components/aboutconfig/test/browser/browser_clipboard.js
new file mode 100644
index 0000000000..bcaa2c0328
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_clipboard.js
@@ -0,0 +1,141 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["test.aboutconfig.copy.false", false],
+ ["test.aboutconfig.copy.number", 10],
+ ["test.aboutconfig.copy.spaces.1", " "],
+ ["test.aboutconfig.copy.spaces.2", " "],
+ ["test.aboutconfig.copy.spaces.3", " "],
+ ["test.aboutconfig.copy.string", "010.5"],
+ ],
+ });
+});
+
+add_task(async function test_copy() {
+ await AboutConfigTest.withNewTab(async function () {
+ for (let [name, expectedString] of [
+ [PREF_BOOLEAN_DEFAULT_TRUE, "true"],
+ [PREF_BOOLEAN_USERVALUE_TRUE, "true"],
+ [PREF_STRING_DEFAULT_EMPTY, ""],
+ ["test.aboutconfig.copy.false", "false"],
+ ["test.aboutconfig.copy.number", "10"],
+ ["test.aboutconfig.copy.spaces.1", " "],
+ ["test.aboutconfig.copy.spaces.2", " "],
+ ["test.aboutconfig.copy.spaces.3", " "],
+ ["test.aboutconfig.copy.string", "010.5"],
+ ]) {
+ // Limit the number of preferences shown so all the rows are visible.
+ this.search(name);
+ let row = this.getRow(name);
+
+ let selectText = async target => {
+ let { width, height } = target.getBoundingClientRect();
+ EventUtils.synthesizeMouse(
+ target,
+ 1,
+ 1,
+ { type: "mousedown" },
+ this.browser.contentWindow
+ );
+ EventUtils.synthesizeMouse(
+ target,
+ width - 1,
+ height - 1,
+ { type: "mousemove" },
+ this.browser.contentWindow
+ );
+ EventUtils.synthesizeMouse(
+ target,
+ width - 1,
+ height - 1,
+ { type: "mouseup" },
+ this.browser.contentWindow
+ );
+ };
+
+ // Drag across the name cell.
+ await selectText(row.nameCell);
+ Assert.ok(row.nameCell.contains(this.window.getSelection().anchorNode));
+ await SimpleTest.promiseClipboardChange(name, async () => {
+ await BrowserTestUtils.synthesizeKey(
+ "c",
+ { accelKey: true },
+ this.browser
+ );
+ });
+
+ // Drag across the value cell.
+ await selectText(row.valueCell);
+ let selection = this.window.getSelection();
+ Assert.ok(row.valueCell.contains(selection.anchorNode));
+
+ if (expectedString !== "") {
+ // Non-empty values should have a selection.
+ Assert.ok(!selection.isCollapsed);
+ await SimpleTest.promiseClipboardChange(expectedString, async () => {
+ await BrowserTestUtils.synthesizeKey(
+ "c",
+ { accelKey: true },
+ this.browser
+ );
+ });
+ } else {
+ // Nothing is selected for an empty value.
+ Assert.equal(selection.toString(), "");
+ }
+ }
+ });
+});
+
+add_task(async function test_copy_multiple() {
+ await AboutConfigTest.withNewTab(async function () {
+ // Lines are separated by a single LF character on all platforms.
+ let expectedString =
+ "test.aboutconfig.copy.false\tfalse\t\n" +
+ "test.aboutconfig.copy.number\t10\t\n" +
+ "test.aboutconfig.copy.spaces.1\t \t\n" +
+ "test.aboutconfig.copy.spaces.2\t \t\n" +
+ "test.aboutconfig.copy.spaces.3\t \t\n" +
+ "test.aboutconfig.copy.string\t010.5";
+
+ this.search("test.aboutconfig.copy.");
+ let startRow = this.getRow("test.aboutconfig.copy.false");
+ let endRow = this.getRow("test.aboutconfig.copy.string");
+ let { width, height } = endRow.valueCell.getBoundingClientRect();
+
+ // Drag from the top left of the first row to the bottom right of the last.
+ EventUtils.synthesizeMouse(
+ startRow.nameCell,
+ 1,
+ 1,
+ { type: "mousedown" },
+ this.browser.contentWindow
+ );
+
+ EventUtils.synthesizeMouse(
+ endRow.valueCell,
+ width - 1,
+ height - 1,
+ { type: "mousemove" },
+ this.browser.contentWindow
+ );
+ EventUtils.synthesizeMouse(
+ endRow.valueCell,
+ width - 1,
+ height - 1,
+ { type: "mouseup" },
+ this.browser.contentWindow
+ );
+
+ await SimpleTest.promiseClipboardChange(expectedString, async () => {
+ await BrowserTestUtils.synthesizeKey(
+ "c",
+ { accelKey: true },
+ this.browser
+ );
+ });
+ });
+});
diff --git a/toolkit/components/aboutconfig/test/browser/browser_edit.js b/toolkit/components/aboutconfig/test/browser/browser_edit.js
new file mode 100644
index 0000000000..9d10fb1e75
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_edit.js
@@ -0,0 +1,430 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const PREF_MODIFY_PREFIX = "test.aboutconfig.modify";
+const PREF_MODIFY_BOOLEAN = "test.aboutconfig.modify.boolean";
+const PREF_MODIFY_NUMBER = "test.aboutconfig.modify.number";
+const PREF_MODIFY_STRING = "test.aboutconfig.modify.string";
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_MODIFY_BOOLEAN, true],
+ [PREF_MODIFY_NUMBER, 1337],
+ [
+ PREF_MODIFY_STRING,
+ "the answer to the life the universe and everything",
+ ],
+ ],
+ });
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(PREF_BOOLEAN_DEFAULT_TRUE);
+ Services.prefs.clearUserPref(PREF_NUMBER_DEFAULT_ZERO);
+ Services.prefs.clearUserPref(PREF_STRING_DEFAULT_EMPTY);
+ });
+});
+
+add_task(async function test_add_user_pref() {
+ Assert.equal(
+ Services.prefs.getPrefType(PREF_NEW),
+ Ci.nsIPrefBranch.PREF_INVALID
+ );
+
+ await AboutConfigTest.withNewTab(async function () {
+ // The row for a new preference appears when searching for its name.
+ Assert.ok(!this.getRow(PREF_NEW));
+
+ for (let [radioIndex, expectedValue, expectedEditingMode] of [
+ [0, true, false],
+ [1, 0, true],
+ [2, "", true],
+ ]) {
+ this.search(PREF_NEW);
+ let row = this.getRow(PREF_NEW);
+ Assert.ok(row.hasClass("deleted"));
+ Assert.ok(row.hasClass("add"));
+
+ // Adding the preference should set the default for the data type.
+ row.element.querySelectorAll("input")[radioIndex].click();
+ row.editColumnButton.click();
+ Assert.ok(!row.hasClass("deleted"));
+ Assert.ok(!row.hasClass("add"));
+ Assert.ok(Preferences.get(PREF_NEW) === expectedValue);
+
+ // Number and String preferences should be in edit mode.
+ Assert.equal(!!row.valueInput, expectedEditingMode);
+
+ // Repeat the search to verify that the preference remains.
+ this.search(PREF_NEW);
+ row = this.getRow(PREF_NEW);
+ Assert.ok(!row.hasClass("deleted"));
+ Assert.ok(!row.hasClass("add"));
+ Assert.ok(!row.valueInput);
+
+ // Reset the preference, then continue by adding a different type.
+ row.resetColumnButton.click();
+ Assert.equal(
+ Services.prefs.getPrefType(PREF_NEW),
+ Ci.nsIPrefBranch.PREF_INVALID
+ );
+ }
+ });
+});
+
+add_task(async function test_delete_user_pref() {
+ for (let [radioIndex, testValue] of [
+ [0, false],
+ [1, -1],
+ [2, "value"],
+ ]) {
+ Preferences.set(PREF_NEW, testValue);
+ await AboutConfigTest.withNewTab(async function () {
+ // Deleting the preference should keep the row.
+ let row = this.getRow(PREF_NEW);
+ row.resetColumnButton.click();
+ Assert.ok(row.hasClass("deleted"));
+ Assert.equal(
+ Services.prefs.getPrefType(PREF_NEW),
+ Ci.nsIPrefBranch.PREF_INVALID
+ );
+
+ // Re-adding the preference should keep the same value.
+ Assert.ok(row.element.querySelectorAll("input")[radioIndex].checked);
+ row.editColumnButton.click();
+ Assert.ok(!row.hasClass("deleted"));
+ Assert.ok(Preferences.get(PREF_NEW) === testValue);
+
+ // Filtering again after deleting should remove the row.
+ row.resetColumnButton.click();
+ this.showAll();
+ Assert.ok(!this.getRow(PREF_NEW));
+ });
+ }
+});
+
+add_task(async function test_click_type_label_multiple_forms() {
+ // This test displays the row to add a preference while other preferences are
+ // also displayed, and tries to select the type of the new preference by
+ // clicking the label next to the radio button. This should work even if the
+ // user has deleted a different preference, and multiple forms are displayed.
+ const PREF_TO_DELETE = "test.aboutconfig.modify.boolean";
+ const PREF_NEW_WHILE_DELETED = "test.aboutconfig.modify.";
+
+ await AboutConfigTest.withNewTab(async function () {
+ this.search(PREF_NEW_WHILE_DELETED);
+
+ // This preference will remain deleted during the test.
+ let existingRow = this.getRow(PREF_TO_DELETE);
+ existingRow.resetColumnButton.click();
+
+ let newRow = this.getRow(PREF_NEW_WHILE_DELETED);
+
+ for (let [radioIndex, expectedValue] of [
+ [0, true],
+ [1, 0],
+ [2, ""],
+ ]) {
+ let radioLabels = newRow.element.querySelectorAll("label > span");
+ await this.document.l10n.translateElements(radioLabels);
+
+ // Even if this is the second form on the page, the click should select
+ // the radio button next to the label, not the one on the first form.
+ EventUtils.synthesizeMouseAtCenter(
+ radioLabels[radioIndex],
+ {},
+ this.browser.contentWindow
+ );
+
+ // Adding the preference should set the default for the data type.
+ newRow.editColumnButton.click();
+ Assert.ok(Preferences.get(PREF_NEW_WHILE_DELETED) === expectedValue);
+
+ // Reset the preference, then continue by adding a different type.
+ newRow.resetColumnButton.click();
+ }
+
+ // Re-adding the deleted preference should restore the value.
+ existingRow.editColumnButton.click();
+ Assert.ok(Preferences.get(PREF_TO_DELETE) === true);
+ });
+});
+
+add_task(async function test_reset_user_pref() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_BOOLEAN_DEFAULT_TRUE, false],
+ [PREF_STRING_LOCALIZED_MISSING, "user-value"],
+ ],
+ });
+
+ await AboutConfigTest.withNewTab(async function () {
+ // Click reset.
+ let row = this.getRow(PREF_BOOLEAN_DEFAULT_TRUE);
+ row.resetColumnButton.click();
+ // Check new layout and reset.
+ Assert.ok(!row.hasClass("has-user-value"));
+ Assert.ok(!row.resetColumnButton);
+ Assert.ok(!Services.prefs.prefHasUserValue(PREF_BOOLEAN_DEFAULT_TRUE));
+ Assert.equal(this.getRow(PREF_BOOLEAN_DEFAULT_TRUE).value, "true");
+
+ // Filter again to test the preference cache.
+ this.showAll();
+ row = this.getRow(PREF_BOOLEAN_DEFAULT_TRUE);
+ Assert.ok(!row.hasClass("has-user-value"));
+ Assert.ok(!row.resetColumnButton);
+ Assert.equal(this.getRow(PREF_BOOLEAN_DEFAULT_TRUE).value, "true");
+
+ // Clicking reset on a localized preference without a corresponding value.
+ row = this.getRow(PREF_STRING_LOCALIZED_MISSING);
+ Assert.equal(row.value, "user-value");
+ row.resetColumnButton.click();
+ // Check new layout and reset.
+ Assert.ok(!row.hasClass("has-user-value"));
+ Assert.ok(!row.resetColumnButton);
+ Assert.ok(!Services.prefs.prefHasUserValue(PREF_STRING_LOCALIZED_MISSING));
+ Assert.equal(this.getRow(PREF_STRING_LOCALIZED_MISSING).value, "");
+ });
+});
+
+add_task(async function test_modify() {
+ await AboutConfigTest.withNewTab(async function () {
+ // Test toggle for boolean prefs.
+ for (let nameOfBoolPref of [
+ PREF_MODIFY_BOOLEAN,
+ PREF_BOOLEAN_DEFAULT_TRUE,
+ ]) {
+ let row = this.getRow(nameOfBoolPref);
+ // Do this a two times to reset the pref.
+ for (let i = 0; i < 2; i++) {
+ row.editColumnButton.click();
+ // Check new layout and saving in backend.
+ Assert.equal(
+ this.getRow(nameOfBoolPref).value,
+ "" + Preferences.get(nameOfBoolPref)
+ );
+ let prefHasUserValue = Services.prefs.prefHasUserValue(nameOfBoolPref);
+ Assert.equal(row.hasClass("has-user-value"), prefHasUserValue);
+ Assert.equal(!!row.resetColumnButton, prefHasUserValue);
+ }
+ }
+
+ // Test abort of edit by starting with string and continuing with editing Int pref.
+ let row = this.getRow(PREF_MODIFY_STRING);
+ row.editColumnButton.click();
+ row.valueInput.value = "test";
+ let intRow = this.getRow(PREF_MODIFY_NUMBER);
+ intRow.editColumnButton.click();
+ Assert.equal(intRow.valueInput.value, Preferences.get(PREF_MODIFY_NUMBER));
+ Assert.ok(!row.valueInput);
+ Assert.equal(row.value, Preferences.get(PREF_MODIFY_STRING));
+
+ // Test validation of integer values.
+ for (let invalidValue of [
+ "",
+ " ",
+ "a",
+ "1.5",
+ "-2147483649",
+ "2147483648",
+ ]) {
+ intRow.valueInput.value = invalidValue;
+ intRow.editColumnButton.click();
+ // We should still be in edit mode.
+ Assert.ok(intRow.valueInput);
+ }
+
+ // Test correct saving and DOM-update.
+ for (let [prefName, willDelete] of [
+ [PREF_MODIFY_STRING, true],
+ [PREF_MODIFY_NUMBER, true],
+ [PREF_NUMBER_DEFAULT_ZERO, false],
+ [PREF_STRING_DEFAULT_EMPTY, false],
+ ]) {
+ row = this.getRow(prefName);
+ // Activate edit and check displaying.
+ row.editColumnButton.click();
+ Assert.equal(row.valueInput.value, Preferences.get(prefName));
+ row.valueInput.value = "42";
+ // Save and check saving.
+ row.editColumnButton.click();
+ Assert.equal(Preferences.get(prefName), "42");
+ Assert.equal(row.value, "42");
+ Assert.ok(row.hasClass("has-user-value"));
+ // Reset or delete the preference while editing.
+ row.editColumnButton.click();
+ Assert.equal(row.valueInput.value, Preferences.get(prefName));
+ row.resetColumnButton.click();
+ Assert.ok(!row.hasClass("has-user-value"));
+ Assert.equal(row.hasClass("deleted"), willDelete);
+ }
+ });
+
+ // This test would have opened the invalid form popup, so just close it so as not to
+ // affect later tests.
+ let invalidFormPopup = window.document.getElementById("invalid-form-popup");
+ invalidFormPopup.hidePopup();
+ await BrowserTestUtils.waitForCondition(() => {
+ return invalidFormPopup.state == "closed";
+ }, "form validation popup closed");
+});
+
+add_task(async function test_edit_field_selected() {
+ let prefsToCheck = [
+ [PREF_MODIFY_STRING, "A string", "A new string"],
+ [PREF_MODIFY_NUMBER, "100", "500"],
+ ];
+ await AboutConfigTest.withNewTab(async function () {
+ for (let [prefName, startValue, endValue] of prefsToCheck) {
+ Preferences.set(prefName, startValue);
+ let row = this.getRow(prefName);
+
+ Assert.equal(row.value, startValue);
+ row.editColumnButton.click();
+ Assert.equal(row.valueInput.value, startValue);
+
+ EventUtils.sendString(endValue, this.window);
+
+ row.editColumnButton.click();
+ Assert.equal(row.value, endValue);
+ Assert.equal(Preferences.get(prefName), endValue);
+ }
+ });
+});
+
+add_task(async function test_escape_cancels_edit() {
+ await AboutConfigTest.withNewTab(async function () {
+ let row = this.getRow(PREF_MODIFY_STRING);
+ Preferences.set(PREF_MODIFY_STRING, "Edit me, maybe");
+
+ for (let blurInput of [false, true]) {
+ Assert.ok(!row.valueInput);
+ row.editColumnButton.click();
+
+ Assert.ok(row.valueInput);
+
+ Assert.equal(row.valueInput.value, "Edit me, maybe");
+ row.valueInput.value = "Edited";
+
+ // Test both cases of the input being focused and not being focused.
+ if (blurInput) {
+ row.valueInput.blur();
+ Assert.notEqual(this.document.activeElement, row.valueInput);
+ } else {
+ Assert.equal(this.document.activeElement, row.valueInput);
+ }
+
+ EventUtils.synthesizeKey("KEY_Escape", {}, this.window);
+
+ Assert.ok(!row.valueInput);
+ Assert.equal(row.value, "Edit me, maybe");
+ Assert.equal(row.value, Preferences.get(PREF_MODIFY_STRING));
+ }
+ });
+});
+
+add_task(async function test_double_click_modify() {
+ Preferences.set(PREF_MODIFY_BOOLEAN, true);
+ Preferences.set(PREF_MODIFY_NUMBER, 10);
+ Preferences.set(PREF_MODIFY_STRING, "Hello!");
+
+ await AboutConfigTest.withNewTab(async function () {
+ this.search(PREF_MODIFY_PREFIX);
+
+ let click = (target, opts) =>
+ EventUtils.synthesizeMouseAtCenter(target, opts, this.window);
+ let doubleClick = target => {
+ // Trigger two mouse events to simulate the first then second click.
+ click(target, { clickCount: 1 });
+ click(target, { clickCount: 2 });
+ };
+ let tripleClick = target => {
+ // Trigger all 3 mouse events to simulate the three mouse events we'd see.
+ click(target, { clickCount: 1 });
+ click(target, { clickCount: 2 });
+ click(target, { clickCount: 3 });
+ };
+
+ // Check double-click to edit a boolean.
+ let boolRow = this.getRow(PREF_MODIFY_BOOLEAN);
+ Assert.equal(boolRow.value, "true");
+ doubleClick(boolRow.valueCell);
+ Assert.equal(boolRow.value, "false");
+ doubleClick(boolRow.nameCell);
+ Assert.equal(boolRow.value, "true");
+
+ // Check double-click to edit a number.
+ let intRow = this.getRow(PREF_MODIFY_NUMBER);
+ Assert.equal(intRow.value, 10);
+ doubleClick(intRow.valueCell);
+ Assert.equal(this.document.activeElement, intRow.valueInput);
+ EventUtils.sendString("75");
+ EventUtils.synthesizeKey("KEY_Enter");
+ Assert.equal(intRow.value, 75);
+
+ // Check double-click focuses input when already editing.
+ Assert.equal(intRow.value, 75);
+ doubleClick(intRow.nameCell);
+ Assert.equal(this.document.activeElement, intRow.valueInput);
+ intRow.valueInput.blur();
+ Assert.notEqual(this.document.activeElement, intRow.valueInput);
+ doubleClick(intRow.nameCell);
+ Assert.equal(this.document.activeElement, intRow.valueInput);
+ EventUtils.sendString("20");
+ EventUtils.synthesizeKey("KEY_Enter");
+ Assert.equal(intRow.value, 20);
+
+ // Check double-click to edit a string.
+ let stringRow = this.getRow(PREF_MODIFY_STRING);
+ Assert.equal(stringRow.value, "Hello!");
+ doubleClick(stringRow.valueCell);
+ Assert.equal(
+ this.document.activeElement,
+ stringRow.valueInput,
+ "The input is focused"
+ );
+ EventUtils.sendString("New String!");
+ EventUtils.synthesizeKey("KEY_Enter");
+ Assert.equal(stringRow.value, "New String!");
+
+ // Check triple-click also edits the pref and selects the text inside.
+ tripleClick(stringRow.nameCell);
+ Assert.equal(
+ this.document.activeElement,
+ stringRow.valueInput,
+ "The input is focused"
+ );
+
+ // Check double-click inside input selects a word.
+ let newString = "Another string...";
+ EventUtils.sendString(newString);
+ Assert.equal(this.window.getSelection().toString(), "");
+ let stringInput = stringRow.valueInput;
+ doubleClick(stringInput);
+ let selectionLength = stringInput.selectionEnd - stringInput.selectionStart;
+ Assert.greater(selectionLength, 0);
+ Assert.less(selectionLength, newString.length);
+ EventUtils.synthesizeKey("KEY_Enter");
+ Assert.equal(stringRow.value, newString);
+
+ // Check that double/triple-click on the add row selects text as usual.
+ let addRow = this.getRow(PREF_MODIFY_PREFIX);
+ Assert.ok(addRow.hasClass("deleted"));
+ doubleClick(addRow.nameCell);
+ Assert.ok(PREF_MODIFY_PREFIX.includes(this.window.getSelection()));
+ tripleClick(addRow.nameCell);
+ Assert.equal(this.window.getSelection().toString(), PREF_MODIFY_PREFIX);
+ // Make sure the localized text is set in the value cell.
+ let labels = Array.from(addRow.valueCell.querySelectorAll("label > span"));
+ await this.document.l10n.translateElements(labels);
+ Assert.ok(labels.every(label => !!label.textContent));
+ // Double-click the first input label text.
+ doubleClick(labels[0]);
+ Assert.equal(this.window.getSelection().toString(), labels[0].textContent);
+ tripleClick(addRow.valueCell.querySelector("label > span"));
+ Assert.equal(
+ this.window.getSelection().toString(),
+ labels.map(l => l.textContent).join("")
+ );
+ });
+});
diff --git a/toolkit/components/aboutconfig/test/browser/browser_locked.js b/toolkit/components/aboutconfig/test/browser/browser_locked.js
new file mode 100644
index 0000000000..6b06f22218
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_locked.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const PREF_STRING_NO_DEFAULT = "test.aboutconfig.a";
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_STRING_NO_DEFAULT, "some value"]],
+ });
+});
+
+add_task(async function test_locked() {
+ registerCleanupFunction(() => {
+ Services.prefs.unlockPref(PREF_STRING_DEFAULT_NOTEMPTY);
+ Services.prefs.unlockPref(PREF_BOOLEAN_DEFAULT_TRUE);
+ Services.prefs.unlockPref(PREF_STRING_NO_DEFAULT);
+ });
+
+ Services.prefs.lockPref(PREF_STRING_DEFAULT_NOTEMPTY);
+ Services.prefs.lockPref(PREF_BOOLEAN_DEFAULT_TRUE);
+ Services.prefs.lockPref(PREF_STRING_NO_DEFAULT);
+
+ await AboutConfigTest.withNewTab(async function () {
+ // Test locked default string pref.
+ let lockedPref = this.getRow(PREF_STRING_DEFAULT_NOTEMPTY);
+ Assert.ok(lockedPref.hasClass("locked"));
+ Assert.equal(lockedPref.value, PREF_STRING_DEFAULT_NOTEMPTY_VALUE);
+ Assert.ok(lockedPref.editColumnButton.classList.contains("button-edit"));
+ Assert.ok(lockedPref.editColumnButton.disabled);
+
+ // Test locked default boolean pref.
+ lockedPref = this.getRow(PREF_BOOLEAN_DEFAULT_TRUE);
+ Assert.ok(lockedPref.hasClass("locked"));
+ Assert.equal(lockedPref.value, "true");
+ Assert.ok(lockedPref.editColumnButton.classList.contains("button-toggle"));
+ Assert.ok(lockedPref.editColumnButton.disabled);
+
+ // Test locked user added pref.
+ lockedPref = this.getRow(PREF_STRING_NO_DEFAULT);
+ Assert.ok(lockedPref.hasClass("locked"));
+ Assert.equal(lockedPref.value, "");
+ Assert.ok(lockedPref.editColumnButton.classList.contains("button-edit"));
+ Assert.ok(lockedPref.editColumnButton.disabled);
+
+ // Test pref not locked.
+ let unlockedPref = this.getRow(PREF_BOOLEAN_USERVALUE_TRUE);
+ Assert.ok(!unlockedPref.hasClass("locked"));
+ Assert.equal(unlockedPref.value, "true");
+ Assert.ok(
+ unlockedPref.editColumnButton.classList.contains("button-toggle")
+ );
+ Assert.ok(!unlockedPref.editColumnButton.disabled);
+ });
+});
diff --git a/toolkit/components/aboutconfig/test/browser/browser_observe.js b/toolkit/components/aboutconfig/test/browser/browser_observe.js
new file mode 100644
index 0000000000..1f1ab5d217
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_observe.js
@@ -0,0 +1,163 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["test.aboutconfig.modify.boolean", true],
+ ["test.aboutconfig.modify.number", 1337],
+ [
+ "test.aboutconfig.modify.string",
+ "the answer to the life the universe and everything",
+ ],
+ ],
+ });
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(PREF_BOOLEAN_DEFAULT_TRUE);
+ Services.prefs.clearUserPref(PREF_NUMBER_DEFAULT_ZERO);
+ Services.prefs.clearUserPref(PREF_STRING_DEFAULT_EMPTY);
+ });
+});
+
+add_task(async function test_observe_add_user_pref_before_search() {
+ Assert.equal(
+ Services.prefs.getPrefType(PREF_NEW),
+ Ci.nsIPrefBranch.PREF_INVALID
+ );
+
+ await AboutConfigTest.withNewTab(
+ async function () {
+ this.bypassWarningButton.click();
+
+ // No results are shown after the warning page is dismissed or bypassed,
+ // and newly added preferences should not be displayed.
+ Preferences.set(PREF_NEW, true);
+ Assert.ok(!this.prefsTable.firstElementChild);
+ Preferences.reset(PREF_NEW);
+ },
+ { dontBypassWarning: true }
+ );
+});
+
+add_task(async function test_observe_add_user_pref() {
+ Assert.equal(
+ Services.prefs.getPrefType(PREF_NEW),
+ Ci.nsIPrefBranch.PREF_INVALID
+ );
+
+ await AboutConfigTest.withNewTab(async function () {
+ for (let value of [false, true, "", "value", 0, -10]) {
+ // A row should be added when a new preference is added.
+ Assert.ok(!this.getRow(PREF_NEW));
+ Preferences.set(PREF_NEW, value);
+ let row = this.getRow(PREF_NEW);
+ Assert.equal(row.value, "" + value);
+
+ // The row should stay when the preference is removed.
+ Preferences.reset(PREF_NEW);
+ Assert.ok(row.hasClass("deleted"));
+
+ // Re-adding the preference from the interface should restore its value.
+ row.editColumnButton.click();
+ if (value.constructor.name != "Boolean") {
+ row.editColumnButton.click();
+ }
+ Assert.equal(row.value, "" + value);
+ Assert.ok(Preferences.get(PREF_NEW) === value);
+
+ // Filtering again after deleting should remove the row.
+ Preferences.reset(PREF_NEW);
+ this.showAll();
+ Assert.ok(!this.getRow(PREF_NEW));
+
+ // Searching for the preference name should give the ability to add it.
+ Preferences.reset(PREF_NEW);
+ this.search(PREF_NEW);
+ row = this.getRow(PREF_NEW);
+ Assert.ok(row.hasClass("deleted"));
+
+ // The row for adding should be reused if the new preference is added.
+ Preferences.set(PREF_NEW, value);
+ Assert.equal(row.value, "" + value);
+
+ // If a new preference does not match the filter it is not displayed.
+ Preferences.reset(PREF_NEW);
+ this.search(PREF_NEW + ".extra");
+ Assert.ok(!this.getRow(PREF_NEW));
+ Preferences.set(PREF_NEW, value);
+ Assert.ok(!this.getRow(PREF_NEW));
+
+ // Resetting the filter should display the new preference.
+ this.showAll();
+ Assert.equal(this.getRow(PREF_NEW).value, "" + value);
+
+ // Reset the preference, then continue by adding a different value.
+ Preferences.reset(PREF_NEW);
+ this.showAll();
+ }
+ });
+});
+
+add_task(async function test_observe_delete_user_pref() {
+ for (let value of [true, "value", -10]) {
+ Preferences.set(PREF_NEW, value);
+ await AboutConfigTest.withNewTab(async function () {
+ // Deleting the preference should keep the row.
+ let row = this.getRow(PREF_NEW);
+ Preferences.reset(PREF_NEW);
+ Assert.ok(row.hasClass("deleted"));
+
+ // Filtering again should remove the row.
+ this.showAll();
+ Assert.ok(!this.getRow(PREF_NEW));
+ });
+ }
+});
+
+add_task(async function test_observe_reset_user_pref() {
+ await SpecialPowers.pushPrefEnv({
+ set: [[PREF_BOOLEAN_DEFAULT_TRUE, false]],
+ });
+
+ await AboutConfigTest.withNewTab(async function () {
+ let row = this.getRow(PREF_BOOLEAN_DEFAULT_TRUE);
+ Preferences.reset(PREF_BOOLEAN_DEFAULT_TRUE);
+ Assert.ok(!row.hasClass("has-user-value"));
+ Assert.equal(row.value, "true");
+ });
+});
+
+add_task(async function test_observe_modify() {
+ await AboutConfigTest.withNewTab(async function () {
+ for (let [name, value] of [
+ ["test.aboutconfig.modify.boolean", false],
+ ["test.aboutconfig.modify.number", -10],
+ ["test.aboutconfig.modify.string", "value"],
+ [PREF_BOOLEAN_DEFAULT_TRUE, false],
+ [PREF_NUMBER_DEFAULT_ZERO, 1],
+ [PREF_STRING_DEFAULT_EMPTY, "string"],
+ ]) {
+ let row = this.getRow(name);
+ Assert.notEqual(row.value, "" + value);
+ Preferences.set(name, value);
+ Assert.equal(row.value, "" + value);
+
+ if (value.constructor.name == "Boolean") {
+ continue;
+ }
+
+ // Changing the value or removing while editing should not take effect.
+ row.editColumnButton.click();
+ row.valueInput.value = "42";
+ Preferences.reset(name);
+ Assert.equal(row.element, this.getRow(name).element);
+ Assert.equal(row.valueInput.value, "42");
+
+ // Saving should store the value even if the preference was modified.
+ row.editColumnButton.click();
+ Assert.equal(row.value, "42");
+ Assert.equal(Preferences.get(name), "42");
+ }
+ });
+});
diff --git a/toolkit/components/aboutconfig/test/browser/browser_search.js b/toolkit/components/aboutconfig/test/browser/browser_search.js
new file mode 100644
index 0000000000..89a0c0c866
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_search.js
@@ -0,0 +1,177 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["test.aboutconfig.a", "test value 1"],
+ ["test.aboutconfig.ab", "test value 2"],
+ ["test.aboutconfig.bc", "test value 3"],
+ ],
+ });
+});
+
+add_task(async function test_search() {
+ await AboutConfigTest.withNewTab(async function () {
+ await this.document.l10n.translateFragment(this.document.documentElement);
+ let prefArray = Services.prefs.getChildList("");
+
+ // The total number of preferences may change at any time because of
+ // operations running in the background, so we only test approximately.
+ // The change in count would be because of one or two added preferences,
+ // but we tolerate a difference of up to 50 preferences just to be safe.
+ // We want thousands of prefs instead of a few dozen that are filtered.
+ Assert.greater(this.rows.length, prefArray.length - 50);
+
+ // Filter a subset of preferences. The "browser.download." branch is
+ // chosen because it is very unlikely that its preferences would be
+ // modified by other code during the execution of this test.
+ this.search("Wser.down ");
+
+ let filteredPrefArray = prefArray.filter(pref =>
+ pref.includes("wser.down")
+ );
+ // Adding +1 to the list since button does not match an exact
+ // preference name then a row is added for the user to add a
+ // new button preference if desired
+ Assert.equal(this.rows.length, filteredPrefArray.length + 1);
+
+ // Show all preferences again after filtering.
+ this.showAll();
+ Assert.equal(this.searchInput.value, "");
+
+ // The total number of preferences may change at any time because of
+ // operations running in the background, so we only test approximately.
+ // The change in count would be because of one or two added preferences,
+ // but we tolerate a difference of up to 50 preferences just to be safe.
+ // We want thousands of prefs instead of a few dozen that are filtered.
+ Assert.greater(this.rows.length, prefArray.length - 50);
+
+ // Check if "Only show modified" feature works.
+ EventUtils.sendMouseEvent({ type: "click" }, this.showOnlyModifiedCheckbox);
+ Assert.ok(this.rows.every(r => r.hasClass("has-user-value")));
+
+ // Uncheck checkbox
+ EventUtils.sendMouseEvent({ type: "click" }, this.showOnlyModifiedCheckbox);
+ Assert.ok(!this.rows.every(r => r.hasClass("has-user-value")));
+
+ // Pressing ESC while showing all preferences returns to the initial page.
+ EventUtils.sendKey("escape");
+ Assert.equal(this.rows.length, 0);
+
+ // Test invalid search returns no preferences.
+ // Expecting 1 row to be returned since it offers the ability to add.
+ this.search("aJunkValueasdf");
+ Assert.equal(this.rows.length, 1);
+ // The has-visible-prefs attribute is used to style the border of the add row.
+ Assert.ok(!this.prefsTable.hasAttribute("has-visible-prefs"));
+ let addRow = this.getRow("aJunkValueasdf");
+ Assert.equal(getComputedStyle(addRow.valueCell)["border-top-width"], "0px");
+
+ // Pressing ESC clears the field and returns to the initial page.
+ EventUtils.sendKey("escape");
+ Assert.equal(this.searchInput.value, "");
+ Assert.equal(this.rows.length, 0);
+
+ // Two preferences match this filter, and one of those matches exactly.
+ this.search("test.aboutconfig.a");
+ Assert.equal(this.rows.length, 2);
+
+ // When searching case insensitively, there is an additional row to add a
+ // new preference with the same name but a different case.
+ this.search("TEST.aboutconfig.a");
+ Assert.equal(this.rows.length, 3);
+ // The has-visible-prefs attribute is used to style the border of the add row.
+ Assert.ok(this.prefsTable.hasAttribute("has-visible-prefs"));
+ addRow = this.getRow("TEST.aboutconfig.a");
+ Assert.equal(getComputedStyle(addRow.valueCell)["border-top-width"], "1px");
+
+ // Entering an empty string returns to the initial page.
+ this.search("");
+ Assert.equal(this.rows.length, 0);
+ Assert.ok(!this.prefsTable.hasAttribute("has-visible-prefs"));
+ });
+});
+
+add_task(async function test_search_wildcard() {
+ await AboutConfigTest.withNewTab(async function () {
+ const extra = 1; // "Add" row
+
+ // A trailing wildcard
+ this.search("test.about*");
+ Assert.equal(this.rows.length, 3 + extra);
+
+ // A wildcard in middle
+ this.search("test.about*a");
+ Assert.equal(this.rows.length, 2 + extra);
+ this.search("test.about*ab");
+ Assert.equal(this.rows.length, 1 + extra);
+ this.search("test.aboutcon*fig");
+ Assert.equal(this.rows.length, 3 + extra);
+
+ // Multiple wildcards in middle
+ this.search("test.about*fig*ab");
+ Assert.equal(this.rows.length, 1 + extra);
+ this.search("test.about*config*ab");
+ Assert.equal(this.rows.length, 1 + extra);
+ });
+});
+
+add_task(async function test_search_delayed() {
+ await AboutConfigTest.withNewTab(async function () {
+ // Start with the initial empty page.
+ this.search("");
+
+ // We need to wait more than the search typing timeout to make sure that
+ // nothing happens when entering a short string.
+ EventUtils.synthesizeKey("t");
+ EventUtils.synthesizeKey("e");
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ Assert.equal(this.rows.length, 0);
+
+ // Pressing Enter will force a search to occur anyways.
+ EventUtils.sendKey("return");
+ Assert.greater(this.rows.length, 0);
+
+ // Prepare the table and the search field for the next test.
+ this.search("test.aboutconfig.a");
+ Assert.equal(this.rows.length, 2);
+
+ // The table is updated in a single microtask, so we don't need to wait for
+ // specific mutations, we can just continue when any of the children or
+ // their "hidden" attributes are updated.
+ let prefsTableChanged = new Promise(resolve => {
+ let observer = new MutationObserver(() => {
+ observer.disconnect();
+ resolve();
+ });
+ observer.observe(this.prefsTable, { childList: true });
+ for (let element of this.prefsTable.children) {
+ observer.observe(element, { attributes: true });
+ }
+ });
+
+ // Add a character and test that the table is not updated immediately.
+ EventUtils.synthesizeKey("b");
+ Assert.equal(this.rows.length, 2);
+
+ // The table will eventually be updated after a delay.
+ await prefsTableChanged;
+ Assert.equal(this.rows.length, 1);
+ });
+});
+
+add_task(async function test_search_add_row_color() {
+ await AboutConfigTest.withNewTab(async function () {
+ // When the row is the only one displayed, it doesn't have the "odd" class.
+ this.search("test.aboutconfig.add");
+ Assert.equal(this.rows.length, 1);
+ Assert.ok(!this.getRow("test.aboutconfig.add").hasClass("odd"));
+
+ // When displayed with one other preference, the "odd" class is present.
+ this.search("test.aboutconfig.b");
+ Assert.equal(this.rows.length, 2);
+ Assert.ok(this.getRow("test.aboutconfig.b").hasClass("odd"));
+ });
+});
diff --git a/toolkit/components/aboutconfig/test/browser/browser_warning.js b/toolkit/components/aboutconfig/test/browser/browser_warning.js
new file mode 100644
index 0000000000..d95e8f49ea
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/browser_warning.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.aboutConfig.showWarning", true]],
+ });
+});
+
+add_task(async function test_showWarningNextTime() {
+ for (let test of [
+ { expectWarningPage: true, disableShowWarningNextTime: false },
+ { expectWarningPage: true, disableShowWarningNextTime: true },
+ { expectWarningPage: false },
+ ]) {
+ await AboutConfigTest.withNewTab(
+ async function () {
+ if (test.expectWarningPage) {
+ this.assertWarningPage(true);
+ Assert.ok(
+ this.document.getElementById("showWarningNextTime").checked
+ );
+ if (test.disableShowWarningNextTime) {
+ this.document.getElementById("showWarningNextTime").click();
+ }
+ this.bypassWarningButton.click();
+ }
+
+ // No results are shown after the warning page is dismissed or bypassed.
+ this.assertWarningPage(false);
+ Assert.ok(!this.prefsTable.firstElementChild);
+ Assert.equal(this.document.activeElement, this.searchInput);
+
+ // The show all button should be present and show all results immediately.
+ this.showAll();
+ Assert.ok(this.prefsTable.firstElementChild);
+ },
+ { dontBypassWarning: true }
+ );
+ }
+});
diff --git a/toolkit/components/aboutconfig/test/browser/head.js b/toolkit/components/aboutconfig/test/browser/head.js
new file mode 100644
index 0000000000..511fc07a37
--- /dev/null
+++ b/toolkit/components/aboutconfig/test/browser/head.js
@@ -0,0 +1,173 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { Preferences } = ChromeUtils.importESModule(
+ "resource://gre/modules/Preferences.sys.mjs"
+);
+
+// List of default preferences that can be used for tests, chosen because they
+// have little or no side-effects when they are modified for a brief time. If
+// any of these preferences are removed or their default state changes, just
+// update the constant to point to a different preference with the same default.
+const PREF_BOOLEAN_DEFAULT_TRUE = "accessibility.typeaheadfind.manual";
+const PREF_BOOLEAN_USERVALUE_TRUE = "browser.dom.window.dump.enabled";
+const PREF_NUMBER_DEFAULT_ZERO = "accessibility.typeaheadfind.casesensitive";
+const PREF_STRING_DEFAULT_EMPTY = "browser.helperApps.neverAsk.openFile";
+const PREF_STRING_DEFAULT_NOTEMPTY = "accessibility.typeaheadfind.soundURL";
+const PREF_STRING_DEFAULT_NOTEMPTY_VALUE = "beep";
+const PREF_STRING_LOCALIZED_MISSING = "intl.menuitems.alwaysappendaccesskeys";
+
+// Other preference names used in tests.
+const PREF_NEW = "test.aboutconfig.new";
+
+// These tests can be slow to execute because they show all the preferences
+// several times, and each time can require a second on some virtual machines.
+requestLongerTimeout(2);
+
+class AboutConfigRowTest {
+ constructor(element) {
+ this.element = element;
+ }
+
+ querySelector(selector) {
+ return this.element.querySelector(selector);
+ }
+
+ get nameCell() {
+ return this.querySelector("th");
+ }
+
+ get name() {
+ return this.nameCell.textContent;
+ }
+
+ get valueCell() {
+ return this.querySelector("td.cell-value");
+ }
+
+ get value() {
+ return this.valueCell.textContent;
+ }
+
+ /**
+ * Text input field when the row is in edit mode.
+ */
+ get valueInput() {
+ return this.valueCell.querySelector("input");
+ }
+
+ /**
+ * This is normally "edit" or "toggle" based on the preference type, "save"
+ * when the row is in edit mode, or "add" when the preference does not exist.
+ */
+ get editColumnButton() {
+ return this.querySelector("td.cell-edit > button");
+ }
+
+ /**
+ * This can be "reset" or "delete" based on whether a default exists.
+ */
+ get resetColumnButton() {
+ return this.querySelector("td:last-child > button");
+ }
+
+ hasClass(className) {
+ return this.element.classList.contains(className);
+ }
+}
+
+class AboutConfigTest {
+ static withNewTab(testFn, options = {}) {
+ return BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "chrome://global/content/aboutconfig/aboutconfig.html",
+ },
+ async browser => {
+ let scope = new this(browser);
+ await scope.setupNewTab(options);
+ await testFn.call(scope);
+ }
+ );
+ }
+
+ constructor(browser) {
+ this.browser = browser;
+ this.document = browser.contentDocument;
+ this.window = browser.contentWindow;
+ }
+
+ async setupNewTab(options) {
+ await this.document.l10n.ready;
+ if (!options.dontBypassWarning) {
+ this.bypassWarningButton.click();
+ this.showAll();
+ }
+ }
+
+ get showWarningNextTimeInput() {
+ return this.document.getElementById("showWarningNextTime");
+ }
+
+ get bypassWarningButton() {
+ return this.document.getElementById("warningButton");
+ }
+
+ get searchInput() {
+ return this.document.getElementById("about-config-search");
+ }
+
+ get showOnlyModifiedCheckbox() {
+ return this.document.getElementById("about-config-show-only-modified");
+ }
+
+ get prefsTable() {
+ return this.document.getElementById("prefs");
+ }
+
+ /**
+ * Array of AboutConfigRowTest objects, one for each row in the main table.
+ */
+ get rows() {
+ let elements = this.prefsTable.querySelectorAll("tr:not(.hidden)");
+ return Array.from(elements, element => new AboutConfigRowTest(element));
+ }
+
+ /**
+ * Returns the AboutConfigRowTest object for the row in the main table which
+ * corresponds to the given preference name, or undefined if none is present.
+ */
+ getRow(name) {
+ return this.rows.find(row => row.name == name);
+ }
+
+ /**
+ * Shows all preferences using the dedicated button.
+ */
+ showAll() {
+ this.search("");
+ this.document.getElementById("show-all").click();
+ }
+
+ /**
+ * Performs a new search using the dedicated textbox. This also makes sure
+ * that the list of preferences displayed is up to date.
+ */
+ search(value) {
+ this.searchInput.value = value;
+ this.searchInput.focus();
+ EventUtils.sendKey("return");
+ }
+
+ /**
+ * Checks whether or not the initial warning page is displayed.
+ */
+ assertWarningPage(expected) {
+ Assert.equal(!!this.showWarningNextTimeInput, expected);
+ Assert.equal(!!this.bypassWarningButton, expected);
+ Assert.equal(!this.searchInput, expected);
+ Assert.equal(!this.prefsTable, expected);
+ }
+}