From 7a4cae215f9e9c1f6f194626c72194304b27d1ad Mon Sep 17 00:00:00 2001 From: Shane Hughes Date: Sun, 30 Jun 2024 17:20:25 +0000 Subject: [PATCH] Bug 1900486 - Re-render Review Checker messaging content when opt-in state changes. a=RyanVM Original Revision: https://phabricator.services.mozilla.com/D212498 Differential Revision: https://phabricator.services.mozilla.com/D215150 --- .../actors/AboutWelcomeChild.sys.mjs | 25 ++-- .../shopping/content/onboarding.mjs | 4 +- .../tests/browser/browser_shopping_survey.js | 111 +++++++++++++++++- 3 files changed, 128 insertions(+), 12 deletions(-) diff --git a/browser/components/aboutwelcome/actors/AboutWelcomeChild.sys.mjs b/browser/components/aboutwelcome/actors/AboutWelcomeChild.sys.mjs index 23a3d41ee08a..39c6106e6ecd 100644 --- a/browser/components/aboutwelcome/actors/AboutWelcomeChild.sys.mjs +++ b/browser/components/aboutwelcome/actors/AboutWelcomeChild.sys.mjs @@ -754,7 +754,7 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild { lazy.pdpVisits >= MIN_VISITS_TO_SHOW_SURVEY && hasBeen24HrsSinceOptin; - if (this.showMicroSurvey) { + if (this.showMicroSurvey && !this.showOnboarding) { this.renderMessage(); } } @@ -775,6 +775,7 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild { handleEvent(event) { // Decide when to show/hide onboarding and survey message const { productUrl, showOnboarding, data } = event.detail; + this.showOnboarding = showOnboarding; // Display onboarding if a user hasn't opted-in const optInReady = showOnboarding && productUrl; @@ -819,18 +820,24 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild { } renderMessage() { + this.contentWindow.clearTimeout(this.thankYouFadeTimeout); this.document.getElementById("multi-stage-message-root").hidden = false; this.document.dispatchEvent( - new this.contentWindow.CustomEvent("RenderWelcome", { - bubbles: true, - }) + new this.contentWindow.CustomEvent("RenderWelcome", { bubbles: true }) ); } + resetOnboardingContainer(root) { + root.innerHTML = ""; + let newRoot = root.cloneNode(false); + root.replaceWith(newRoot); + return newRoot; + } + // TODO - Move messages into an ASRouter message provider. See bug 1848251. AWGetFeatureConfig() { let messageContent = optInDynamicContent; - if (this.showMicroSurvey) { + if (this.showMicroSurvey && !this.showOnboarding) { messageContent = SHOPPING_MICROSURVEY; this.setShoppingSurveySeen(); } @@ -855,13 +862,15 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild { if (this._destroyed) { return; } - const root = this.document.getElementById("multi-stage-message-root"); + let root = this.document.getElementById("multi-stage-message-root"); if (root) { - root.innerHTML = ""; + root = this.resetOnboardingContainer(root); root .appendChild(this.document.createElement("shopping-message-bar")) .setAttribute("type", "thank-you-for-feedback"); - this.contentWindow.setTimeout(() => { + this.contentWindow.clearTimeout(this.thankYouFadeTimeout); + this.thankYouFadeTimeout = this.contentWindow.setTimeout(() => { + root = this.resetOnboardingContainer(root); root.hidden = true; }, 5000); } diff --git a/browser/components/shopping/content/onboarding.mjs b/browser/components/shopping/content/onboarding.mjs index 68124c0253c3..a5d3c1a082a3 100644 --- a/browser/components/shopping/content/onboarding.mjs +++ b/browser/components/shopping/content/onboarding.mjs @@ -8,9 +8,7 @@ const BUNDLE_SRC = class Onboarding { constructor({ win } = {}) { this.doc = win.document; - win.addEventListener("RenderWelcome", () => this._addScriptsAndRender(), { - once: true, - }); + win.addEventListener("RenderWelcome", () => this._addScriptsAndRender()); } async _addScriptsAndRender() { diff --git a/browser/components/shopping/tests/browser/browser_shopping_survey.js b/browser/components/shopping/tests/browser/browser_shopping_survey.js index aebe6e9dcf7e..33108816fdb9 100644 --- a/browser/components/shopping/tests/browser/browser_shopping_survey.js +++ b/browser/components/shopping/tests/browser/browser_shopping_survey.js @@ -7,7 +7,7 @@ const currentTime = Date.now() / 1000; const time25HrsAgo = currentTime - 25 * 60 * 60; const time1HrAgo = currentTime - 1 * 60 * 60; -add_task(async function test_setup() { +add_setup(async function test_setup() { await BrowserTestUtils.withNewTab( { url: "about:shoppingsidebar", @@ -300,6 +300,10 @@ add_task(async function test_confirmation_screen() { await shoppingContainer.updateComplete; + let childActor = content.windowGlobalChild.getExistingActor( + "AboutWelcomeShopping" + ); + let surveyScreen1 = await ContentTaskUtils.waitForCondition( () => content.document.querySelector( @@ -329,6 +333,111 @@ add_task(async function test_confirmation_screen() { ); ok(confirmationScreen, "Survey confirmation screen is rendered"); + + childActor.resetChildStates(); + } + ); + } + ); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function test_onboarding_resets_after_opt_out() { + // Verify the fix for bug 1900486. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.shopping.experience2023.optedIn", 1], + ["browser.shopping.experience2023.survey.enabled", true], + ["browser.shopping.experience2023.autoOpen.enabled", true], + ["browser.shopping.experience2023.survey.hasSeen", false], + ["browser.shopping.experience2023.survey.pdpVisits", 5], + ["browser.shopping.experience2023.survey.optedInTime", time25HrsAgo], + ], + }); + await BrowserTestUtils.withNewTab( + { + url: "about:shoppingsidebar", + gBrowser, + }, + async browser => { + await SpecialPowers.spawn( + browser, + [MOCK_ANALYZED_PRODUCT_RESPONSE], + async mockData => { + const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" + ); + let surveyPrefChanged = TestUtils.waitForPrefChange( + "browser.shopping.experience2023.survey.hasSeen" + ); + let shoppingContainer = + content.document.querySelector( + "shopping-container" + ).wrappedJSObject; + shoppingContainer.data = Cu.cloneInto(mockData, content); + + // Manually send data update event, as it isn't set due to the lack of mock APIs. + // TODO: Support for the mocks will be added in Bug 1853474. + let mockObj = { + data: mockData, + productUrl: "https://example.com/product/1234", + }; + let evt = new content.CustomEvent("Update", { + bubbles: true, + detail: Cu.cloneInto(mockObj, content), + }); + content.document.dispatchEvent(evt); + + await shoppingContainer.updateComplete; + await surveyPrefChanged; + + let childActor = content.windowGlobalChild.getExistingActor( + "AboutWelcomeShopping" + ); + + ok(childActor.surveyEnabled, "Survey is Enabled"); + + let surveyScreen = await ContentTaskUtils.waitForCondition( + () => + content.document.querySelector( + "shopping-container .screen.SHOPPING_MICROSURVEY_SCREEN_1" + ), + "survey-screen" + ); + + ok(surveyScreen, "Survey screen is rendered"); + ok( + childActor.showMicroSurvey, + "Show Survey targeting conditions met" + ); + + let root = content.document.getElementById( + "multi-stage-message-root" + ); + ok(!root.hidden, "Survey Message container is shown"); + + let optInShown = ContentTaskUtils.waitForMutationCondition( + root, + { childList: true }, + () => root.querySelector(".screen.FS_OPT_IN") + ); + content.document.dispatchEvent( + new content.CustomEvent("Update", { + bubbles: true, + detail: Cu.cloneInto( + { + data: mockData, + productUrl: "https://example.com/product/1234", + showOnboarding: true, + }, + content + ), + }) + ); + await shoppingContainer.updateComplete; + await optInShown; + + childActor.resetChildStates(); } ); }