forked from mirrors/gecko-dev
Bug 1715158 - Remove tests and ASRouterAdmin capabilities for snippets / ASRouterUISurface. r=pdahiya
Differential Revision: https://phabricator.services.mozilla.com/D193851
This commit is contained in:
parent
e2e35a1b69
commit
898169df2a
33 changed files with 71 additions and 3637 deletions
|
|
@ -1662,10 +1662,6 @@ pref("browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts",
|
|||
pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "{\"id\":\"cfr\",\"enabled\":true,\"type\":\"remote-settings\",\"collection\":\"cfr\",\"updateCycleInMs\":3600000}");
|
||||
pref("browser.newtabpage.activity-stream.asrouter.providers.whats-new-panel", "{\"id\":\"whats-new-panel\",\"enabled\":false,\"type\":\"remote-settings\",\"collection\":\"whats-new-panel\",\"updateCycleInMs\":3600000}");
|
||||
pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "{\"id\":\"message-groups\",\"enabled\":true,\"type\":\"remote-settings\",\"collection\":\"message-groups\",\"updateCycleInMs\":3600000}");
|
||||
// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
|
||||
// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
|
||||
// repackager of this code using an alternate snippet url, please keep your users safe
|
||||
pref("browser.newtabpage.activity-stream.asrouter.providers.snippets", "{\"id\":\"snippets\",\"enabled\":false,\"type\":\"remote\",\"url\":\"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/\",\"updateCycleInMs\":14400000}");
|
||||
pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"updateCycleInMs\":3600000}");
|
||||
|
||||
// ASRouter user prefs
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
PanelTestProvider: "resource://activity-stream/lib/PanelTestProvider.sys.mjs",
|
||||
RemoteL10n: "resource://activity-stream/lib/RemoteL10n.sys.mjs",
|
||||
SnippetsTestMessageProvider:
|
||||
"resource://activity-stream/lib/SnippetsTestMessageProvider.sys.mjs",
|
||||
SpecialMessageActions:
|
||||
"resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
|
||||
TargetingContext: "resource://messaging-system/targeting/Targeting.sys.mjs",
|
||||
|
|
@ -1131,7 +1129,6 @@ class _ASRouter {
|
|||
if (lazy.ASRouterPreferences.devtoolsEnabled) {
|
||||
this._localProviders = {
|
||||
...this._localProviders,
|
||||
SnippetsTestMessageProvider: lazy.SnippetsTestMessageProvider,
|
||||
PanelTestProvider: lazy.PanelTestProvider,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ const DEFAULT_STATE = {
|
|||
};
|
||||
|
||||
const USER_PREFERENCES = {
|
||||
snippets: "browser.newtabpage.activity-stream.feeds.snippets",
|
||||
cfrAddons: "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
|
||||
cfrFeatures:
|
||||
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
|
||||
|
|
@ -41,12 +40,6 @@ const USER_PREFERENCES = {
|
|||
const TARGETING_PREFERENCES = [FXA_USERNAME_PREF];
|
||||
|
||||
const TEST_PROVIDERS = [
|
||||
{
|
||||
id: "snippets_local_testing",
|
||||
type: "local",
|
||||
localProvider: "SnippetsTestMessageProvider",
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: "panel_local_testing",
|
||||
type: "local",
|
||||
|
|
|
|||
|
|
@ -1,688 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const TEST_ICON = "chrome://branding/content/icon64.png";
|
||||
const TEST_ICON_16 = "chrome://branding/content/icon16.png";
|
||||
const TEST_ICON_BW =
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfjBQ8QDifrKGc/AAABf0lEQVQoz4WRO08UUQCFvztzd1AgG9jRgGwkhEoMIYGSygYt+A00tpZGY0jYxAJKEwkNjX9AK2xACx4dhFiQQCiMMRr2kYXdnQcz7L0z91qAMVac6hTfSU7OgVsk/prtyfSNfRb7ge2cd7dmVucP/wM2lwqVqoyICahRx9Nz71+8AnAAvlTct+dSYDBYcgJ+Fj68XFu/AfamnIoWFoHFYrAUuYMSn55/fAIOxIs1t4MhQpNxRYsUD0ld7r8DCfZph4QecrqkhCREgMLSeISQkAy0UBgE0CYgIkeRA9HdsCQhpEGCxichpItHigEcPH4XJLRbTf8STY0iiiuu60Ifxexx04F0N+aCgJCAhPQmD/cp/RC5A79WvUyhUHSIidAIoESv9VfAhW9n8+XqTCoyMsz1cviMMrGz9BrjAuboYHZajyXCInEocI8yvccbC+0muABanR4/tONjQz3DzgNKtj9sfv66XD9B/3tT9g/akb7h0bJwzxqqmlRHLr4rLPwBlYWoYj77l2AAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDUtMTVUMTY6MTQ6MzkrMDA6MDD5/4XBAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA1LTE1VDE2OjE0OjM5KzAwOjAwiKI9fQAAAABJRU5ErkJggg==";
|
||||
|
||||
const MESSAGES = () => [
|
||||
{
|
||||
template: "simple_snippet",
|
||||
template_version: "1.1.2",
|
||||
content: {
|
||||
text: "This is for <link0>preferences</link0> and <link1>about</link1>",
|
||||
icon: "https://snippets.cdn.mozilla.net/media/icons/1a8bb10e-8166-4e14-9e41-c1f85a41bcd2.png",
|
||||
button_label: "Button Label",
|
||||
section_title_icon:
|
||||
"https://snippets.cdn.mozilla.net/media/icons/5878847e-a1fb-4204-aad9-09f6cf7f99ee.png",
|
||||
section_title_text: "Messages from Firefox",
|
||||
section_title_url:
|
||||
"https://support.mozilla.org/kb/snippets-firefox-faq?utm_source=desktop-snippet&utm_medium=snippet&utm_campaign=&utm_term=&utm_content=",
|
||||
tall: false,
|
||||
block_button_text: "Remove this",
|
||||
do_not_autoblock: true,
|
||||
links: {
|
||||
link0: {
|
||||
action: "OPEN_PREFERENCES_PAGE",
|
||||
entrypoint_value: "snippet",
|
||||
args: "sync",
|
||||
},
|
||||
link1: {
|
||||
action: "OPEN_ABOUT_PAGE",
|
||||
args: "about",
|
||||
entrypoint_name: "entryPoint",
|
||||
entrypoint_value: "snippet",
|
||||
},
|
||||
},
|
||||
button_action: "OPEN_PREFERENCES_PAGE",
|
||||
button_entrypoint_value: "snippet",
|
||||
},
|
||||
id: "preview-13516_button_preferences",
|
||||
},
|
||||
{
|
||||
template: "simple_snippet",
|
||||
template_version: "1.1.2",
|
||||
content: {
|
||||
text: "This is for <link0>preferences</link0> and <link1>about</link1>",
|
||||
icon: "https://snippets.cdn.mozilla.net/media/icons/1a8bb10e-8166-4e14-9e41-c1f85a41bcd2.png",
|
||||
button_label: "Button Label",
|
||||
section_title_icon:
|
||||
"https://snippets.cdn.mozilla.net/media/icons/5878847e-a1fb-4204-aad9-09f6cf7f99ee.png",
|
||||
section_title_text: "Messages from Firefox",
|
||||
section_title_url:
|
||||
"https://support.mozilla.org/kb/snippets-firefox-faq?utm_source=desktop-snippet&utm_medium=snippet&utm_campaign=&utm_term=&utm_content=",
|
||||
tall: false,
|
||||
block_button_text: "Remove this",
|
||||
do_not_autoblock: true,
|
||||
links: {
|
||||
link0: {
|
||||
action: "OPEN_PREFERENCES_PAGE",
|
||||
entrypoint_value: "snippet",
|
||||
},
|
||||
link1: {
|
||||
action: "OPEN_ABOUT_PAGE",
|
||||
args: "about",
|
||||
entrypoint_name: "entryPoint",
|
||||
entrypoint_value: "snippet",
|
||||
},
|
||||
},
|
||||
button_action: "OPEN_ABOUT_PAGE",
|
||||
button_action_args: "logins",
|
||||
button_entrypoint_name: "entryPoint",
|
||||
button_entrypoint_value: "snippet",
|
||||
},
|
||||
id: "preview-13517_button_about",
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_1",
|
||||
template: "simple_snippet",
|
||||
campaign: "test_campaign_blocking",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
title: "Firefox Account!",
|
||||
title_icon: TEST_ICON_16,
|
||||
title_icon_dark_theme: TEST_ICON_BW,
|
||||
text: "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_1_NO_DARK_THEME",
|
||||
template: "simple_snippet",
|
||||
campaign: "test_campaign_blocking",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: "",
|
||||
title: "Firefox Account!",
|
||||
title_icon: TEST_ICON_16,
|
||||
title_icon_dark_theme: "",
|
||||
text: "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_1_SAME_CAMPAIGN",
|
||||
template: "simple_snippet",
|
||||
campaign: "test_campaign_blocking",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
text: "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_TALL",
|
||||
template: "simple_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
text: "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
button_label: "Get one now!",
|
||||
button_url: "https://www.mozilla.org/en-US/firefox/accounts",
|
||||
block_button_text: "Block",
|
||||
tall: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_BUTTON_URL_1",
|
||||
template: "simple_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
button_label: "Get one now!",
|
||||
button_url: "https://www.mozilla.org/en-US/firefox/accounts",
|
||||
text: "Sync it, link it, take it with you. All this and more with a Firefox Account.",
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_BUTTON_ACTION_1",
|
||||
template: "simple_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
button_label: "Open about:config",
|
||||
button_action: "OPEN_ABOUT_PAGE",
|
||||
button_action_args: "config",
|
||||
text: "Testing the OPEN_ABOUT_PAGE action",
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_WITH_TITLE_TEST_1",
|
||||
template: "simple_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
title: "Ready to sync?",
|
||||
text: "Get connected with a <syncLink>Firefox account</syncLink>.",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "NEWSLETTER_TEST_DEFAULTS",
|
||||
template: "newsletter_snippet",
|
||||
content: {
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_title: "Be a part of a movement.",
|
||||
scene1_title_icon: TEST_ICON_16,
|
||||
scene1_title_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_text:
|
||||
"Internet shutdowns, hackers, harassment – the health of the internet is on the line. Sign up and Mozilla will keep you updated on how you can help.",
|
||||
scene1_button_label: "Continue",
|
||||
scene1_button_color: "#712b00",
|
||||
scene1_button_background_color: "#ff9400",
|
||||
scene2_title: "Let's do this!",
|
||||
locale: "en-CA",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
scene2_text:
|
||||
"Sign up for the Mozilla newsletter and we will keep you updated on how you can help.",
|
||||
scene2_privacy_html:
|
||||
"I'm okay with Mozilla handling my info as explained in this <privacyLink>Privacy Notice</privacyLink>.",
|
||||
scene2_newsletter: "mozilla-foundation",
|
||||
success_text: "Check your inbox for the confirmation!",
|
||||
error_text: "Error!",
|
||||
retry_button_label: "Try again?",
|
||||
links: {
|
||||
privacyLink: {
|
||||
url: "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "NEWSLETTER_TEST_1",
|
||||
template: "newsletter_snippet",
|
||||
content: {
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_title: "Be a part of a movement.",
|
||||
scene1_title_icon: "",
|
||||
scene1_text:
|
||||
"Internet shutdowns, hackers, harassment – the health of the internet is on the line. Sign up and Mozilla will keep you updated on how you can help.",
|
||||
scene1_button_label: "Continue",
|
||||
scene1_button_color: "#712b00",
|
||||
scene1_button_background_color: "#ff9400",
|
||||
scene2_title: "Let's do this!",
|
||||
locale: "en-CA",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
scene2_text:
|
||||
"Sign up for the Mozilla newsletter and we will keep you updated on how you can help.",
|
||||
scene2_privacy_html:
|
||||
"I'm okay with Mozilla handling my info as explained in this <privacyLink>Privacy Notice</privacyLink>.",
|
||||
scene2_button_label: "Sign Me up",
|
||||
scene2_email_placeholder_text: "Your email here",
|
||||
scene2_newsletter: "mozilla-foundation",
|
||||
success_text: "Check your inbox for the confirmation!",
|
||||
error_text: "Error!",
|
||||
links: {
|
||||
privacyLink: {
|
||||
url: "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "NEWSLETTER_TEST_SCENE1_SECTION_TITLE_ICON",
|
||||
template: "newsletter_snippet",
|
||||
content: {
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_title: "Be a part of a movement.",
|
||||
scene1_title_icon: "",
|
||||
scene1_text:
|
||||
"Internet shutdowns, hackers, harassment – the health of the internet is on the line. Sign up and Mozilla will keep you updated on how you can help.",
|
||||
scene1_button_label: "Continue",
|
||||
scene1_button_color: "#712b00",
|
||||
scene1_button_background_color: "#ff9400",
|
||||
scene1_section_title_icon: "chrome://global/skin/icons/pocket.svg",
|
||||
scene1_section_title_text:
|
||||
"All the Firefox news that's fit to Firefox print!",
|
||||
scene2_title: "Let's do this!",
|
||||
locale: "en-CA",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
scene2_text:
|
||||
"Sign up for the Mozilla newsletter and we will keep you updated on how you can help.",
|
||||
scene2_privacy_html:
|
||||
"I'm okay with Mozilla handling my info as explained in this <privacyLink>Privacy Notice</privacyLink>.",
|
||||
scene2_button_label: "Sign Me up",
|
||||
scene2_email_placeholder_text: "Your email here",
|
||||
scene2_newsletter: "mozilla-foundation",
|
||||
success_text: "Check your inbox for the confirmation!",
|
||||
error_text: "Error!",
|
||||
links: {
|
||||
privacyLink: {
|
||||
url: "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "FXA_SNIPPET_TEST_1",
|
||||
template: "fxa_signup_snippet",
|
||||
content: {
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_button_label: "Get connected with sync!",
|
||||
scene1_button_color: "#712b00",
|
||||
scene1_button_background_color: "#ff9400",
|
||||
|
||||
scene1_text:
|
||||
"Connect to Firefox by securely syncing passwords, bookmarks, and open tabs.",
|
||||
scene1_title: "Browser better.",
|
||||
scene1_title_icon: TEST_ICON_16,
|
||||
scene1_title_icon_dark_theme: TEST_ICON_BW,
|
||||
|
||||
scene2_text:
|
||||
"Connect to your Firefox account to securely sync passwords, bookmarks, and open tabs.",
|
||||
scene2_title: "Title 123",
|
||||
scene2_email_placeholder_text: "Your email",
|
||||
scene2_button_label: "Continue",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "FXA_SNIPPET_TEST_TITLE_ICON",
|
||||
template: "fxa_signup_snippet",
|
||||
content: {
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_button_label: "Get connected with sync!",
|
||||
scene1_button_color: "#712b00",
|
||||
scene1_button_background_color: "#ff9400",
|
||||
|
||||
scene1_text:
|
||||
"Connect to Firefox by securely syncing passwords, bookmarks, and open tabs.",
|
||||
scene1_title: "Browser better.",
|
||||
scene1_title_icon: TEST_ICON_16,
|
||||
scene1_title_icon_dark_theme: TEST_ICON_BW,
|
||||
|
||||
scene1_section_title_icon: "chrome://global/skin/icons/pocket.svg",
|
||||
scene1_section_title_text: "Firefox Accounts: Receivable benefits",
|
||||
|
||||
scene2_text:
|
||||
"Connect to your Firefox account to securely sync passwords, bookmarks, and open tabs.",
|
||||
scene2_title: "Title 123",
|
||||
scene2_email_placeholder_text: "Your email",
|
||||
scene2_button_label: "Continue",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SNIPPETS_SEND_TO_DEVICE_TEST",
|
||||
template: "send_to_device_snippet",
|
||||
content: {
|
||||
include_sms: true,
|
||||
locale: "en-CA",
|
||||
country: "us",
|
||||
message_id_sms: "ff-mobilesn-download",
|
||||
message_id_email: "download-firefox-mobile",
|
||||
|
||||
scene1_button_background_color: "#6200a4",
|
||||
scene1_button_color: "#FFFFFF",
|
||||
scene1_button_label: "Install now",
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_text: "Browse without compromise with Firefox Mobile.",
|
||||
scene1_title: "Full-featured. Customizable. Lightning fast",
|
||||
scene1_title_icon: TEST_ICON_16,
|
||||
scene1_title_icon_dark_theme: TEST_ICON_BW,
|
||||
|
||||
scene2_button_label: "Send",
|
||||
scene2_disclaimer_html:
|
||||
"The intended recipient of the email must have consented. <privacyLink>Learn more</privacyLink>.",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
scene2_icon: TEST_ICON,
|
||||
scene2_icon_dark_theme: TEST_ICON_BW,
|
||||
scene2_input_placeholder: "Your email address or phone number",
|
||||
scene2_text:
|
||||
"Send Firefox to your phone and take a powerful independent browser with you.",
|
||||
scene2_title: "Let's do this!",
|
||||
|
||||
error_text: "Oops, there was a problem.",
|
||||
success_title: "Your download link was sent.",
|
||||
success_text: "Check your device for the email message!",
|
||||
links: {
|
||||
privacyLink: {
|
||||
url: "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SNIPPETS_SCENE2_SEND_TO_DEVICE_TEST",
|
||||
template: "send_to_device_scene2_snippet",
|
||||
content: {
|
||||
include_sms: true,
|
||||
locale: "en-CA",
|
||||
country: "us",
|
||||
message_id_sms: "ff-mobilesn-download",
|
||||
message_id_email: "download-firefox-mobile",
|
||||
scene2_icon: TEST_ICON,
|
||||
section_title_icon:
|
||||
"https://snippets.cdn.mozilla.net/media/icons/094b0707-ab65-4b2e-99a1-a84122b6ab26.png",
|
||||
section_title_text: "Messages from Firefox",
|
||||
section_title_url: "https://support.mozilla.org/kb",
|
||||
scene2_button_label: "Send",
|
||||
scene2_disclaimer_html:
|
||||
"The intended recipient of the email must have consented. <privacyLink>Learn more</privacyLink>.",
|
||||
scene2_input_placeholder: "Your email address or phone number",
|
||||
scene2_text:
|
||||
"Send Firefox to your phone and take a powerful independent browser with you.",
|
||||
error_text: "Oops, there was a problem.",
|
||||
success_title: "Your download link was sent.",
|
||||
success_text: "Check your device for the email message!",
|
||||
links: {
|
||||
privacyLink: {
|
||||
url: "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SNIPPETS_SEND_TO_DEVICE_TEST_NO_DARK_THEME",
|
||||
template: "send_to_device_snippet",
|
||||
content: {
|
||||
include_sms: true,
|
||||
locale: "en-CA",
|
||||
country: "us",
|
||||
message_id_sms: "ff-mobilesn-download",
|
||||
message_id_email: "download-firefox-mobile",
|
||||
|
||||
scene1_button_background_color: "#6200a4",
|
||||
scene1_button_color: "#FFFFFF",
|
||||
scene1_button_label: "Install now",
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: "",
|
||||
scene1_text: "Browse without compromise with Firefox Mobile.",
|
||||
scene1_title: "Full-featured. Customizable. Lightning fast",
|
||||
scene1_title_icon: TEST_ICON_16,
|
||||
scene1_title_icon_dark_theme: "",
|
||||
|
||||
scene2_button_label: "Send",
|
||||
scene2_disclaimer_html:
|
||||
"The intended recipient of the email must have consented. <privacyLink>Learn more</privacyLink>.",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
scene2_icon: TEST_ICON,
|
||||
scene2_icon_dark_theme: "",
|
||||
scene2_input_placeholder: "Your email address or phone number",
|
||||
scene2_text:
|
||||
"Send Firefox to your phone and take a powerful independent browser with you.",
|
||||
scene2_title: "Let's do this!",
|
||||
|
||||
error_text: "Oops, there was a problem.",
|
||||
success_title: "Your download link was sent.",
|
||||
success_text: "Check your device for the email message!",
|
||||
links: {
|
||||
privacyLink: {
|
||||
url: "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SNIPPETS_SEND_TO_DEVICE_TEST_SECTION_TITLE_ICON",
|
||||
template: "send_to_device_snippet",
|
||||
content: {
|
||||
include_sms: true,
|
||||
locale: "en-CA",
|
||||
country: "us",
|
||||
message_id_sms: "ff-mobilesn-download",
|
||||
message_id_email: "download-firefox-mobile",
|
||||
|
||||
scene1_button_background_color: "#6200a4",
|
||||
scene1_button_color: "#FFFFFF",
|
||||
scene1_button_label: "Install now",
|
||||
scene1_icon: TEST_ICON,
|
||||
scene1_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_text: "Browse without compromise with Firefox Mobile.",
|
||||
scene1_title: "Full-featured. Customizable. Lightning fast",
|
||||
scene1_title_icon: TEST_ICON_16,
|
||||
scene1_title_icon_dark_theme: TEST_ICON_BW,
|
||||
scene1_section_title_icon: "chrome://global/skin/icons/pocket.svg",
|
||||
scene1_section_title_text: "Send Firefox to your mobile device!",
|
||||
|
||||
scene2_button_label: "Send",
|
||||
scene2_disclaimer_html:
|
||||
"The intended recipient of the email must have consented. <privacyLink>Learn more</privacyLink>.",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
scene2_icon: TEST_ICON,
|
||||
scene2_icon_dark_theme: TEST_ICON_BW,
|
||||
scene2_input_placeholder: "Your email address or phone number",
|
||||
scene2_text:
|
||||
"Send Firefox to your phone and take a powerful independent browser with you.",
|
||||
scene2_title: "Let's do this!",
|
||||
|
||||
error_text: "Oops, there was a problem.",
|
||||
success_title: "Your download link was sent.",
|
||||
success_text: "Check your device for the email message!",
|
||||
links: {
|
||||
privacyLink: {
|
||||
url: "https://www.mozilla.org/privacy/websites/?sample_rate=0.001&snippet_name=7894",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "EOY_TEST_1",
|
||||
template: "eoy_snippet",
|
||||
content: {
|
||||
highlight_color: "#f05",
|
||||
background_color: "#ddd",
|
||||
text_color: "yellow",
|
||||
selected_button: "donation_amount_first",
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
button_label: "Donate",
|
||||
monthly_checkbox_label_text: "Make my donation monthly",
|
||||
currency_code: "usd",
|
||||
donation_amount_first: 50,
|
||||
donation_amount_second: 25,
|
||||
donation_amount_third: 10,
|
||||
donation_amount_fourth: 5,
|
||||
donation_form_url:
|
||||
"https://donate.mozilla.org/pl/?utm_source=desktop-snippet&utm_medium=snippet&utm_campaign=donate&utm_term=7556",
|
||||
text: "Big corporations want to restrict how we access the web. Fake news is making it harder for us to find the truth. Online bullies are silencing inspired voices. The <em>not-for-profit Mozilla Foundation</em> fights for a healthy internet with programs like our Tech Policy Fellowships and Internet Health Report; <b>will you donate today</b>?",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "EOY_BOLD_TEST_1",
|
||||
template: "eoy_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
selected_button: "donation_amount_second",
|
||||
button_label: "Donate",
|
||||
monthly_checkbox_label_text: "Make my donation monthly",
|
||||
currency_code: "usd",
|
||||
donation_amount_first: 50,
|
||||
donation_amount_second: 25,
|
||||
donation_amount_third: 10,
|
||||
donation_amount_fourth: 5,
|
||||
donation_form_url: "https://donate.mozilla.org",
|
||||
text: "Big corporations want to restrict how we access the web. Fake news is making it harder for us to find the truth. Online bullies are silencing inspired voices. The <em>not-for-profit Mozilla Foundation</em> fights for a healthy internet with programs like our Tech Policy Fellowships and Internet Health Report; <b>will you donate today</b>?",
|
||||
test: "bold",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "EOY_TAKEOVER_TEST_1",
|
||||
template: "eoy_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
button_label: "Donate",
|
||||
monthly_checkbox_label_text: "Make my donation monthly",
|
||||
currency_code: "usd",
|
||||
donation_amount_first: 50,
|
||||
donation_amount_second: 25,
|
||||
donation_amount_third: 10,
|
||||
donation_amount_fourth: 5,
|
||||
donation_form_url: "https://donate.mozilla.org",
|
||||
text: "Big corporations want to restrict how we access the web. Fake news is making it harder for us to find the truth. Online bullies are silencing inspired voices. The <em>not-for-profit Mozilla Foundation</em> fights for a healthy internet with programs like our Tech Policy Fellowships and Internet Health Report; <b>will you donate today</b>?",
|
||||
test: "takeover",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_WITH_SECTION_HEADING",
|
||||
template: "simple_snippet",
|
||||
content: {
|
||||
button_label: "Get one now!",
|
||||
button_url: "https://www.mozilla.org/en-US/firefox/accounts",
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
title: "Firefox Account!",
|
||||
text: "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
section_title_icon: "chrome://global/skin/icons/pocket.svg",
|
||||
section_title_text: "Messages from Mozilla",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_TEST_WITH_SECTION_HEADING_AND_LINK",
|
||||
template: "simple_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
title: "Firefox Account!",
|
||||
text: "Sync it, link it, take it with you. All this and more with a Firefox Account.",
|
||||
block_button_text: "Block",
|
||||
section_title_icon: "chrome://global/skin/icons/pocket.svg",
|
||||
section_title_text: "Messages from Mozilla (click for info)",
|
||||
section_title_url: "https://www.mozilla.org/about",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_BELOW_SEARCH_TEST_1",
|
||||
template: "simple_below_search_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
text: "Securely store passwords, bookmarks, and more with a Firefox Account. <syncLink>Sign up</syncLink>",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_BELOW_SEARCH_TEST_2",
|
||||
template: "simple_below_search_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
text: "<syncLink>Connect your Firefox Account to Sync</syncLink> your protected passwords, open tabs and bookmarks, and they'll always be available to you - on all of your devices.",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SIMPLE_BELOW_SEARCH_TEST_TITLE",
|
||||
template: "simple_below_search_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
title: "See if you've been part of an online data breach.",
|
||||
text: "Securely store passwords, bookmarks, and more with a Firefox Account. <syncLink>Sign up</syncLink>",
|
||||
links: {
|
||||
syncLink: { url: "https://www.mozilla.org/en-US/firefox/accounts" },
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SPECIAL_SNIPPET_BUTTON_1",
|
||||
template: "simple_below_search_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
button_label: "Find Out Now",
|
||||
button_url: "https://www.mozilla.org/en-US/firefox/accounts",
|
||||
title: "See if you've been part of an online data breach.",
|
||||
text: "Firefox Monitor tells you what hackers already know about you.",
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SPECIAL_SNIPPET_LONG_CONTENT",
|
||||
template: "simple_below_search_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
button_label: "Find Out Now",
|
||||
button_url: "https://www.mozilla.org/en-US/firefox/accounts",
|
||||
title: "See if you've been part of an online data breach.",
|
||||
text: "Firefox Monitor tells you what hackers already know about you. Here's some extra text to make the content really long.",
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SPECIAL_SNIPPET_NO_TITLE",
|
||||
template: "simple_below_search_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
icon_dark_theme: TEST_ICON_BW,
|
||||
button_label: "Find Out Now",
|
||||
button_url: "https://www.mozilla.org/en-US/firefox/accounts",
|
||||
text: "Firefox Monitor tells you what hackers already know about you.",
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "SPECIAL_SNIPPET_MONITOR",
|
||||
template: "simple_below_search_snippet",
|
||||
content: {
|
||||
icon: TEST_ICON,
|
||||
title: "See if you've been part of an online data breach.",
|
||||
text: "Firefox Monitor tells you what hackers already know about you.",
|
||||
button_label: "Get monitor",
|
||||
button_action: "ENABLE_FIREFOX_MONITOR",
|
||||
button_action_args: {
|
||||
url: "https://monitor.firefox.com/oauth/init?utm_source=snippets&utm_campaign=monitor-snippet-test&form_type=email&entrypoint=newtab",
|
||||
flowRequestParams: {
|
||||
entrypoint: "snippets",
|
||||
utm_term: "monitor",
|
||||
form_type: "email",
|
||||
},
|
||||
},
|
||||
block_button_text: "Block",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const SnippetsTestMessageProvider = {
|
||||
getMessages() {
|
||||
return Promise.resolve(
|
||||
MESSAGES()
|
||||
// Ensures we never actually show test except when triggered by debug tools
|
||||
.map(message => ({
|
||||
...message,
|
||||
targeting: `providerCohorts.snippets_local_testing == "SHOW_TEST"`,
|
||||
}))
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
@ -7,9 +7,6 @@ support-files = [
|
|||
"annotation_third.html",
|
||||
"head.js",
|
||||
"redirect_to.sjs",
|
||||
"snippet.json",
|
||||
"snippet_below_search_test.json",
|
||||
"snippet_simple_test.json",
|
||||
"topstories.json",
|
||||
"file_pdf.PDF",
|
||||
]
|
||||
|
|
@ -48,12 +45,6 @@ skip-if = ["os == 'linux' && bits == 64 && !debug"] # Bug 1643036
|
|||
["browser_asrouter_momentspagehub.js"]
|
||||
tags = "remote-settings"
|
||||
|
||||
["browser_asrouter_snippets.js"]
|
||||
https_first_disabled = true
|
||||
|
||||
["browser_asrouter_snippets_dismiss.js"]
|
||||
support-files = ["../../../../base/content/aboutRobots-icon.png"]
|
||||
|
||||
["browser_asrouter_targeting.js"]
|
||||
|
||||
["browser_asrouter_toast_notification.js"]
|
||||
|
|
|
|||
|
|
@ -742,7 +742,6 @@ add_task(async function test_providerNames() {
|
|||
for (const prefName of cfrProviderPrefs) {
|
||||
const prefValue = JSON.parse(Services.prefs.getStringPref(prefName));
|
||||
if (prefValue && prefValue.id) {
|
||||
// Snippets are disabled in tests and value is set to []
|
||||
Assert.equal(
|
||||
prefValue.id,
|
||||
prefName.slice(providersBranch.length),
|
||||
|
|
|
|||
|
|
@ -1,196 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
// Ask for more time for Verify.
|
||||
requestLongerTimeout(5);
|
||||
|
||||
const { ASRouter } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/ASRouter.jsm"
|
||||
);
|
||||
|
||||
const { TelemetryFeed } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/TelemetryFeed.jsm"
|
||||
);
|
||||
|
||||
add_task(async function render_below_search_snippet() {
|
||||
ASRouter._validPreviewEndpoint = () => true;
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:newtab?endpoint=https://example.com/browser/browser/components/newtab/test/browser/snippet_below_search_test.json",
|
||||
},
|
||||
async browser => {
|
||||
await waitForPreloaded(browser);
|
||||
|
||||
const complete = await SpecialPowers.spawn(browser, [], async () => {
|
||||
// Verify the simple_below_search_snippet renders in container below searchbox
|
||||
// and nothing is rendered in the footer.
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector(
|
||||
".below-search-snippet .SimpleBelowSearchSnippet"
|
||||
),
|
||||
"Should find the snippet inside the below search container"
|
||||
);
|
||||
|
||||
is(
|
||||
0,
|
||||
content.document.querySelector("#footer-asrouter-container")
|
||||
.childNodes.length,
|
||||
"Should not find any snippets in the footer container"
|
||||
);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
Assert.ok(complete, "Test complete.");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function render_snippets_icon_and_link() {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:newtab?endpoint=https://example.com/browser/browser/components/newtab/test/browser/snippet_simple_test.json",
|
||||
},
|
||||
async browser => {
|
||||
await waitForPreloaded(browser);
|
||||
|
||||
const complete = await SpecialPowers.spawn(browser, [], async () => {
|
||||
const syncLink = "https://www.mozilla.org/en-US/firefox/accounts";
|
||||
// Verify the simple_snippet renders in the footer and the container below
|
||||
// searchbox is not rendered.
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
),
|
||||
"Should find the snippet inside the footer container"
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet .icon"
|
||||
),
|
||||
"Should render an icon"
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector(
|
||||
`#footer-asrouter-container .SimpleSnippet a[href='${syncLink}']`
|
||||
),
|
||||
"Should render an anchor with the correct href"
|
||||
);
|
||||
|
||||
ok(
|
||||
!content.document.querySelector(".below-search-snippet"),
|
||||
"Should not find any snippets below search"
|
||||
);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
Assert.ok(complete, "Test complete.");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function render_preview_snippet() {
|
||||
ASRouter._validPreviewEndpoint = () => true;
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:newtab?endpoint=https://example.com/browser/browser/components/newtab/test/browser/snippet.json",
|
||||
},
|
||||
async browser => {
|
||||
let text = await SpecialPowers.spawn(browser, [], async () => {
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => content.document.querySelector(".activity-stream"),
|
||||
`Should render Activity Stream`
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
),
|
||||
"Should find the snippet inside the footer container"
|
||||
);
|
||||
|
||||
return content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
).innerText;
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
text,
|
||||
"On January 30th Nightly will introduce dedicated profiles, making it simpler to run different installations of Firefox side by side. Learn what this means for you.",
|
||||
"Snippet content match"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_snippets_telemetry() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[
|
||||
"browser.newtabpage.activity-stream.asrouter.providers.snippets",
|
||||
`{"id":"snippets","enabled":true,"type":"remote","url":"https://example.com/browser/browser/components/newtab/test/browser/snippet.json","updateCycleInMs":0}`,
|
||||
],
|
||||
["browser.newtabpage.activity-stream.feeds.snippets", true],
|
||||
["browser.newtabpage.activity-stream.telemetry", true],
|
||||
],
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(
|
||||
"browser.newtabpage.activity-stream.telemetry"
|
||||
);
|
||||
});
|
||||
|
||||
Services.fog.testResetFOG();
|
||||
let pingSubmitted = false;
|
||||
GleanPings.messagingSystem.testBeforeNextSubmit(() => {
|
||||
pingSubmitted = true;
|
||||
Assert.equal(Glean.messagingSystem.pingType.testGetValue(), "snippets");
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
// Work around any issues caching might introduce by navigating to
|
||||
// about blank first
|
||||
url: "about:blank",
|
||||
},
|
||||
async browser => {
|
||||
BrowserTestUtils.startLoadingURIString(browser, "about:home");
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
let text = await SpecialPowers.spawn(browser, [], async () => {
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => content.document.querySelector(".activity-stream"),
|
||||
`Should render Activity Stream`
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
),
|
||||
"Should find the snippet inside the footer container"
|
||||
);
|
||||
|
||||
return content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
).innerText;
|
||||
});
|
||||
|
||||
Assert.equal(
|
||||
text,
|
||||
"On January 30th Nightly will introduce dedicated profiles, making it simpler to run different installations of Firefox side by side. Learn what this means for you.",
|
||||
"Snippet content match"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
await Services.fog.testFlushAllChildren();
|
||||
Assert.ok(pingSubmitted, "Submitted Glean ping");
|
||||
});
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Snippets endpoint has two snippets that share the same campaign id.
|
||||
* We want to make sure that dismissing the snippet on the first about:newtab
|
||||
* will clear the snippet on the next (preloaded) about:newtab.
|
||||
*/
|
||||
|
||||
const { ASRouter } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/ASRouter.jsm"
|
||||
);
|
||||
|
||||
async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[
|
||||
"browser.newtabpage.activity-stream.asrouter.providers.snippets",
|
||||
'{"id":"snippets","enabled":true,"type":"remote","url":"https://example.com/browser/browser/components/newtab/test/browser/snippet.json","updateCycleInMs":14400000}',
|
||||
],
|
||||
["browser.newtabpage.activity-stream.feeds.snippets", true],
|
||||
// Disable onboarding, this would prevent snippets from showing up
|
||||
[
|
||||
"browser.newtabpage.activity-stream.asrouter.providers.onboarding",
|
||||
'{"id":"onboarding","type":"local","localProvider":"OnboardingMessageProvider","enabled":false,"exclude":[]}',
|
||||
],
|
||||
// Ensure this is true, this is the main behavior we want to test for
|
||||
["browser.newtab.preload", true],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_campaign_dismiss() {
|
||||
await setup();
|
||||
let tab1 = await BrowserTestUtils.openNewForegroundTab({
|
||||
gBrowser,
|
||||
opening: "about:home",
|
||||
});
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {}, async () => {
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => content.document.querySelector(".activity-stream"),
|
||||
`Should render Activity Stream`
|
||||
);
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
),
|
||||
"Should find the snippet inside the footer container"
|
||||
);
|
||||
|
||||
content.document
|
||||
.querySelector("#footer-asrouter-container .blockButton")
|
||||
.click();
|
||||
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() =>
|
||||
!content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
),
|
||||
"Should wait for the snippet to block"
|
||||
);
|
||||
});
|
||||
|
||||
ok(
|
||||
ASRouter.state.messageBlockList.length,
|
||||
"Should have the campaign blocked"
|
||||
);
|
||||
|
||||
let tab2 = await BrowserTestUtils.openNewForegroundTab({
|
||||
gBrowser,
|
||||
opening: "about:newtab",
|
||||
// This is important because the newtab is preloaded and doesn't behave
|
||||
// like a regular page load
|
||||
waitForLoad: false,
|
||||
});
|
||||
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {}, async () => {
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => content.document.querySelector(".activity-stream"),
|
||||
`Should render Activity Stream`
|
||||
);
|
||||
let snippet = content.document.querySelector(
|
||||
"#footer-asrouter-container .SimpleSnippet"
|
||||
);
|
||||
Assert.equal(
|
||||
snippet,
|
||||
null,
|
||||
"No snippets shown because campaign is blocked"
|
||||
);
|
||||
});
|
||||
|
||||
BrowserTestUtils.removeTab(tab1);
|
||||
BrowserTestUtils.removeTab(tab2);
|
||||
await ASRouter.unblockMessageById(["10533", "10534"]);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"messages": [
|
||||
{
|
||||
"weight": 50,
|
||||
"id": "10533",
|
||||
"template": "simple_snippet",
|
||||
"template_version": "1.0.0",
|
||||
"content": {
|
||||
"icon": "",
|
||||
"text": "On January 30th Nightly will introduce dedicated profiles, making it simpler to run different installations of Firefox side by side. <link0> Learn what this means for you</link0>.",
|
||||
"tall": false,
|
||||
"do_not_autoblock": false,
|
||||
"links": {
|
||||
"link0": {
|
||||
"url": "https://example.com/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"campaign": "nightly-profile-management",
|
||||
"targeting": "true",
|
||||
"provider_url": "https://snippets.cdn.mozilla.net/6/Firefox/66.0a1/20190122215349/Darwin_x86_64-gcc3/en-US/default/Darwin%2018.0.0/default/default/",
|
||||
"provider": "snippets"
|
||||
},
|
||||
{
|
||||
"weight": 50,
|
||||
"id": "10534",
|
||||
"template": "simple_snippet",
|
||||
"template_version": "1.0.0",
|
||||
"content": {
|
||||
"icon": "",
|
||||
"text": "On January 30th Nightly will introduce dedicated profiles, making it simpler to run different installations of Firefox side by side. <link0> Learn what this means for you</link0>.",
|
||||
"tall": false,
|
||||
"do_not_autoblock": false,
|
||||
"links": {
|
||||
"link0": {
|
||||
"url": "https://example.com/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"campaign": "nightly-profile-management",
|
||||
"targeting": "true",
|
||||
"provider_url": "https://snippets.cdn.mozilla.net/6/Firefox/66.0a1/20190122215349/Darwin_x86_64-gcc3/en-US/default/Darwin%2018.0.0/default/default/",
|
||||
"provider": "snippets"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"messages": [
|
||||
{
|
||||
"id": "SIMPLE_BELOW_SEARCH_TEST_1",
|
||||
"template": "simple_below_search_snippet",
|
||||
"content": {
|
||||
"icon": "chrome://branding/content/icon64.png",
|
||||
"icon_dark_theme": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfjBQ8QDifrKGc/AAABf0lEQVQoz4WRO08UUQCFvztzd1AgG9jRgGwkhEoMIYGSygYt+A00tpZGY0jYxAJKEwkNjX9AK2xACx4dhFiQQCiMMRr2kYXdnQcz7L0z91qAMVac6hTfSU7OgVsk/prtyfSNfRb7ge2cd7dmVucP/wM2lwqVqoyICahRx9Nz71+8AnAAvlTct+dSYDBYcgJ+Fj68XFu/AfamnIoWFoHFYrAUuYMSn55/fAIOxIs1t4MhQpNxRYsUD0ld7r8DCfZph4QecrqkhCREgMLSeISQkAy0UBgE0CYgIkeRA9HdsCQhpEGCxichpItHigEcPH4XJLRbTf8STY0iiiuu60Ifxexx04F0N+aCgJCAhPQmD/cp/RC5A79WvUyhUHSIidAIoESv9VfAhW9n8+XqTCoyMsz1cviMMrGz9BrjAuboYHZajyXCInEocI8yvccbC+0muABanR4/tONjQz3DzgNKtj9sfv66XD9B/3tT9g/akb7h0bJwzxqqmlRHLr4rLPwBlYWoYj77l2AAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDUtMTVUMTY6MTQ6MzkrMDA6MDD5/4XBAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA1LTE1VDE2OjE0OjM5KzAwOjAwiKI9fQAAAABJRU5ErkJggg==",
|
||||
"text": "Securely store passwords, bookmarks, and more with a Firefox Account. <syncLink>Sign up</syncLink>",
|
||||
"links": {
|
||||
"syncLink": {
|
||||
"url": "https://www.mozilla.org/en-US/firefox/accounts"
|
||||
}
|
||||
},
|
||||
"block_button_text": "Block"
|
||||
},
|
||||
"targeting": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"messages": [
|
||||
{
|
||||
"id": "SIMPLE_TEST_1",
|
||||
"template": "simple_snippet",
|
||||
"campaign": "test_campaign_blocking",
|
||||
"content": {
|
||||
"icon": "chrome://branding/content/icon64.png",
|
||||
"icon_dark_theme": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfjBQ8QDifrKGc/AAABf0lEQVQoz4WRO08UUQCFvztzd1AgG9jRgGwkhEoMIYGSygYt+A00tpZGY0jYxAJKEwkNjX9AK2xACx4dhFiQQCiMMRr2kYXdnQcz7L0z91qAMVac6hTfSU7OgVsk/prtyfSNfRb7ge2cd7dmVucP/wM2lwqVqoyICahRx9Nz71+8AnAAvlTct+dSYDBYcgJ+Fj68XFu/AfamnIoWFoHFYrAUuYMSn55/fAIOxIs1t4MhQpNxRYsUD0ld7r8DCfZph4QecrqkhCREgMLSeISQkAy0UBgE0CYgIkeRA9HdsCQhpEGCxichpItHigEcPH4XJLRbTf8STY0iiiuu60Ifxexx04F0N+aCgJCAhPQmD/cp/RC5A79WvUyhUHSIidAIoESv9VfAhW9n8+XqTCoyMsz1cviMMrGz9BrjAuboYHZajyXCInEocI8yvccbC+0muABanR4/tONjQz3DzgNKtj9sfv66XD9B/3tT9g/akb7h0bJwzxqqmlRHLr4rLPwBlYWoYj77l2AAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDUtMTVUMTY6MTQ6MzkrMDA6MDD5/4XBAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA1LTE1VDE2OjE0OjM5KzAwOjAwiKI9fQAAAABJRU5ErkJggg==",
|
||||
"title": "Firefox Account!",
|
||||
"title_icon": "chrome://branding/content/icon16.png",
|
||||
"title_icon_dark_theme": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfjBQ8QDifrKGc/AAABf0lEQVQoz4WRO08UUQCFvztzd1AgG9jRgGwkhEoMIYGSygYt+A00tpZGY0jYxAJKEwkNjX9AK2xACx4dhFiQQCiMMRr2kYXdnQcz7L0z91qAMVac6hTfSU7OgVsk/prtyfSNfRb7ge2cd7dmVucP/wM2lwqVqoyICahRx9Nz71+8AnAAvlTct+dSYDBYcgJ+Fj68XFu/AfamnIoWFoHFYrAUuYMSn55/fAIOxIs1t4MhQpNxRYsUD0ld7r8DCfZph4QecrqkhCREgMLSeISQkAy0UBgE0CYgIkeRA9HdsCQhpEGCxichpItHigEcPH4XJLRbTf8STY0iiiuu60Ifxexx04F0N+aCgJCAhPQmD/cp/RC5A79WvUyhUHSIidAIoESv9VfAhW9n8+XqTCoyMsz1cviMMrGz9BrjAuboYHZajyXCInEocI8yvccbC+0muABanR4/tONjQz3DzgNKtj9sfv66XD9B/3tT9g/akb7h0bJwzxqqmlRHLr4rLPwBlYWoYj77l2AAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDUtMTVUMTY6MTQ6MzkrMDA6MDD5/4XBAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA1LTE1VDE2OjE0OjM5KzAwOjAwiKI9fQAAAABJRU5ErkJggg==",
|
||||
"text": "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
|
||||
"links": {
|
||||
"syncLink": {
|
||||
"url": "https://www.mozilla.org/en-US/firefox/accounts"
|
||||
}
|
||||
},
|
||||
"block_button_text": "Block"
|
||||
},
|
||||
"targeting": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -17,10 +17,9 @@ import { CFRPageActions } from "lib/CFRPageActions.jsm";
|
|||
import { GlobalOverrider } from "test/unit/utils";
|
||||
import { PanelTestProvider } from "lib/PanelTestProvider.sys.mjs";
|
||||
import ProviderResponseSchema from "content-src/asrouter/schemas/provider-response.schema.json";
|
||||
import { SnippetsTestMessageProvider } from "lib/SnippetsTestMessageProvider.sys.mjs";
|
||||
|
||||
const MESSAGE_PROVIDER_PREF_NAME =
|
||||
"browser.newtabpage.activity-stream.asrouter.providers.snippets";
|
||||
"browser.newtabpage.activity-stream.asrouter.providers.cfr";
|
||||
const FAKE_PROVIDERS = [
|
||||
FAKE_LOCAL_PROVIDER,
|
||||
FAKE_REMOTE_PROVIDER,
|
||||
|
|
@ -153,7 +152,6 @@ describe("ASRouter", () => {
|
|||
cfr: "",
|
||||
"message-groups": "",
|
||||
"messaging-experiments": "",
|
||||
snippets: "",
|
||||
"whats-new-panel": "",
|
||||
},
|
||||
totalBookmarksCount: {},
|
||||
|
|
@ -166,7 +164,6 @@ describe("ASRouter", () => {
|
|||
userPrefs: {
|
||||
cfrFeatures: true,
|
||||
cfrAddons: true,
|
||||
snippets: true,
|
||||
},
|
||||
totalBlockedCount: {},
|
||||
blockedCountByType: {},
|
||||
|
|
@ -253,7 +250,6 @@ describe("ASRouter", () => {
|
|||
gURLBar: {},
|
||||
isSeparateAboutWelcome: true,
|
||||
AttributionCode: fakeAttributionCode,
|
||||
SnippetsTestMessageProvider,
|
||||
PanelTestProvider,
|
||||
MacAttribution: { applicationPath: "" },
|
||||
ToolbarBadgeHub: FakeToolbarBadgeHub,
|
||||
|
|
@ -494,11 +490,6 @@ describe("ASRouter", () => {
|
|||
await createRouterAndInit();
|
||||
|
||||
assert.lengthOf(Object.keys(Router.ALLOWLIST_HOSTS), 2);
|
||||
assert.propertyVal(
|
||||
Router.ALLOWLIST_HOSTS,
|
||||
"snippets-admin.mozilla.org",
|
||||
"preview"
|
||||
);
|
||||
assert.propertyVal(
|
||||
Router.ALLOWLIST_HOSTS,
|
||||
"activity-stream-icons.services.mozilla.com",
|
||||
|
|
@ -545,7 +536,6 @@ describe("ASRouter", () => {
|
|||
|
||||
await createRouterAndInit();
|
||||
|
||||
assert.property(Router._localProviders, "SnippetsTestMessageProvider");
|
||||
assert.property(Router._localProviders, "PanelTestProvider");
|
||||
});
|
||||
it("should not add the local test providers on init if devtools are disabled", async () => {
|
||||
|
|
@ -553,10 +543,6 @@ describe("ASRouter", () => {
|
|||
|
||||
await createRouterAndInit();
|
||||
|
||||
assert.notProperty(
|
||||
Router._localProviders,
|
||||
"SnippetsTestMessageProvider"
|
||||
);
|
||||
assert.notProperty(Router._localProviders, "PanelTestProvider");
|
||||
});
|
||||
});
|
||||
|
|
@ -580,18 +566,18 @@ describe("ASRouter", () => {
|
|||
id: "1",
|
||||
campaign: "foocampaign",
|
||||
targeting: "true",
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
const messageNotTargeted = {
|
||||
id: "2",
|
||||
campaign: "foocampaign",
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
await Router.setState({
|
||||
messages: [messageTargeted, messageNotTargeted],
|
||||
providers: [{ id: "snippets" }],
|
||||
providers: [{ id: "cfr" }],
|
||||
});
|
||||
fakeTargetingContext.evalWithDefault.resolves(false);
|
||||
|
||||
|
|
@ -813,7 +799,12 @@ describe("ASRouter", () => {
|
|||
assert.notCalled(FakeMomentsPageHub.executeAction);
|
||||
});
|
||||
it("should route default to sending to content", () => {
|
||||
Router.routeCFRMessage({ template: "snippets" }, browser, {}, true);
|
||||
Router.routeCFRMessage(
|
||||
{ template: "some_other_template" },
|
||||
browser,
|
||||
{},
|
||||
true
|
||||
);
|
||||
|
||||
assert.notCalled(FakeToolbarPanelHub.forceShowMessage);
|
||||
assert.notCalled(CFRPageActions.forceRecommendation);
|
||||
|
|
@ -1154,23 +1145,23 @@ describe("ASRouter", () => {
|
|||
describe("#handleMessageRequest", () => {
|
||||
beforeEach(async () => {
|
||||
await Router.setState(() => ({
|
||||
providers: [{ id: "snippets" }, { id: "badge" }],
|
||||
providers: [{ id: "cfr" }, { id: "badge" }],
|
||||
}));
|
||||
});
|
||||
it("should not return a blocked message", async () => {
|
||||
// Block all messages except the first
|
||||
await Router.setState(() => ({
|
||||
messages: [
|
||||
{ id: "foo", provider: "snippets", groups: ["snippets"] },
|
||||
{ id: "bar", provider: "snippets", groups: ["snippets"] },
|
||||
{ id: "foo", provider: "cfr", groups: ["cfr"] },
|
||||
{ id: "bar", provider: "cfr", groups: ["cfr"] },
|
||||
],
|
||||
messageBlockList: ["foo"],
|
||||
}));
|
||||
await Router.handleMessageRequest({
|
||||
provider: "snippets",
|
||||
provider: "cfr",
|
||||
});
|
||||
assert.calledWithMatch(ASRouterTargeting.findMatchingMessage, {
|
||||
messages: [{ id: "bar", provider: "snippets", groups: ["snippets"] }],
|
||||
messages: [{ id: "bar", provider: "cfr", groups: ["cfr"] }],
|
||||
});
|
||||
});
|
||||
it("should not return a message from a disabled group", async () => {
|
||||
|
|
@ -1180,13 +1171,13 @@ describe("ASRouter", () => {
|
|||
// Block all messages except the first
|
||||
await Router.setState(() => ({
|
||||
messages: [
|
||||
{ id: "foo", provider: "snippets", groups: ["snippets"] },
|
||||
{ id: "bar", provider: "snippets", groups: ["snippets"] },
|
||||
{ id: "foo", provider: "cfr", groups: ["cfr"] },
|
||||
{ id: "bar", provider: "cfr", groups: ["cfr"] },
|
||||
],
|
||||
groups: [{ id: "snippets", enabled: false }],
|
||||
groups: [{ id: "cfr", enabled: false }],
|
||||
}));
|
||||
const result = await Router.handleMessageRequest({
|
||||
provider: "snippets",
|
||||
provider: "cfr",
|
||||
});
|
||||
assert.isNull(result);
|
||||
});
|
||||
|
|
@ -1196,46 +1187,46 @@ describe("ASRouter", () => {
|
|||
messages: [
|
||||
{
|
||||
id: "foo",
|
||||
provider: "snippets",
|
||||
provider: "cfr",
|
||||
campaign: "foocampaign",
|
||||
groups: ["snippets"],
|
||||
groups: ["cfr"],
|
||||
},
|
||||
{ id: "bar", provider: "snippets", groups: ["snippets"] },
|
||||
{ id: "bar", provider: "cfr", groups: ["cfr"] },
|
||||
],
|
||||
messageBlockList: ["foocampaign"],
|
||||
}));
|
||||
|
||||
await Router.handleMessageRequest({
|
||||
provider: "snippets",
|
||||
provider: "cfr",
|
||||
});
|
||||
assert.calledWithMatch(ASRouterTargeting.findMatchingMessage, {
|
||||
messages: [{ id: "bar", provider: "snippets", groups: ["snippets"] }],
|
||||
messages: [{ id: "bar", provider: "cfr", groups: ["cfr"] }],
|
||||
});
|
||||
});
|
||||
it("should not return a message excluded by the provider", async () => {
|
||||
// There are only two providers; block the FAKE_LOCAL_PROVIDER, leaving
|
||||
// only FAKE_REMOTE_PROVIDER unblocked, which provides only one message
|
||||
await Router.setState(() => ({
|
||||
providers: [{ id: "snippets", exclude: ["foo"] }],
|
||||
providers: [{ id: "cfr", exclude: ["foo"] }],
|
||||
}));
|
||||
|
||||
await Router.setState(() => ({
|
||||
messages: [{ id: "foo", provider: "snippets" }],
|
||||
messages: [{ id: "foo", provider: "cfr" }],
|
||||
messageBlockList: ["foocampaign"],
|
||||
}));
|
||||
|
||||
const result = await Router.handleMessageRequest({
|
||||
provider: "snippets",
|
||||
provider: "cfr",
|
||||
});
|
||||
assert.isNull(result);
|
||||
});
|
||||
it("should not return a message if the frequency cap has been hit", async () => {
|
||||
sandbox.stub(Router, "isBelowFrequencyCaps").returns(false);
|
||||
await Router.setState(() => ({
|
||||
messages: [{ id: "foo", provider: "snippets" }],
|
||||
messages: [{ id: "foo", provider: "cfr" }],
|
||||
}));
|
||||
const result = await Router.handleMessageRequest({
|
||||
provider: "snippets",
|
||||
provider: "cfr",
|
||||
});
|
||||
assert.isNull(result);
|
||||
});
|
||||
|
|
@ -1244,15 +1235,15 @@ describe("ASRouter", () => {
|
|||
id: "1",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "foo" },
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
const message2 = {
|
||||
id: "2",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "bar" },
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
await Router.setState({ messages: [message2, message1] });
|
||||
// Just return the first message provided as arg
|
||||
|
|
@ -1276,10 +1267,10 @@ describe("ASRouter", () => {
|
|||
const message2 = {
|
||||
id: "2",
|
||||
campaign: "foocampaign",
|
||||
template: "snippet",
|
||||
template: "test_template",
|
||||
trigger: { id: "foo" },
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
await Router.setState({ messages: [message2, message1] });
|
||||
// Just return the first message provided as arg
|
||||
|
|
@ -1348,8 +1339,8 @@ describe("ASRouter", () => {
|
|||
id: "1",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "foo" },
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
const message2 = {
|
||||
id: "2",
|
||||
|
|
@ -1370,34 +1361,6 @@ describe("ASRouter", () => {
|
|||
assert.propertyVal(options.trigger, "param", trigger.triggerParam);
|
||||
assert.propertyVal(options.trigger, "context", trigger.triggerContext);
|
||||
});
|
||||
it("should cache snippets messages", async () => {
|
||||
const trigger = {
|
||||
triggerId: "foo",
|
||||
triggerParam: "bar",
|
||||
triggerContext: "context",
|
||||
};
|
||||
const message1 = {
|
||||
id: "1",
|
||||
provider: "snippets",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "foo" },
|
||||
groups: ["snippets"],
|
||||
};
|
||||
const message2 = {
|
||||
id: "2",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "bar" },
|
||||
groups: ["snippets"],
|
||||
};
|
||||
await Router.setState({ messages: [message2, message1] });
|
||||
|
||||
Router.handleMessageRequest(trigger);
|
||||
|
||||
assert.calledOnce(ASRouterTargeting.findMatchingMessage);
|
||||
|
||||
const [options] = ASRouterTargeting.findMatchingMessage.firstCall.args;
|
||||
assert.propertyVal(options, "shouldCache", true);
|
||||
});
|
||||
it("should not cache badge messages", async () => {
|
||||
const trigger = {
|
||||
triggerId: "bar",
|
||||
|
|
@ -1406,10 +1369,10 @@ describe("ASRouter", () => {
|
|||
};
|
||||
const message1 = {
|
||||
id: "1",
|
||||
provider: "snippets",
|
||||
provider: "cfr",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "foo" },
|
||||
groups: ["snippets"],
|
||||
groups: ["cfr"],
|
||||
};
|
||||
const message2 = {
|
||||
id: "2",
|
||||
|
|
@ -1434,25 +1397,25 @@ describe("ASRouter", () => {
|
|||
id: "1",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "foo" },
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
const message2 = {
|
||||
id: "2",
|
||||
campaign: "foocampaign",
|
||||
trigger: { id: "bar" },
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
const message3 = {
|
||||
id: "3",
|
||||
campaign: "bazcampaign",
|
||||
groups: ["snippets"],
|
||||
provider: "snippets",
|
||||
groups: ["cfr"],
|
||||
provider: "cfr",
|
||||
};
|
||||
await Router.setState({
|
||||
messages: [message2, message1, message3],
|
||||
groups: [{ id: "snippets", enabled: true }],
|
||||
groups: [{ id: "cfr", enabled: true }],
|
||||
});
|
||||
// Just return the first message provided as arg
|
||||
ASRouterTargeting.findMatchingMessage.callsFake(args => args.messages);
|
||||
|
|
@ -1512,163 +1475,6 @@ describe("ASRouter", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("sendNewTabMessage", () => {
|
||||
it("should construct an appropriate response message", async () => {
|
||||
Router.loadMessagesFromAllProviders.resetHistory();
|
||||
Router.loadMessagesFromAllProviders.onFirstCall().resolves();
|
||||
|
||||
let message = {
|
||||
id: "foo",
|
||||
provider: "snippets",
|
||||
groups: ["snippets"],
|
||||
};
|
||||
|
||||
await Router.setState({
|
||||
messages: [message],
|
||||
providers: [{ id: "snippets" }],
|
||||
});
|
||||
|
||||
ASRouterTargeting.findMatchingMessage.callsFake(
|
||||
({ messages }) => messages[0]
|
||||
);
|
||||
|
||||
let response = await Router.sendNewTabMessage({
|
||||
tabId: 0,
|
||||
browser: {},
|
||||
});
|
||||
|
||||
assert.deepEqual(response.message, message);
|
||||
});
|
||||
it("should send an empty object message if no messages are available", async () => {
|
||||
await Router.setState({ messages: [] });
|
||||
let response = await Router.sendNewTabMessage({
|
||||
tabId: 0,
|
||||
browser: {},
|
||||
});
|
||||
|
||||
assert.deepEqual(response.message, {});
|
||||
});
|
||||
|
||||
describe("#addPreviewEndpoint", () => {
|
||||
it("should make a request to the provided endpoint", async () => {
|
||||
const url = "https://snippets-admin.mozilla.org/foo";
|
||||
const browser = {};
|
||||
browser.sendMessageToActor = sandbox.stub();
|
||||
|
||||
await Router.sendNewTabMessage({
|
||||
endpoint: { url },
|
||||
tabId: 0,
|
||||
browser,
|
||||
});
|
||||
|
||||
assert.calledWith(global.fetch, url);
|
||||
assert.lengthOf(
|
||||
Router.state.providers.filter(p => p.url === url),
|
||||
0
|
||||
);
|
||||
});
|
||||
it("should send EnterSnippetPreviewMode when adding a preview endpoint", async () => {
|
||||
const url = "https://snippets-admin.mozilla.org/foo";
|
||||
const browser = {};
|
||||
browser.sendMessageToActor = sandbox.stub();
|
||||
|
||||
await Router.addPreviewEndpoint(url, browser);
|
||||
|
||||
assert.calledWithExactly(
|
||||
browser.sendMessageToActor,
|
||||
"EnterSnippetsPreviewMode",
|
||||
{},
|
||||
"ASRouter"
|
||||
);
|
||||
});
|
||||
it("should not add a url that is not from an allowed host", async () => {
|
||||
const url = "https://mozilla.org";
|
||||
const browser = {};
|
||||
browser.sendMessageToActor = sandbox.stub();
|
||||
|
||||
await Router.addPreviewEndpoint(url, browser);
|
||||
|
||||
assert.lengthOf(
|
||||
Router.state.providers.filter(p => p.url === url),
|
||||
0
|
||||
);
|
||||
});
|
||||
it("should reject bad urls", async () => {
|
||||
const url = "foo";
|
||||
const browser = {};
|
||||
browser.sendMessageToActor = sandbox.stub();
|
||||
|
||||
await Router.addPreviewEndpoint(url, browser);
|
||||
|
||||
assert.lengthOf(
|
||||
Router.state.providers.filter(p => p.url === url),
|
||||
0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should record telemetry for message request duration", async () => {
|
||||
const startTelemetryStopwatch = sandbox.stub(
|
||||
global.TelemetryStopwatch,
|
||||
"start"
|
||||
);
|
||||
const finishTelemetryStopwatch = sandbox.stub(
|
||||
global.TelemetryStopwatch,
|
||||
"finish"
|
||||
);
|
||||
sandbox.stub(Router, "handleMessageRequest");
|
||||
const tabId = 123;
|
||||
await Router.sendNewTabMessage({
|
||||
tabId,
|
||||
browser: {},
|
||||
});
|
||||
|
||||
// Called once for the messagesLoaded trigger and once for the above call.
|
||||
assert.calledTwice(startTelemetryStopwatch);
|
||||
assert.calledWithExactly(
|
||||
startTelemetryStopwatch,
|
||||
"MS_MESSAGE_REQUEST_TIME_MS",
|
||||
{ tabId }
|
||||
);
|
||||
assert.calledTwice(finishTelemetryStopwatch);
|
||||
assert.calledWithExactly(
|
||||
finishTelemetryStopwatch,
|
||||
"MS_MESSAGE_REQUEST_TIME_MS",
|
||||
{ tabId }
|
||||
);
|
||||
});
|
||||
it("should return the preview message if that's available and remove it from Router.state", async () => {
|
||||
const expectedObj = {
|
||||
id: "foo",
|
||||
groups: ["preview"],
|
||||
provider: "preview",
|
||||
};
|
||||
await Router.setState({
|
||||
messages: [expectedObj],
|
||||
providers: [{ id: "preview" }],
|
||||
});
|
||||
|
||||
ASRouterTargeting.findMatchingMessage.callsFake(
|
||||
({ messages }) => expectedObj
|
||||
);
|
||||
|
||||
Router.loadMessagesFromAllProviders.resetHistory();
|
||||
Router.loadMessagesFromAllProviders.onFirstCall().resolves();
|
||||
|
||||
let response = await Router.sendNewTabMessage({
|
||||
endpoint: { url: "foo.com" },
|
||||
tabId: 0,
|
||||
browser: {},
|
||||
});
|
||||
|
||||
assert.deepEqual(response.message, expectedObj);
|
||||
|
||||
assert.isUndefined(
|
||||
Router.state.messages.find(m => m.provider === "preview")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setMessageById", async () => {
|
||||
it("should send an empty message if provided id did not resolve to a message", async () => {
|
||||
let response = await Router.setMessageById({ id: -1 }, true, {});
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ describe("ASRouterNewTabHook", () => {
|
|||
describe("connect", () => {
|
||||
it("before connection messageHandler methods are not called", async () => {
|
||||
routerParams.clearChildMessages([1]);
|
||||
routerParams.clearChildProviders(["snippets"]);
|
||||
routerParams.clearChildProviders(["test_provider"]);
|
||||
routerParams.updateAdminState({ messages: {} });
|
||||
assert.notCalled(messageHandler.clearChildMessages);
|
||||
assert.notCalled(messageHandler.clearChildProviders);
|
||||
|
|
@ -90,7 +90,7 @@ describe("ASRouterNewTabHook", () => {
|
|||
it("after connect updateAdminState and clearChildMessages calls are forwarded to handler", async () => {
|
||||
instance.connect(messageHandler);
|
||||
routerParams.clearChildMessages([1]);
|
||||
routerParams.clearChildProviders(["snippets"]);
|
||||
routerParams.clearChildProviders(["test_provider"]);
|
||||
routerParams.updateAdminState({ messages: {} });
|
||||
assert.called(messageHandler.clearChildMessages);
|
||||
assert.called(messageHandler.clearChildProviders);
|
||||
|
|
@ -98,11 +98,11 @@ describe("ASRouterNewTabHook", () => {
|
|||
});
|
||||
it("calls from before connection are dropped", async () => {
|
||||
routerParams.clearChildMessages([1]);
|
||||
routerParams.clearChildProviders(["snippets"]);
|
||||
routerParams.clearChildProviders(["test_provider"]);
|
||||
routerParams.updateAdminState({ messages: {} });
|
||||
instance.connect(messageHandler);
|
||||
routerParams.clearChildMessages([1]);
|
||||
routerParams.clearChildProviders(["snippets"]);
|
||||
routerParams.clearChildProviders(["test_provider"]);
|
||||
routerParams.updateAdminState({ messages: {} });
|
||||
assert.calledOnce(messageHandler.clearChildMessages);
|
||||
assert.calledOnce(messageHandler.clearChildProviders);
|
||||
|
|
@ -114,7 +114,7 @@ describe("ASRouterNewTabHook", () => {
|
|||
instance.connect(messageHandler);
|
||||
instance.disconnect();
|
||||
routerParams.clearChildMessages([1]);
|
||||
routerParams.clearChildProviders(["snippets"]);
|
||||
routerParams.clearChildProviders(["test_provider"]);
|
||||
routerParams.updateAdminState({ messages: {} });
|
||||
assert.notCalled(messageHandler.clearChildMessages);
|
||||
assert.notCalled(messageHandler.clearChildProviders);
|
||||
|
|
|
|||
|
|
@ -97,7 +97,6 @@ describe("ASRouterParent", () => {
|
|||
ASRouterParent.tabs.messageAll,
|
||||
"ClearMessages",
|
||||
// When blocking an id the entire campaign is blocked
|
||||
// and all other snippets become invalid
|
||||
["message-campaign"]
|
||||
);
|
||||
assert.equal(result, "handle-message-result");
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ const PROVIDER_PREF_BRANCH =
|
|||
"browser.newtabpage.activity-stream.asrouter.providers.";
|
||||
const DEVTOOLS_PREF =
|
||||
"browser.newtabpage.activity-stream.asrouter.devtoolsEnabled";
|
||||
const SNIPPETS_USER_PREF = "browser.newtabpage.activity-stream.feeds.snippets";
|
||||
const CFR_USER_PREF_ADDONS =
|
||||
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons";
|
||||
const CFR_USER_PREF_FEATURES =
|
||||
|
|
@ -18,12 +17,11 @@ const CFR_USER_PREF_FEATURES =
|
|||
/** NUMBER_OF_PREFS_TO_OBSERVE includes:
|
||||
* 1. asrouter.providers. pref branch
|
||||
* 2. asrouter.devtoolsEnabled
|
||||
* 3. browser.newtabpage.activity-stream.feeds.snippets (user preference - snippets)
|
||||
* 4. browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons (user preference - cfr)
|
||||
* 3. browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons (user preference - cfr)
|
||||
* 4. browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features (user preference - cfr)
|
||||
* 5. services.sync.username
|
||||
*/
|
||||
const NUMBER_OF_PREFS_TO_OBSERVE = 6;
|
||||
const NUMBER_OF_PREFS_TO_OBSERVE = 5;
|
||||
|
||||
describe("ASRouterPreferences", () => {
|
||||
let ASRouterPreferences;
|
||||
|
|
@ -324,20 +322,12 @@ describe("ASRouterPreferences", () => {
|
|||
assert.calledTwice(boolPrefStub);
|
||||
});
|
||||
});
|
||||
describe("#getUserPreference(providerId)", () => {
|
||||
it("should return the user preference for snippets", () => {
|
||||
boolPrefStub.withArgs(SNIPPETS_USER_PREF).returns(true);
|
||||
assert.isTrue(ASRouterPreferences.getUserPreference("snippets"));
|
||||
});
|
||||
});
|
||||
describe("#getAllUserPreferences", () => {
|
||||
it("should return all user preferences", () => {
|
||||
boolPrefStub.withArgs(SNIPPETS_USER_PREF).returns(true);
|
||||
boolPrefStub.withArgs(CFR_USER_PREF_ADDONS).returns(false);
|
||||
boolPrefStub.withArgs(CFR_USER_PREF_FEATURES).returns(true);
|
||||
const result = ASRouterPreferences.getAllUserPreferences();
|
||||
assert.deepEqual(result, {
|
||||
snippets: true,
|
||||
cfrAddons: false,
|
||||
cfrFeatures: true,
|
||||
});
|
||||
|
|
@ -389,8 +379,8 @@ describe("ASRouterPreferences", () => {
|
|||
});
|
||||
it("should set the given pref", () => {
|
||||
const setStub = sandbox.stub(global.Services.prefs, "setBoolPref");
|
||||
ASRouterPreferences.setUserPreference("snippets", true);
|
||||
assert.calledWith(setStub, SNIPPETS_USER_PREF, true);
|
||||
ASRouterPreferences.setUserPreference("cfrAddons", true);
|
||||
assert.calledWith(setStub, CFR_USER_PREF_ADDONS, true);
|
||||
});
|
||||
});
|
||||
describe("#resetProviderPref", () => {
|
||||
|
|
@ -399,7 +389,6 @@ describe("ASRouterPreferences", () => {
|
|||
FAKE_PROVIDERS.forEach(provider => {
|
||||
assert.calledWith(resetStub, getPrefNameForProvider(provider.id));
|
||||
});
|
||||
assert.calledWith(resetStub, SNIPPETS_USER_PREF);
|
||||
assert.calledWith(resetStub, CFR_USER_PREF_ADDONS);
|
||||
assert.calledWith(resetStub, CFR_USER_PREF_FEATURES);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
import EOYSnippetSchema from "../../../content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json";
|
||||
import SimpleBelowSearchSnippetSchema from "../../../content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.schema.json";
|
||||
import SimpleSnippetSchema from "../../../content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.schema.json";
|
||||
import { SnippetsTestMessageProvider } from "../../../lib/SnippetsTestMessageProvider.sys.mjs";
|
||||
import SubmitFormSnippetSchema from "../../../content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json";
|
||||
import SubmitFormScene2SnippetSchema from "../../../content-src/asrouter/templates/SubmitFormSnippet/SubmitFormScene2Snippet.schema.json";
|
||||
|
||||
const schemas = {
|
||||
simple_snippet: SimpleSnippetSchema,
|
||||
newsletter_snippet: SubmitFormSnippetSchema,
|
||||
fxa_signup_snippet: SubmitFormSnippetSchema,
|
||||
send_to_device_snippet: SubmitFormSnippetSchema,
|
||||
send_to_device_scene2_snippet: SubmitFormScene2SnippetSchema,
|
||||
eoy_snippet: EOYSnippetSchema,
|
||||
simple_below_search_snippet: SimpleBelowSearchSnippetSchema,
|
||||
};
|
||||
|
||||
describe("SnippetsTestMessageProvider", async () => {
|
||||
let messages = await SnippetsTestMessageProvider.getMessages();
|
||||
|
||||
it("should return an array of messages", () => {
|
||||
assert.isArray(messages);
|
||||
});
|
||||
|
||||
it("should have a valid example of each schema", () => {
|
||||
Object.keys(schemas).forEach(templateName => {
|
||||
const example = messages.find(
|
||||
message => message.template === templateName
|
||||
);
|
||||
assert.ok(example, `has a ${templateName} example`);
|
||||
});
|
||||
});
|
||||
|
||||
it("should have examples that are valid", () => {
|
||||
messages.forEach(example => {
|
||||
assert.jsonSchema(
|
||||
example.content,
|
||||
schemas[example.template],
|
||||
`${example.id} should be valid`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,516 +0,0 @@
|
|||
import { ASRouterUISurface } from "content-src/asrouter/asrouter-content";
|
||||
import { ASRouterUtils } from "content-src/asrouter/asrouter-utils";
|
||||
import { GlobalOverrider } from "test/unit/utils";
|
||||
import { FAKE_LOCAL_MESSAGES } from "./constants";
|
||||
import React from "react";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
let [FAKE_MESSAGE] = FAKE_LOCAL_MESSAGES;
|
||||
const FAKE_NEWSLETTER_SNIPPET = FAKE_LOCAL_MESSAGES.find(
|
||||
msg => msg.id === "newsletter"
|
||||
);
|
||||
const FAKE_FXA_SNIPPET = FAKE_LOCAL_MESSAGES.find(msg => msg.id === "fxa");
|
||||
const FAKE_BELOW_SEARCH_SNIPPET = FAKE_LOCAL_MESSAGES.find(
|
||||
msg => msg.id === "belowsearch"
|
||||
);
|
||||
|
||||
FAKE_MESSAGE = Object.assign({}, FAKE_MESSAGE, { provider: "fakeprovider" });
|
||||
|
||||
describe("ASRouterUtils", () => {
|
||||
let globalOverrider;
|
||||
let sandbox;
|
||||
let globals;
|
||||
beforeEach(() => {
|
||||
globalOverrider = new GlobalOverrider();
|
||||
sandbox = sinon.createSandbox();
|
||||
globals = {
|
||||
ASRouterMessage: sandbox.stub(),
|
||||
};
|
||||
globalOverrider.set(globals);
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
globalOverrider.restore();
|
||||
});
|
||||
it("should send a message with the right payload data", () => {
|
||||
ASRouterUtils.sendTelemetry({ id: 1, event: "CLICK" });
|
||||
|
||||
assert.calledOnce(globals.ASRouterMessage);
|
||||
assert.calledWith(globals.ASRouterMessage, {
|
||||
type: "AS_ROUTER_TELEMETRY_USER_EVENT",
|
||||
meta: { from: "ActivityStream:Content", to: "ActivityStream:Main" },
|
||||
data: {
|
||||
id: 1,
|
||||
event: "CLICK",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("ASRouterUISurface", () => {
|
||||
let wrapper;
|
||||
let globalOverrider;
|
||||
let sandbox;
|
||||
let headerPortal;
|
||||
let footerPortal;
|
||||
let root;
|
||||
let fakeDocument;
|
||||
let globals;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
headerPortal = document.createElement("div");
|
||||
footerPortal = document.createElement("div");
|
||||
root = document.createElement("div");
|
||||
sandbox.stub(footerPortal, "querySelector").returns(footerPortal);
|
||||
fakeDocument = {
|
||||
location: { href: "" },
|
||||
_listeners: new Set(),
|
||||
_visibilityState: "hidden",
|
||||
head: {
|
||||
appendChild(el) {
|
||||
return el;
|
||||
},
|
||||
},
|
||||
get visibilityState() {
|
||||
return this._visibilityState;
|
||||
},
|
||||
set visibilityState(value) {
|
||||
if (this._visibilityState === value) {
|
||||
return;
|
||||
}
|
||||
this._visibilityState = value;
|
||||
this._listeners.forEach(l => l());
|
||||
},
|
||||
addEventListener(event, listener) {
|
||||
this._listeners.add(listener);
|
||||
},
|
||||
removeEventListener(event, listener) {
|
||||
this._listeners.delete(listener);
|
||||
},
|
||||
get body() {
|
||||
return document.createElement("body");
|
||||
},
|
||||
getElementById(id) {
|
||||
switch (id) {
|
||||
case "header-asrouter-container":
|
||||
return headerPortal;
|
||||
case "root":
|
||||
return root;
|
||||
default:
|
||||
return footerPortal;
|
||||
}
|
||||
},
|
||||
createElement(tag) {
|
||||
return document.createElement(tag);
|
||||
},
|
||||
};
|
||||
globals = {
|
||||
ASRouterMessage: sandbox.stub().resolves(),
|
||||
ASRouterAddParentListener: sandbox.stub(),
|
||||
ASRouterRemoveParentListener: sandbox.stub(),
|
||||
fetch: sandbox.stub().resolves({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: () => Promise.resolve({}),
|
||||
}),
|
||||
};
|
||||
globalOverrider = new GlobalOverrider();
|
||||
globalOverrider.set(globals);
|
||||
sandbox.stub(ASRouterUtils, "sendTelemetry");
|
||||
|
||||
wrapper = mount(<ASRouterUISurface document={fakeDocument} />);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
globalOverrider.restore();
|
||||
});
|
||||
|
||||
it("should render the component if a message id is defined", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.isTrue(wrapper.exists());
|
||||
});
|
||||
|
||||
it("should pass in the correct form_method for newsletter snippets", () => {
|
||||
wrapper.setState({ message: FAKE_NEWSLETTER_SNIPPET });
|
||||
|
||||
assert.isTrue(wrapper.find("SubmitFormSnippet").exists());
|
||||
assert.propertyVal(
|
||||
wrapper.find("SubmitFormSnippet").props(),
|
||||
"form_method",
|
||||
"POST"
|
||||
);
|
||||
});
|
||||
|
||||
it("should pass in the correct form_method for fxa snippets", () => {
|
||||
wrapper.setState({ message: FAKE_FXA_SNIPPET });
|
||||
|
||||
assert.isTrue(wrapper.find("SubmitFormSnippet").exists());
|
||||
assert.propertyVal(
|
||||
wrapper.find("SubmitFormSnippet").props(),
|
||||
"form_method",
|
||||
"GET"
|
||||
);
|
||||
});
|
||||
|
||||
it("should render a preview banner if message provider is preview", () => {
|
||||
wrapper.setState({ message: { ...FAKE_MESSAGE, provider: "preview" } });
|
||||
assert.isTrue(wrapper.find(".snippets-preview-banner").exists());
|
||||
});
|
||||
|
||||
it("should not render a preview banner if message provider is not preview", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.isFalse(wrapper.find(".snippets-preview-banner").exists());
|
||||
});
|
||||
|
||||
it("should render a SimpleSnippet in the footer portal", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.isTrue(footerPortal.childElementCount > 0);
|
||||
assert.equal(headerPortal.childElementCount, 0);
|
||||
});
|
||||
|
||||
it("should not render a SimpleBelowSearchSnippet in a portal", () => {
|
||||
wrapper.setState({ message: FAKE_BELOW_SEARCH_SNIPPET });
|
||||
assert.equal(headerPortal.childElementCount, 0);
|
||||
assert.equal(footerPortal.childElementCount, 0);
|
||||
});
|
||||
|
||||
it("should dispatch an event to select the correct theme", () => {
|
||||
const stub = sandbox.stub(window, "dispatchEvent");
|
||||
sandbox
|
||||
.stub(ASRouterUtils, "getPreviewEndpoint")
|
||||
.returns({ theme: "dark" });
|
||||
|
||||
wrapper = mount(<ASRouterUISurface document={fakeDocument} />);
|
||||
|
||||
assert.calledOnce(stub);
|
||||
assert.property(stub.firstCall.args[0].detail.data, "ntp_background");
|
||||
assert.property(stub.firstCall.args[0].detail.data, "ntp_text");
|
||||
assert.property(stub.firstCall.args[0].detail.data, "sidebar");
|
||||
assert.property(stub.firstCall.args[0].detail.data, "sidebar_text");
|
||||
});
|
||||
|
||||
it("should set `dir=rtl` on the page's <html> element if the dir param is set", () => {
|
||||
assert.notPropertyVal(fakeDocument, "dir", "rtl");
|
||||
sandbox.stub(ASRouterUtils, "getPreviewEndpoint").returns({ dir: "rtl" });
|
||||
|
||||
wrapper = mount(<ASRouterUISurface document={fakeDocument} />);
|
||||
assert.propertyVal(fakeDocument, "dir", "rtl");
|
||||
});
|
||||
|
||||
describe("snippets", () => {
|
||||
it("should send correct event and source when snippet is blocked", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
|
||||
wrapper.find(".blockButton").simulate("click");
|
||||
assert.propertyVal(
|
||||
ASRouterUtils.sendTelemetry.firstCall.args[0],
|
||||
"event",
|
||||
"BLOCK"
|
||||
);
|
||||
assert.propertyVal(
|
||||
ASRouterUtils.sendTelemetry.firstCall.args[0],
|
||||
"source",
|
||||
"NEWTAB_FOOTER_BAR"
|
||||
);
|
||||
});
|
||||
|
||||
it("should not send telemetry when a preview snippet is blocked", () => {
|
||||
wrapper.setState({ message: { ...FAKE_MESSAGE, provider: "preview" } });
|
||||
|
||||
wrapper.find(".blockButton").simulate("click");
|
||||
assert.notCalled(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
});
|
||||
|
||||
describe("impressions", () => {
|
||||
function simulateVisibilityChange(value) {
|
||||
fakeDocument.visibilityState = value;
|
||||
}
|
||||
|
||||
it("should call blockById after CTA link is clicked", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
sandbox.stub(ASRouterUtils, "blockById").resolves();
|
||||
wrapper.instance().sendClick({ target: { dataset: { metric: "" } } });
|
||||
|
||||
assert.calledOnce(ASRouterUtils.blockById);
|
||||
assert.calledWith(ASRouterUtils.blockById, FAKE_MESSAGE.id);
|
||||
});
|
||||
|
||||
it("should executeAction if defined on the anchor", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
sandbox.spy(ASRouterUtils, "executeAction");
|
||||
wrapper.instance().sendClick({
|
||||
target: { dataset: { action: "OPEN_MENU", args: "appMenu" } },
|
||||
});
|
||||
|
||||
assert.calledOnce(ASRouterUtils.executeAction);
|
||||
assert.calledWithExactly(ASRouterUtils.executeAction, {
|
||||
type: "OPEN_MENU",
|
||||
data: { args: "appMenu" },
|
||||
});
|
||||
});
|
||||
|
||||
it("should not call blockById if do_not_autoblock is true", () => {
|
||||
wrapper.setState({
|
||||
message: {
|
||||
...FAKE_MESSAGE,
|
||||
...{ content: { ...FAKE_MESSAGE.content, do_not_autoblock: true } },
|
||||
},
|
||||
});
|
||||
sandbox.stub(ASRouterUtils, "blockById");
|
||||
wrapper.instance().sendClick({ target: { dataset: { metric: "" } } });
|
||||
|
||||
assert.notCalled(ASRouterUtils.blockById);
|
||||
});
|
||||
|
||||
it("should not send an impression if no message exists", () => {
|
||||
simulateVisibilityChange("visible");
|
||||
|
||||
assert.notCalled(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should not send an impression if the page is not visible", () => {
|
||||
simulateVisibilityChange("hidden");
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
|
||||
assert.notCalled(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should not send an impression for a preview message", () => {
|
||||
wrapper.setState({ message: { ...FAKE_MESSAGE, provider: "preview" } });
|
||||
assert.notCalled(ASRouterUtils.sendTelemetry);
|
||||
|
||||
simulateVisibilityChange("visible");
|
||||
assert.notCalled(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should send an impression ping when there is a message and the page becomes visible", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.notCalled(ASRouterUtils.sendTelemetry);
|
||||
|
||||
simulateVisibilityChange("visible");
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should send the correct impression source", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
simulateVisibilityChange("visible");
|
||||
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
assert.propertyVal(
|
||||
ASRouterUtils.sendTelemetry.firstCall.args[0],
|
||||
"event",
|
||||
"IMPRESSION"
|
||||
);
|
||||
assert.propertyVal(
|
||||
ASRouterUtils.sendTelemetry.firstCall.args[0],
|
||||
"source",
|
||||
"NEWTAB_FOOTER_BAR"
|
||||
);
|
||||
});
|
||||
|
||||
it("should send an impression ping when the page is visible and a message gets loaded", () => {
|
||||
simulateVisibilityChange("visible");
|
||||
wrapper.setState({ message: {} });
|
||||
assert.notCalled(ASRouterUtils.sendTelemetry);
|
||||
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should send another impression ping if the message id changes", () => {
|
||||
simulateVisibilityChange("visible");
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
|
||||
wrapper.setState({ message: FAKE_LOCAL_MESSAGES[1] });
|
||||
assert.calledTwice(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should not send another impression ping if the message id has not changed", () => {
|
||||
simulateVisibilityChange("visible");
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
|
||||
wrapper.setState({ somethingElse: 123 });
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should not send another impression ping if the message is cleared", () => {
|
||||
simulateVisibilityChange("visible");
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
|
||||
wrapper.setState({ message: {} });
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
});
|
||||
|
||||
it("should call .sendTelemetry with the right message data", () => {
|
||||
simulateVisibilityChange("visible");
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
|
||||
assert.calledOnce(ASRouterUtils.sendTelemetry);
|
||||
const [payload] = ASRouterUtils.sendTelemetry.firstCall.args;
|
||||
|
||||
assert.propertyVal(payload, "message_id", FAKE_MESSAGE.id);
|
||||
assert.propertyVal(payload, "event", "IMPRESSION");
|
||||
assert.propertyVal(
|
||||
payload,
|
||||
"action",
|
||||
`${FAKE_MESSAGE.provider}_user_event`
|
||||
);
|
||||
assert.propertyVal(payload, "source", "NEWTAB_FOOTER_BAR");
|
||||
});
|
||||
|
||||
it("should construct a OPEN_ABOUT_PAGE action with attribution", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
const stub = sandbox.stub(ASRouterUtils, "executeAction");
|
||||
|
||||
wrapper.instance().sendClick({
|
||||
target: {
|
||||
dataset: {
|
||||
metric: "",
|
||||
entrypoint_value: "snippet",
|
||||
action: "OPEN_PREFERENCES_PAGE",
|
||||
args: "home",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.calledOnce(stub);
|
||||
assert.calledWithExactly(stub, {
|
||||
type: "OPEN_PREFERENCES_PAGE",
|
||||
data: { args: "home", entrypoint: "snippet" },
|
||||
});
|
||||
});
|
||||
|
||||
it("should construct a OPEN_ABOUT_PAGE action with attribution", () => {
|
||||
wrapper.setState({ message: FAKE_MESSAGE });
|
||||
const stub = sandbox.stub(ASRouterUtils, "executeAction");
|
||||
|
||||
wrapper.instance().sendClick({
|
||||
target: {
|
||||
dataset: {
|
||||
metric: "",
|
||||
entrypoint_name: "entryPoint",
|
||||
entrypoint_value: "snippet",
|
||||
action: "OPEN_ABOUT_PAGE",
|
||||
args: "logins",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.calledOnce(stub);
|
||||
assert.calledWithExactly(stub, {
|
||||
type: "OPEN_ABOUT_PAGE",
|
||||
data: { args: "logins", entrypoint: "entryPoint=snippet" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(".fetchFlowParams", () => {
|
||||
let dispatchStub;
|
||||
const assertCalledWithURL = url =>
|
||||
assert.calledWith(globals.fetch, new URL(url).toString(), {
|
||||
credentials: "omit",
|
||||
});
|
||||
beforeEach(() => {
|
||||
dispatchStub = sandbox.stub();
|
||||
wrapper = mount(
|
||||
<ASRouterUISurface
|
||||
dispatch={dispatchStub}
|
||||
fxaEndpoint="https://accounts.firefox.com"
|
||||
/>
|
||||
);
|
||||
});
|
||||
it("should use the base url returned from the endpoint pref", async () => {
|
||||
wrapper = mount(
|
||||
<ASRouterUISurface
|
||||
dispatch={dispatchStub}
|
||||
fxaEndpoint="https://foo.com"
|
||||
/>
|
||||
);
|
||||
await wrapper.instance().fetchFlowParams();
|
||||
|
||||
assertCalledWithURL("https://foo.com/metrics-flow");
|
||||
});
|
||||
it("should add given search params to the URL", async () => {
|
||||
const params = { foo: "1", bar: "2" };
|
||||
|
||||
await wrapper.instance().fetchFlowParams(params);
|
||||
|
||||
assertCalledWithURL(
|
||||
"https://accounts.firefox.com/metrics-flow?foo=1&bar=2"
|
||||
);
|
||||
});
|
||||
it("should return flowId, flowBeginTime, deviceId on a 200 response", async () => {
|
||||
const flowInfo = { flowId: "foo", flowBeginTime: 123, deviceId: "bar" };
|
||||
globals.fetch
|
||||
.withArgs("https://accounts.firefox.com/metrics-flow")
|
||||
.resolves({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: () => Promise.resolve(flowInfo),
|
||||
});
|
||||
|
||||
const result = await wrapper.instance().fetchFlowParams();
|
||||
assert.deepEqual(result, flowInfo);
|
||||
});
|
||||
|
||||
describe(".onUserAction", () => {
|
||||
it("if the action.type is ENABLE_FIREFOX_MONITOR, it should generate the right monitor URL given some flowParams", async () => {
|
||||
const flowInfo = { flowId: "foo", flowBeginTime: 123, deviceId: "bar" };
|
||||
globals.fetch
|
||||
.withArgs(
|
||||
"https://accounts.firefox.com/metrics-flow?utm_term=avocado"
|
||||
)
|
||||
.resolves({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: () => Promise.resolve(flowInfo),
|
||||
});
|
||||
|
||||
sandbox.spy(ASRouterUtils, "executeAction");
|
||||
|
||||
const msg = {
|
||||
type: "ENABLE_FIREFOX_MONITOR",
|
||||
data: {
|
||||
args: {
|
||||
url: "https://monitor.firefox.com?foo=bar",
|
||||
flowRequestParams: {
|
||||
utm_term: "avocado",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await wrapper.instance().onUserAction(msg);
|
||||
|
||||
assertCalledWithURL(
|
||||
"https://accounts.firefox.com/metrics-flow?utm_term=avocado"
|
||||
);
|
||||
assert.calledWith(ASRouterUtils.executeAction, {
|
||||
type: "OPEN_URL",
|
||||
data: {
|
||||
args: new URL(
|
||||
"https://monitor.firefox.com?foo=bar&deviceId=bar&flowId=foo&flowBeginTime=123"
|
||||
).toString(),
|
||||
},
|
||||
});
|
||||
});
|
||||
it("if the action.type is not ENABLE_FIREFOX_MONITOR, it should just call ASRouterUtils.executeAction", async () => {
|
||||
const msg = {
|
||||
type: "FOO",
|
||||
data: {
|
||||
args: "bar",
|
||||
},
|
||||
};
|
||||
sandbox.spy(ASRouterUtils, "executeAction");
|
||||
await wrapper.instance().onUserAction(msg);
|
||||
assert.calledWith(ASRouterUtils.executeAction, msg);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import EOYSnippetSchema from "content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json";
|
||||
import { expectedValues } from "./snippets-fx57";
|
||||
import SimpleSnippetSchema from "content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.schema.json";
|
||||
import SubmitFormSchema from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json";
|
||||
|
||||
export const SnippetsSchemas = {
|
||||
eoy_snippet: EOYSnippetSchema,
|
||||
simple_snippet: SimpleSnippetSchema,
|
||||
newsletter_snippet: SubmitFormSchema,
|
||||
fxa_signup_snippet: SubmitFormSchema,
|
||||
send_to_device_snippet: SubmitFormSchema,
|
||||
};
|
||||
|
||||
describe("Firefox 57 compatibility test", () => {
|
||||
Object.keys(expectedValues).forEach(template => {
|
||||
describe(template, () => {
|
||||
const schema = SnippetsSchemas[template];
|
||||
it(`should have a schema for ${template}`, () => {
|
||||
assert.ok(schema);
|
||||
});
|
||||
it(`should validate with the schema for ${template}`, () => {
|
||||
assert.jsonSchema(expectedValues[template], schema);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/**
|
||||
* IMPORTANT NOTE!!!
|
||||
*
|
||||
* Please DO NOT introduce breaking changes file without contacting snippets endpoint engineers
|
||||
* and changing the schema version to reflect a breaking change.
|
||||
*
|
||||
*/
|
||||
|
||||
const DATA_URI_IMAGE =
|
||||
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||
|
||||
export const expectedValues = {
|
||||
// Simple Snippet (https://github.com/mozmeao/snippets/blob/master/activity-stream/simple-snippet.html)
|
||||
simple_snippet: {
|
||||
icon: DATA_URI_IMAGE,
|
||||
button_label: "Click me",
|
||||
button_url: "https://mozilla.org",
|
||||
button_background_color: "#FF0000",
|
||||
button_color: "#FFFFFF",
|
||||
text: "Hello world",
|
||||
title: "Hi!",
|
||||
title_icon: DATA_URI_IMAGE,
|
||||
tall: true,
|
||||
},
|
||||
|
||||
// FXA Snippet (https://github.com/mozmeao/snippets/blob/master/activity-stream/fxa.html)
|
||||
fxa_signup_snippet: {
|
||||
scene1_icon: DATA_URI_IMAGE,
|
||||
scene1_button_label: "Click me",
|
||||
scene1_button_background_color: "#FF0000",
|
||||
scene1_button_color: "#FFFFFF",
|
||||
scene1_text: "Hello <em>world</em>",
|
||||
scene1_title: "Hi!",
|
||||
scene1_title_icon: DATA_URI_IMAGE,
|
||||
|
||||
scene2_text: "Second scene",
|
||||
scene2_title: "Second scene title",
|
||||
scene2_email_placeholder_text: "Email here",
|
||||
scene2_button_label: "Sign Me Up",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
|
||||
utm_campaign: "snippets123",
|
||||
utm_term: "123term",
|
||||
},
|
||||
|
||||
// Send To Device Snippet (https://github.com/mozmeao/snippets/blob/master/activity-stream/send-to-device.html)
|
||||
send_to_device_snippet: {
|
||||
include_sms: true,
|
||||
locale: "de",
|
||||
country: "DE",
|
||||
message_id_sms: "foo",
|
||||
message_id_email: "foo",
|
||||
scene1_button_background_color: "#FF0000",
|
||||
scene1_button_color: "#FFFFFF",
|
||||
scene1_button_label: "Click me",
|
||||
scene1_icon: DATA_URI_IMAGE,
|
||||
scene1_text: "Hello world",
|
||||
scene1_title: "Hi!",
|
||||
scene1_title_icon: DATA_URI_IMAGE,
|
||||
|
||||
scene2_button_label: "Sign Me Up",
|
||||
scene2_disclaimer_html: "Hello <em>world</em>",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
scene2_icon: DATA_URI_IMAGE,
|
||||
scene2_input_placeholder: "Email here",
|
||||
|
||||
scene2_text: "Second scene",
|
||||
scene2_title: "Second scene title",
|
||||
|
||||
error_text: "error",
|
||||
success_text: "all good",
|
||||
success_title: "Ok!",
|
||||
},
|
||||
|
||||
// Newsletter Snippet (https://github.com/mozmeao/snippets/blob/master/activity-stream/newsletter-subscribe.html)
|
||||
newsletter_snippet: {
|
||||
scene1_icon: DATA_URI_IMAGE,
|
||||
scene1_button_label: "Click me",
|
||||
scene1_button_background_color: "#FF0000",
|
||||
scene1_button_color: "#FFFFFF",
|
||||
scene1_text: "Hello world",
|
||||
scene1_title: "Hi!",
|
||||
scene1_title_icon: DATA_URI_IMAGE,
|
||||
|
||||
scene2_text: "Second scene",
|
||||
scene2_title: "Second scene title",
|
||||
scene2_newsletter: "foo",
|
||||
scene2_email_placeholder_text: "Email here",
|
||||
scene2_button_label: "Sign Me Up",
|
||||
scene2_privacy_html: "Hello <em>world</em>",
|
||||
scene2_dismiss_button_text: "Dismiss",
|
||||
|
||||
locale: "de",
|
||||
|
||||
error_text: "error",
|
||||
success_text: "all good",
|
||||
},
|
||||
|
||||
// EOY Snippet (https://github.com/mozmeao/snippets/blob/master/activity-stream/mofo-eoy-2017.html)
|
||||
eoy_snippet: {
|
||||
block_button_text: "Block",
|
||||
|
||||
donation_form_url: "https://donate.mozilla.org/",
|
||||
text: "Big corporations want to restrict how we access the web. Fake news is making it harder for us to find the truth. Online bullies are silencing inspired voices. The not-for-profit Mozilla Foundation fights for a healthy internet with programs like our Tech Policy Fellowships and Internet Health Report; will you donate today?",
|
||||
icon: DATA_URI_IMAGE,
|
||||
button_label: "Donate",
|
||||
monthly_checkbox_label_text: "Make my donation monthly",
|
||||
button_background_color: "#0060DF",
|
||||
button_color: "#FFFFFF",
|
||||
background_color: "#FFFFFF",
|
||||
text_color: "#000000",
|
||||
highlight_color: "#FFE900",
|
||||
|
||||
locale: "en-US",
|
||||
currency_code: "usd",
|
||||
|
||||
donation_amount_first: 50,
|
||||
donation_amount_second: 25,
|
||||
donation_amount_third: 10,
|
||||
donation_amount_fourth: 3,
|
||||
selected_button: "donation_amount_second",
|
||||
|
||||
test: "bold",
|
||||
},
|
||||
};
|
||||
|
|
@ -4,22 +4,19 @@ export const PARENT_TO_CHILD_MESSAGE_NAME = "ASRouter:parent-to-child";
|
|||
export const FAKE_LOCAL_MESSAGES = [
|
||||
{
|
||||
id: "foo",
|
||||
provider: "snippets",
|
||||
template: "simple_snippet",
|
||||
template: "milestone_message",
|
||||
content: { title: "Foo", body: "Foo123" },
|
||||
},
|
||||
{
|
||||
id: "foo1",
|
||||
template: "simple_snippet",
|
||||
provider: "snippets",
|
||||
template: "fancy_template",
|
||||
bundled: 2,
|
||||
order: 1,
|
||||
content: { title: "Foo1", body: "Foo123-1" },
|
||||
},
|
||||
{
|
||||
id: "foo2",
|
||||
template: "simple_snippet",
|
||||
provider: "snippets",
|
||||
template: "fancy_template",
|
||||
bundled: 2,
|
||||
order: 2,
|
||||
content: { title: "Foo2", body: "Foo123-2" },
|
||||
|
|
@ -32,20 +29,17 @@ export const FAKE_LOCAL_MESSAGES = [
|
|||
{ id: "baz", content: { title: "Foo", body: "Foo123" } },
|
||||
{
|
||||
id: "newsletter",
|
||||
provider: "snippets",
|
||||
template: "newsletter_snippet",
|
||||
template: "fancy_template",
|
||||
content: { title: "Foo", body: "Foo123" },
|
||||
},
|
||||
{
|
||||
id: "fxa",
|
||||
provider: "snippets",
|
||||
template: "fxa_signup_snippet",
|
||||
template: "fancy_template",
|
||||
content: { title: "Foo", body: "Foo123" },
|
||||
},
|
||||
{
|
||||
id: "belowsearch",
|
||||
provider: "snippets",
|
||||
template: "simple_below_search_snippet",
|
||||
template: "fancy_template",
|
||||
content: { text: "Foo" },
|
||||
},
|
||||
];
|
||||
|
|
@ -65,7 +59,7 @@ export const FAKE_LOCAL_PROVIDERS = {
|
|||
export const FAKE_REMOTE_MESSAGES = [
|
||||
{
|
||||
id: "qux",
|
||||
template: "simple_snippet",
|
||||
template: "fancy_template",
|
||||
content: { title: "Qux", body: "hello world" },
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,213 +0,0 @@
|
|||
import { EOYSnippet } from "content-src/asrouter/templates/EOYSnippet/EOYSnippet";
|
||||
import { GlobalOverrider } from "test/unit/utils";
|
||||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import { FluentBundle, FluentResource } from "@fluent/bundle";
|
||||
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
|
||||
import schema from "content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json";
|
||||
|
||||
const DEFAULT_CONTENT = {
|
||||
text: "foo",
|
||||
donation_amount_first: 50,
|
||||
donation_amount_second: 25,
|
||||
donation_amount_third: 10,
|
||||
donation_amount_fourth: 5,
|
||||
donation_form_url: "https://submit.form",
|
||||
button_label: "Donate",
|
||||
};
|
||||
|
||||
describe("EOYSnippet", () => {
|
||||
let sandbox;
|
||||
let wrapper;
|
||||
|
||||
function mockL10nWrapper(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
for (const [id, value] of Object.entries(content)) {
|
||||
if (typeof value === "string") {
|
||||
bundle.addResource(new FluentResource(`${id} = ${value}`));
|
||||
}
|
||||
}
|
||||
const l10n = new ReactLocalization([bundle]);
|
||||
return {
|
||||
wrappingComponent: LocalizationProvider,
|
||||
wrappingComponentProps: { l10n },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* mountAndCheckProps - Mounts a EOYSnippet with DEFAULT_CONTENT extended with any props
|
||||
* passed in the content param and validates props against the schema.
|
||||
* @param {obj} content Object containing custom message content (e.g. {text, icon, title})
|
||||
* @returns enzyme wrapper for EOYSnippet
|
||||
*/
|
||||
function mountAndCheckProps(content = {}, provider = "test-provider") {
|
||||
const props = {
|
||||
content: Object.assign({}, DEFAULT_CONTENT, content),
|
||||
provider,
|
||||
onAction: sandbox.stub(),
|
||||
onBlock: sandbox.stub(),
|
||||
sendClick: sandbox.stub(),
|
||||
};
|
||||
const comp = mount(
|
||||
<EOYSnippet {...props} />,
|
||||
mockL10nWrapper(props.content)
|
||||
);
|
||||
// Check schema with the final props the component receives (including defaults)
|
||||
assert.jsonSchema(comp.children().get(0).props.content, schema);
|
||||
return comp;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
wrapper = mountAndCheckProps();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should have the correct defaults", () => {
|
||||
wrapper = mountAndCheckProps();
|
||||
// SendToDeviceSnippet is a wrapper around SubmitFormSnippet
|
||||
const { props } = wrapper.children().get(0);
|
||||
|
||||
const defaultProperties = Object.keys(schema.properties).filter(
|
||||
prop => schema.properties[prop].default
|
||||
);
|
||||
assert.lengthOf(defaultProperties, 4);
|
||||
defaultProperties.forEach(prop =>
|
||||
assert.propertyVal(props.content, prop, schema.properties[prop].default)
|
||||
);
|
||||
});
|
||||
|
||||
it("should render 4 donation options", () => {
|
||||
assert.lengthOf(wrapper.find("input[type='radio']"), 4);
|
||||
});
|
||||
|
||||
it("should have a data-metric field", () => {
|
||||
assert.ok(wrapper.find("form[data-metric='EOYSnippetForm']").exists());
|
||||
});
|
||||
|
||||
it("should select the second donation option", () => {
|
||||
wrapper = mountAndCheckProps({ selected_button: "donation_amount_second" });
|
||||
|
||||
assert.propertyVal(
|
||||
wrapper.find("input[type='radio']").get(1).props,
|
||||
"defaultChecked",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("should set frequency value to monthly", () => {
|
||||
const form = wrapper.find("form").instance();
|
||||
assert.equal(form.querySelector("[name='frequency']").value, "single");
|
||||
|
||||
form.querySelector("#monthly-checkbox").checked = true;
|
||||
wrapper.find("form").simulate("submit");
|
||||
|
||||
assert.equal(form.querySelector("[name='frequency']").value, "monthly");
|
||||
});
|
||||
|
||||
it("should block after submitting the form", () => {
|
||||
const onBlockStub = sandbox.stub();
|
||||
wrapper.setProps({ onBlock: onBlockStub });
|
||||
|
||||
wrapper.find("form").simulate("submit");
|
||||
|
||||
assert.calledOnce(onBlockStub);
|
||||
});
|
||||
|
||||
it("should not block if do_not_autoblock is true", () => {
|
||||
const onBlockStub = sandbox.stub();
|
||||
wrapper = mountAndCheckProps({ do_not_autoblock: true });
|
||||
wrapper.setProps({ onBlock: onBlockStub });
|
||||
|
||||
wrapper.find("form").simulate("submit");
|
||||
|
||||
assert.notCalled(onBlockStub);
|
||||
});
|
||||
|
||||
it("should report form submissions", () => {
|
||||
wrapper = mountAndCheckProps();
|
||||
const { sendClick } = wrapper.props();
|
||||
|
||||
wrapper.find("form").simulate("submit");
|
||||
|
||||
assert.calledOnce(sendClick);
|
||||
assert.equal(
|
||||
sendClick.firstCall.args[0].target.dataset.metric,
|
||||
"EOYSnippetForm"
|
||||
);
|
||||
});
|
||||
|
||||
it("it should preserve URL GET params as hidden inputs", () => {
|
||||
wrapper = mountAndCheckProps({
|
||||
donation_form_url:
|
||||
"https://donate.mozilla.org/pl/?utm_source=desktop-snippet&utm_medium=snippet&utm_campaign=donate&utm_term=7556",
|
||||
});
|
||||
|
||||
const hiddenInputs = wrapper.find("input[type='hidden']");
|
||||
|
||||
assert.propertyVal(
|
||||
hiddenInputs.find("[name='utm_source']").props(),
|
||||
"value",
|
||||
"desktop-snippet"
|
||||
);
|
||||
assert.propertyVal(
|
||||
hiddenInputs.find("[name='amp;utm_medium']").props(),
|
||||
"value",
|
||||
"snippet"
|
||||
);
|
||||
assert.propertyVal(
|
||||
hiddenInputs.find("[name='amp;utm_campaign']").props(),
|
||||
"value",
|
||||
"donate"
|
||||
);
|
||||
assert.propertyVal(
|
||||
hiddenInputs.find("[name='amp;utm_term']").props(),
|
||||
"value",
|
||||
"7556"
|
||||
);
|
||||
});
|
||||
|
||||
describe("locale", () => {
|
||||
let stub;
|
||||
let globals;
|
||||
beforeEach(() => {
|
||||
globals = new GlobalOverrider();
|
||||
stub = sandbox.stub().returns({ format: () => {} });
|
||||
|
||||
globals = new GlobalOverrider();
|
||||
globals.set({ Intl: { NumberFormat: stub } });
|
||||
});
|
||||
afterEach(() => {
|
||||
globals.restore();
|
||||
});
|
||||
|
||||
it("should use content.locale for Intl", () => {
|
||||
// triggers component rendering and calls the function we're testing
|
||||
wrapper.setProps({
|
||||
content: {
|
||||
locale: "locale-foo",
|
||||
donation_form_url: DEFAULT_CONTENT.donation_form_url,
|
||||
},
|
||||
});
|
||||
|
||||
assert.calledOnce(stub);
|
||||
assert.calledWithExactly(stub, "locale-foo", sinon.match.object);
|
||||
});
|
||||
|
||||
it("should use navigator.language as locale fallback", () => {
|
||||
// triggers component rendering and calls the function we're testing
|
||||
wrapper.setProps({
|
||||
content: {
|
||||
locale: null,
|
||||
donation_form_url: DEFAULT_CONTENT.donation_form_url,
|
||||
},
|
||||
});
|
||||
|
||||
assert.calledOnce(stub);
|
||||
assert.calledWithExactly(stub, navigator.language, sinon.match.object);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
import { FXASignupSnippet } from "content-src/asrouter/templates/FXASignupSnippet/FXASignupSnippet";
|
||||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import { FluentBundle, FluentResource } from "@fluent/bundle";
|
||||
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
|
||||
import schema from "content-src/asrouter/templates/FXASignupSnippet/FXASignupSnippet.schema.json";
|
||||
import { SnippetsTestMessageProvider } from "lib/SnippetsTestMessageProvider.sys.mjs";
|
||||
|
||||
describe("FXASignupSnippet", () => {
|
||||
let DEFAULT_CONTENT;
|
||||
let sandbox;
|
||||
|
||||
function mockL10nWrapper(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
for (const [id, value] of Object.entries(content)) {
|
||||
if (typeof value === "string") {
|
||||
bundle.addResource(new FluentResource(`${id} = ${value}`));
|
||||
}
|
||||
}
|
||||
const l10n = new ReactLocalization([bundle]);
|
||||
return {
|
||||
wrappingComponent: LocalizationProvider,
|
||||
wrappingComponentProps: { l10n },
|
||||
};
|
||||
}
|
||||
|
||||
function mountAndCheckProps(content = {}) {
|
||||
const props = {
|
||||
id: "foo123",
|
||||
content: Object.assign(
|
||||
{ utm_campaign: "foo", utm_term: "bar" },
|
||||
DEFAULT_CONTENT,
|
||||
content
|
||||
),
|
||||
onBlock() {},
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
const comp = mount(
|
||||
<FXASignupSnippet {...props} />,
|
||||
mockL10nWrapper(props.content)
|
||||
);
|
||||
// Check schema with the final props the component receives (including defaults)
|
||||
assert.jsonSchema(comp.children().get(0).props.content, schema);
|
||||
return comp;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox();
|
||||
DEFAULT_CONTENT = (await SnippetsTestMessageProvider.getMessages()).find(
|
||||
msg => msg.template === "fxa_signup_snippet"
|
||||
).content;
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should have the correct defaults", () => {
|
||||
const defaults = {
|
||||
id: "foo123",
|
||||
onBlock() {},
|
||||
content: {},
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
const wrapper = mount(
|
||||
<FXASignupSnippet {...defaults} />,
|
||||
mockL10nWrapper(DEFAULT_CONTENT)
|
||||
);
|
||||
// FXASignupSnippet is a wrapper around SubmitFormSnippet
|
||||
const { props } = wrapper.children().get(0);
|
||||
|
||||
const defaultProperties = Object.keys(schema.properties).filter(
|
||||
prop => schema.properties[prop].default
|
||||
);
|
||||
assert.lengthOf(defaultProperties, 5);
|
||||
defaultProperties.forEach(prop =>
|
||||
assert.propertyVal(props.content, prop, schema.properties[prop].default)
|
||||
);
|
||||
|
||||
const defaultHiddenProperties = Object.keys(
|
||||
schema.properties.hidden_inputs.properties
|
||||
).filter(prop => schema.properties.hidden_inputs.properties[prop].default);
|
||||
assert.lengthOf(defaultHiddenProperties, 0);
|
||||
});
|
||||
|
||||
it("should have a form_action", () => {
|
||||
const wrapper = mountAndCheckProps();
|
||||
|
||||
assert.propertyVal(
|
||||
wrapper.children().get(0).props,
|
||||
"form_action",
|
||||
"https://accounts.firefox.com/"
|
||||
);
|
||||
});
|
||||
|
||||
it("should navigate to scene2", () => {
|
||||
const wrapper = mountAndCheckProps({});
|
||||
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
|
||||
assert.lengthOf(wrapper.find(".mainInput"), 1);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
import { mount } from "enzyme";
|
||||
import { NewsletterSnippet } from "content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet";
|
||||
import React from "react";
|
||||
import { FluentBundle, FluentResource } from "@fluent/bundle";
|
||||
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
|
||||
import schema from "content-src/asrouter/templates/NewsletterSnippet/NewsletterSnippet.schema.json";
|
||||
import { SnippetsTestMessageProvider } from "lib/SnippetsTestMessageProvider.sys.mjs";
|
||||
|
||||
describe("NewsletterSnippet", () => {
|
||||
let sandbox;
|
||||
let DEFAULT_CONTENT;
|
||||
|
||||
function mockL10nWrapper(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
for (const [id, value] of Object.entries(content)) {
|
||||
if (typeof value === "string") {
|
||||
bundle.addResource(new FluentResource(`${id} = ${value}`));
|
||||
}
|
||||
}
|
||||
const l10n = new ReactLocalization([bundle]);
|
||||
return {
|
||||
wrappingComponent: LocalizationProvider,
|
||||
wrappingComponentProps: { l10n },
|
||||
};
|
||||
}
|
||||
|
||||
function mountAndCheckProps(content = {}) {
|
||||
const props = {
|
||||
id: "foo123",
|
||||
content: Object.assign({}, DEFAULT_CONTENT, content),
|
||||
onBlock() {},
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
const comp = mount(
|
||||
<NewsletterSnippet {...props} />,
|
||||
mockL10nWrapper(props.content)
|
||||
);
|
||||
// Check schema with the final props the component receives (including defaults)
|
||||
assert.jsonSchema(comp.children().get(0).props.content, schema);
|
||||
return comp;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox();
|
||||
DEFAULT_CONTENT = (await SnippetsTestMessageProvider.getMessages()).find(
|
||||
msg => msg.template === "newsletter_snippet"
|
||||
).content;
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("schema test", () => {
|
||||
it("should validate the schema and defaults", () => {
|
||||
const wrapper = mountAndCheckProps();
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
assert.equal(wrapper.find(".mainInput").instance().type, "email");
|
||||
});
|
||||
|
||||
it("should have all of the default fields", () => {
|
||||
const defaults = {
|
||||
id: "foo123",
|
||||
content: {},
|
||||
onBlock() {},
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
const wrapper = mount(
|
||||
<NewsletterSnippet {...defaults} />,
|
||||
mockL10nWrapper(DEFAULT_CONTENT)
|
||||
);
|
||||
// NewsletterSnippet is a wrapper around SubmitFormSnippet
|
||||
const { props } = wrapper.children().get(0);
|
||||
|
||||
// the `locale` properties gets used as part of hidden_fields so we
|
||||
// check for it separately
|
||||
const properties = { ...schema.properties };
|
||||
const { locale } = properties;
|
||||
delete properties.locale;
|
||||
|
||||
const defaultProperties = Object.keys(properties).filter(
|
||||
prop => properties[prop].default
|
||||
);
|
||||
assert.lengthOf(defaultProperties, 6);
|
||||
defaultProperties.forEach(prop =>
|
||||
assert.propertyVal(props.content, prop, properties[prop].default)
|
||||
);
|
||||
|
||||
const defaultHiddenProperties = Object.keys(
|
||||
schema.properties.hidden_inputs.properties
|
||||
).filter(
|
||||
prop => schema.properties.hidden_inputs.properties[prop].default
|
||||
);
|
||||
assert.lengthOf(defaultHiddenProperties, 1);
|
||||
defaultHiddenProperties.forEach(prop =>
|
||||
assert.propertyVal(
|
||||
props.content.hidden_inputs,
|
||||
prop,
|
||||
schema.properties.hidden_inputs.properties[prop].default
|
||||
)
|
||||
);
|
||||
assert.propertyVal(props.content.hidden_inputs, "lang", locale.default);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,277 +0,0 @@
|
|||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import { FluentBundle, FluentResource } from "@fluent/bundle";
|
||||
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
|
||||
import schema from "content-src/asrouter/templates/SendToDeviceSnippet/SendToDeviceSnippet.schema.json";
|
||||
import {
|
||||
SendToDeviceSnippet,
|
||||
SendToDeviceScene2Snippet,
|
||||
} from "content-src/asrouter/templates/SendToDeviceSnippet/SendToDeviceSnippet";
|
||||
import { SnippetsTestMessageProvider } from "lib/SnippetsTestMessageProvider.sys.mjs";
|
||||
|
||||
async function testBodyContains(body, key, value) {
|
||||
const regex = new RegExp(
|
||||
`Content-Disposition: form-data; name="${key}"${value}`
|
||||
);
|
||||
const match = regex.exec(body);
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates opening the second panel (form view), filling in the input, and submitting
|
||||
* @param {EnzymeWrapper} wrapper A SendToDevice wrapper
|
||||
* @param {string} value Email or phone number
|
||||
* @param {function?} setCustomValidity setCustomValidity stub
|
||||
*/
|
||||
function openFormAndSetValue(wrapper, value, setCustomValidity = () => {}) {
|
||||
// expand
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
// Fill in email
|
||||
const input = wrapper.find(".mainInput");
|
||||
input.instance().value = value;
|
||||
input.simulate("change", { target: { value, setCustomValidity } });
|
||||
wrapper.find("form").simulate("submit");
|
||||
}
|
||||
|
||||
describe("SendToDeviceSnippet", () => {
|
||||
let sandbox;
|
||||
let fetchStub;
|
||||
let jsonResponse;
|
||||
let DEFAULT_CONTENT;
|
||||
let DEFAULT_SCENE2_CONTENT;
|
||||
|
||||
function mockL10nWrapper(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
for (const [id, value] of Object.entries(content)) {
|
||||
if (typeof value === "string") {
|
||||
bundle.addResource(new FluentResource(`${id} = ${value}`));
|
||||
}
|
||||
}
|
||||
const l10n = new ReactLocalization([bundle]);
|
||||
return {
|
||||
wrappingComponent: LocalizationProvider,
|
||||
wrappingComponentProps: { l10n },
|
||||
};
|
||||
}
|
||||
|
||||
function mountAndCheckProps(content = {}) {
|
||||
const props = {
|
||||
id: "foo123",
|
||||
content: Object.assign({}, DEFAULT_CONTENT, content),
|
||||
onBlock() {},
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
const comp = mount(
|
||||
<SendToDeviceSnippet {...props} />,
|
||||
mockL10nWrapper(props.content)
|
||||
);
|
||||
// Check schema with the final props the component receives (including defaults)
|
||||
assert.jsonSchema(comp.children().get(0).props.content, schema);
|
||||
return comp;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
DEFAULT_CONTENT = (await SnippetsTestMessageProvider.getMessages()).find(
|
||||
msg => msg.template === "send_to_device_snippet"
|
||||
).content;
|
||||
DEFAULT_SCENE2_CONTENT = (
|
||||
await SnippetsTestMessageProvider.getMessages()
|
||||
).find(msg => msg.template === "send_to_device_scene2_snippet").content;
|
||||
sandbox = sinon.createSandbox();
|
||||
jsonResponse = { status: "ok" };
|
||||
fetchStub = sandbox
|
||||
.stub(global, "fetch")
|
||||
.returns(Promise.resolve({ json: () => Promise.resolve(jsonResponse) }));
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should have the correct defaults", () => {
|
||||
const defaults = {
|
||||
id: "foo123",
|
||||
onBlock() {},
|
||||
content: {},
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
form_method: "POST",
|
||||
};
|
||||
const wrapper = mount(
|
||||
<SendToDeviceSnippet {...defaults} />,
|
||||
mockL10nWrapper(DEFAULT_CONTENT)
|
||||
);
|
||||
// SendToDeviceSnippet is a wrapper around SubmitFormSnippet
|
||||
const { props } = wrapper.children().get(0);
|
||||
|
||||
const defaultProperties = Object.keys(schema.properties).filter(
|
||||
prop => schema.properties[prop].default
|
||||
);
|
||||
assert.lengthOf(defaultProperties, 7);
|
||||
defaultProperties.forEach(prop =>
|
||||
assert.propertyVal(props.content, prop, schema.properties[prop].default)
|
||||
);
|
||||
|
||||
const defaultHiddenProperties = Object.keys(
|
||||
schema.properties.hidden_inputs.properties
|
||||
).filter(prop => schema.properties.hidden_inputs.properties[prop].default);
|
||||
assert.lengthOf(defaultHiddenProperties, 0);
|
||||
});
|
||||
|
||||
describe("form input", () => {
|
||||
it("should set the input type to text if content.include_sms is true", () => {
|
||||
const wrapper = mountAndCheckProps({ include_sms: true });
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
assert.equal(wrapper.find(".mainInput").instance().type, "text");
|
||||
});
|
||||
it("should set the input type to email if content.include_sms is false", () => {
|
||||
const wrapper = mountAndCheckProps({ include_sms: false });
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
assert.equal(wrapper.find(".mainInput").instance().type, "email");
|
||||
});
|
||||
it("should validate the input with isEmailOrPhoneNumber if include_sms is true", () => {
|
||||
const wrapper = mountAndCheckProps({ include_sms: true });
|
||||
const setCustomValidity = sandbox.stub();
|
||||
openFormAndSetValue(wrapper, "foo", setCustomValidity);
|
||||
assert.calledWith(
|
||||
setCustomValidity,
|
||||
"Must be an email or a phone number."
|
||||
);
|
||||
});
|
||||
it("should not custom validate the input if include_sms is false", () => {
|
||||
const wrapper = mountAndCheckProps({ include_sms: false });
|
||||
const setCustomValidity = sandbox.stub();
|
||||
openFormAndSetValue(wrapper, "foo", setCustomValidity);
|
||||
assert.notCalled(setCustomValidity);
|
||||
});
|
||||
});
|
||||
|
||||
describe("submitting", () => {
|
||||
it("should send the right information to basket.mozilla.org/news/subscribe for an email", async () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
locale: "fr-CA",
|
||||
include_sms: true,
|
||||
message_id_email: "foo",
|
||||
});
|
||||
|
||||
openFormAndSetValue(wrapper, "foo@bar.com");
|
||||
wrapper.find("form").simulate("submit");
|
||||
|
||||
assert.calledOnce(fetchStub);
|
||||
const [request] = fetchStub.firstCall.args;
|
||||
|
||||
assert.equal(request.url, "https://basket.mozilla.org/news/subscribe/");
|
||||
const body = await request.text();
|
||||
assert.ok(testBodyContains(body, "email", "foo@bar.com"), "has email");
|
||||
assert.ok(testBodyContains(body, "lang", "fr-CA"), "has lang");
|
||||
assert.ok(
|
||||
testBodyContains(body, "newsletters", "foo"),
|
||||
"has newsletters"
|
||||
);
|
||||
assert.ok(
|
||||
testBodyContains(body, "source_url", "foo"),
|
||||
"https%3A%2F%2Fsnippets.mozilla.com%2Fshow%2Ffoo123"
|
||||
);
|
||||
});
|
||||
it("should send the right information for an sms", async () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
locale: "fr-CA",
|
||||
include_sms: true,
|
||||
message_id_sms: "foo",
|
||||
country: "CA",
|
||||
});
|
||||
|
||||
openFormAndSetValue(wrapper, "5371283767");
|
||||
wrapper.find("form").simulate("submit");
|
||||
|
||||
assert.calledOnce(fetchStub);
|
||||
const [request] = fetchStub.firstCall.args;
|
||||
|
||||
assert.equal(
|
||||
request.url,
|
||||
"https://basket.mozilla.org/news/subscribe_sms/"
|
||||
);
|
||||
const body = await request.text();
|
||||
assert.ok(
|
||||
testBodyContains(body, "mobile_number", "5371283767"),
|
||||
"has number"
|
||||
);
|
||||
assert.ok(testBodyContains(body, "lang", "fr-CA"), "has lang");
|
||||
assert.ok(testBodyContains(body, "country", "CA"), "CA");
|
||||
assert.ok(testBodyContains(body, "msg_name", "foo"), "has msg_name");
|
||||
});
|
||||
});
|
||||
|
||||
describe("SendToDeviceScene2Snippet", () => {
|
||||
function mountWithProps(content = {}) {
|
||||
const props = {
|
||||
id: "foo123",
|
||||
content: Object.assign({}, DEFAULT_SCENE2_CONTENT, content),
|
||||
onBlock() {},
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
return mount(
|
||||
<SendToDeviceScene2Snippet {...props} />,
|
||||
mockL10nWrapper(props.content)
|
||||
);
|
||||
}
|
||||
|
||||
it("should render scene 2", () => {
|
||||
const wrapper = mountWithProps();
|
||||
|
||||
assert.lengthOf(wrapper.find(".scene2Icon"), 1, "Found scene 2 icon");
|
||||
assert.lengthOf(
|
||||
wrapper.find(".scene2Title"),
|
||||
0,
|
||||
"Should not have a large header"
|
||||
);
|
||||
});
|
||||
it("should have block button", () => {
|
||||
const wrapper = mountWithProps();
|
||||
|
||||
assert.lengthOf(
|
||||
wrapper.find(".blockButton"),
|
||||
1,
|
||||
"Found the block button"
|
||||
);
|
||||
});
|
||||
it("should render title text", () => {
|
||||
const wrapper = mountWithProps();
|
||||
|
||||
assert.lengthOf(
|
||||
wrapper.find(".section-title-text"),
|
||||
1,
|
||||
"Found the section title"
|
||||
);
|
||||
assert.lengthOf(
|
||||
wrapper.find(".section-title .icon"),
|
||||
2, // light and dark theme
|
||||
"Found scene 2 title"
|
||||
);
|
||||
});
|
||||
it("should wrap the header in an anchor tag if condition is defined", () => {
|
||||
const sectionTitleProp = {
|
||||
section_title_url: "https://support.mozilla.org",
|
||||
};
|
||||
let wrapper = mountWithProps(sectionTitleProp);
|
||||
|
||||
const element = wrapper.find(".section-title a");
|
||||
assert.lengthOf(element, 1);
|
||||
});
|
||||
it("should render a header without an anchor", () => {
|
||||
const sectionTitleProp = {
|
||||
section_title_url: undefined,
|
||||
};
|
||||
let wrapper = mountWithProps(sectionTitleProp);
|
||||
assert.lengthOf(wrapper.find(".section-title a"), 0);
|
||||
assert.equal(
|
||||
wrapper.find(".section-title").instance().innerText,
|
||||
DEFAULT_SCENE2_CONTENT.section_title_text
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import { FluentBundle, FluentResource } from "@fluent/bundle";
|
||||
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
|
||||
import schema from "content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.schema.json";
|
||||
import { SimpleBelowSearchSnippet } from "content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.jsx";
|
||||
|
||||
const DEFAULT_CONTENT = { text: "foo" };
|
||||
|
||||
describe("SimpleBelowSearchSnippet", () => {
|
||||
let sandbox;
|
||||
let sendUserActionTelemetryStub;
|
||||
|
||||
function mockL10nWrapper(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
for (const [id, value] of Object.entries(content)) {
|
||||
if (typeof value === "string") {
|
||||
bundle.addResource(new FluentResource(`${id} = ${value}`));
|
||||
}
|
||||
}
|
||||
const l10n = new ReactLocalization([bundle]);
|
||||
return {
|
||||
wrappingComponent: LocalizationProvider,
|
||||
wrappingComponentProps: { l10n },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* mountAndCheckProps - Mounts a SimpleBelowSearchSnippet with DEFAULT_CONTENT extended with any props
|
||||
* passed in the content param and validates props against the schema.
|
||||
* @param {obj} content Object containing custom message content (e.g. {text, icon})
|
||||
* @returns enzyme wrapper for SimpleSnippet
|
||||
*/
|
||||
function mountAndCheckProps(content = {}, provider = "test-provider") {
|
||||
const props = {
|
||||
content: { ...DEFAULT_CONTENT, ...content },
|
||||
provider,
|
||||
sendUserActionTelemetry: sendUserActionTelemetryStub,
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
assert.jsonSchema(props.content, schema);
|
||||
return mount(
|
||||
<SimpleBelowSearchSnippet {...props} />,
|
||||
mockL10nWrapper(props.content)
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
sendUserActionTelemetryStub = sandbox.stub();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should render .text", () => {
|
||||
const wrapper = mountAndCheckProps({ text: "bar" });
|
||||
assert.equal(wrapper.find(".body").text(), "bar");
|
||||
});
|
||||
|
||||
it("should render .icon (light theme)", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
icon: "data:image/gif;base64,R0lGODl",
|
||||
});
|
||||
assert.equal(
|
||||
wrapper.find(".icon-light-theme").prop("src"),
|
||||
"data:image/gif;base64,R0lGODl"
|
||||
);
|
||||
});
|
||||
|
||||
it("should render .icon (dark theme)", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
icon_dark_theme: "data:image/gif;base64,R0lGODl",
|
||||
});
|
||||
assert.equal(
|
||||
wrapper.find(".icon-dark-theme").prop("src"),
|
||||
"data:image/gif;base64,R0lGODl"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import { FluentBundle, FluentResource } from "@fluent/bundle";
|
||||
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
|
||||
import schema from "content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.schema.json";
|
||||
import { SimpleSnippet } from "content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.jsx";
|
||||
|
||||
const DEFAULT_CONTENT = { text: "foo" };
|
||||
|
||||
describe("SimpleSnippet", () => {
|
||||
let sandbox;
|
||||
let onBlockStub;
|
||||
let sendUserActionTelemetryStub;
|
||||
|
||||
function mockL10nWrapper(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
for (const [id, value] of Object.entries(content)) {
|
||||
if (typeof value === "string") {
|
||||
bundle.addResource(new FluentResource(`${id} = ${value}`));
|
||||
}
|
||||
}
|
||||
const l10n = new ReactLocalization([bundle]);
|
||||
return {
|
||||
wrappingComponent: LocalizationProvider,
|
||||
wrappingComponentProps: { l10n },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* mountAndCheckProps - Mounts a SimpleSnippet with DEFAULT_CONTENT extended with any props
|
||||
* passed in the content param and validates props against the schema.
|
||||
* @param {obj} content Object containing custom message content (e.g. {text, icon, title})
|
||||
* @returns enzyme wrapper for SimpleSnippet
|
||||
*/
|
||||
function mountAndCheckProps(content = {}, provider = "test-provider") {
|
||||
const props = {
|
||||
content: Object.assign({}, DEFAULT_CONTENT, content),
|
||||
provider,
|
||||
onBlock: onBlockStub,
|
||||
sendUserActionTelemetry: sendUserActionTelemetryStub,
|
||||
onAction: sandbox.stub(),
|
||||
};
|
||||
assert.jsonSchema(props.content, schema);
|
||||
return mount(<SimpleSnippet {...props} />, mockL10nWrapper(props.content));
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
onBlockStub = sandbox.stub();
|
||||
sendUserActionTelemetryStub = sandbox.stub();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should have the correct defaults", () => {
|
||||
const wrapper = mountAndCheckProps();
|
||||
[["button", "title", "block_button_text"]].forEach(prop => {
|
||||
const props = wrapper.find(prop[0]).props();
|
||||
assert.propertyVal(props, prop[1], schema.properties[prop[2]].default);
|
||||
});
|
||||
});
|
||||
|
||||
it("should render .text", () => {
|
||||
const wrapper = mountAndCheckProps({ text: "bar" });
|
||||
assert.equal(wrapper.find(".body").text(), "bar");
|
||||
});
|
||||
it("should not render title element if no .title prop is supplied", () => {
|
||||
const wrapper = mountAndCheckProps();
|
||||
assert.lengthOf(wrapper.find(".title"), 0);
|
||||
});
|
||||
it("should render .title", () => {
|
||||
const wrapper = mountAndCheckProps({ title: "Foo" });
|
||||
assert.equal(wrapper.find(".title").text().trim(), "Foo");
|
||||
});
|
||||
it("should render a light theme variant .icon", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
icon: "data:image/gif;base64,R0lGODl",
|
||||
});
|
||||
assert.equal(
|
||||
wrapper.find(".icon-light-theme").prop("src"),
|
||||
"data:image/gif;base64,R0lGODl"
|
||||
);
|
||||
});
|
||||
it("should render a dark theme variant .icon", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
icon_dark_theme: "data:image/gif;base64,R0lGODl",
|
||||
});
|
||||
assert.equal(
|
||||
wrapper.find(".icon-dark-theme").prop("src"),
|
||||
"data:image/gif;base64,R0lGODl"
|
||||
);
|
||||
});
|
||||
it("should render a light theme variant .icon as fallback", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
icon_dark_theme: "",
|
||||
icon: "data:image/gif;base64,R0lGODp",
|
||||
});
|
||||
assert.equal(
|
||||
wrapper.find(".icon-dark-theme").prop("src"),
|
||||
"data:image/gif;base64,R0lGODp"
|
||||
);
|
||||
});
|
||||
it("should render .button_label and default className", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
button_label: "Click here",
|
||||
button_action: "OPEN_APPLICATIONS_MENU",
|
||||
button_action_args: "appMenu",
|
||||
});
|
||||
|
||||
const button = wrapper.find("button.ASRouterButton");
|
||||
button.simulate("click");
|
||||
|
||||
assert.equal(button.text(), "Click here");
|
||||
assert.equal(button.prop("className"), "ASRouterButton secondary");
|
||||
assert.calledOnce(wrapper.props().onAction);
|
||||
assert.calledWithExactly(wrapper.props().onAction, {
|
||||
type: "OPEN_APPLICATIONS_MENU",
|
||||
data: { args: "appMenu" },
|
||||
});
|
||||
});
|
||||
it("should not wrap the main content if a section header is not present", () => {
|
||||
const wrapper = mountAndCheckProps({ text: "bar" });
|
||||
assert.lengthOf(wrapper.find(".innerContentWrapper"), 0);
|
||||
});
|
||||
it("should wrap the main content if a section header is present", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
section_title_icon: "data:image/gif;base64,R0lGODl",
|
||||
section_title_text: "Messages from Mozilla",
|
||||
});
|
||||
|
||||
assert.lengthOf(wrapper.find(".innerContentWrapper"), 1);
|
||||
});
|
||||
it("should render a section header if text and icon (light-theme) are specified", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
section_title_icon: "data:image/gif;base64,R0lGODl",
|
||||
section_title_text: "Messages from Mozilla",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
wrapper.find(".section-title .icon-light-theme").prop("style")
|
||||
.backgroundImage,
|
||||
'url("data:image/gif;base64,R0lGODl")'
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find(".section-title-text").text().trim(),
|
||||
"Messages from Mozilla"
|
||||
);
|
||||
// ensure there is no <a> when a section_title_url is not specified
|
||||
assert.lengthOf(wrapper.find(".section-title a"), 0);
|
||||
});
|
||||
it("should render a section header if text and icon (light-theme) are specified", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
section_title_icon: "data:image/gif;base64,R0lGODl",
|
||||
section_title_icon_dark_theme: "data:image/gif;base64,R0lGODl",
|
||||
section_title_text: "Messages from Mozilla",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
wrapper.find(".section-title .icon-dark-theme").prop("style")
|
||||
.backgroundImage,
|
||||
'url("data:image/gif;base64,R0lGODl")'
|
||||
);
|
||||
assert.equal(
|
||||
wrapper.find(".section-title-text").text().trim(),
|
||||
"Messages from Mozilla"
|
||||
);
|
||||
// ensure there is no <a> when a section_title_url is not specified
|
||||
assert.lengthOf(wrapper.find(".section-title a"), 0);
|
||||
});
|
||||
it("should render a section header wrapped in an <a> tag if a url is provided", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
section_title_icon: "data:image/gif;base64,R0lGODl",
|
||||
section_title_text: "Messages from Mozilla",
|
||||
section_title_url: "https://www.mozilla.org",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
wrapper.find(".section-title a").prop("href"),
|
||||
"https://www.mozilla.org"
|
||||
);
|
||||
});
|
||||
it("should send an OPEN_URL action when button_url is defined and button is clicked", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
button_label: "Button",
|
||||
button_url: "https://mozilla.org",
|
||||
});
|
||||
|
||||
const button = wrapper.find("button.ASRouterButton");
|
||||
button.simulate("click");
|
||||
|
||||
assert.calledOnce(wrapper.props().onAction);
|
||||
assert.calledWithExactly(wrapper.props().onAction, {
|
||||
type: "OPEN_URL",
|
||||
data: { args: "https://mozilla.org" },
|
||||
});
|
||||
});
|
||||
it("should send an OPEN_ABOUT_PAGE action with entrypoint when the button is clicked", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
button_label: "Button",
|
||||
button_action: "OPEN_ABOUT_PAGE",
|
||||
button_entrypoint_value: "snippet",
|
||||
button_entrypoint_name: "entryPoint",
|
||||
button_action_args: "logins",
|
||||
});
|
||||
|
||||
const button = wrapper.find("button.ASRouterButton");
|
||||
button.simulate("click");
|
||||
|
||||
assert.calledOnce(wrapper.props().onAction);
|
||||
assert.calledWithExactly(wrapper.props().onAction, {
|
||||
type: "OPEN_ABOUT_PAGE",
|
||||
data: { args: "logins", entrypoint: "entryPoint=snippet" },
|
||||
});
|
||||
});
|
||||
it("should send an OPEN_PREFERENCE_PAGE action with entrypoint when the button is clicked", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
button_label: "Button",
|
||||
button_action: "OPEN_PREFERENCE_PAGE",
|
||||
button_entrypoint_value: "entry=snippet",
|
||||
button_action_args: "home",
|
||||
});
|
||||
|
||||
const button = wrapper.find("button.ASRouterButton");
|
||||
button.simulate("click");
|
||||
|
||||
assert.calledOnce(wrapper.props().onAction);
|
||||
assert.calledWithExactly(wrapper.props().onAction, {
|
||||
type: "OPEN_PREFERENCE_PAGE",
|
||||
data: { args: "home", entrypoint: "entry=snippet" },
|
||||
});
|
||||
});
|
||||
it("should call props.onBlock and sendUserActionTelemetry when CTA button is clicked", () => {
|
||||
const wrapper = mountAndCheckProps({ text: "bar" });
|
||||
|
||||
wrapper.instance().onButtonClick();
|
||||
|
||||
assert.calledOnce(onBlockStub);
|
||||
assert.calledOnce(sendUserActionTelemetryStub);
|
||||
});
|
||||
|
||||
it("should not call props.onBlock if do_not_autoblock is true", () => {
|
||||
const wrapper = mountAndCheckProps({ text: "bar", do_not_autoblock: true });
|
||||
|
||||
wrapper.instance().onButtonClick();
|
||||
|
||||
assert.notCalled(onBlockStub);
|
||||
});
|
||||
|
||||
it("should not call sendUserActionTelemetry for preview message when CTA button is clicked", () => {
|
||||
const wrapper = mountAndCheckProps({ text: "bar" }, "preview");
|
||||
|
||||
wrapper.instance().onButtonClick();
|
||||
|
||||
assert.calledOnce(onBlockStub);
|
||||
assert.notCalled(sendUserActionTelemetryStub);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,354 +0,0 @@
|
|||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import { FluentBundle, FluentResource } from "@fluent/bundle";
|
||||
import { LocalizationProvider, ReactLocalization } from "@fluent/react";
|
||||
import { RichText } from "content-src/asrouter/components/RichText/RichText.jsx";
|
||||
import schema from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json";
|
||||
import { SubmitFormSnippet } from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.jsx";
|
||||
|
||||
const DEFAULT_CONTENT = {
|
||||
scene1_text: "foo",
|
||||
scene2_text: "bar",
|
||||
scene1_button_label: "Sign Up",
|
||||
retry_button_label: "Try again",
|
||||
form_action: "foo.com",
|
||||
hidden_inputs: { foo: "foo" },
|
||||
error_text: "error",
|
||||
success_text: "success",
|
||||
};
|
||||
|
||||
describe("SubmitFormSnippet", () => {
|
||||
let sandbox;
|
||||
let onBlockStub;
|
||||
|
||||
function mockL10nWrapper(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
for (const [id, value] of Object.entries(content)) {
|
||||
if (typeof value === "string") {
|
||||
bundle.addResource(new FluentResource(`${id} = ${value}`));
|
||||
}
|
||||
}
|
||||
const l10n = new ReactLocalization([bundle]);
|
||||
return {
|
||||
wrappingComponent: LocalizationProvider,
|
||||
wrappingComponentProps: { l10n },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* mountAndCheckProps - Mounts a SubmitFormSnippet with DEFAULT_CONTENT extended with any props
|
||||
* passed in the content param and validates props against the schema.
|
||||
* @param {obj} content Object containing custom message content (e.g. {text, icon, title})
|
||||
* @returns enzyme wrapper for SubmitFormSnippet
|
||||
*/
|
||||
function mountAndCheckProps(content = {}) {
|
||||
const props = {
|
||||
content: Object.assign({}, DEFAULT_CONTENT, content),
|
||||
onBlock: onBlockStub,
|
||||
onDismiss: sandbox.stub(),
|
||||
sendUserActionTelemetry: sandbox.stub(),
|
||||
onAction: sandbox.stub(),
|
||||
form_method: "POST",
|
||||
};
|
||||
assert.jsonSchema(props.content, schema);
|
||||
return mount(
|
||||
<SubmitFormSnippet {...props} />,
|
||||
mockL10nWrapper(props.content)
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
onBlockStub = sandbox.stub();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should render .text", () => {
|
||||
const wrapper = mountAndCheckProps({ scene1_text: "bar" });
|
||||
assert.equal(wrapper.find(".body").text(), "bar");
|
||||
});
|
||||
it("should not render title element if no .title prop is supplied", () => {
|
||||
const wrapper = mountAndCheckProps();
|
||||
assert.lengthOf(wrapper.find(".title"), 0);
|
||||
});
|
||||
it("should render .title", () => {
|
||||
const wrapper = mountAndCheckProps({ scene1_title: "Foo" });
|
||||
assert.equal(wrapper.find(".title").text().trim(), "Foo");
|
||||
});
|
||||
it("should render light-theme .icon", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
scene1_icon: "data:image/gif;base64,R0lGODl",
|
||||
});
|
||||
assert.equal(
|
||||
wrapper.find(".icon-light-theme").prop("src"),
|
||||
"data:image/gif;base64,R0lGODl"
|
||||
);
|
||||
});
|
||||
it("should render dark-theme .icon", () => {
|
||||
const wrapper = mountAndCheckProps({
|
||||
scene1_icon_dark_theme: "data:image/gif;base64,R0lGODl",
|
||||
});
|
||||
assert.equal(
|
||||
wrapper.find(".icon-dark-theme").prop("src"),
|
||||
"data:image/gif;base64,R0lGODl"
|
||||
);
|
||||
});
|
||||
it("should render .button_label and default className", () => {
|
||||
const wrapper = mountAndCheckProps({ scene1_button_label: "Click here" });
|
||||
|
||||
const button = wrapper.find("button.ASRouterButton");
|
||||
assert.equal(button.text(), "Click here");
|
||||
assert.equal(button.prop("className"), "ASRouterButton secondary");
|
||||
});
|
||||
|
||||
describe("#SignupView", () => {
|
||||
let wrapper;
|
||||
const fetchOk = { json: () => Promise.resolve({ status: "ok" }) };
|
||||
const fetchFail = { json: () => Promise.resolve({ status: "fail" }) };
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mountAndCheckProps({
|
||||
scene1_text: "bar",
|
||||
scene2_email_placeholder_text: "Email",
|
||||
scene2_text: "signup",
|
||||
});
|
||||
});
|
||||
|
||||
it("should set the input type if provided through props.inputType", () => {
|
||||
wrapper.setProps({ inputType: "number" });
|
||||
wrapper.setState({ expanded: true });
|
||||
assert.equal(wrapper.find(".mainInput").instance().type, "number");
|
||||
});
|
||||
|
||||
it("should validate via props.validateInput if provided", () => {
|
||||
function validateInput(value, content) {
|
||||
if (content.country === "CA" && value === "poutine") {
|
||||
return "";
|
||||
}
|
||||
return "Must be poutine";
|
||||
}
|
||||
const setCustomValidity = sandbox.stub();
|
||||
wrapper.setProps({
|
||||
validateInput,
|
||||
content: { ...DEFAULT_CONTENT, country: "CA" },
|
||||
});
|
||||
wrapper.setState({ expanded: true });
|
||||
const input = wrapper.find(".mainInput");
|
||||
input.instance().value = "poutine";
|
||||
input.simulate("change", {
|
||||
target: { value: "poutine", setCustomValidity },
|
||||
});
|
||||
assert.calledWith(setCustomValidity, "");
|
||||
|
||||
input.instance().value = "fried chicken";
|
||||
input.simulate("change", {
|
||||
target: { value: "fried chicken", setCustomValidity },
|
||||
});
|
||||
assert.calledWith(setCustomValidity, "Must be poutine");
|
||||
});
|
||||
|
||||
it("should show the signup form if state.expanded is true", () => {
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
assert.isTrue(wrapper.find("form").exists());
|
||||
});
|
||||
it("should dismiss the snippet", () => {
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
wrapper.find(".ASRouterButton.secondary").simulate("click");
|
||||
|
||||
assert.calledOnce(wrapper.props().onDismiss);
|
||||
});
|
||||
it("should send a DISMISS event ping", () => {
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
wrapper.find(".ASRouterButton.secondary").simulate("click");
|
||||
|
||||
assert.equal(
|
||||
wrapper.props().sendUserActionTelemetry.firstCall.args[0].event,
|
||||
"DISMISS"
|
||||
);
|
||||
});
|
||||
it("should render hidden inputs + email input", () => {
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
assert.lengthOf(wrapper.find("input[type='hidden']"), 1);
|
||||
});
|
||||
it("should open the SignupView when the action button is clicked", () => {
|
||||
assert.isFalse(wrapper.find("form").exists());
|
||||
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
|
||||
assert.isTrue(wrapper.state().expanded);
|
||||
assert.isTrue(wrapper.find("form").exists());
|
||||
});
|
||||
it("should submit telemetry when the action button is clicked", () => {
|
||||
assert.isFalse(wrapper.find("form").exists());
|
||||
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
|
||||
assert.equal(
|
||||
wrapper.props().sendUserActionTelemetry.firstCall.args[0].event_context,
|
||||
"scene1-button-learn-more"
|
||||
);
|
||||
});
|
||||
it("should submit form data when submitted", () => {
|
||||
sandbox.stub(window, "fetch").resolves(fetchOk);
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
wrapper.find("form").simulate("submit");
|
||||
assert.calledOnce(window.fetch);
|
||||
});
|
||||
it("should send user telemetry when submitted", () => {
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
wrapper.find("form").simulate("submit");
|
||||
|
||||
assert.equal(
|
||||
wrapper.props().sendUserActionTelemetry.firstCall.args[0].event_context,
|
||||
"conversion-subscribe-activation"
|
||||
);
|
||||
});
|
||||
it("should set signupSuccess when submission status is ok", async () => {
|
||||
sandbox.stub(window, "fetch").resolves(fetchOk);
|
||||
wrapper.setState({ expanded: true });
|
||||
await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
|
||||
|
||||
assert.equal(wrapper.state().signupSuccess, true);
|
||||
assert.equal(wrapper.state().signupSubmitted, true);
|
||||
assert.calledOnce(onBlockStub);
|
||||
assert.calledWithExactly(onBlockStub, { preventDismiss: true });
|
||||
});
|
||||
it("should send user telemetry when submission status is ok", async () => {
|
||||
sandbox.stub(window, "fetch").resolves(fetchOk);
|
||||
wrapper.setState({ expanded: true });
|
||||
await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
|
||||
|
||||
assert.equal(
|
||||
wrapper.props().sendUserActionTelemetry.secondCall.args[0]
|
||||
.event_context,
|
||||
"subscribe-success"
|
||||
);
|
||||
});
|
||||
it("should not block the snippet if submission failed", async () => {
|
||||
sandbox.stub(window, "fetch").resolves(fetchFail);
|
||||
wrapper.setState({ expanded: true });
|
||||
await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
|
||||
|
||||
assert.equal(wrapper.state().signupSuccess, false);
|
||||
assert.equal(wrapper.state().signupSubmitted, true);
|
||||
assert.notCalled(onBlockStub);
|
||||
});
|
||||
it("should not block if do_not_autoblock is true", async () => {
|
||||
sandbox.stub(window, "fetch").resolves(fetchOk);
|
||||
wrapper = mountAndCheckProps({
|
||||
scene1_text: "bar",
|
||||
scene2_email_placeholder_text: "Email",
|
||||
scene2_text: "signup",
|
||||
do_not_autoblock: true,
|
||||
});
|
||||
wrapper.setState({ expanded: true });
|
||||
await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
|
||||
|
||||
assert.equal(wrapper.state().signupSuccess, true);
|
||||
assert.equal(wrapper.state().signupSubmitted, true);
|
||||
assert.notCalled(onBlockStub);
|
||||
});
|
||||
it("should send user telemetry if submission failed", async () => {
|
||||
sandbox.stub(window, "fetch").resolves(fetchFail);
|
||||
wrapper.setState({ expanded: true });
|
||||
await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
|
||||
|
||||
assert.equal(
|
||||
wrapper.props().sendUserActionTelemetry.secondCall.args[0]
|
||||
.event_context,
|
||||
"subscribe-error"
|
||||
);
|
||||
});
|
||||
it("should render the signup success message", () => {
|
||||
wrapper.setProps({ content: { success_text: "success" } });
|
||||
wrapper.setState({ signupSuccess: true, signupSubmitted: true });
|
||||
|
||||
assert.isTrue(wrapper.find(".submissionStatus").exists());
|
||||
assert.propertyVal(
|
||||
wrapper.find(RichText).props(),
|
||||
"localization_id",
|
||||
"success_text"
|
||||
);
|
||||
assert.propertyVal(
|
||||
wrapper.find(RichText).props(),
|
||||
"success_text",
|
||||
"success"
|
||||
);
|
||||
assert.isFalse(wrapper.find(".ASRouterButton").exists());
|
||||
});
|
||||
it("should render the signup error message", () => {
|
||||
wrapper.setProps({ content: { error_text: "trouble" } });
|
||||
wrapper.setState({ signupSuccess: false, signupSubmitted: true });
|
||||
|
||||
assert.isTrue(wrapper.find(".submissionStatus").exists());
|
||||
assert.propertyVal(
|
||||
wrapper.find(RichText).props(),
|
||||
"localization_id",
|
||||
"error_text"
|
||||
);
|
||||
assert.propertyVal(
|
||||
wrapper.find(RichText).props(),
|
||||
"error_text",
|
||||
"trouble"
|
||||
);
|
||||
assert.isTrue(wrapper.find(".ASRouterButton").exists());
|
||||
});
|
||||
it("should render the button to return to the signup form if there was an error", () => {
|
||||
wrapper.setState({ signupSubmitted: true, signupSuccess: false });
|
||||
|
||||
const button = wrapper.find("button.ASRouterButton");
|
||||
assert.equal(button.text(), "Try again");
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
|
||||
assert.equal(wrapper.state().signupSubmitted, false);
|
||||
});
|
||||
it("should not render the privacy notice checkbox if prop is missing", () => {
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
assert.isFalse(wrapper.find(".privacyNotice").exists());
|
||||
});
|
||||
it("should render the privacy notice checkbox if prop is provided", () => {
|
||||
wrapper.setProps({
|
||||
content: { ...DEFAULT_CONTENT, scene2_privacy_html: "privacy notice" },
|
||||
});
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
assert.isTrue(wrapper.find(".privacyNotice").exists());
|
||||
});
|
||||
it("should not call fetch if form_method is GET", async () => {
|
||||
sandbox.stub(window, "fetch").resolves(fetchOk);
|
||||
wrapper.setProps({ form_method: "GET" });
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
|
||||
|
||||
assert.notCalled(window.fetch);
|
||||
});
|
||||
it("should block the snippet when form_method is GET", () => {
|
||||
wrapper.setProps({ form_method: "GET" });
|
||||
wrapper.setState({ expanded: true });
|
||||
|
||||
wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
|
||||
|
||||
assert.calledOnce(onBlockStub);
|
||||
assert.calledWithExactly(onBlockStub, { preventDismiss: true });
|
||||
});
|
||||
it("should return to scene 2 alt when clicking the retry button", async () => {
|
||||
wrapper.setState({ signupSubmitted: true });
|
||||
wrapper.setProps({ expandedAlt: true });
|
||||
|
||||
wrapper.find(".ASRouterButton").simulate("click");
|
||||
|
||||
assert.isTrue(wrapper.find(".scene2Alt").exists());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
import { isEmailOrPhoneNumber } from "content-src/asrouter/templates/SendToDeviceSnippet/isEmailOrPhoneNumber";
|
||||
|
||||
const CONTENT = {};
|
||||
|
||||
describe("isEmailOrPhoneNumber", () => {
|
||||
it("should return 'email' for emails", () => {
|
||||
assert.equal(isEmailOrPhoneNumber("foobar@asd.com", CONTENT), "email");
|
||||
assert.equal(isEmailOrPhoneNumber("foobar@asd.co.uk", CONTENT), "email");
|
||||
});
|
||||
it("should return 'phone' for valid en-US/en-CA phone numbers", () => {
|
||||
assert.equal(
|
||||
isEmailOrPhoneNumber("14582731273", { locale: "en-US" }),
|
||||
"phone"
|
||||
);
|
||||
assert.equal(
|
||||
isEmailOrPhoneNumber("4582731273", { locale: "en-CA" }),
|
||||
"phone"
|
||||
);
|
||||
});
|
||||
it("should return an empty string for invalid phone number lengths in en-US/en-CA", () => {
|
||||
// Not enough digits
|
||||
assert.equal(isEmailOrPhoneNumber("4522", { locale: "en-US" }), "");
|
||||
assert.equal(isEmailOrPhoneNumber("4522", { locale: "en-CA" }), "");
|
||||
});
|
||||
it("should return 'phone' for valid German phone numbers", () => {
|
||||
assert.equal(
|
||||
isEmailOrPhoneNumber("145827312732", { locale: "de" }),
|
||||
"phone"
|
||||
);
|
||||
});
|
||||
it("should return 'phone' for any number of digits in other locales", () => {
|
||||
assert.equal(isEmailOrPhoneNumber("4", CONTENT), "phone");
|
||||
});
|
||||
it("should return an empty string for other invalid inputs", () => {
|
||||
assert.equal(
|
||||
isEmailOrPhoneNumber("abc", CONTENT),
|
||||
"",
|
||||
"abc should be invalid"
|
||||
);
|
||||
assert.equal(
|
||||
isEmailOrPhoneNumber("abc@", CONTENT),
|
||||
"",
|
||||
"abc@ should be invalid"
|
||||
);
|
||||
assert.equal(
|
||||
isEmailOrPhoneNumber("abc@foo", CONTENT),
|
||||
"",
|
||||
"abc@foo should be invalid"
|
||||
);
|
||||
assert.equal(
|
||||
isEmailOrPhoneNumber("123d1232", CONTENT),
|
||||
"",
|
||||
"123d1232 should be invalid"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -2,7 +2,6 @@ import { INITIAL_STATE, insertPinned, reducers } from "common/Reducers.sys.mjs";
|
|||
const {
|
||||
TopSites,
|
||||
App,
|
||||
Snippets,
|
||||
Prefs,
|
||||
Dialog,
|
||||
Sections,
|
||||
|
|
@ -257,11 +256,6 @@ describe("Reducers", () => {
|
|||
});
|
||||
assert.deepEqual(shortcuts, nextState.searchShortcuts);
|
||||
});
|
||||
it("should remove all content on SNIPPETS_PREVIEW_MODE", () => {
|
||||
const oldState = { rows: [{ url: "foo.com" }, { url: "bar.com" }] };
|
||||
const nextState = TopSites(oldState, { type: at.SNIPPETS_PREVIEW_MODE });
|
||||
assert.lengthOf(nextState.rows, 0);
|
||||
});
|
||||
it("should set sov positions and state", () => {
|
||||
const positions = [
|
||||
{ position: 0, assignedPartner: "amp" },
|
||||
|
|
@ -741,13 +735,6 @@ describe("Reducers", () => {
|
|||
// old row is unchanged
|
||||
assert.equal(oldRow, oldState[0].rows[1]);
|
||||
});
|
||||
it("should remove all content on SNIPPETS_PREVIEW_MODE", () => {
|
||||
const previewMode = { type: at.SNIPPETS_PREVIEW_MODE };
|
||||
const newState = Sections(oldState, previewMode);
|
||||
newState.forEach(section => {
|
||||
assert.lengthOf(section.rows, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("#insertPinned", () => {
|
||||
let links;
|
||||
|
|
@ -819,45 +806,6 @@ describe("Reducers", () => {
|
|||
assert.equal(typeof pinned[0].isPinned, "undefined");
|
||||
});
|
||||
});
|
||||
describe("Snippets", () => {
|
||||
it("should return INITIAL_STATE by default", () => {
|
||||
assert.equal(
|
||||
Snippets(undefined, { type: "some_action" }),
|
||||
INITIAL_STATE.Snippets
|
||||
);
|
||||
});
|
||||
it("should set initialized to true on a SNIPPETS_DATA action", () => {
|
||||
const state = Snippets(undefined, { type: at.SNIPPETS_DATA, data: {} });
|
||||
assert.isTrue(state.initialized);
|
||||
});
|
||||
it("should set the snippet data on a SNIPPETS_DATA action", () => {
|
||||
const data = { snippetsURL: "foo.com", version: 4 };
|
||||
const state = Snippets(undefined, { type: at.SNIPPETS_DATA, data });
|
||||
assert.propertyVal(state, "snippetsURL", data.snippetsURL);
|
||||
assert.propertyVal(state, "version", data.version);
|
||||
});
|
||||
it("should reset to the initial state on a SNIPPETS_RESET action", () => {
|
||||
const state = Snippets(
|
||||
{ initialized: true, foo: "bar" },
|
||||
{ type: at.SNIPPETS_RESET }
|
||||
);
|
||||
assert.equal(state, INITIAL_STATE.Snippets);
|
||||
});
|
||||
it("should set the new blocklist on SNIPPET_BLOCKED", () => {
|
||||
const state = Snippets(
|
||||
{ blockList: [] },
|
||||
{ type: at.SNIPPET_BLOCKED, data: 1 }
|
||||
);
|
||||
assert.deepEqual(state.blockList, [1]);
|
||||
});
|
||||
it("should clear the blocklist on SNIPPETS_BLOCKLIST_CLEARED", () => {
|
||||
const state = Snippets(
|
||||
{ blockList: [1, 2] },
|
||||
{ type: at.SNIPPETS_BLOCKLIST_CLEARED }
|
||||
);
|
||||
assert.deepEqual(state.blockList, []);
|
||||
});
|
||||
});
|
||||
describe("Pocket", () => {
|
||||
it("should return INITIAL_STATE by default", () => {
|
||||
assert.equal(
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@ describe("ASRouterAdmin", () => {
|
|||
let FAKE_PROVIDER_PREF = [
|
||||
{
|
||||
enabled: true,
|
||||
id: "snippets_local_testing",
|
||||
localProvider: "SnippetsProvider",
|
||||
id: "local_testing",
|
||||
localProvider: "TestProvider",
|
||||
type: "local",
|
||||
},
|
||||
];
|
||||
let FAKE_PROVIDER = [
|
||||
{
|
||||
enabled: true,
|
||||
id: "snippets_local_testing",
|
||||
localProvider: "SnippetsProvider",
|
||||
id: "local_testing",
|
||||
localProvider: "TestProvider",
|
||||
messages: [],
|
||||
type: "local",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1053,20 +1053,6 @@ describe("TelemetryFeed", () => {
|
|||
assert.propertyVal(ping, "message_id", "moments_message_01");
|
||||
});
|
||||
});
|
||||
describe("#applySnippetsPolicy", () => {
|
||||
it("should include client_id", async () => {
|
||||
const data = {
|
||||
action: "snippets_user_event",
|
||||
event: "IMPRESSION",
|
||||
message_id: "snippets_message_01",
|
||||
};
|
||||
const { ping, pingType } = await instance.applySnippetsPolicy(data);
|
||||
|
||||
assert.equal(pingType, "snippets");
|
||||
assert.propertyVal(ping, "client_id", FAKE_TELEMETRY_ID);
|
||||
assert.propertyVal(ping, "message_id", "snippets_message_01");
|
||||
});
|
||||
});
|
||||
describe("#applyOnboardingPolicy", () => {
|
||||
it("should include client_id", async () => {
|
||||
const data = {
|
||||
|
|
@ -1177,9 +1163,9 @@ describe("TelemetryFeed", () => {
|
|||
describe("#createASRouterEvent", () => {
|
||||
it("should create a valid AS Router event", async () => {
|
||||
const data = {
|
||||
action: "snippets_user_event",
|
||||
action: "cfr_user_event",
|
||||
event: "CLICK",
|
||||
message_id: "snippets_message_01",
|
||||
message_id: "cfr_message_01",
|
||||
};
|
||||
const action = ac.ASRouterUserEvent(data);
|
||||
const { ping } = await instance.createASRouterEvent(action);
|
||||
|
|
@ -1199,30 +1185,6 @@ describe("TelemetryFeed", () => {
|
|||
|
||||
assert.calledOnce(instance.applyCFRPolicy);
|
||||
});
|
||||
it("should call applySnippetsPolicy if action equals to snippets_user_event", async () => {
|
||||
const data = {
|
||||
action: "snippets_user_event",
|
||||
event: "IMPRESSION",
|
||||
message_id: "snippets_message_01",
|
||||
};
|
||||
sandbox.stub(instance, "applySnippetsPolicy");
|
||||
const action = ac.ASRouterUserEvent(data);
|
||||
await instance.createASRouterEvent(action);
|
||||
|
||||
assert.calledOnce(instance.applySnippetsPolicy);
|
||||
});
|
||||
it("should call applySnippetsPolicy if action equals to snippets_local_testing_user_event", async () => {
|
||||
const data = {
|
||||
action: "snippets_local_testing_user_event",
|
||||
event: "IMPRESSION",
|
||||
message_id: "snippets_message_01",
|
||||
};
|
||||
sandbox.stub(instance, "applySnippetsPolicy");
|
||||
const action = ac.ASRouterUserEvent(data);
|
||||
await instance.createASRouterEvent(action);
|
||||
|
||||
assert.calledOnce(instance.applySnippetsPolicy);
|
||||
});
|
||||
it("should call applyOnboardingPolicy if action equals to onboarding_user_event", async () => {
|
||||
const data = {
|
||||
action: "onboarding_user_event",
|
||||
|
|
|
|||
|
|
@ -11,12 +11,10 @@ user_pref("browser.dom.window.dump.enabled", true);
|
|||
user_pref("devtools.console.stdout.chrome", true);
|
||||
// asrouter expects a plain object or null
|
||||
user_pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "null");
|
||||
user_pref("browser.newtabpage.activity-stream.asrouter.providers.snippets", "null");
|
||||
user_pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "null");
|
||||
user_pref("browser.newtabpage.activity-stream.asrouter.providers.whats-new-panel", "null");
|
||||
user_pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "null");
|
||||
user_pref("browser.newtabpage.activity-stream.feeds.system.topstories", false);
|
||||
user_pref("browser.newtabpage.activity-stream.feeds.snippets", false);
|
||||
user_pref("browser.newtabpage.activity-stream.tippyTop.service.endpoint", "");
|
||||
user_pref("browser.newtabpage.activity-stream.discoverystream.config", "[]");
|
||||
|
||||
|
|
|
|||
|
|
@ -326,7 +326,6 @@
|
|||
"resource://activity-stream/lib/SectionsManager.jsm": "browser/components/newtab/lib/SectionsManager.jsm",
|
||||
"resource://activity-stream/lib/ShortURL.jsm": "browser/components/newtab/lib/ShortURL.jsm",
|
||||
"resource://activity-stream/lib/SiteClassifier.jsm": "browser/components/newtab/lib/SiteClassifier.jsm",
|
||||
"resource://activity-stream/lib/SnippetsTestMessageProvider.jsm": "browser/components/newtab/lib/SnippetsTestMessageProvider.jsm",
|
||||
"resource://activity-stream/lib/Spotlight.jsm": "browser/components/newtab/lib/Spotlight.jsm",
|
||||
"resource://activity-stream/lib/Store.jsm": "browser/components/newtab/lib/Store.jsm",
|
||||
"resource://activity-stream/lib/SystemTickFeed.jsm": "browser/components/newtab/lib/SystemTickFeed.jsm",
|
||||
|
|
|
|||
Loading…
Reference in a new issue