diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/components/normandy/test/browser/browser_about_studies.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/normandy/test/browser/browser_about_studies.js')
-rw-r--r-- | toolkit/components/normandy/test/browser/browser_about_studies.js | 825 |
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(); +}); |