зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1694257 - Add ability for targeted defaults for AboutWelcome r=k88hudson
Differential Revision: https://phabricator.services.mozilla.com/D106418
This commit is contained in:
Родитель
d8f842d8c1
Коммит
2bb63845fa
|
@ -15,6 +15,8 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
ExperimentAPI: "resource://nimbus/ExperimentAPI.jsm",
|
||||
shortURL: "resource://activity-stream/lib/ShortURL.jsm",
|
||||
TippyTopProvider: "resource://activity-stream/lib/TippyTopProvider.jsm",
|
||||
AboutWelcomeDefaults:
|
||||
"resource://activity-stream/aboutwelcome/lib/AboutWelcomeDefaults.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
|
@ -156,10 +158,6 @@ class AboutWelcomeChild extends JSWindowActorChild {
|
|||
defineAs: "AWGetFeatureConfig",
|
||||
});
|
||||
|
||||
Cu.exportFunction(this.AWGetAttributionData.bind(this), window, {
|
||||
defineAs: "AWGetAttributionData",
|
||||
});
|
||||
|
||||
Cu.exportFunction(this.AWGetFxAMetricsFlowURI.bind(this), window, {
|
||||
defineAs: "AWGetFxAMetricsFlowURI",
|
||||
});
|
||||
|
@ -216,79 +214,15 @@ class AboutWelcomeChild extends JSWindowActorChild {
|
|||
);
|
||||
}
|
||||
|
||||
async getAddonInfo(attrbObj) {
|
||||
let { content, source } = attrbObj;
|
||||
try {
|
||||
if (!content || source !== "addons.mozilla.org") {
|
||||
return null;
|
||||
}
|
||||
// Attribution data can be double encoded
|
||||
while (content.includes("%")) {
|
||||
try {
|
||||
const result = decodeURIComponent(content);
|
||||
if (result === content) {
|
||||
break;
|
||||
}
|
||||
content = result;
|
||||
} catch (e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return await this.sendQuery("AWPage:GET_ADDON_FROM_REPOSITORY", content);
|
||||
} catch (e) {
|
||||
Cu.reportError(
|
||||
"Failed to get the latest add-on version for Return to AMO"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
hasAMOAttribution(attributionData) {
|
||||
return (
|
||||
attributionData &&
|
||||
attributionData.campaign === "non-fx-button" &&
|
||||
attributionData.source === "addons.mozilla.org"
|
||||
);
|
||||
}
|
||||
|
||||
async formatAttributionData(attribution) {
|
||||
let result = {};
|
||||
if (this.hasAMOAttribution(attribution)) {
|
||||
let extraProps = await this.getAddonInfo(attribution);
|
||||
if (extraProps) {
|
||||
result = {
|
||||
template: "return_to_amo",
|
||||
extraProps,
|
||||
};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async getAttributionData() {
|
||||
return Cu.cloneInto(
|
||||
await this.formatAttributionData(
|
||||
await this.sendQuery("AWPage:GET_ATTRIBUTION_DATA")
|
||||
),
|
||||
this.contentWindow
|
||||
);
|
||||
}
|
||||
|
||||
AWGetAttributionData() {
|
||||
return this.wrapPromise(this.getAttributionData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Send initial data to page including experiment information
|
||||
*/
|
||||
AWGetFeatureConfig() {
|
||||
// Note that we specifically don't wait for `ready` so if
|
||||
// about:welcome loads outside of the "FirstStartup" scenario this will likely not be ready
|
||||
async getAWContent() {
|
||||
let experimentMetadata =
|
||||
ExperimentAPI.getExperimentMetaData({
|
||||
featureId: "aboutwelcome",
|
||||
}) || {};
|
||||
let featureConfig = aboutWelcomeFeature.getValue({ defaultValue: {} });
|
||||
let featureConfig = aboutWelcomeFeature.getValue() || {};
|
||||
|
||||
if (experimentMetadata?.slug) {
|
||||
log.debug(
|
||||
|
@ -296,18 +230,35 @@ class AboutWelcomeChild extends JSWindowActorChild {
|
|||
);
|
||||
} else {
|
||||
log.debug("Loading about:welcome without experiment");
|
||||
}
|
||||
return Cu.cloneInto(
|
||||
{
|
||||
// All experimentation right now is using the multistage template
|
||||
template: "multistage",
|
||||
...experimentMetadata,
|
||||
let attributionData = await this.sendQuery("AWPage:GET_ATTRIBUTION_DATA");
|
||||
if (attributionData) {
|
||||
log.debug("Loading about:welcome with attribution data");
|
||||
featureConfig = { ...attributionData, ...featureConfig };
|
||||
} else {
|
||||
log.debug("Loading about:welcome with default data");
|
||||
let defaults = AboutWelcomeDefaults.getDefaults();
|
||||
// FeatureConfig (from prefs or experiments) has higher precendence
|
||||
// to defaults. But the `screens` property isn't defined we shouldn't
|
||||
// override the default with `null`
|
||||
let screens = featureConfig.screens || defaults.screens;
|
||||
featureConfig = {
|
||||
...defaults,
|
||||
...featureConfig,
|
||||
},
|
||||
screens,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return Cu.cloneInto(
|
||||
{ ...experimentMetadata, ...featureConfig },
|
||||
this.contentWindow
|
||||
);
|
||||
}
|
||||
|
||||
AWGetFeatureConfig() {
|
||||
return this.wrapPromise(this.getAWContent());
|
||||
}
|
||||
|
||||
AWGetFxAMetricsFlowURI() {
|
||||
return this.wrapPromise(this.sendQuery("AWPage:FXA_METRICS_FLOW_URI"));
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ const { XPCOMUtils } = ChromeUtils.import(
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||
AddonRepository: "resource://gre/modules/addons/AddonRepository.jsm",
|
||||
FxAccounts: "resource://gre/modules/FxAccounts.jsm",
|
||||
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
|
@ -21,7 +20,8 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
"resource://messaging-system/lib/SpecialMessageActions.jsm",
|
||||
AboutWelcomeTelemetry:
|
||||
"resource://activity-stream/aboutwelcome/lib/AboutWelcomeTelemetry.jsm",
|
||||
AttributionCode: "resource:///modules/AttributionCode.jsm",
|
||||
AboutWelcomeDefaults:
|
||||
"resource://activity-stream/aboutwelcome/lib/AboutWelcomeDefaults.jsm",
|
||||
PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
|
||||
Region: "resource://gre/modules/Region.jsm",
|
||||
});
|
||||
|
@ -224,8 +224,6 @@ class AboutWelcomeParent extends JSWindowActorParent {
|
|||
break;
|
||||
case "AWPage:FXA_METRICS_FLOW_URI":
|
||||
return FxAccounts.config.promiseMetricsFlowURI("aboutwelcome");
|
||||
case "AWPage:GET_ATTRIBUTION_DATA":
|
||||
return AttributionCode.getAttrDataAsync();
|
||||
case "AWPage:IMPORTABLE_SITES":
|
||||
return getImportableSites();
|
||||
case "AWPage:TELEMETRY_EVENT":
|
||||
|
@ -235,16 +233,9 @@ class AboutWelcomeParent extends JSWindowActorParent {
|
|||
this.AboutWelcomeObserver.terminateReason =
|
||||
AWTerminate.ADDRESS_BAR_NAVIGATED;
|
||||
break;
|
||||
case "AWPage:GET_ADDON_FROM_REPOSITORY":
|
||||
const [addonInfo] = await AddonRepository.getAddonsByIDs([data]);
|
||||
if (addonInfo.sourceURI.scheme !== "https") {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: addonInfo.name,
|
||||
url: addonInfo.sourceURI.spec,
|
||||
iconURL: addonInfo.icons["64"] || addonInfo.icons["32"],
|
||||
};
|
||||
case "AWPage:GET_ATTRIBUTION_DATA":
|
||||
let attributionData = await AboutWelcomeDefaults.getAttributionContent();
|
||||
return attributionData;
|
||||
case "AWPage:SELECT_THEME":
|
||||
return AddonManager.getAddonByID(
|
||||
LIGHT_WEIGHT_THEMES[data]
|
||||
|
|
|
@ -102,7 +102,6 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
||||
/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
|
||||
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7);
|
||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
|
@ -113,7 +112,6 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|||
|
||||
|
||||
|
||||
|
||||
class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -189,9 +187,8 @@ class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComp
|
|||
});
|
||||
}
|
||||
|
||||
}
|
||||
} // Computes messageId and UTMTerm info used in telemetry
|
||||
|
||||
AboutWelcome.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_4__["DEFAULT_WELCOME_CONTENT"]; // Computes messageId and UTMTerm info used in telemetry
|
||||
|
||||
function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) {
|
||||
let messageId = welcomeContent.template === "return_to_amo" ? "RTAMO_DEFAULT_WELCOME" : "DEFAULT_ABOUTWELCOME";
|
||||
|
@ -212,36 +209,18 @@ function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) {
|
|||
}
|
||||
|
||||
async function retrieveRenderContent() {
|
||||
var _aboutWelcomeProps;
|
||||
|
||||
// Check for featureConfig and retrieve content
|
||||
const featureConfig = await window.AWGetFeatureConfig();
|
||||
let aboutWelcomeProps;
|
||||
|
||||
if (!featureConfig.screens) {
|
||||
const attribution = await window.AWGetAttributionData();
|
||||
aboutWelcomeProps = {
|
||||
template: attribution.template,
|
||||
...attribution.extraProps
|
||||
};
|
||||
} else {
|
||||
// If screens is defined then we have multi stage AW content to show
|
||||
aboutWelcomeProps = featureConfig.screens ? featureConfig : {};
|
||||
} // Set design if exists in featureConfig
|
||||
|
||||
|
||||
if (featureConfig.design && !((_aboutWelcomeProps = aboutWelcomeProps) !== null && _aboutWelcomeProps !== void 0 && _aboutWelcomeProps.design)) {
|
||||
aboutWelcomeProps = { ...aboutWelcomeProps,
|
||||
design: featureConfig.design
|
||||
};
|
||||
}
|
||||
|
||||
// Feature config includes:
|
||||
// user prefs
|
||||
// experiment data
|
||||
// attribution data
|
||||
// defaults
|
||||
let featureConfig = await window.AWGetFeatureConfig();
|
||||
let {
|
||||
messageId,
|
||||
UTMTerm
|
||||
} = ComputeTelemetryInfo(aboutWelcomeProps, featureConfig.slug, featureConfig.branch && featureConfig.branch.slug);
|
||||
} = ComputeTelemetryInfo(featureConfig, featureConfig.slug, featureConfig.branch && featureConfig.branch.slug);
|
||||
return {
|
||||
aboutWelcomeProps,
|
||||
featureConfig,
|
||||
messageId,
|
||||
UTMTerm
|
||||
};
|
||||
|
@ -249,7 +228,7 @@ async function retrieveRenderContent() {
|
|||
|
||||
async function mount() {
|
||||
let {
|
||||
aboutWelcomeProps,
|
||||
featureConfig: aboutWelcomeProps,
|
||||
messageId,
|
||||
UTMTerm
|
||||
} = await retrieveRenderContent();
|
||||
|
@ -880,7 +859,6 @@ const HelpText = props => {
|
|||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AboutWelcomeUtils", function() { return AboutWelcomeUtils; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DEFAULT_RTAMO_CONTENT", function() { return DEFAULT_RTAMO_CONTENT; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DEFAULT_WELCOME_CONTENT", function() { return DEFAULT_WELCOME_CONTENT; });
|
||||
/* 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/. */
|
||||
|
@ -985,172 +963,6 @@ const DEFAULT_RTAMO_CONTENT = {
|
|||
}
|
||||
}
|
||||
};
|
||||
const DEFAULT_WELCOME_CONTENT = {
|
||||
template: "multistage",
|
||||
screens: [{
|
||||
id: "AW_SET_DEFAULT",
|
||||
order: 0,
|
||||
content: {
|
||||
zap: true,
|
||||
title: {
|
||||
string_id: "onboarding-multistage-set-default-header"
|
||||
},
|
||||
subtitle: {
|
||||
string_id: "onboarding-multistage-set-default-subtitle"
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-set-default-primary-button-label"
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
type: "SET_DEFAULT_BROWSER"
|
||||
}
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-set-default-secondary-button-label"
|
||||
},
|
||||
action: {
|
||||
navigate: true
|
||||
}
|
||||
},
|
||||
secondary_button_top: {
|
||||
text: {
|
||||
string_id: "onboarding-multistage-welcome-secondary-button-text"
|
||||
},
|
||||
label: {
|
||||
string_id: "onboarding-multistage-welcome-secondary-button-label"
|
||||
},
|
||||
action: {
|
||||
data: {
|
||||
entrypoint: "activity-stream-firstrun"
|
||||
},
|
||||
type: "SHOW_FIREFOX_ACCOUNTS",
|
||||
addFlowParams: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
id: "AW_IMPORT_SETTINGS",
|
||||
order: 1,
|
||||
content: {
|
||||
zap: true,
|
||||
help_text: {
|
||||
text: {
|
||||
string_id: "onboarding-import-sites-disclaimer"
|
||||
}
|
||||
},
|
||||
title: {
|
||||
string_id: "onboarding-multistage-import-header"
|
||||
},
|
||||
subtitle: {
|
||||
string_id: "onboarding-multistage-import-subtitle"
|
||||
},
|
||||
tiles: {
|
||||
type: "topsites",
|
||||
showTitles: true
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-import-primary-button-label"
|
||||
},
|
||||
action: {
|
||||
type: "SHOW_MIGRATION_WIZARD",
|
||||
navigate: true
|
||||
}
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-import-secondary-button-label"
|
||||
},
|
||||
action: {
|
||||
navigate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
id: "AW_CHOOSE_THEME",
|
||||
order: 2,
|
||||
content: {
|
||||
zap: true,
|
||||
title: {
|
||||
string_id: "onboarding-multistage-theme-header"
|
||||
},
|
||||
subtitle: {
|
||||
string_id: "onboarding-multistage-theme-subtitle"
|
||||
},
|
||||
tiles: {
|
||||
type: "theme",
|
||||
action: {
|
||||
theme: "<event>"
|
||||
},
|
||||
data: [{
|
||||
theme: "automatic",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-automatic"
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-automatic-2"
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-automatic-2"
|
||||
}
|
||||
}, {
|
||||
theme: "light",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-light"
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-light-2"
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-light"
|
||||
}
|
||||
}, {
|
||||
theme: "dark",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-dark"
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-dark-2"
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-dark"
|
||||
}
|
||||
}, {
|
||||
theme: "alpenglow",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-alpenglow"
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-alpenglow-2"
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-alpenglow"
|
||||
}
|
||||
}]
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-primary-button-label2"
|
||||
},
|
||||
action: {
|
||||
navigate: true
|
||||
}
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-secondary-button-label"
|
||||
},
|
||||
action: {
|
||||
theme: "automatic",
|
||||
navigate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 8 */
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
/* 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";
|
||||
|
||||
const EXPORTED_SYMBOLS = ["AboutWelcomeDefaults"];
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
ShellService: "resource:///modules/ShellService.jsm",
|
||||
AttributionCode: "resource:///modules/AttributionCode.jsm",
|
||||
AddonRepository: "resource://gre/modules/addons/AddonRepository.jsm",
|
||||
});
|
||||
|
||||
const DID_SEE_ABOUT_WELCOME_PREF = "trailhead.firstrun.didSeeAboutWelcome";
|
||||
|
||||
const DEFAULT_WELCOME_CONTENT = {
|
||||
template: "multistage",
|
||||
screens: [
|
||||
{
|
||||
id: "AW_SET_DEFAULT",
|
||||
order: 0,
|
||||
content: {
|
||||
zap: true,
|
||||
title: {
|
||||
string_id: "onboarding-multistage-set-default-header",
|
||||
},
|
||||
subtitle: {
|
||||
string_id: "onboarding-multistage-set-default-subtitle",
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-set-default-primary-button-label",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
type: "SET_DEFAULT_BROWSER",
|
||||
},
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id:
|
||||
"onboarding-multistage-set-default-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
secondary_button_top: {
|
||||
text: {
|
||||
string_id: "onboarding-multistage-welcome-secondary-button-text",
|
||||
},
|
||||
label: {
|
||||
string_id: "onboarding-multistage-welcome-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
data: {
|
||||
entrypoint: "activity-stream-firstrun",
|
||||
},
|
||||
type: "SHOW_FIREFOX_ACCOUNTS",
|
||||
addFlowParams: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "AW_IMPORT_SETTINGS",
|
||||
order: 1,
|
||||
content: {
|
||||
zap: true,
|
||||
help_text: {
|
||||
text: {
|
||||
string_id: "onboarding-import-sites-disclaimer",
|
||||
},
|
||||
},
|
||||
title: {
|
||||
string_id: "onboarding-multistage-import-header",
|
||||
},
|
||||
subtitle: {
|
||||
string_id: "onboarding-multistage-import-subtitle",
|
||||
},
|
||||
tiles: {
|
||||
type: "topsites",
|
||||
showTitles: true,
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-import-primary-button-label",
|
||||
},
|
||||
action: {
|
||||
type: "SHOW_MIGRATION_WIZARD",
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-import-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "AW_CHOOSE_THEME",
|
||||
order: 2,
|
||||
content: {
|
||||
zap: true,
|
||||
title: {
|
||||
string_id: "onboarding-multistage-theme-header",
|
||||
},
|
||||
subtitle: {
|
||||
string_id: "onboarding-multistage-theme-subtitle",
|
||||
},
|
||||
tiles: {
|
||||
type: "theme",
|
||||
action: {
|
||||
theme: "<event>",
|
||||
},
|
||||
data: [
|
||||
{
|
||||
theme: "automatic",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-automatic",
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-automatic-2",
|
||||
},
|
||||
description: {
|
||||
string_id:
|
||||
"onboarding-multistage-theme-description-automatic-2",
|
||||
},
|
||||
},
|
||||
{
|
||||
theme: "light",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-light",
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-light-2",
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-light",
|
||||
},
|
||||
},
|
||||
{
|
||||
theme: "dark",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-dark",
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-dark-2",
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-dark",
|
||||
},
|
||||
},
|
||||
{
|
||||
theme: "alpenglow",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-alpenglow",
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-alpenglow-2",
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-alpenglow",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-primary-button-label2",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
theme: "automatic",
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Helper function to determine if Windows platform supports
|
||||
// automated pinning to taskbar.
|
||||
// See https://searchfox.org/mozilla-central/rev/002023eb262be9db3479142355e1675645d52d52/browser/components/shell/nsIWindowsShellService.idl#17
|
||||
function canPinCurrentAppToTaskbar() {
|
||||
try {
|
||||
ShellService.QueryInterface(
|
||||
Ci.nsIWindowsShellService
|
||||
).checkPinCurrentAppToTaskbar();
|
||||
return true;
|
||||
} catch (e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function getAddonFromRepository(data) {
|
||||
const [addonInfo] = await AddonRepository.getAddonsByIDs([data]);
|
||||
if (addonInfo.sourceURI.scheme !== "https") {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: addonInfo.name,
|
||||
url: addonInfo.sourceURI.spec,
|
||||
iconURL: addonInfo.icons["64"] || addonInfo.icons["32"],
|
||||
};
|
||||
}
|
||||
|
||||
async function getAddonInfo(attrbObj) {
|
||||
let { content, source } = attrbObj;
|
||||
try {
|
||||
if (!content || source !== "addons.mozilla.org") {
|
||||
return null;
|
||||
}
|
||||
// Attribution data can be double encoded
|
||||
while (content.includes("%")) {
|
||||
try {
|
||||
const result = decodeURIComponent(content);
|
||||
if (result === content) {
|
||||
break;
|
||||
}
|
||||
content = result;
|
||||
} catch (e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return await getAddonFromRepository(content);
|
||||
} catch (e) {
|
||||
Cu.reportError("Failed to get the latest add-on version for Return to AMO");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function hasAMOAttribution(attributionData) {
|
||||
return (
|
||||
attributionData &&
|
||||
attributionData.campaign === "non-fx-button" &&
|
||||
attributionData.source === "addons.mozilla.org"
|
||||
);
|
||||
}
|
||||
|
||||
async function formatAttributionData(attribution) {
|
||||
if (hasAMOAttribution(attribution)) {
|
||||
let extraProps = await getAddonInfo(attribution);
|
||||
if (extraProps) {
|
||||
return extraProps;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getAttributionContent() {
|
||||
let attributionContent = await formatAttributionData(
|
||||
await AttributionCode.getAttrDataAsync()
|
||||
);
|
||||
|
||||
if (attributionContent) {
|
||||
return { ...attributionContent, template: "return_to_amo" };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const RULES = [
|
||||
{
|
||||
description: "Windows pin to task bar screen",
|
||||
getDefaults() {
|
||||
if (
|
||||
!Services.prefs.getBoolPref(DID_SEE_ABOUT_WELCOME_PREF, true) &&
|
||||
canPinCurrentAppToTaskbar()
|
||||
) {
|
||||
return { ...DEFAULT_WELCOME_CONTENT };
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Default AW content",
|
||||
getDefaults() {
|
||||
return { ...DEFAULT_WELCOME_CONTENT };
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function getDefaults() {
|
||||
for (const rule of RULES) {
|
||||
const result = rule.getDefaults();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const AboutWelcomeDefaults = {
|
||||
getDefaults,
|
||||
getAttributionContent,
|
||||
};
|
|
@ -7,8 +7,6 @@ import ReactDOM from "react-dom";
|
|||
import { MultiStageAboutWelcome } from "./components/MultiStageAboutWelcome";
|
||||
import { ReturnToAMO } from "./components/ReturnToAMO";
|
||||
|
||||
import { DEFAULT_WELCOME_CONTENT } from "../lib/aboutwelcome-utils";
|
||||
|
||||
class AboutWelcome extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -81,8 +79,6 @@ class AboutWelcome extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
AboutWelcome.defaultProps = DEFAULT_WELCOME_CONTENT;
|
||||
|
||||
// Computes messageId and UTMTerm info used in telemetry
|
||||
function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) {
|
||||
let messageId =
|
||||
|
@ -105,36 +101,27 @@ function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) {
|
|||
}
|
||||
|
||||
async function retrieveRenderContent() {
|
||||
// Check for featureConfig and retrieve content
|
||||
const featureConfig = await window.AWGetFeatureConfig();
|
||||
let aboutWelcomeProps;
|
||||
|
||||
if (!featureConfig.screens) {
|
||||
const attribution = await window.AWGetAttributionData();
|
||||
aboutWelcomeProps = {
|
||||
template: attribution.template,
|
||||
...attribution.extraProps,
|
||||
};
|
||||
} else {
|
||||
// If screens is defined then we have multi stage AW content to show
|
||||
aboutWelcomeProps = featureConfig.screens ? featureConfig : {};
|
||||
}
|
||||
|
||||
// Set design if exists in featureConfig
|
||||
if (featureConfig.design && !aboutWelcomeProps?.design) {
|
||||
aboutWelcomeProps = { ...aboutWelcomeProps, design: featureConfig.design };
|
||||
}
|
||||
// Feature config includes:
|
||||
// user prefs
|
||||
// experiment data
|
||||
// attribution data
|
||||
// defaults
|
||||
let featureConfig = await window.AWGetFeatureConfig();
|
||||
|
||||
let { messageId, UTMTerm } = ComputeTelemetryInfo(
|
||||
aboutWelcomeProps,
|
||||
featureConfig,
|
||||
featureConfig.slug,
|
||||
featureConfig.branch && featureConfig.branch.slug
|
||||
);
|
||||
return { aboutWelcomeProps, messageId, UTMTerm };
|
||||
return { featureConfig, messageId, UTMTerm };
|
||||
}
|
||||
|
||||
async function mount() {
|
||||
let { aboutWelcomeProps, messageId, UTMTerm } = await retrieveRenderContent();
|
||||
let {
|
||||
featureConfig: aboutWelcomeProps,
|
||||
messageId,
|
||||
UTMTerm,
|
||||
} = await retrieveRenderContent();
|
||||
ReactDOM.render(
|
||||
<AboutWelcome
|
||||
messageId={messageId}
|
||||
|
|
|
@ -80,163 +80,3 @@ export const DEFAULT_RTAMO_CONTENT = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_WELCOME_CONTENT = {
|
||||
template: "multistage",
|
||||
screens: [
|
||||
{
|
||||
id: "AW_SET_DEFAULT",
|
||||
order: 0,
|
||||
content: {
|
||||
zap: true,
|
||||
title: {
|
||||
string_id: "onboarding-multistage-set-default-header",
|
||||
},
|
||||
subtitle: { string_id: "onboarding-multistage-set-default-subtitle" },
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-set-default-primary-button-label",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
type: "SET_DEFAULT_BROWSER",
|
||||
},
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id:
|
||||
"onboarding-multistage-set-default-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
secondary_button_top: {
|
||||
text: {
|
||||
string_id: "onboarding-multistage-welcome-secondary-button-text",
|
||||
},
|
||||
label: {
|
||||
string_id: "onboarding-multistage-welcome-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
data: { entrypoint: "activity-stream-firstrun" },
|
||||
type: "SHOW_FIREFOX_ACCOUNTS",
|
||||
addFlowParams: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "AW_IMPORT_SETTINGS",
|
||||
order: 1,
|
||||
content: {
|
||||
zap: true,
|
||||
help_text: {
|
||||
text: { string_id: "onboarding-import-sites-disclaimer" },
|
||||
},
|
||||
title: { string_id: "onboarding-multistage-import-header" },
|
||||
subtitle: { string_id: "onboarding-multistage-import-subtitle" },
|
||||
tiles: {
|
||||
type: "topsites",
|
||||
showTitles: true,
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-import-primary-button-label",
|
||||
},
|
||||
action: {
|
||||
type: "SHOW_MIGRATION_WIZARD",
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-import-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "AW_CHOOSE_THEME",
|
||||
order: 2,
|
||||
content: {
|
||||
zap: true,
|
||||
title: { string_id: "onboarding-multistage-theme-header" },
|
||||
subtitle: { string_id: "onboarding-multistage-theme-subtitle" },
|
||||
tiles: {
|
||||
type: "theme",
|
||||
action: {
|
||||
theme: "<event>",
|
||||
},
|
||||
data: [
|
||||
{
|
||||
theme: "automatic",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-automatic",
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-automatic-2",
|
||||
},
|
||||
description: {
|
||||
string_id:
|
||||
"onboarding-multistage-theme-description-automatic-2",
|
||||
},
|
||||
},
|
||||
{
|
||||
theme: "light",
|
||||
label: { string_id: "onboarding-multistage-theme-label-light" },
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-light-2",
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-light",
|
||||
},
|
||||
},
|
||||
{
|
||||
theme: "dark",
|
||||
label: { string_id: "onboarding-multistage-theme-label-dark" },
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-dark-2",
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-dark",
|
||||
},
|
||||
},
|
||||
{
|
||||
theme: "alpenglow",
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-label-alpenglow",
|
||||
},
|
||||
tooltip: {
|
||||
string_id: "onboarding-multistage-theme-tooltip-alpenglow-2",
|
||||
},
|
||||
description: {
|
||||
string_id: "onboarding-multistage-theme-description-alpenglow",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-primary-button-label2",
|
||||
},
|
||||
action: {
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-multistage-theme-secondary-button-label",
|
||||
},
|
||||
action: {
|
||||
theme: "automatic",
|
||||
navigate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -847,15 +847,15 @@ add_task(async function test_AWMultistage_Import() {
|
|||
);
|
||||
});
|
||||
|
||||
add_task(async function test_onContentMessage() {
|
||||
add_task(async function test_updatesPrefOnAWOpen() {
|
||||
Services.prefs.setBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false);
|
||||
await setAboutWelcomePref(true);
|
||||
|
||||
await openAboutWelcome();
|
||||
Assert.equal(
|
||||
Services.prefs.getBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false),
|
||||
true,
|
||||
"Pref was set"
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() =>
|
||||
Services.prefs.getBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false) === true,
|
||||
"Updated pref to seen AW"
|
||||
);
|
||||
Services.prefs.clearUserPref(DID_SEE_ABOUT_WELCOME_PREF);
|
||||
});
|
||||
|
|
|
@ -5,10 +5,10 @@ import {
|
|||
} from "content-src/aboutwelcome/components/MultiStageAboutWelcome";
|
||||
import React from "react";
|
||||
import { shallow, mount } from "enzyme";
|
||||
import {
|
||||
DEFAULT_WELCOME_CONTENT,
|
||||
AboutWelcomeUtils,
|
||||
} from "content-src/lib/aboutwelcome-utils";
|
||||
import { AboutWelcomeDefaults } from "aboutwelcome/lib/AboutWelcomeDefaults.jsm";
|
||||
import { AboutWelcomeUtils } from "content-src/lib/aboutwelcome-utils";
|
||||
|
||||
const DEFAULT_WELCOME_CONTENT = AboutWelcomeDefaults.getDefaults();
|
||||
|
||||
describe("MultiStageAboutWelcome module", () => {
|
||||
let globals;
|
||||
|
|
|
@ -4,10 +4,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { AboutWelcomeChild } = ChromeUtils.import(
|
||||
"resource:///actors/AboutWelcomeChild.jsm"
|
||||
const { AboutWelcomeDefaults } = ChromeUtils.import(
|
||||
"resource://activity-stream/aboutwelcome/lib/AboutWelcomeDefaults.jsm"
|
||||
);
|
||||
const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm");
|
||||
const { AttributionCode } = ChromeUtils.import(
|
||||
"resource:///modules/AttributionCode.jsm"
|
||||
);
|
||||
const { AddonRepository } = ChromeUtils.import(
|
||||
"resource://gre/modules/addons/AddonRepository.jsm"
|
||||
);
|
||||
|
||||
const TEST_ATTRIBUTION_DATA = {
|
||||
source: "addons.mozilla.org",
|
||||
|
@ -17,22 +23,32 @@ const TEST_ATTRIBUTION_DATA = {
|
|||
};
|
||||
|
||||
add_task(async function test_handleAddonInfoNotFound() {
|
||||
let AWChild = new AboutWelcomeChild();
|
||||
const stub = sinon.stub(AWChild, "getAddonInfo").resolves(null);
|
||||
let result = await AWChild.formatAttributionData(TEST_ATTRIBUTION_DATA);
|
||||
let sandbox = sinon.createSandbox();
|
||||
const stub = sandbox.stub(AttributionCode, "getAttrDataAsync").resolves(null);
|
||||
let result = await AboutWelcomeDefaults.getAttributionContent();
|
||||
equal(stub.callCount, 1, "Call was made");
|
||||
equal(result.template, undefined, "No template returned");
|
||||
equal(result, null, "No data is returned");
|
||||
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
add_task(async function test_formatAttributionData() {
|
||||
let AWChild = new AboutWelcomeChild();
|
||||
let sandbox = sinon.createSandbox();
|
||||
const TEST_ADDON_INFO = {
|
||||
sourceURI: { scheme: "https", spec: "https://test.xpi" },
|
||||
name: "Test Add-on",
|
||||
url: "https://test.xpi",
|
||||
iconURL: "http://test.svg",
|
||||
icons: { "64": "http://test.svg" },
|
||||
};
|
||||
sinon.stub(AWChild, "getAddonInfo").resolves(TEST_ADDON_INFO);
|
||||
let result = await AWChild.formatAttributionData(TEST_ATTRIBUTION_DATA);
|
||||
sandbox
|
||||
.stub(AttributionCode, "getAttrDataAsync")
|
||||
.resolves(TEST_ATTRIBUTION_DATA);
|
||||
sandbox.stub(AddonRepository, "getAddonsByIDs").resolves([TEST_ADDON_INFO]);
|
||||
let result = await AboutWelcomeDefaults.getAttributionContent(
|
||||
TEST_ATTRIBUTION_DATA
|
||||
);
|
||||
equal(AddonRepository.getAddonsByIDs.callCount, 1, "Retrieve addon content");
|
||||
equal(result.template, "return_to_amo", "RTAMO template returned");
|
||||
equal(result.extraProps, TEST_ADDON_INFO, "AddonInfo returned");
|
||||
equal(result.name, TEST_ADDON_INFO.name, "AddonInfo returned");
|
||||
|
||||
sandbox.restore();
|
||||
});
|
||||
|
|
|
@ -324,7 +324,7 @@ class ExperimentFeature {
|
|||
static MANIFEST = MANIFEST;
|
||||
constructor(featureId, manifest) {
|
||||
this.featureId = featureId;
|
||||
this.defaultPrefValues = {};
|
||||
this.prefGetters = {};
|
||||
this.manifest = manifest || ExperimentFeature.MANIFEST[featureId];
|
||||
if (!this.manifest) {
|
||||
Cu.reportError(
|
||||
|
@ -353,7 +353,7 @@ class ExperimentFeature {
|
|||
const { type, fallbackPref } = variables[key];
|
||||
if (fallbackPref) {
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this.defaultPrefValues,
|
||||
this.prefGetters,
|
||||
key,
|
||||
fallbackPref,
|
||||
null,
|
||||
|
@ -369,6 +369,22 @@ class ExperimentFeature {
|
|||
});
|
||||
}
|
||||
|
||||
_getUserPrefsValues() {
|
||||
let userPrefs = {};
|
||||
Object.keys(this.manifest?.variables || {}).forEach(variable => {
|
||||
if (
|
||||
this.manifest.variables[variable].fallbackPref &&
|
||||
Services.prefs.prefHasUserValue(
|
||||
this.manifest.variables[variable].fallbackPref
|
||||
)
|
||||
) {
|
||||
userPrefs[variable] = this.prefGetters[variable];
|
||||
}
|
||||
});
|
||||
|
||||
return userPrefs;
|
||||
}
|
||||
|
||||
ready() {
|
||||
return ExperimentAPI.ready();
|
||||
}
|
||||
|
@ -405,18 +421,18 @@ class ExperimentFeature {
|
|||
* @param {{sendExposureEvent: boolean, defaultValue?: any}} options
|
||||
* @returns {obj} The feature value
|
||||
*/
|
||||
getValue({ sendExposureEvent, defaultValue = null } = {}) {
|
||||
getValue({ sendExposureEvent } = {}) {
|
||||
// Any user pref will override any other configuration
|
||||
let userPrefs = this._getUserPrefsValues();
|
||||
const branch = ExperimentAPI.activateBranch({
|
||||
featureId: this.featureId,
|
||||
sendExposureEvent,
|
||||
});
|
||||
if (branch?.feature?.value) {
|
||||
return branch.feature.value;
|
||||
return { ...branch.feature.value, ...userPrefs };
|
||||
}
|
||||
|
||||
return Object.keys(this.defaultPrefValues).length
|
||||
? this.defaultPrefValues
|
||||
: defaultValue;
|
||||
return this.prefGetters;
|
||||
}
|
||||
|
||||
recordExposureEvent() {
|
||||
|
@ -442,11 +458,12 @@ class ExperimentFeature {
|
|||
featureId: this.featureId,
|
||||
}),
|
||||
fallbackPrefs:
|
||||
this.defaultPrefValues &&
|
||||
Object.keys(this.defaultPrefValues).map(prefName => [
|
||||
this.prefGetters &&
|
||||
Object.keys(this.prefGetters).map(prefName => [
|
||||
prefName,
|
||||
this.defaultPrefValues[prefName],
|
||||
this.prefGetters[prefName],
|
||||
]),
|
||||
userPrefs: this._getUserPrefsValues(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,8 +67,7 @@ Defaults values inline:
|
|||
```jsx
|
||||
feature.isEnabled({ defaultValue: true });
|
||||
|
||||
// Default values work here too
|
||||
const { skipFocus } = feature.getValue({ defaultValue: { skipFocus: false } });
|
||||
const { skipFocus } = feature.getValue() || {};
|
||||
```
|
||||
|
||||
Listen to changes:
|
||||
|
|
|
@ -37,6 +37,11 @@ async function setupForExperimentFeature() {
|
|||
return { sandbox, manager };
|
||||
}
|
||||
|
||||
function setDefaultBranch(pref, value) {
|
||||
let branch = Services.prefs.getDefaultBranch("");
|
||||
branch.setStringPref(pref, value);
|
||||
}
|
||||
|
||||
const TEST_FALLBACK_PREF = "testprefbranch.config";
|
||||
const FAKE_FEATURE_MANIFEST = {
|
||||
enabledFallbackPref: "testprefbranch.enabled",
|
||||
|
@ -106,14 +111,6 @@ add_task(async function test_ExperimentFeature_getValue() {
|
|||
|
||||
const featureInstance = new ExperimentFeature("foo", FAKE_FEATURE_MANIFEST);
|
||||
|
||||
Services.prefs.clearUserPref("testprefbranch.value");
|
||||
|
||||
Assert.deepEqual(
|
||||
featureInstance.getValue({ defaultValue: { hello: 1 } }),
|
||||
{ hello: 1 },
|
||||
"should return the defaultValue if no fallback pref is set"
|
||||
);
|
||||
|
||||
Services.prefs.setStringPref(TEST_FALLBACK_PREF, `{"bar": 123}`);
|
||||
|
||||
Assert.deepEqual(
|
||||
|
@ -145,7 +142,7 @@ add_task(
|
|||
|
||||
manager.store.addExperiment(expected);
|
||||
|
||||
Services.prefs.setStringPref(TEST_FALLBACK_PREF, `{"bar": 123}`);
|
||||
setDefaultBranch(TEST_FALLBACK_PREF, `{"bar": 123}`);
|
||||
|
||||
Assert.deepEqual(
|
||||
featureInstance.getValue(),
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
"use strict";
|
||||
|
||||
const { ExperimentAPI, ExperimentFeature } = ChromeUtils.import(
|
||||
"resource://nimbus/ExperimentAPI.jsm"
|
||||
);
|
||||
const { ExperimentFakes } = ChromeUtils.import(
|
||||
"resource://testing-common/NimbusTestUtils.jsm"
|
||||
);
|
||||
const { TestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/TestUtils.jsm"
|
||||
);
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
async function setupForExperimentFeature() {
|
||||
const sandbox = sinon.createSandbox();
|
||||
const manager = ExperimentFakes.manager();
|
||||
await manager.onStartup();
|
||||
|
||||
sandbox.stub(ExperimentAPI, "_store").get(() => manager.store);
|
||||
|
||||
return { sandbox, manager };
|
||||
}
|
||||
|
||||
const FEATURE_ID = "aboutwelcome";
|
||||
const TEST_FALLBACK_PREF = "browser.aboutwelcome.screens";
|
||||
const FAKE_FEATURE_MANIFEST = {
|
||||
variables: {
|
||||
screens: {
|
||||
type: "json",
|
||||
fallbackPref: TEST_FALLBACK_PREF,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
add_task(async function test_ExperimentFeature_getValue_prefsOverDefaults() {
|
||||
const { sandbox } = await setupForExperimentFeature();
|
||||
|
||||
const featureInstance = new ExperimentFeature(
|
||||
FEATURE_ID,
|
||||
FAKE_FEATURE_MANIFEST
|
||||
);
|
||||
|
||||
Services.prefs.clearUserPref(TEST_FALLBACK_PREF);
|
||||
|
||||
Assert.equal(
|
||||
featureInstance.getValue().screens?.length,
|
||||
undefined,
|
||||
"pref is not set"
|
||||
);
|
||||
|
||||
Services.prefs.setStringPref(TEST_FALLBACK_PREF, "[]");
|
||||
|
||||
Assert.deepEqual(
|
||||
featureInstance.getValue().screens.length,
|
||||
0,
|
||||
"should return the user pref value over the defaults"
|
||||
);
|
||||
|
||||
Services.prefs.clearUserPref(TEST_FALLBACK_PREF);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
add_task(async function test_ExperimentFeature_getValue_prefsOverExperiment() {
|
||||
const { sandbox, manager } = await setupForExperimentFeature();
|
||||
const recipe = ExperimentFakes.experiment("awexperiment", {
|
||||
branch: {
|
||||
slug: "treatment",
|
||||
feature: {
|
||||
featureId: "aboutwelcome",
|
||||
enabled: true,
|
||||
value: { screens: ["test-value"] },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
manager.store.addExperiment(recipe);
|
||||
|
||||
const featureInstance = new ExperimentFeature(
|
||||
FEATURE_ID,
|
||||
FAKE_FEATURE_MANIFEST
|
||||
);
|
||||
|
||||
Services.prefs.clearUserPref(TEST_FALLBACK_PREF);
|
||||
|
||||
Assert.ok(
|
||||
!!featureInstance.getValue().screens,
|
||||
"should return the AW experiment value"
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
featureInstance.getValue().screens[0],
|
||||
"test-value",
|
||||
"should return the AW experiment value"
|
||||
);
|
||||
|
||||
Services.prefs.setStringPref(TEST_FALLBACK_PREF, "[]");
|
||||
|
||||
Assert.deepEqual(
|
||||
featureInstance.getValue().screens.length,
|
||||
0,
|
||||
"should return the user pref value"
|
||||
);
|
||||
|
||||
Services.prefs.clearUserPref(TEST_FALLBACK_PREF);
|
||||
manager.store._deleteForTests(recipe.slug);
|
||||
sandbox.restore();
|
||||
});
|
|
@ -15,5 +15,6 @@ support-files =
|
|||
[test_SharedDataMap.js]
|
||||
[test_ExperimentAPI.js]
|
||||
[test_ExperimentAPI_ExperimentFeature.js]
|
||||
[test_ExperimentAPI_ExperimentFeature_getValue.js]
|
||||
[test_RemoteSettingsExperimentLoader.js]
|
||||
[test_RemoteSettingsExperimentLoader_updateRecipes.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче