зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
34addd85cb
Коммит
e3d54cbdf7
|
@ -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 product’s 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 product’s 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();
|
||||
}
|
||||
);
|
||||
|
|
Загрузка…
Ссылка в новой задаче