removed local trafficcop and replaced with npm package (#11746)

This commit is contained in:
Nathan Barrett 2022-06-08 20:22:34 -07:00 коммит произвёл GitHub
Родитель 204f2eb878
Коммит 3db2c9d226
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 22 добавлений и 343 удалений

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

@ -142,7 +142,7 @@ Traffic Cop experiments
More complex experiments, such as those that feature full page redesigns, or
multi-page user flows, should be implemented using `Traffic Cop
<https://github.com/mozilla/trafficcop/>`_. Traffic Cop small javascript
<https://github.com/mozmeao/trafficcop/>`_. Traffic Cop small javascript
library which will direct site traffic to different variants in a/b
experiments and make sure a visitor always sees the same variation.

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

@ -8,7 +8,7 @@ Accepted
## Context
Our current method for implementing multi-variant tests involves frequent, often non-trivial code changes to our most high traffic download pages. Prioritizing and running concurrent experiments on such pages is also often complex, increasing the risk of accidental breakage and making longer-term changes harder to roll out. Our current tool, [Traffic Cop](https://github.com/mozilla/trafficcop/), also requires significant custom code to accomodate these types of situations. Accurately measuring and reporting on the outcome of experiments is also a time consuming step of the process for our data science team, often requiring custom instrumentation and analysis.
Our current method for implementing multi-variant tests involves frequent, often non-trivial code changes to our most high traffic download pages. Prioritizing and running concurrent experiments on such pages is also often complex, increasing the risk of accidental breakage and making longer-term changes harder to roll out. Our current tool, [Traffic Cop](https://github.com/mozmeao/trafficcop/), also requires significant custom code to accomodate these types of situations. Accurately measuring and reporting on the outcome of experiments is also a time consuming step of the process for our data science team, often requiring custom instrumentation and analysis.
We would like to make our end-to-end experimentation process faster, with increased capacity, whilst also minimizing the performance impact and volume of code churn related to experiments running on our most important web pages.

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

@ -432,7 +432,7 @@ To configure switches for a demo branch. Follow the `configuration instructions
Traffic Cop
-----------
Currently, these switches are used to enable/disable `Traffic Cop <https://github.com/mozilla/trafficcop/>`_ experiments
Currently, these switches are used to enable/disable `Traffic Cop <https://github.com/mozmeao/trafficcop/>`_ experiments
on many pages of the site. We only add the Traffic Cop JavaScript snippet to a page when there is an active test. You
can see the current state of these switches and other configuration values in our `configuration
repo <https://mozmeao.github.io/www-config/configs/>`_.

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

@ -6,7 +6,7 @@
(function (Mozilla) {
'use strict';
require('@mozmeao/trafficcop');
var href = window.location.href;
var initTrafficCop = function () {

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

@ -6,6 +6,7 @@
(function (Mozilla) {
'use strict';
require('@mozmeao/trafficcop');
/* update dataLayer with experiment info */
var href = window.location.href;

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

@ -7,6 +7,7 @@
(function (Mozilla) {
'use strict';
require('@mozmeao/trafficcop');
var href = window.location.href;
var initTrafficCop = function () {

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

@ -7,6 +7,7 @@
(function (Mozilla) {
'use strict';
require('@mozmeao/trafficcop');
var href = window.location.href;
var initTrafficCop = function () {
@ -27,8 +28,8 @@
id: 'exp-wnp-99-en',
cookieExpires: 0,
variations: {
'v=1': 90,
'v=2': 10
'v=1': 10,
'v=2': 90
}
});
cop.init();

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

@ -1,333 +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 https://mozilla.org/MPL/2.0/.
*/
// Create namespace
if (typeof window.Mozilla === 'undefined') {
window.Mozilla = {};
}
/**
* Traffic Cop traffic redirector for A/B/x testing
*
* Example usage:
*
* var cop = new Mozilla.TrafficCop({
* id: 'exp_firefox_new_all_link',
* cookieExpires: 48,
* variations: {
* 'v=1': 25,
* 'v=2': 25,
* 'v=3': 25
* }
* });
*
* cop.init();
*
*
* @param Object config: Object literal containing the following:
* [String] id (required): Unique-ish string for cookie identification.
* Only needs to be unique to other currently running tests.
* [Number] cookieExpires (optional): Number of hours browser should
* remember the variation chosen for the user. Defaults to 24 (hours).
* A value of 0 will result in a session-length cookie.
* [Boolean] storeReferrerCookie (optional): Flag to specify whether or not
* original HTTP referrer should be placed in a cookie for later use.
* Defaults to true.
* [Function] customCallback (optional): Arbitrary function to run when
* a variation (or lack thereof) is chosen. This function will be
* passed the variation value (if chosen), or the value of
* noVariationCookieValue if no variation was chosen. *Specifying this
* function means no redirect will occur.*
* Object variations (required): Object holding key/value pairs of
* variations and their respective traffic percentages. Example:
*
* variations: {
* 'v=1': 20,
* 'v=2': 20,
* 'v=3': 20
* }
*/
Mozilla.TrafficCop = function(config) {
'use strict';
// make sure config is an object
this.config = (typeof config === 'object') ? config : {};
// store id
this.id = this.config.id;
// store variations
this.variations = this.config.variations;
// store total percentage of users targeted
this.totalPercentage = 0;
// store custom callback function (if supplied)
this.customCallback = (typeof this.config.customCallback === 'function') ? this.config.customCallback : null;
// store experiment cookie expiry (defaults to 24 hours)
this.cookieExpires = (this.config.cookieExpires !== undefined) ? this.config.cookieExpires : Mozilla.TrafficCop.defaultCookieExpires;
this.cookieExpiresDate = Mozilla.TrafficCop.generateCookieExpiresDate(this.cookieExpires);
// store pref to store referrer cookie on redirect (default to true)
this.storeReferrerCookie = (this.config.storeReferrerCookie === false) ? false : true;
this.chosenVariation = null;
// calculate and store total percentage of variations
for (var v in this.variations) {
if (this.variations.hasOwnProperty(v) && typeof this.variations[v] === 'number') {
// multiply by 100 to allow for percentages to the hundredth
// (and avoid floating point math errors)
this.totalPercentage += (this.variations[v] * 100);
}
}
this.totalPercentage = this.totalPercentage / 100;
return this;
};
Mozilla.TrafficCop.defaultCookieExpires = 24;
Mozilla.TrafficCop.noVariationCookieValue = 'novariation';
Mozilla.TrafficCop.referrerCookieName = 'mozilla-traffic-cop-original-referrer';
/*
* Initialize the traffic cop. Validates variations, ensures user is not
* currently viewing a variation, and (possibly) redirects to a variation
*/
Mozilla.TrafficCop.prototype.init = function() {
// respect the DNT
if (typeof Mozilla.dntEnabled === 'function' && Mozilla.dntEnabled()) {
return;
}
// If cookie helper is not defined or cookies are not enabled, do nothing.
if (typeof Mozilla.Cookies === 'undefined' || !Mozilla.Cookies.enabled()) {
return;
}
// make sure config is valid (id & variations present)
if (this.verifyConfig()) {
// determine which (if any) variation to choose for this user/experiment
this.chosenVariation = Mozilla.TrafficCop.chooseVariation(this.id, this.variations, this.totalPercentage);
// developer specified callback takes precedence
if (this.customCallback) {
this.initiateCustomCallbackRoutine();
// if no customCallback is supplied, initiate redirect routine
} else {
this.initiateRedirectRoutine();
}
}
};
/*
* Executes the logic around firing the custom callback specified by the
* developer.
*/
Mozilla.TrafficCop.prototype.initiateCustomCallbackRoutine = function() {
// set a cookie to remember the chosen variation
Mozilla.Cookies.setItem(this.id, this.chosenVariation || Mozilla.TrafficCop.noVariationCookieValue, this.cookieExpiresDate, undefined, undefined, false, 'lax');
// invoke the developer supplied callback, passing in the chosen variation
this.customCallback(this.chosenVariation);
};
/*
* Executes logic around determining variation to which the visitor will be
* redirected. May result in no redirect.
*/
Mozilla.TrafficCop.prototype.initiateRedirectRoutine = function() {
var redirectUrl;
// make sure current page doesn't match a variation
// (avoid infinite redirects)
if (!Mozilla.TrafficCop.isRedirectVariation(this.variations)) {
// roll the dice to see if user should be send to a variation
redirectUrl = Mozilla.TrafficCop.generateRedirectUrl(this.chosenVariation);
// if we get a variation, send the user and store a cookie
if (redirectUrl) {
// Store the original referrer for use after redirect takes place so analytics can
// keep track of where visitors are coming from.
// Traffic Cop does nothing with this referrer - it's up to the implementing site to
// send it on to an analytics platform (or whatever).
if (this.storeReferrerCookie) {
Mozilla.TrafficCop.setReferrerCookie(this.cookieExpiresDate);
// a previous experiment may have set the cookie, so explicitly remove it here
} else {
Mozilla.TrafficCop.clearReferrerCookie();
}
// set a cookie containing the chosen variation
Mozilla.Cookies.setItem(this.id, this.chosenVariation, this.cookieExpiresDate, undefined, undefined, false, 'lax');
Mozilla.TrafficCop.performRedirect(redirectUrl);
} else {
// if no variation, set a cookie so user isn't re-entered into
// the dice roll on next page load
Mozilla.Cookies.setItem(this.id, Mozilla.TrafficCop.noVariationCookieValue, this.cookieExpiresDate, undefined, undefined, false, 'lax');
// same as above - referrer cookie could be set from previous experiment, so best to clear
Mozilla.TrafficCop.clearReferrerCookie();
}
}
};
/*
* Ensures variations were provided and in total capture between 1 and 99%
* of users.
*/
Mozilla.TrafficCop.prototype.verifyConfig = function() {
if (!this.id || typeof this.id !== 'string') {
return false;
}
// make sure totalPercent is between 0 and 100
if (this.totalPercentage === 0 || this.totalPercentage > 100) {
return false;
}
// make sure cookieExpires is null or a number
if (typeof this.cookieExpires !== 'number') {
return false;
}
return true;
};
/*
* Generates an expiration date for the visitor's cookie.
* 'date' param used only for unit testing.
*/
Mozilla.TrafficCop.generateCookieExpiresDate = function(cookieExpires, date) {
// default to null, meaning a session-length cookie
var d = null;
if (cookieExpires > 0) {
d = date || new Date();
d.setHours(d.getHours() + cookieExpires);
}
return d;
};
/*
* Checks to see if user is currently viewing a variation.
*/
Mozilla.TrafficCop.isRedirectVariation = function(variations, queryString) {
var isVariation = false;
queryString = queryString || window.location.search;
// check queryString for presence of variation
for (var v in variations) {
if (queryString.indexOf('?' + v) > -1 || queryString.indexOf('&' + v) > -1) {
isVariation = true;
break;
}
}
return isVariation;
};
/*
* Returns the variation chosen for the current user/experiment.
*/
Mozilla.TrafficCop.chooseVariation = function(id, variations, totalPercentage) {
var rando;
var runningTotal;
var choice = Mozilla.TrafficCop.noVariationCookieValue;
// check to see if user has a cookie from a previously visited variation
// also make sure variation in cookie is still valid (you never know)
if (Mozilla.Cookies.hasItem(id) && (variations[Mozilla.Cookies.getItem(id)] || Mozilla.Cookies.getItem(id) === Mozilla.TrafficCop.noVariationCookieValue)) {
choice = Mozilla.Cookies.getItem(id);
// no cookie exists, or cookie has invalid variation, so choose a shiny new
// variation
} else {
// conjure a random float between 1 and 100 (inclusive)
rando = Math.floor(Math.random() * 10000) + 1;
rando = rando/100;
// make sure random number falls in the distribution range
if (rando <= totalPercentage) {
runningTotal = 0;
// loop through all variations
for (var v in variations) {
// check if random number falls within current variation range
if (rando <= (variations[v] + runningTotal)) {
// if so, we have found our variation
choice = v;
break;
}
// tally variation percentages for the next loop iteration
runningTotal += variations[v];
}
}
}
return choice;
};
/*
* Generates a random percentage (between 1 and 100, inclusive) and determines
* which (if any) variation should be matched.
*/
Mozilla.TrafficCop.generateRedirectUrl = function(chosenVariation, url) {
var hash;
var redirect;
var urlParts;
// url parameter only supplied for unit tests
url = url || window.location.href;
// strip hash from URL (if present)
if (url.indexOf('#') > -1) {
urlParts = url.split('#');
url = urlParts[0];
hash = urlParts[1];
}
// if a variation was chosen, construct a new URL
if (chosenVariation !== Mozilla.TrafficCop.noVariationCookieValue) {
redirect = url + (url.indexOf('?') > -1 ? '&' : '?') + chosenVariation;
// re-insert hash (if originally present)
if (hash) {
redirect += '#' + hash;
}
}
return redirect;
};
Mozilla.TrafficCop.performRedirect = function(redirectURL) {
window.location.href = redirectURL;
};
Mozilla.TrafficCop.setReferrerCookie = function(expirationDate, referrer) {
// in order of precedence, referrer should be:
// 1. custom value passed in via unit test
// 2. value of document.referrer
// 3. 'direct' string literal
referrer = referrer || Mozilla.TrafficCop.getDocumentReferrer() || 'direct';
Mozilla.Cookies.setItem(Mozilla.TrafficCop.referrerCookieName, referrer, expirationDate, undefined, undefined, false, 'lax');
};
Mozilla.TrafficCop.clearReferrerCookie = function() {
Mozilla.Cookies.removeItem(Mozilla.TrafficCop.referrerCookieName);
};
// wrapper around document.referrer for better unit testing
Mozilla.TrafficCop.getDocumentReferrer = function() {
return document.referrer;
};

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

@ -1204,7 +1204,6 @@
},
{
"files": [
"js/libs/mozilla-traffic-cop.js",
"js/firefox/welcome/welcome8-traffic-cop.js"
],
"name": "firefox_welcome_page8_traffic_cop"
@ -1219,14 +1218,13 @@
},
{
"files": [
"js/libs/mozilla-traffic-cop.js",
"js/firefox/whatsnew/whatsnew-99-en-experiment.js"
],
"name": "firefox_whatsnew_99_en_experiment"
},
{
"files": [
"js/libs/mozilla-traffic-cop.js",
"js/firefox/whatsnew/whatsnew-101-en-experiment.js"
],
"name": "firefox_whatsnew_101_en_experiment"
@ -1397,7 +1395,6 @@
},
{
"files": [
"js/libs/mozilla-traffic-cop.js",
"js/firefox/browsers/firefox-ios-sms-experiment.js"
],
"name": "firefox-ios-sms-experiment"

11
package-lock.json сгенерированный
Просмотреть файл

@ -13,6 +13,7 @@
"@babel/preset-env": "^7.16.11",
"@mozilla-protocol/core": "16.0.0",
"@mozilla/glean": "^1.0.0",
"@mozmeao/trafficcop": "^1.0.1",
"@sentry/browser": "^7.0.0",
"babel-loader": "^8.2.4",
"clean-webpack-plugin": "^4.0.0",
@ -1634,6 +1635,11 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/@mozmeao/trafficcop": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@mozmeao/trafficcop/-/trafficcop-1.0.1.tgz",
"integrity": "sha512-F+jHoF6kft/33jCR0fSdqynzf68+gNK8Dk+08LUhvoonq0F1EbcjHx/L8ObXqHg0QFow599LMdp/D649nQSd3w=="
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -10938,6 +10944,11 @@
}
}
},
"@mozmeao/trafficcop": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@mozmeao/trafficcop/-/trafficcop-1.0.1.tgz",
"integrity": "sha512-F+jHoF6kft/33jCR0fSdqynzf68+gNK8Dk+08LUhvoonq0F1EbcjHx/L8ObXqHg0QFow599LMdp/D649nQSd3w=="
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",

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

@ -8,6 +8,7 @@
"@babel/preset-env": "^7.16.11",
"@mozilla-protocol/core": "16.0.0",
"@mozilla/glean": "^1.0.0",
"@mozmeao/trafficcop": "^1.0.1",
"@sentry/browser": "^7.0.0",
"babel-loader": "^8.2.4",
"clean-webpack-plugin": "^4.0.0",