Bug 1916016 - update opt-in card for migration to Firefox sidebar r=fluent-reviewers,omc-reviewers,shopping-reviewers,fchasen,pdahiya

- Updated `AboutWelcomeChild.sys.mjs` to a new opt-in card `FS_OPT_IN_SIDEBAR_VARIANT` that is rendered only if `browser.shopping.experience2023.integratedSidebar`
- Updated styling for the integrated RC in `aboutwelcome.css`
- Added non-exposed strings to `shopping.ftl`
- Updated tests to work for both integrated and non-integrated versions

Differential Revision: https://phabricator.services.mozilla.com/D221396
This commit is contained in:
kpatenio 2024-09-18 20:51:03 +00:00
Родитель 34addd85cb
Коммит e3d54cbdf7
7 изменённых файлов: 442 добавлений и 5 удалений

Просмотреть файл

@ -388,6 +388,90 @@ export class AboutWelcomeChild extends JSWindowActorChild {
}
}
const OPTIN_SIDEBAR_VARIANT = {
id: "FAKESPOT_OPTIN_SIDEBAR_VARIANT",
template: "multistage",
backdrop: "transparent",
aria_role: "alert",
UTMTerm: "opt-in",
screens: [
{
id: "FS_OPT_IN_SIDEBAR_VARIANT",
content: {
position: "split",
title: { string_id: "shopping-opt-in-integrated-headline" },
logo: {
type: "image",
imageURL: "chrome://browser/content/shopping/assets/optInLight.avif",
darkModeImageURL:
"chrome://browser/content/shopping/assets/optInDark.avif",
marginInline: "24px",
marginBlock: "50% 0",
},
above_button_content: [
{
type: "text",
text: {
string_id: "",
},
link_keys: ["learn_more"],
args: {},
},
{
type: "text",
text: {
string_id:
"shopping-opt-in-integrated-privacy-policy-and-terms-of-use",
},
link_keys: ["privacy_policy", "terms_of_use"],
font_styles: "legal",
},
],
learn_more: {
action: {
type: "OPEN_URL",
data: {
args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/review-checker-review-quality?utm_source=review-checker&utm_campaign=learn-more&utm_medium=in-product",
where: "tab",
},
},
},
privacy_policy: {
action: {
type: "OPEN_URL",
data: {
args: "https://www.mozilla.org/privacy/firefox?utm_source=review-checker&utm_campaign=privacy-policy&utm_medium=in-product&utm_term=opt-in-screen",
where: "tab",
},
},
},
terms_of_use: {
action: {
type: "OPEN_URL",
data: {
args: "https://www.fakespot.com/terms?utm_source=review-checker&utm_campaign=terms-of-use&utm_medium=in-product",
where: "tab",
},
},
},
primary_button: {
should_focus_button: true,
label: { string_id: "shopping-opt-in-integrated-button" },
action: {
type: "SET_PREF",
data: {
pref: {
name: "browser.shopping.experience2023.optedIn",
value: 1,
},
},
},
},
},
},
],
};
const OPTIN_DEFAULT = {
id: "FAKESPOT_OPTIN_DEFAULT",
template: "multistage",
@ -685,6 +769,13 @@ const SHOPPING_MICROSURVEY = {
const OPTED_IN_TIME_PREF = "browser.shopping.experience2023.survey.optedInTime";
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"isIntegratedSidebar",
"browser.shopping.experience2023.integratedSidebar",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"isSurveySeen",
@ -887,6 +978,65 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild {
}
AWSetProductURL(productUrl) {
let content = lazy.isIntegratedSidebar
? this._AWGetOptInSidebarVariantContent(productUrl)
: this._AWGetOptInDefaultContent(productUrl);
optInDynamicContent = content;
}
_AWGetOptInSidebarVariantContent(productUrl) {
let content = JSON.parse(JSON.stringify(OPTIN_SIDEBAR_VARIANT));
const [optInScreen] = content.screens;
if (productUrl) {
optInScreen.content.above_button_content[0].text.string_id =
"shopping-opt-in-integrated-subtitle-all-sites";
switch (
productUrl // Insert the productUrl into content
) {
case "www.amazon.fr":
case "www.amazon.de":
optInScreen.content.above_button_content[0].text.string_id =
"shopping-opt-in-integrated-subtitle-single-site";
optInScreen.content.above_button_content[0].text.args = {
currentSite: "Amazon",
};
break;
case "www.amazon.com":
optInScreen.content.above_button_content[0].text.args = {
currentSite: "Amazon",
secondSite: "Walmart",
thirdSite: "Best Buy",
};
break;
case "www.walmart.com":
optInScreen.content.above_button_content[0].text.args = {
currentSite: "Walmart",
secondSite: "Amazon",
thirdSite: "Best Buy",
};
break;
case "www.bestbuy.com":
optInScreen.content.above_button_content[0].text.args = {
currentSite: "Best Buy",
secondSite: "Amazon",
thirdSite: "Walmart",
};
break;
default:
optInScreen.content.above_button_content[0].text.args = {
currentSite: "Amazon",
secondSite: "Walmart",
thirdSite: "Best Buy",
};
}
}
return content;
}
_AWGetOptInDefaultContent(productUrl) {
let content = JSON.parse(JSON.stringify(OPTIN_DEFAULT));
const [optInScreen] = content.screens;
@ -935,7 +1085,7 @@ export class AboutWelcomeShoppingChild extends AboutWelcomeChild {
}
}
optInDynamicContent = content;
return content;
}
AWEnsureLangPackInstalled() {}

Просмотреть файл

@ -774,6 +774,37 @@ panel#feature-callout[arrow-position=inline-start-bottom] {
.onboardingContainer.shopping .screen[pos=split]::before {
display: none;
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT {
box-shadow: none;
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT .section-main .main-content {
background-color: transparent;
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT .section-main .main-content.no-steps {
padding: 0;
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT .section-main .main-content .main-content-inner {
justify-content: unset;
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT .section-main .main-content .welcome-text {
padding-inline: 16px;
margin-block: 20px 8px;
text-align: center;
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT .section-main .main-content .welcome-text h1 {
font-weight: var(--font-weight-bold);
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT .section-main .main-content .link-paragraph {
padding-inline: 16px;
margin-block: 0 20px;
text-align: center;
}
.onboardingContainer.shopping .screen[pos=split].FS_OPT_IN_SIDEBAR_VARIANT .section-main .main-content .legal-paragraph {
padding-inline: 16px;
margin-block: 0 40px;
color: var(--text-color-deemphasized);
text-align: center;
}
.onboardingContainer.shopping .screen[pos=split] .section-main {
width: auto;
height: auto;

Просмотреть файл

@ -235,7 +235,10 @@ export class AboutWelcomeTelemetry {
handleShoppingPings(ping, event_context) {
const message_id = ping?.message_id;
// This function helps direct a shopping ping to the correct Glean event.
if (message_id.startsWith("FAKESPOT_OPTIN_DEFAULT")) {
if (
message_id.startsWith("FAKESPOT_OPTIN_DEFAULT") ||
message_id.startsWith("FAKESPOT_OPTIN_SIDEBAR_VARIANT")
) {
// Onboarding page message IDs are generated, but can reliably be
// assumed to start in this manner.
switch (ping?.event) {

Просмотреть файл

@ -28,6 +28,45 @@
display: none;
}
&.FS_OPT_IN_SIDEBAR_VARIANT {
box-shadow: none;
.section-main .main-content {
background-color: transparent;
&.no-steps {
padding: 0;
}
.main-content-inner {
justify-content: unset;
}
.welcome-text {
padding-inline: 16px;
margin-block: 20px 8px;
text-align: center;
h1 {
font-weight: var(--font-weight-bold);
}
}
.link-paragraph {
padding-inline: 16px;
margin-block: 0 20px;
text-align: center;
}
.legal-paragraph {
padding-inline: 16px;
margin-block: 0 40px;
color: var(--text-color-deemphasized);
text-align: center;
}
}
}
.section-main {
width: auto;
height: auto;

Просмотреть файл

@ -5,3 +5,18 @@
### This file is not in a locales directory to prevent it from
### being translated as the feature is still in heavy development
### and strings are likely to change often.
## Opt-in message strings for Review Checker when it is integrated into the global sidebar.
shopping-opt-in-integrated-headline = Can you trust these reviews?
shopping-opt-in-integrated-subtitle-single-site = Try Review Checker from { -brand-product-name } to find out. It helps you know if a products reviews are real or fake, before you buy. <a data-l10n-name="learn_more">Learn more</a>
# Dynamic subtitle. Sites displayed are limited to "Amazon", "Walmart" or "Best Buy".
# Variables:
# $currentSite (str) - The current shopping page name
# $secondSite (str) - A second shopping page name
# $thirdSite (str) - A third shopping page name
shopping-opt-in-integrated-subtitle-all-sites = Try Review Checker from { -brand-product-name } to find out. It helps you know if a products reviews are real or fake, before you buy. It works on { $secondSite } and { $thirdSite }, too. <a data-l10n-name="learn_more">Learn more</a>
shopping-opt-in-integrated-privacy-policy-and-terms-of-use = Review Checker is powered by { -fakespot-brand-full-name }. By selecting “{ shopping-opt-in-integrated-button }“ you agree to { -brand-product-name }s <a data-l10n-name="privacy_policy">privacy notice</a> and { -fakespot-brand-name }s <a data-l10n-name="terms_of_use">terms of use.</a>
shopping-opt-in-integrated-button = Try Review Checker

Просмотреть файл

@ -96,6 +96,10 @@ add_task(async function test_showOnboarding_notOptedIn() {
Services.fog.testResetFOG();
await Services.fog.testFlushAllChildren();
await SpecialPowers.pushPrefEnv({
set: [["browser.shopping.experience2023.integratedSidebar", false]],
});
await BrowserTestUtils.withNewTab(
{
url: "about:shoppingsidebar",
@ -132,6 +136,86 @@ add_task(async function test_showOnboarding_notOptedIn() {
!content.document.getElementById("multi-stage-message-root").hidden,
"message is shown"
);
ok(
content.document.querySelector(".FS_OPT_IN"),
"Rendered correct message"
);
});
}
);
if (!AppConstants.platform != "linux") {
await Services.fog.testFlushAllChildren();
const events = Glean.shopping.surfaceOnboardingDisplayed.testGetValue();
if (events) {
Assert.greater(events.length, 0);
Assert.equal(events[0].category, "shopping");
Assert.equal(events[0].name, "surface_onboarding_displayed");
} else {
info("Failed to get Glean value due to unknown bug. See bug 1862389.");
}
}
});
/**
* Test to check onboarding message container is rendered
* when user is not opted-in for the sidebar integrated version
* of Review Checker.
*/
add_task(async function test_showOnboarding_notOptedIn_integrated_sidebar() {
// OptedIn pref Value is 0 when a user hasn't opted-in
setOnboardingPrefs({ active: false, optedIn: 0, telemetryEnabled: true });
Services.fog.testResetFOG();
await Services.fog.testFlushAllChildren();
await SpecialPowers.pushPrefEnv({
set: [["browser.shopping.experience2023.integratedSidebar", true]],
});
await BrowserTestUtils.withNewTab(
{
url: "about:shoppingsidebar",
gBrowser,
},
async browser => {
// Get the actor to update the product URL, since no content will render without one
let actor =
gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getExistingActor(
"ShoppingSidebar"
);
actor.updateProductURL("https://example.com/product/B09TJGHL5F");
await SpecialPowers.spawn(browser, [], async () => {
let shoppingContainer = await ContentTaskUtils.waitForCondition(
() => content.document.querySelector("shopping-container"),
"shopping-container"
);
let containerElem =
shoppingContainer.shadowRoot.getElementById("shopping-container");
let messageSlot = containerElem.getElementsByTagName("slot");
// Check multi-stage-message-slot used to show opt-In message is
// rendered inside shopping container when user optedIn pref value is 0
ok(messageSlot.length, `message slot element exists`);
is(
messageSlot[0].name,
"multi-stage-message-slot",
"multi-stage-message-slot showing opt-in message rendered"
);
ok(
!content.document.getElementById("multi-stage-message-root").hidden,
"message is shown"
);
ok(
content.document.querySelector(".FS_OPT_IN_SIDEBAR_VARIANT"),
"Rendered correct message"
);
});
}
);
@ -185,7 +269,8 @@ add_task(async function test_hideOnboarding_optedIn() {
});
/**
* Test to check onboarding message does not show when selecting "not now"
* Test to check onboarding message does not show when selecting "not now".
* This is only applicable to the non-integrated version of Review Checker.
*
* Also confirms a Glean event was triggered.
*/
@ -194,6 +279,11 @@ add_task(async function test_hideOnboarding_onClose() {
Services.fog.testResetFOG();
// OptedIn pref value is 0 when a user has not opted-in
setOnboardingPrefs({ active: false, optedIn: 0, telemetryEnabled: true });
await SpecialPowers.pushPrefEnv({
set: [["browser.shopping.experience2023.integratedSidebar", false]],
});
await BrowserTestUtils.withNewTab(
{
url: "about:shoppingsidebar",
@ -242,8 +332,9 @@ add_task(async function test_hideOnboarding_onClose() {
});
/**
* Test to check behavior when selecting 'Yes, try it to opt in to the
* shopping experience.
* Test to check behavior when selecting the opt-in button.
* This is the 'Yes, try it' button for the non-integrated version of Review Checker
* or the 'Try Review Checker' button for the integrated version of Review Checker.
*
* Also tests if a Glean event was correctly recorded.
*/

Просмотреть файл

@ -356,6 +356,7 @@ add_task(async function test_onboarding_resets_after_opt_out() {
["browser.shopping.experience2023.survey.hasSeen", false],
["browser.shopping.experience2023.survey.pdpVisits", 5],
["browser.shopping.experience2023.survey.optedInTime", time25HrsAgo],
["browser.shopping.experience2023.integratedSidebar", false],
],
});
await BrowserTestUtils.withNewTab(
@ -449,3 +450,110 @@ add_task(async function test_onboarding_resets_after_opt_out() {
await SpecialPowers.popPrefEnv();
Services.fog.testResetFOG();
});
add_task(
async function test_onboarding_resets_after_opt_out_sidebar_integrated() {
// 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],
["browser.shopping.experience2023.integratedSidebar", true],
],
});
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_SIDEBAR_VARIANT")
);
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();
}
);
}
);
await SpecialPowers.popPrefEnv();
Services.fog.testResetFOG();
}
);