summaryrefslogtreecommitdiffstats
path: root/toolkit/components/normandy/test/browser/browser_about_studies.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/normandy/test/browser/browser_about_studies.js')
-rw-r--r--toolkit/components/normandy/test/browser/browser_about_studies.js825
1 files changed, 825 insertions, 0 deletions
diff --git a/toolkit/components/normandy/test/browser/browser_about_studies.js b/toolkit/components/normandy/test/browser/browser_about_studies.js
new file mode 100644
index 0000000000..745e961b9a
--- /dev/null
+++ b/toolkit/components/normandy/test/browser/browser_about_studies.js
@@ -0,0 +1,825 @@
+"use strict";
+
+const { PreferenceExperiments } = ChromeUtils.importESModule(
+ "resource://normandy/lib/PreferenceExperiments.sys.mjs"
+);
+const { RecipeRunner } = ChromeUtils.importESModule(
+ "resource://normandy/lib/RecipeRunner.sys.mjs"
+);
+const { ExperimentFakes } = ChromeUtils.importESModule(
+ "resource://testing-common/NimbusTestUtils.sys.mjs"
+);
+const { ExperimentManager } = ChromeUtils.importESModule(
+ "resource://nimbus/lib/ExperimentManager.sys.mjs"
+);
+const { RemoteSettingsExperimentLoader } = ChromeUtils.importESModule(
+ "resource://nimbus/lib/RemoteSettingsExperimentLoader.sys.mjs"
+);
+
+const { NormandyTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/NormandyTestUtils.sys.mjs"
+);
+const { addonStudyFactory, preferenceStudyFactory } =
+ NormandyTestUtils.factories;
+
+function withAboutStudies() {
+ return function (testFunc) {
+ return async args =>
+ BrowserTestUtils.withNewTab("about:studies", async browser =>
+ testFunc({ ...args, browser })
+ );
+ };
+}
+
+// Test that the code renders at all
+decorate_task(
+ withAboutStudies(),
+ async function testAboutStudiesWorks({ browser }) {
+ const appFound = await SpecialPowers.spawn(
+ browser,
+ [],
+ () => !!content.document.getElementById("app")
+ );
+ ok(appFound, "App element was found");
+ }
+);
+
+// Test that the learn more element is displayed correctly
+decorate_task(
+ withPrefEnv({
+ set: [["app.normandy.shieldLearnMoreUrl", "http://test/%OS%/"]],
+ }),
+ withAboutStudies(),
+ async function testLearnMore({ browser }) {
+ SpecialPowers.spawn(browser, [], async () => {
+ const doc = content.document;
+ await ContentTaskUtils.waitForCondition(() =>
+ doc.getElementById("shield-studies-learn-more")
+ );
+ doc.getElementById("shield-studies-learn-more").click();
+ });
+ await BrowserTestUtils.waitForLocationChange(gBrowser);
+
+ const location = browser.currentURI.spec;
+ is(
+ location,
+ AboutPages.aboutStudies.getShieldLearnMoreHref(),
+ "Clicking Learn More opens the correct page on SUMO."
+ );
+ ok(!location.includes("%OS%"), "The Learn More URL is formatted.");
+ }
+);
+
+// Test that jumping to preferences worked as expected
+decorate_task(
+ withAboutStudies(),
+ async function testUpdatePreferences({ browser }) {
+ let loadPromise = BrowserTestUtils.firstBrowserLoaded(window);
+
+ // We have to use gBrowser instead of browser in most spots since we're
+ // dealing with a new tab outside of the about:studies tab.
+ const tab = await BrowserTestUtils.switchTab(gBrowser, () => {
+ SpecialPowers.spawn(browser, [], async () => {
+ const doc = content.document;
+ await ContentTaskUtils.waitForCondition(() =>
+ doc.getElementById("shield-studies-update-preferences")
+ );
+ content.document
+ .getElementById("shield-studies-update-preferences")
+ .click();
+ });
+ });
+
+ await loadPromise;
+
+ const location = gBrowser.currentURI.spec;
+ is(
+ location,
+ "about:preferences#privacy",
+ "Clicking Update Preferences opens the privacy section of the new about:preferences."
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ }
+);
+
+// Test that the study listing shows studies in the proper order and grouping
+decorate_task(
+ AddonStudies.withStudies([
+ addonStudyFactory({
+ slug: "fake-study-a",
+ userFacingName: "A Fake Add-on Study",
+ active: true,
+ userFacingDescription: "A fake description",
+ studyStartDate: new Date(2018, 0, 4),
+ }),
+ addonStudyFactory({
+ slug: "fake-study-b",
+ userFacingName: "B Fake Add-on Study",
+ active: false,
+ userFacingDescription: "B fake description",
+ studyStartDate: new Date(2018, 0, 2),
+ }),
+ addonStudyFactory({
+ slug: "fake-study-c",
+ userFacingName: "C Fake Add-on Study",
+ active: true,
+ userFacingDescription: "C fake description",
+ studyStartDate: new Date(2018, 0, 1),
+ }),
+ ]),
+ PreferenceExperiments.withMockExperiments([
+ preferenceStudyFactory({
+ slug: "fake-study-d",
+ userFacingName: null,
+ userFacingDescription: null,
+ lastSeen: new Date(2018, 0, 3),
+ expired: false,
+ }),
+ preferenceStudyFactory({
+ slug: "fake-study-e",
+ userFacingName: "E Fake Preference Study",
+ lastSeen: new Date(2018, 0, 5),
+ expired: true,
+ }),
+ preferenceStudyFactory({
+ slug: "fake-study-f",
+ userFacingName: "F Fake Preference Study",
+ lastSeen: new Date(2018, 0, 6),
+ expired: false,
+ }),
+ ]),
+ withAboutStudies(),
+ async function testStudyListing({ addonStudies, prefExperiments, browser }) {
+ await SpecialPowers.spawn(
+ browser,
+ [{ addonStudies, prefExperiments }],
+ async ({ addonStudies, prefExperiments }) => {
+ const doc = content.document;
+
+ function getStudyRow(docElem, slug) {
+ return docElem.querySelector(`.study[data-study-slug="${slug}"]`);
+ }
+
+ await ContentTaskUtils.waitForCondition(
+ () => doc.querySelectorAll(".active-study-list .study").length
+ );
+ const activeNames = Array.from(
+ doc.querySelectorAll(".active-study-list .study")
+ ).map(row => row.dataset.studySlug);
+ const inactiveNames = Array.from(
+ doc.querySelectorAll(".inactive-study-list .study")
+ ).map(row => row.dataset.studySlug);
+
+ Assert.deepEqual(
+ activeNames,
+ [
+ prefExperiments[2].slug,
+ addonStudies[0].slug,
+ prefExperiments[0].slug,
+ addonStudies[2].slug,
+ ],
+ "Active studies are grouped by enabled status, and sorted by date"
+ );
+ Assert.deepEqual(
+ inactiveNames,
+ [prefExperiments[1].slug, addonStudies[1].slug],
+ "Inactive studies are grouped by enabled status, and sorted by date"
+ );
+
+ const activeAddonStudy = getStudyRow(doc, addonStudies[0].slug);
+ ok(
+ activeAddonStudy
+ .querySelector(".study-description")
+ .textContent.includes(addonStudies[0].userFacingDescription),
+ "Study descriptions are shown in about:studies."
+ );
+ is(
+ activeAddonStudy.querySelector(".study-status").textContent,
+ "Active",
+ "Active studies show an 'Active' indicator."
+ );
+ ok(
+ activeAddonStudy.querySelector(".remove-button"),
+ "Active studies show a remove button"
+ );
+ is(
+ activeAddonStudy
+ .querySelector(".study-icon")
+ .textContent.toLowerCase(),
+ "a",
+ "Study icons use the first letter of the study name."
+ );
+
+ const inactiveAddonStudy = getStudyRow(doc, addonStudies[1].slug);
+ is(
+ inactiveAddonStudy.querySelector(".study-status").textContent,
+ "Complete",
+ "Inactive studies are marked as complete."
+ );
+ ok(
+ !inactiveAddonStudy.querySelector(".remove-button"),
+ "Inactive studies do not show a remove button"
+ );
+
+ const activePrefStudy = getStudyRow(doc, prefExperiments[0].slug);
+ const preferenceName = Object.keys(prefExperiments[0].preferences)[0];
+ ok(
+ activePrefStudy
+ .querySelector(".study-description")
+ .textContent.includes(preferenceName),
+ "Preference studies show the preference they are changing"
+ );
+ is(
+ activePrefStudy.querySelector(".study-status").textContent,
+ "Active",
+ "Active studies show an 'Active' indicator."
+ );
+ ok(
+ activePrefStudy.querySelector(".remove-button"),
+ "Active studies show a remove button"
+ );
+
+ const inactivePrefStudy = getStudyRow(doc, prefExperiments[1].slug);
+ is(
+ inactivePrefStudy.querySelector(".study-status").textContent,
+ "Complete",
+ "Inactive studies are marked as complete."
+ );
+ ok(
+ !inactivePrefStudy.querySelector(".remove-button"),
+ "Inactive studies do not show a remove button"
+ );
+
+ activeAddonStudy.querySelector(".remove-button").click();
+ await ContentTaskUtils.waitForCondition(() =>
+ getStudyRow(doc, addonStudies[0].slug).matches(".study.disabled")
+ );
+ ok(
+ getStudyRow(doc, addonStudies[0].slug).matches(".study.disabled"),
+ "Clicking the remove button updates the UI to show that the study has been disabled."
+ );
+
+ activePrefStudy.querySelector(".remove-button").click();
+ await ContentTaskUtils.waitForCondition(() =>
+ getStudyRow(doc, prefExperiments[0].slug).matches(".study.disabled")
+ );
+ ok(
+ getStudyRow(doc, prefExperiments[0].slug).matches(".study.disabled"),
+ "Clicking the remove button updates the UI to show that the study has been disabled."
+ );
+ }
+ );
+
+ const updatedAddonStudy = await AddonStudies.get(addonStudies[0].recipeId);
+ ok(
+ !updatedAddonStudy.active,
+ "Clicking the remove button marks addon studies as inactive in storage."
+ );
+
+ const updatedPrefStudy = await PreferenceExperiments.get(
+ prefExperiments[0].slug
+ );
+ ok(
+ updatedPrefStudy.expired,
+ "Clicking the remove button marks preference studies as expired in storage."
+ );
+ }
+);
+
+// Test that a message is shown when no studies have been run
+decorate_task(
+ AddonStudies.withStudies([]),
+ withAboutStudies(),
+ async function testStudyListingNoStudies({ browser }) {
+ await SpecialPowers.spawn(browser, [], async () => {
+ const doc = content.document;
+ await ContentTaskUtils.waitForCondition(
+ () => doc.querySelectorAll(".study-list-info").length
+ );
+ const studyRows = doc.querySelectorAll(".study-list .study");
+ is(studyRows.length, 0, "There should be no studies");
+ is(
+ doc.querySelector(".study-list-info").textContent,
+ "You have not participated in any studies.",
+ "A message is shown when no studies exist"
+ );
+ });
+ }
+);
+
+// Test that the message shown when studies are disabled and studies exist
+decorate_task(
+ withAboutStudies(),
+ AddonStudies.withStudies([
+ addonStudyFactory({
+ userFacingName: "A Fake Add-on Study",
+ slug: "fake-addon-study",
+ active: false,
+ userFacingDescription: "A fake description",
+ studyStartDate: new Date(2018, 0, 4),
+ }),
+ ]),
+ PreferenceExperiments.withMockExperiments([
+ preferenceStudyFactory({
+ slug: "fake-pref-study",
+ userFacingName: "B Fake Preference Study",
+ lastSeen: new Date(2018, 0, 5),
+ expired: true,
+ }),
+ ]),
+ async function testStudyListingDisabled({ browser }) {
+ try {
+ RecipeRunner.disable();
+
+ await SpecialPowers.spawn(browser, [], async () => {
+ const doc = content.document;
+ await ContentTaskUtils.waitForCondition(() =>
+ doc.querySelector(".info-box-content > span")
+ );
+
+ is(
+ doc.querySelector(".info-box-content > span").textContent,
+ "This is a list of studies that you have participated in. No new studies will run.",
+ "A message is shown when studies are disabled"
+ );
+ });
+ } finally {
+ // reset RecipeRunner.enabled
+ RecipeRunner.checkPrefs();
+ }
+ }
+);
+
+// Test for bug 1498940 - detects studies disabled when only study opt-out is set
+decorate_task(
+ withPrefEnv({
+ set: [
+ ["datareporting.healthreport.uploadEnabled", true],
+ ["app.normandy.api_url", "https://example.com"],
+ ["app.shield.optoutstudies.enabled", false],
+ ],
+ }),
+ withAboutStudies(),
+ AddonStudies.withStudies([]),
+ PreferenceExperiments.withMockExperiments([]),
+ async function testStudyListingStudiesOptOut({ browser }) {
+ RecipeRunner.checkPrefs();
+ ok(
+ RecipeRunner.enabled,
+ "RecipeRunner should be enabled as a Precondition"
+ );
+
+ await SpecialPowers.spawn(browser, [], async () => {
+ const doc = content.document;
+ await ContentTaskUtils.waitForCondition(() => {
+ const span = doc.querySelector(".info-box-content > span");
+ return span && span.textContent;
+ });
+
+ is(
+ doc.querySelector(".info-box-content > span").textContent,
+ "This is a list of studies that you have participated in. No new studies will run.",
+ "A message is shown when studies are disabled"
+ );
+ });
+ }
+);
+
+// Test that clicking remove on a study that was disabled by an outside source
+// since the page loaded correctly updates.
+decorate_task(
+ AddonStudies.withStudies([
+ addonStudyFactory({
+ slug: "fake-addon-study",
+ userFacingName: "Fake Add-on Study",
+ active: true,
+ userFacingDescription: "A fake description",
+ studyStartDate: new Date(2018, 0, 4),
+ }),
+ ]),
+ PreferenceExperiments.withMockExperiments([
+ preferenceStudyFactory({
+ slug: "fake-pref-study",
+ userFacingName: "Fake Preference Study",
+ lastSeen: new Date(2018, 0, 3),
+ expired: false,
+ }),
+ ]),
+ withAboutStudies(),
+ async function testStudyListing({
+ addonStudies: [addonStudy],
+ prefExperiments: [prefStudy],
+ browser,
+ }) {
+ // The content page has already loaded. Disabling the studies here shouldn't
+ // affect it, since it doesn't live-update.
+ await AddonStudies.markAsEnded(addonStudy, "disabled-automatically-test");
+ await PreferenceExperiments.stop(prefStudy.slug, {
+ resetValue: false,
+ reason: "disabled-automatically-test",
+ });
+
+ await SpecialPowers.spawn(
+ browser,
+ [{ addonStudy, prefStudy }],
+ async ({ addonStudy, prefStudy }) => {
+ const doc = content.document;
+
+ function getStudyRow(docElem, slug) {
+ return docElem.querySelector(`.study[data-study-slug="${slug}"]`);
+ }
+
+ await ContentTaskUtils.waitForCondition(
+ () => doc.querySelectorAll(".remove-button").length == 2
+ );
+ let activeNames = Array.from(
+ doc.querySelectorAll(".active-study-list .study")
+ ).map(row => row.dataset.studySlug);
+ let inactiveNames = Array.from(
+ doc.querySelectorAll(".inactive-study-list .study")
+ ).map(row => row.dataset.studySlug);
+
+ Assert.deepEqual(
+ activeNames,
+ [addonStudy.slug, prefStudy.slug],
+ "Both studies should be listed as active, even though they have been disabled outside of the page"
+ );
+ Assert.deepEqual(
+ inactiveNames,
+ [],
+ "No studies should be listed as inactive"
+ );
+
+ const activeAddonStudy = getStudyRow(doc, addonStudy.slug);
+ const activePrefStudy = getStudyRow(doc, prefStudy.slug);
+
+ activeAddonStudy.querySelector(".remove-button").click();
+ await ContentTaskUtils.waitForCondition(() =>
+ getStudyRow(doc, addonStudy.slug).matches(".study.disabled")
+ );
+ ok(
+ getStudyRow(doc, addonStudy.slug).matches(".study.disabled"),
+ "Clicking the remove button updates the UI to show that the study has been disabled."
+ );
+
+ activePrefStudy.querySelector(".remove-button").click();
+ await ContentTaskUtils.waitForCondition(() =>
+ getStudyRow(doc, prefStudy.slug).matches(".study.disabled")
+ );
+ ok(
+ getStudyRow(doc, prefStudy.slug).matches(".study.disabled"),
+ "Clicking the remove button updates the UI to show that the study has been disabled."
+ );
+
+ activeNames = Array.from(
+ doc.querySelectorAll(".active-study-list .study")
+ ).map(row => row.dataset.studySlug);
+
+ Assert.deepEqual(
+ activeNames,
+ [],
+ "No studies should be listed as active"
+ );
+ }
+ );
+ }
+);
+
+// Test that clicking remove on a study updates even about:studies pages
+// that are not currently in focus.
+decorate_task(
+ AddonStudies.withStudies([
+ addonStudyFactory({
+ slug: "fake-addon-study",
+ userFacingName: "Fake Add-on Study",
+ active: true,
+ userFacingDescription: "A fake description",
+ studyStartDate: new Date(2018, 0, 4),
+ }),
+ ]),
+ PreferenceExperiments.withMockExperiments([
+ preferenceStudyFactory({
+ slug: "fake-pref-study",
+ userFacingName: "Fake Preference Study",
+ lastSeen: new Date(2018, 0, 3),
+ expired: false,
+ }),
+ ]),
+ withAboutStudies(),
+ async function testOtherTabsUpdated({
+ addonStudies: [addonStudy],
+ prefExperiments: [prefStudy],
+ browser,
+ }) {
+ // Ensure that both our studies are active in the current tab.
+ await SpecialPowers.spawn(
+ browser,
+ [{ addonStudy, prefStudy }],
+ async ({ addonStudy, prefStudy }) => {
+ const doc = content.document;
+ await ContentTaskUtils.waitForCondition(
+ () => doc.querySelectorAll(".remove-button").length == 2,
+ "waiting for page to load"
+ );
+ let activeNames = Array.from(
+ doc.querySelectorAll(".active-study-list .study")
+ ).map(row => row.dataset.studySlug);
+ let inactiveNames = Array.from(
+ doc.querySelectorAll(".inactive-study-list .study")
+ ).map(row => row.dataset.studySlug);
+
+ Assert.deepEqual(
+ activeNames,
+ [addonStudy.slug, prefStudy.slug],
+ "Both studies should be listed as active"
+ );
+ Assert.deepEqual(
+ inactiveNames,
+ [],
+ "No studies should be listed as inactive"
+ );
+ }
+ );
+
+ // Open a new about:studies tab.
+ await BrowserTestUtils.withNewTab("about:studies", async browser => {
+ // Delete both studies in this tab; this should pass if previous tests have passed.
+ await SpecialPowers.spawn(
+ browser,
+ [{ addonStudy, prefStudy }],
+ async ({ addonStudy, prefStudy }) => {
+ const doc = content.document;
+
+ function getStudyRow(docElem, slug) {
+ return docElem.querySelector(`.study[data-study-slug="${slug}"]`);
+ }
+
+ await ContentTaskUtils.waitForCondition(
+ () => doc.querySelectorAll(".remove-button").length == 2,
+ "waiting for page to load"
+ );
+ let activeNames = Array.from(
+ doc.querySelectorAll(".active-study-list .study")
+ ).map(row => row.dataset.studySlug);
+ let inactiveNames = Array.from(
+ doc.querySelectorAll(".inactive-study-list .study")
+ ).map(row => row.dataset.studySlug);
+
+ Assert.deepEqual(
+ activeNames,
+ [addonStudy.slug, prefStudy.slug],
+ "Both studies should be listed as active in the new tab"
+ );
+ Assert.deepEqual(
+ inactiveNames,
+ [],
+ "No studies should be listed as inactive in the new tab"
+ );
+
+ const activeAddonStudy = getStudyRow(doc, addonStudy.slug);
+ const activePrefStudy = getStudyRow(doc, prefStudy.slug);
+
+ activeAddonStudy.querySelector(".remove-button").click();
+ await ContentTaskUtils.waitForCondition(() =>
+ getStudyRow(doc, addonStudy.slug).matches(".study.disabled")
+ );
+ ok(
+ getStudyRow(doc, addonStudy.slug).matches(".study.disabled"),
+ "Clicking the remove button updates the UI in the new tab"
+ );
+
+ activePrefStudy.querySelector(".remove-button").click();
+ await ContentTaskUtils.waitForCondition(() =>
+ getStudyRow(doc, prefStudy.slug).matches(".study.disabled")
+ );
+ ok(
+ getStudyRow(doc, prefStudy.slug).matches(".study.disabled"),
+ "Clicking the remove button updates the UI in the new tab"
+ );
+
+ activeNames = Array.from(
+ doc.querySelectorAll(".active-study-list .study")
+ ).map(row => row.dataset.studySlug);
+
+ Assert.deepEqual(
+ activeNames,
+ [],
+ "No studies should be listed as active"
+ );
+ }
+ );
+ });
+
+ // Ensure that the original tab has updated correctly.
+ await SpecialPowers.spawn(
+ browser,
+ [{ addonStudy, prefStudy }],
+ async ({ addonStudy, prefStudy }) => {
+ const doc = content.document;
+ await ContentTaskUtils.waitForCondition(
+ () => doc.querySelectorAll(".inactive-study-list .study").length == 2,
+ "Two studies should load into the inactive list, since they were disabled in a different tab"
+ );
+ let activeNames = Array.from(
+ doc.querySelectorAll(".active-study-list .study")
+ ).map(row => row.dataset.studySlug);
+ let inactiveNames = Array.from(
+ doc.querySelectorAll(".inactive-study-list .study")
+ ).map(row => row.dataset.studySlug);
+ Assert.deepEqual(
+ activeNames,
+ [],
+ "No studies should be listed as active, since they were disabled in a different tab"
+ );
+ Assert.deepEqual(
+ inactiveNames,
+ [addonStudy.slug, prefStudy.slug],
+ "Both studies should be listed as inactive, since they were disabled in a different tab"
+ );
+ }
+ );
+ }
+);
+
+add_task(async function test_nimbus_about_studies_experiment() {
+ const recipe = ExperimentFakes.recipe("about-studies-foo");
+ await ExperimentManager.enroll(recipe);
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:studies" },
+ async browser => {
+ const name = await SpecialPowers.spawn(browser, [], async () => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector(".nimbus .remove-button"),
+ "waiting for page/experiment to load"
+ );
+ return content.document.querySelector(".study-name").innerText;
+ });
+ // Make sure strings are properly shown
+ Assert.equal(
+ name,
+ recipe.userFacingName,
+ "Correct active experiment name"
+ );
+ }
+ );
+ ExperimentManager.unenroll(recipe.slug);
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:studies" },
+ async browser => {
+ const name = await SpecialPowers.spawn(browser, [], async () => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector(".nimbus.disabled"),
+ "waiting for experiment to become disabled"
+ );
+ return content.document.querySelector(".study-name").innerText;
+ });
+ // Make sure strings are properly shown
+ Assert.equal(
+ name,
+ recipe.userFacingName,
+ "Correct disabled experiment name"
+ );
+ }
+ );
+ // Cleanup for multiple test runs
+ ExperimentManager.store._deleteForTests(recipe.slug);
+ Assert.equal(ExperimentManager.store.getAll().length, 0, "Cleanup done");
+});
+
+add_task(async function test_nimbus_about_studies_rollout() {
+ let recipe = ExperimentFakes.recipe("test_nimbus_about_studies_rollout");
+ let rollout = {
+ ...recipe,
+ branches: [recipe.branches[0]],
+ isRollout: true,
+ };
+ await ExperimentManager.enroll(rollout);
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:studies" },
+ async browser => {
+ const studyCount = await SpecialPowers.spawn(browser, [], async () => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector("#shield-studies-learn-more"),
+ "waiting for page/experiment to load"
+ );
+ return content.document.querySelectorAll(".study-name").length;
+ });
+ // Make sure strings are properly shown
+ Assert.equal(studyCount, 0, "Rollout not loaded in non-debug mode");
+ }
+ );
+ Services.prefs.setBoolPref("nimbus.debug", true);
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:studies" },
+ async browser => {
+ const studyName = await SpecialPowers.spawn(browser, [], async () => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector(".nimbus .remove-button"),
+ "waiting for page/experiment to load"
+ );
+ return content.document.querySelector(".study-header").innerText;
+ });
+ // Make sure strings are properly shown
+ Assert.ok(studyName.includes("Active"), "Rollout loaded in debug mode");
+ }
+ );
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:studies" },
+ async browser => {
+ const name = await SpecialPowers.spawn(browser, [], async () => {
+ content.document.querySelector(".remove-button").click();
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector(".nimbus.disabled"),
+ "waiting for experiment to become disabled"
+ );
+ return content.document.querySelector(".study-header").innerText;
+ });
+ // Make sure strings are properly shown
+ Assert.ok(name.includes("Complete"), "Rollout was removed");
+ }
+ );
+ // Cleanup for multiple test runs
+ ExperimentManager.store._deleteForTests(rollout.slug);
+ Services.prefs.clearUserPref("nimbus.debug");
+});
+
+add_task(async function test_getStudiesEnabled() {
+ RecipeRunner.initializedPromise = PromiseUtils.defer();
+ let promise = AboutPages.aboutStudies.getStudiesEnabled();
+
+ RecipeRunner.initializedPromise.resolve();
+ let result = await promise;
+
+ Assert.equal(
+ result,
+ Services.prefs.getBoolPref("app.shield.optoutstudies.enabled"),
+ "about:studies is enabled if the pref is enabled"
+ );
+});
+
+add_task(async function test_forceEnroll() {
+ let sandbox = sinon.createSandbox();
+
+ // This simulates a succesful enrollment
+ let stub = sandbox.stub(RemoteSettingsExperimentLoader, "optInToExperiment");
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:studies?optin_collection=collection123&optin_branch=branch123&optin_slug=slug123",
+ },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], async () => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector(".opt-in-box"),
+ "Should show the opt in message"
+ );
+
+ Assert.equal(
+ content.document
+ .querySelector(".opt-in-box")
+ .classList.contains("opt-in-error"),
+ false,
+ "should not have an error class since the enrollment was successful"
+ );
+
+ return true;
+ });
+ }
+ );
+
+ // Simulates a problem force enrolling
+ stub.rejects(new Error("Testing error"));
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:studies?optin_collection=collection123&optin_branch=branch123&optin_slug=slug123",
+ },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], async () => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector(".opt-in-box"),
+ "Should show the opt in message"
+ );
+
+ Assert.ok(
+ content.document
+ .querySelector(".opt-in-box")
+ .classList.contains("opt-in-error"),
+ "should have an error class since the enrollment rejected"
+ );
+
+ Assert.equal(
+ content.document.querySelector(".opt-in-box").textContent,
+ "Testing error",
+ "should render the error"
+ );
+
+ return true;
+ });
+ }
+ );
+
+ sandbox.restore();
+});