From 8acab062759bba5c89668457b404d60b8113be28 Mon Sep 17 00:00:00 2001 From: leuryr Date: Fri, 11 Sep 2020 14:09:20 -0400 Subject: [PATCH 1/4] Implement A/B test for hub with modified tabs --- .../SideNavigationView/SideNavigationView.jsx | 8 ++++++- .../SideNavigationViewContainer.jsx | 10 ++++++++- app/hub/index.jsx | 6 +++++- manifest.json | 3 ++- src/background.js | 21 ++++++++++++++++--- src/classes/ConfData.js | 1 + 6 files changed, 42 insertions(+), 7 deletions(-) diff --git a/app/hub/Views/SideNavigationView/SideNavigationView.jsx b/app/hub/Views/SideNavigationView/SideNavigationView.jsx index 525fb7af2..c61d7871a 100644 --- a/app/hub/Views/SideNavigationView/SideNavigationView.jsx +++ b/app/hub/Views/SideNavigationView/SideNavigationView.jsx @@ -15,6 +15,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import ClassNames from 'classnames'; import { NavLink } from 'react-router-dom'; +import QueryString from 'query-string'; + +// Flag to display alternate hub view (used in A/B testing) +const ah = (QueryString.parse(window.location.search).ah === 'true') || false; /** * Helper render function for rendering a list item for the Navigation Main section @@ -94,10 +98,12 @@ const SideNavigationView = (props) => { disabled: disableNav, }); + const menuClassNames = ClassNames(`SideNavigation__menu ${!ah ? 'flex-child-grow' : ''} flex-container flex-dir-column`); + return (
-
+
{menuItems.map(item => _renderMenuItem(item, disableNav))}
diff --git a/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx b/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx index 644f773b1..4e4644132 100644 --- a/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx +++ b/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx @@ -13,12 +13,16 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import QueryString from 'query-string'; import SideNavigationView from './SideNavigationView'; import globals from '../../../../src/classes/Globals'; const { IS_CLIQZ, BROWSER_INFO } = globals; const IS_ANDROID = (BROWSER_INFO.os === 'android'); +// Flag to display alternate hub view (used in A/B testing) +const ah = (QueryString.parse(window.location.search).ah === 'true') || false; + /** * @class Implement the Side Navigation View for the Ghostery Hub * @extends Component @@ -50,7 +54,10 @@ class SideNavigationViewContainer extends Component { const { user, location } = this.props; const disableRegEx = /^(\/setup(?!\/4$))|(\/tutorial(?!\/6$))/; - const menuItems = [ + const menuItems = ah ? [ + { href: '/home', icon: 'home', text: t('hub_side_navigation_home') }, + { href: '/setup', icon: 'setup', text: t('customize_setup') }, + ] : [ { href: '/home', icon: 'home', text: t('hub_side_navigation_home') }, { href: '/', icon: 'shield', text: t('hub_side_navigation_upgrade_plan') }, { href: '/setup', icon: 'setup', text: t('customize_setup') }, @@ -59,6 +66,7 @@ class SideNavigationViewContainer extends Component { ...((IS_CLIQZ || IS_ANDROID) ? [] : [{ href: '/rewards', icon: 'rewards', text: t('hub_side_navigation_rewards') }]), ...((IS_ANDROID) ? [] : [{ href: '/products', icon: 'products', text: t('hub_side_navigation_products') }]) ]; + const bottomItems = user ? [ { id: 'email', href: `${globals.ACCOUNT_BASE_URL}/`, text: user.email }, { id: 'logout', text: t('sign_out'), clickHandler: this._handleLogoutClick }, diff --git a/app/hub/index.jsx b/app/hub/index.jsx index 6e8f215ab..409c6c7e4 100644 --- a/app/hub/index.jsx +++ b/app/hub/index.jsx @@ -17,6 +17,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { HashRouter as Router, Route } from 'react-router-dom'; import { Provider } from 'react-redux'; +import QueryString from 'query-string'; import createStore from './createStore'; // Containers @@ -34,6 +35,9 @@ import UpgradePlanView from './Views/UpgradePlanView'; const store = createStore(); +// Flag to display alternate hub view (used in A/B testing) +const ah = (QueryString.parse(window.location.search).ah === 'true') || false; + /** * Top-Level Component for the Ghostery Hub * @memberof HubComponents @@ -41,7 +45,7 @@ const store = createStore(); const Hub = () => ( - + diff --git a/manifest.json b/manifest.json index 0cc3b9ee0..5d66a00a0 100644 --- a/manifest.json +++ b/manifest.json @@ -114,5 +114,6 @@ "cliqz/offers-templates/reminder.html", "cliqz/offers-templates/checkout.html", "cliqz/offers-templates/control-center.html" - ] + ], + "debug": true } diff --git a/src/background.js b/src/background.js index f17467dfd..e18381ef8 100644 --- a/src/background.js +++ b/src/background.js @@ -1129,6 +1129,17 @@ function setupHubPromoABTest() { } } +/** + * Set option for Hub Layout A/B test based + * on the results returned from the abtest endpoint. + * @memberOf Background + */ +function setupHubLayoutABTest() { + if (abtest.hasTest('hub_alternate')) { + conf.hub_layout = 'alternate'; + } +} + /** * Adjust antitracking parameters based on the current state * of ABTest and availability of Human Web. @@ -1151,6 +1162,7 @@ function setupABTest() { } setupHubPromoABTest(); + setupHubLayoutABTest(); } /** @@ -1759,10 +1771,13 @@ function initializeGhosteryModules() { // We need to do this after running scheduledTasks for the first time // because of an A/B test that determines which promo variant is shown in the Hub on install if (globals.JUST_INSTALLED) { - const route = ((conf.hub_promo_variant === 'upgrade' || conf.hub_promo_variant === 'not_yet_set') && !IS_ANDROID) ? '' : '#home'; - const showPremiumPromoModal = (conf.hub_promo_variant === 'midnight' && !IS_ANDROID); + // const route = ((conf.hub_promo_variant === 'upgrade' || conf.hub_promo_variant === 'not_yet_set') && !IS_ANDROID) ? '' : '#home'; + // const showPremiumPromoModal = (conf.hub_promo_variant === 'midnight' && !IS_ANDROID); + const showAlternateHub = conf.hub_layout === 'alternate'; + const route = conf.hub_layout === 'alternate' ? '#home' : ''; chrome.tabs.create({ - url: chrome.runtime.getURL(`./app/templates/hub.html?$justInstalled=true&pm=${showPremiumPromoModal}${route}`), + // url: chrome.runtime.getURL(`./app/templates/hub.html?$justInstalled=true&pm=${showPremiumPromoModal}${route}`), + url: chrome.runtime.getURL(`./app/templates/hub.html?$justInstalled=true&ah=${showAlternateHub}${route}`), active: true }); } diff --git a/src/classes/ConfData.js b/src/classes/ConfData.js index c542d5090..9984d3992 100644 --- a/src/classes/ConfData.js +++ b/src/classes/ConfData.js @@ -117,6 +117,7 @@ class ConfData { _initProperty('expand_all_trackers', true); _initProperty('hide_alert_trusted', false); _initProperty('hub_promo_variant', 'not_yet_set'); + _initProperty('hub_layout', 'default'); _initProperty('ignore_first_party', true); _initProperty('import_callout_dismissed', true); _initProperty('insights_promo_modal_last_seen', 0); From 3f05ae27c3985bf4435aaf77e0338b63f74fadc2 Mon Sep 17 00:00:00 2001 From: leuryr Date: Mon, 14 Sep 2020 10:22:51 -0400 Subject: [PATCH 2/4] Remove promo variant test, repurpose ping param --- src/background.js | 30 +++++++----------------------- src/classes/ABTest.js | 2 ++ src/classes/ConfData.js | 3 +-- src/classes/Metrics.js | 26 ++++++++++++-------------- 4 files changed, 22 insertions(+), 39 deletions(-) diff --git a/src/background.js b/src/background.js index e18381ef8..5e0cdf617 100644 --- a/src/background.js +++ b/src/background.js @@ -1110,33 +1110,21 @@ function getAntitrackingTestConfig() { }; } -/** - * Set option for Hub promo A/B/C test based - * on the results returned from the abtest endpoint. - * @memberOf Background - * - * @return {Object} Hub promotion configuration parameters - */ -function setupHubPromoABTest() { - if (conf.hub_promo_variant !== 'not_yet_set') return; - - if (abtest.hasTest('hub_plain')) { - conf.hub_promo_variant = 'plain'; - } else if (abtest.hasTest('hub_midnight')) { - conf.hub_promo_variant = 'midnight'; - } else { - conf.hub_promo_variant = 'upgrade'; - } -} - /** * Set option for Hub Layout A/B test based * on the results returned from the abtest endpoint. * @memberOf Background */ function setupHubLayoutABTest() { + if ( + !abtest.hasBeenFetched + || conf.hub_layout !== 'not_yet_set' + ) { return; } + if (abtest.hasTest('hub_alternate')) { conf.hub_layout = 'alternate'; + } else { + conf.hub_layout = 'default'; } } @@ -1161,7 +1149,6 @@ function setupABTest() { cliqz.prefs.set('attrackBloomFilter', false); } - setupHubPromoABTest(); setupHubLayoutABTest(); } @@ -1771,12 +1758,9 @@ function initializeGhosteryModules() { // We need to do this after running scheduledTasks for the first time // because of an A/B test that determines which promo variant is shown in the Hub on install if (globals.JUST_INSTALLED) { - // const route = ((conf.hub_promo_variant === 'upgrade' || conf.hub_promo_variant === 'not_yet_set') && !IS_ANDROID) ? '' : '#home'; - // const showPremiumPromoModal = (conf.hub_promo_variant === 'midnight' && !IS_ANDROID); const showAlternateHub = conf.hub_layout === 'alternate'; const route = conf.hub_layout === 'alternate' ? '#home' : ''; chrome.tabs.create({ - // url: chrome.runtime.getURL(`./app/templates/hub.html?$justInstalled=true&pm=${showPremiumPromoModal}${route}`), url: chrome.runtime.getURL(`./app/templates/hub.html?$justInstalled=true&ah=${showAlternateHub}${route}`), active: true }); diff --git a/src/classes/ABTest.js b/src/classes/ABTest.js index df7a235fb..1236310ce 100644 --- a/src/classes/ABTest.js +++ b/src/classes/ABTest.js @@ -27,6 +27,7 @@ const { BROWSER_INFO, CMP_BASE_URL, EXTENSION_VERSION } = globals; class ABTest { constructor() { this.tests = {}; + this.hasBeenFetched = false; } /** @@ -63,6 +64,7 @@ class ABTest { (tests, test) => Object.assign(tests, { [test.name]: test.data }), {} ); + this.hasBeenFetched = true; } else { log('A/B Tests: no tests found.'); } diff --git a/src/classes/ConfData.js b/src/classes/ConfData.js index 9984d3992..f50f64145 100644 --- a/src/classes/ConfData.js +++ b/src/classes/ConfData.js @@ -116,8 +116,7 @@ class ConfData { _initProperty('enable_smart_block', true); _initProperty('expand_all_trackers', true); _initProperty('hide_alert_trusted', false); - _initProperty('hub_promo_variant', 'not_yet_set'); - _initProperty('hub_layout', 'default'); + _initProperty('hub_layout', 'not_yet_set'); _initProperty('ignore_first_party', true); _initProperty('import_callout_dismissed', true); _initProperty('insights_promo_modal_last_seen', 0); diff --git a/src/classes/Metrics.js b/src/classes/Metrics.js index 2ebf6d2e6..4fe9642ca 100644 --- a/src/classes/Metrics.js +++ b/src/classes/Metrics.js @@ -369,8 +369,8 @@ class Metrics { `&th=${encodeURIComponent(Metrics._getThemeValue().toString())}` + // New parameters for Ghostery 8.5.2 - // Hub Promo variant - `&hp=${encodeURIComponent(Metrics._getHubPromoVariant().toString())}` + + // Hub Layout View + `&hp=${encodeURIComponent(Metrics._getHubLayoutView().toString())}` + // Subscription Interval `&si=${encodeURIComponent(Metrics._getSubscriptionInterval().toString())}` + // Product ID Parameter @@ -534,20 +534,18 @@ class Metrics { } /** - * Get the Int associated with the Hub promo variant shown on install + * Get the Int associated with the Hub layout view shown on install * @private - * @return {number} Int associated with the Hub promo variant + * @return {number} Int associated with the Hub layout view */ - static _getHubPromoVariant() { - const { hub_promo_variant } = conf; - - switch (hub_promo_variant) { - case 'upgrade': - return 1; - case 'plain': - return 2; - case 'midnight': - return 3; + static _getHubLayoutView() { + const { hub_layout } = conf; + + switch (hub_layout) { + case 'default': + return 4; + case 'alternate': + return 5; case 'not_yet_set': default: return 0; From debd68010c3f57f5fcb9e0896daff8a19931f8b4 Mon Sep 17 00:00:00 2001 From: leuryr Date: Wed, 16 Sep 2020 14:50:26 -0400 Subject: [PATCH 3/4] Make comments and ternaries more clear --- app/hub/Views/SideNavigationView/SideNavigationView.jsx | 4 ++-- .../Views/SideNavigationView/SideNavigationViewContainer.jsx | 2 +- app/hub/index.jsx | 2 +- src/background.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/hub/Views/SideNavigationView/SideNavigationView.jsx b/app/hub/Views/SideNavigationView/SideNavigationView.jsx index c61d7871a..f30eacf28 100644 --- a/app/hub/Views/SideNavigationView/SideNavigationView.jsx +++ b/app/hub/Views/SideNavigationView/SideNavigationView.jsx @@ -17,7 +17,7 @@ import ClassNames from 'classnames'; import { NavLink } from 'react-router-dom'; import QueryString from 'query-string'; -// Flag to display alternate hub view (used in A/B testing) +// Flag to display alternate hub view (used for A/B testing ticket GH-2097) const ah = (QueryString.parse(window.location.search).ah === 'true') || false; /** @@ -98,7 +98,7 @@ const SideNavigationView = (props) => { disabled: disableNav, }); - const menuClassNames = ClassNames(`SideNavigation__menu ${!ah ? 'flex-child-grow' : ''} flex-container flex-dir-column`); + const menuClassNames = ClassNames(`SideNavigation__menu ${ah ? '' : 'flex-child-grow'} flex-container flex-dir-column`); return (
diff --git a/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx b/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx index 4e4644132..6ba4f00fa 100644 --- a/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx +++ b/app/hub/Views/SideNavigationView/SideNavigationViewContainer.jsx @@ -20,7 +20,7 @@ import globals from '../../../../src/classes/Globals'; const { IS_CLIQZ, BROWSER_INFO } = globals; const IS_ANDROID = (BROWSER_INFO.os === 'android'); -// Flag to display alternate hub view (used in A/B testing) +// Flag to display alternate hub view (used for A/B testing ticket GH-2097) const ah = (QueryString.parse(window.location.search).ah === 'true') || false; /** diff --git a/app/hub/index.jsx b/app/hub/index.jsx index 409c6c7e4..aa9e73e1d 100644 --- a/app/hub/index.jsx +++ b/app/hub/index.jsx @@ -35,7 +35,7 @@ import UpgradePlanView from './Views/UpgradePlanView'; const store = createStore(); -// Flag to display alternate hub view (used in A/B testing) +// Flag to display alternate hub view (used for A/B testing ticket GH-2097) const ah = (QueryString.parse(window.location.search).ah === 'true') || false; /** diff --git a/src/background.js b/src/background.js index 5e0cdf617..acc30028c 100644 --- a/src/background.js +++ b/src/background.js @@ -1759,7 +1759,7 @@ function initializeGhosteryModules() { // because of an A/B test that determines which promo variant is shown in the Hub on install if (globals.JUST_INSTALLED) { const showAlternateHub = conf.hub_layout === 'alternate'; - const route = conf.hub_layout === 'alternate' ? '#home' : ''; + const route = showAlternateHub ? '#home' : ''; chrome.tabs.create({ url: chrome.runtime.getURL(`./app/templates/hub.html?$justInstalled=true&ah=${showAlternateHub}${route}`), active: true From 848a650ee4e51b09d29702092983c035734b9e59 Mon Sep 17 00:00:00 2001 From: leuryr Date: Thu, 17 Sep 2020 14:25:46 -0400 Subject: [PATCH 4/4] Change param for A/B test --- src/classes/Metrics.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/classes/Metrics.js b/src/classes/Metrics.js index 4fe9642ca..2efe7b1a8 100644 --- a/src/classes/Metrics.js +++ b/src/classes/Metrics.js @@ -370,7 +370,7 @@ class Metrics { // New parameters for Ghostery 8.5.2 // Hub Layout View - `&hp=${encodeURIComponent(Metrics._getHubLayoutView().toString())}` + + `&t2=${encodeURIComponent(Metrics._getHubLayoutView().toString())}` + // Subscription Interval `&si=${encodeURIComponent(Metrics._getSubscriptionInterval().toString())}` + // Product ID Parameter @@ -543,9 +543,9 @@ class Metrics { switch (hub_layout) { case 'default': - return 4; + return 1; case 'alternate': - return 5; + return 2; case 'not_yet_set': default: return 0;