Skip to content

Commit

Permalink
Add optional check for experiment cookie to persist previously seen v…
Browse files Browse the repository at this point in the history
…ariations (Fixes #93) (#100)

* Add optional check for experiment cookie to persist previously seen variations (Fixes #93)
  • Loading branch information
alexgibson authored Feb 11, 2025
1 parent 06f5725 commit c7e5365
Show file tree
Hide file tree
Showing 21 changed files with 1,931 additions and 987 deletions.
22 changes: 13 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# HEAD

- **js:** Add optional check for experiment cookie to persist previously seen variations (#93).

# 3.0.0

- **js:** Remove use of cookies (#56).
- **js:** Migrate ESLint to use new flat config (#42).
- **js:** Migrate JS tests to jasmine-browser-runner (#40).
- **js:** Remove use of cookies (#56).
- **js:** Migrate ESLint to use new flat config (#42).
- **js:** Migrate JS tests to jasmine-browser-runner (#40).

# 2.0.1

- **js:** Update ESLint config to enforce common rules (#13).
- **js:** Transpile code to ES5 before publishing (#12).
- **demo:** Update demo pages to use `@mozmeao/cookie-helper` v1.1.0 (#10).
- **docs:** Update docs import `@mozmeao/cookie-helper`.
- **js:** Update ESLint config to enforce common rules (#13).
- **js:** Transpile code to ES5 before publishing (#12).
- **demo:** Update demo pages to use `@mozmeao/cookie-helper` v1.1.0 (#10).
- **docs:** Update docs import `@mozmeao/cookie-helper`.

# 2.0.0

- **js:** Remove `Mozilla` JS namespace from library (#4).
- **docs:** Update docs to have info about publishing to NPM
- **js:** Remove `Mozilla` JS namespace from library (#4).
- **docs:** Update docs to have info about publishing to NPM
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Install via npm: `npm install @mozmeao/trafficcop`

Import the library at your applications entrypoint via require, import or by using a global variable in your script tag:

- `import TrafficCop from '@mozmeao/trafficcop';`
- `const TrafficCop = require('@mozmeao/trafficcop');`
- `const TrafficCop = window.TrafficCop;`
- `import TrafficCop from '@mozmeao/trafficcop';`
- `const TrafficCop = require('@mozmeao/trafficcop');`
- `const TrafficCop = window.TrafficCop;`

### What does it do?

Expand All @@ -38,7 +38,7 @@ Most of the content experiments on [mozilla.org](https://www.mozilla.org) simply
In contrast to third-party options (e.g. [Optimizely](https://www.optimizely.com/)), Traffic Cop offers:

1. **Security** — Many third-party options require loading JS from their site, which is a potential [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) vector. Traffic Cop can (and should) be served from your site/CDN.
2. **Privacy** - Traffic Cop does not use cookies of any kind (unlike most third-party solutions), nor does it store or send any experiment data itself (that part is up to you and your consent management solution).
2. **Privacy** - Traffic Cop does not set cookies of any kind (unlike most third-party solutions), nor does it store or send any experiment data itself (that part is up to you and your consent management solution).
3. **Performance** — Traffic Cop is light and has zero dependencies, resulting in less than 2KB of JS when minified. (In our experience, Optimizely's JS bundle was regularly above 200KB.)
4. **Your workflow** — Traffic Cop offers great flexibility in when and how you write and load variation code. No need to type jQuery in a text box on a third-party web page.
5. **Savings** — No need to pay for a third-party service.
Expand Down Expand Up @@ -93,9 +93,9 @@ Check out [the docs](./documentation.md) for more complete information.

## Further Documentation

- [Building the NPM package](docs/#building-the-npm-package)
- [Running tests](docs/#running-tests)
- [Publishing to NPM](docs/#publishing-to-npm)
- [Building the NPM package](docs/#building-the-npm-package)
- [Running tests](docs/#running-tests)
- [Publishing to NPM](docs/#publishing-to-npm)

## License

Expand Down
2,454 changes: 1,526 additions & 928 deletions demo/package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
"description": "Express app demonstrating usage of Traffic Cop.",
"main": "index.js",
"scripts": {
"prestart": "webpack --mode development --config webpack.config.js",
"start": "nodemon src/index.js"
},
"author": "Mozilla",
"license": "MPL-2.0",
"dependencies": {
"@mozmeao/cookie-helper": "^1.1.0",
"express": "^4.21.2",
"nunjucks": "^3.2.4",
"path": "^0.12.7",
"serve-favicon": "^2.3.2"
},
"devDependencies": {
"nodemon": "^3.1.9"
"nodemon": "^3.1.9",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1"
}
}
1 change: 1 addition & 0 deletions demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ app.use(
'/uncompressed-src',
express.static(path.join(__dirname, '/../../src/'))
);
app.use('/libs', express.static(path.join(__dirname, '/../libs/')));

nunjucks.configure('src/views', {
autoescape: true,
Expand Down
8 changes: 8 additions & 0 deletions demo/src/libs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const CookieHelper = require('@mozmeao/cookie-helper');

// create namespace
if (typeof window.Mozilla === 'undefined') {
window.Mozilla = {};
}

window.Mozilla.Cookies = CookieHelper;
23 changes: 21 additions & 2 deletions demo/src/public/experiment-page1.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
(function () {
'use strict';

var eddie = new window.TrafficCop({
function setVariationCookie(exp) {
// set cookie to expire in 24 hours
var date = new Date();
date.setTime(date.getTime() + 1 * 24 * 60 * 60 * 1000);
var expires = date.toUTCString();

window.Mozilla.Cookies.setItem(
exp.id,
exp.chosenVariation,
expires,
undefined,
undefined,
false,
'lax'
);
}

var cop = new window.TrafficCop({
id: 'my-experiment-id-1',
variations: {
'v=1': 40.5,
'v=2': 20.3,
Expand All @@ -11,6 +29,7 @@
'v=6': 0.1
}
});
cop.init();

eddie.init();
setVariationCookie(cop);
})();
20 changes: 20 additions & 0 deletions demo/src/public/experiment-page2.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@
c: 30
};

function setVariationCookie(exp) {
// set cookie to expire in 24 hours
var date = new Date();
date.setTime(date.getTime() + 1 * 24 * 60 * 60 * 1000);
var expires = date.toUTCString();

window.Mozilla.Cookies.setItem(
exp.id,
exp.chosenVariation,
expires,
undefined,
undefined,
false,
'lax'
);
}

function handleVariation(variation) {
// wait until DOM is ready to be manipulated...
domReady(function () {
Expand All @@ -28,9 +45,12 @@
}

var wiggum = new window.TrafficCop({
id: 'my-experiment-id-2',
customCallback: handleVariation,
variations: variants
});

wiggum.init();

setVariationCookie(wiggum);
})();
20 changes: 20 additions & 0 deletions demo/src/public/experiment-page3-customcallback.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@
b: 40
};

function setVariationCookie(exp) {
// set cookie to expire in 24 hours
var date = new Date();
date.setTime(date.getTime() + 1 * 24 * 60 * 60 * 1000);
var expires = date.toUTCString();

window.Mozilla.Cookies.setItem(
exp.id,
exp.chosenVariation,
expires,
undefined,
undefined,
false,
'lax'
);
}

function handleVariation(variation) {
if (Object.prototype.hasOwnProperty.call(variants, variation)) {
var target = document.getElementById('var-' + variation);
Expand All @@ -14,9 +31,12 @@
}

var wiggum = new window.TrafficCop({
id: 'my-experiment-id-3',
customCallback: handleVariation,
variations: variants
});

wiggum.init();

setVariationCookie(wiggum);
})();
20 changes: 20 additions & 0 deletions demo/src/public/experiment-page3-redirect.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
(function () {
'use strict';

function setVariationCookie(exp) {
// set cookie to expire in 24 hours
var date = new Date();
date.setTime(date.getTime() + 1 * 24 * 60 * 60 * 1000);
var expires = date.toUTCString();

window.Mozilla.Cookies.setItem(
exp.id,
exp.chosenVariation,
expires,
undefined,
undefined,
false,
'lax'
);
}

var lou = new window.TrafficCop({
id: 'my-experiment-id-4',
variations: {
'v=a': 40,
'v=b': 40
}
});

lou.init();

setVariationCookie(lou);
})();
5 changes: 5 additions & 0 deletions demo/src/views/pages/page1.njk
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% extends "../index.njk" %}

{% block experiment_js %}
<script src="/libs/index.js"></script>
<script src="/trafficcop/index.js"></script>
<script src="/experiment-page1.js"></script>
{% endblock %}
Expand Down Expand Up @@ -61,5 +62,9 @@
this line. If yes, do. I love you all.
</p>
</article>

<aside>
<p>Note: to clear the variation, you might need to clear cookies for this page and refresh.</p>
</aside>
</div>
{% endblock %}
5 changes: 5 additions & 0 deletions demo/src/views/pages/page2.njk
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% extends "../index.njk" %}

{% block experiment_js %}
<script src="/libs/index.js"></script>
<script src="/trafficcop/index.js"></script>
<script src="/experiment-page2.js"></script>
{% endblock %}
Expand Down Expand Up @@ -39,5 +40,9 @@
No.
</p>
</article>

<aside>
<p>Note: to clear the variation, you might need to clear cookies for this page and refresh.</p>
</aside>
</div>
{% endblock %}
5 changes: 5 additions & 0 deletions demo/src/views/pages/page3.njk
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% extends "../index.njk" %}

{% block experiment_js %}
<script src="/libs/index.js"></script>
<script src="/trafficcop/index.js"></script>
<script src="/experiment-page3-redirect.js"></script>
{% endblock %}
Expand Down Expand Up @@ -45,6 +46,10 @@
<span class="hidden" id="var-b">Herman Blume (customCallback b)</span>
</p>
</article>

<aside>
<p>Note: to clear the variation, you might need to clear cookies for this page and refresh.</p>
</aside>
</div>
{% endblock %}

Expand Down
34 changes: 34 additions & 0 deletions demo/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
entry: './src/libs.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'libs'),
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
performance: {
hints: 'warning'
},
optimization: {
minimize: false
},
plugins: [
// clean out ./demo/libs/ before building
new CleanWebpackPlugin(),
]
};
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

### Contents

- [Building the NPM package](#building-the-npm-package)
- [Running tests](#running-tests)
- [Publishing to NPM](#publishing-to-npm)
- [Building the NPM package](#building-the-npm-package)
- [Running tests](#running-tests)
- [Publishing to NPM](#publishing-to-npm)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Expand Down
Loading

0 comments on commit c7e5365

Please sign in to comment.