Skip to content

Commit

Permalink
Merge pull request #1 from dollarshaveclub/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
jakiestfu committed Dec 14, 2015
2 parents 154c31c + b3ac34a commit 5b613bc
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 162 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ npm test # Run tests
* `data` is an object whose keys represent test cases to be randomly chosen.
* The keys can be any name that you want.
* The available options for each test are an optional `weight`, a `Number`, and an optional `chosen`, a `Function`
* `chosen`: Function to be executed when any test is chosen

```javascript
var data = {
Expand Down
2 changes: 1 addition & 1 deletion build/test.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions gulpfile.babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import babelify from 'babelify';
import browserify from 'browserify';
import buffer from 'vinyl-buffer';
import gulp from 'gulp';
import rename from 'gulp-rename';
import source from 'vinyl-source-stream';
import uglify from 'gulp-uglify';

gulp.task('default', () => {
var bundler = browserify('./index.js', {
standalone: 'Test'
});
bundler.transform(babelify);
bundler.bundle()
.on('error', function (err) { console.error(err); })
.pipe(source('test.min.js'))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest('build'));
});

gulp.task('do-watch', () => {
gulp.watch('lib/**/*.js', ['default']);
});


gulp.task('watch', ['default', 'do-watch']);
21 changes: 0 additions & 21 deletions gulpfile.js

This file was deleted.

133 changes: 1 addition & 132 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,132 +1 @@
(function() {

var utils = {

/**
* Returns a random number between min and max
* @param {Number} min The lower bound, inclusive
* @param {Number} max The upper bound, exclusive
* @return {Number} Random number
*/
rand: function(min, max) {
return Math.random() * (max - min) + min;
},

/**
* Fetches a random number and loops through the bounds of weights
* @param {Array} names List of test names (Strings)
* @param {Array} weights List of test weights (Numbers)
* @return {String} Chosen test name
*
* .1 / 1 = 10% / 90%
* .25 / .75 = 33% / 60%
* .25 / 1 = 25% / 75%
*
* Weight sums represent the bounds.
*/
chooseWeightedItem: function(names, weights) {

// Total out the number of weights
var total = 0, i;
for(i = 0; i < weights.length; i++) {
total += weights[i];
}

var sum = 0;

// Get a random number between 0 and the total number of weights
var n = utils.rand(0, total);

// Loop until we've encountered the first weight greater than our random number
for (i = 0; i < names.length; i++) {
sum += weights[i];

if (n <= sum) {
return names[i];
}
}
}
};

/**
* Test constructor
* @param {String} name Name of the test
* @param {Object} data Tests to execute
* @param {Object} options Additional options
* @return {Object} Selected test
*/
var Test = function(name, data, options) {

// Ensure options exists
options = options || {};
options = {
persist: typeof options.persist !== "undefined" ? options.persist : true,
active: typeof options.active !== "undefined" ? options.active : true
};

// Return if inactive
if (!options.active) {
return {
active: false
};
}

// Set test name
var storageKey = 'test-' + name;
var bucket;

// Retrieve Bucket from storage if possible
if (options.persist && sessionStorage.getItem(storageKey)) {
bucket = sessionStorage.getItem(storageKey);
}

// Determine bucket
else {

// Get a list of test names and test weights
var names = Object.keys(data);
var weights = [];
for (var i = 0; i<names.length; i++) {
if(typeof data[names[i]].weight == "undefined") data[names[i]].weight = 1;
weights.push( data[names[i]].weight );
}

// Select a random weighted bucket
bucket = utils.chooseWeightedItem(names, weights);

// Save
sessionStorage.setItem(storageKey, bucket);
}

// We've now bucketed our user
// Add classname
document.body.classList.add(name);
document.body.classList.add(bucket);

// Call function if provided
if (data[bucket]) {
if (!data[bucket].chosen) data[bucket].chosen = Test.noop;
data[bucket].chosen.call(this);
}

// Record test with GTM if possible
if(typeof dataLayer !== "undefined") {
var metrics = {
abTests: {}
};
metrics.abTests[name] = bucket;
dataLayer.push(metrics);
}

// Return
return {
bucket: bucket,
data: data[bucket],
active: true
};
};

Test.noop = function() {};

window.Test = Test;
})();
module.exports = require('./lib').default;
119 changes: 119 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import storage from '../vendor/storage';

const supportsClasslist = "classList" in document.createElement("_");

class Test {
constructor(name, data = {}, options = {}) {

options = {
persist: typeof options.persist !== "undefined" ? options.persist : true,
active: typeof options.active !== "undefined" ? options.active : true,
chosen: typeof options.chosen !== "undefined" ? options.chosen : false
};

if(!name) { throw new Error('Tests must have a name'); }

// Return if inactive
if (!options.active) {
return {
active: false
};
}

// Set test name
let storageKey = 'test-' + name;
let bucket;

// Retrieve Bucket from storage if possible
if (options.persist && storage.local.getItem(storageKey)) {
bucket = storage.local.getItem(storageKey);
}

// Determine bucket
else {

// Get a list of test names and test weights
let names = Object.keys(data);
let weights = [];
for (var i = 0; i < names.length; i++) {
if (typeof data[names[i]].weight == "undefined")
data[names[i]].weight = 1;
weights.push( data[names[i]].weight );
}

// Select a random weighted bucket
bucket = Test.chooseWeightedItem(names, weights);

// Save
storage.local.setItem(storageKey, bucket);
}

// We've now bucketed our user
// Add classname
if (supportsClasslist) {
document.body.classList.add(name, bucket);
} else {
document.body.className += ` ${name} ${bucket}`;
}

let info = {
bucket: bucket,
data: data[bucket],
active: true
};

// Call function if provided
if (data[bucket]) {
if (!data[bucket].chosen) data[bucket].chosen = Test.noop;
data[bucket].chosen.call(this);
}

// Call chosen function
if (options.chosen) { options.chosen.call(this, info); }

// Record test with GTM if possible
if(typeof dataLayer !== "undefined") {
var metrics = {
abTests: {}
};
metrics.abTests[name] = bucket;
dataLayer.push(metrics);
}

// Return
return info;
}

static chooseWeightedItem (names, weights) {

// Total out the number of weights
var total = 0, i;
for(i = 0; i < weights.length; i++) {
total += weights[i];
}

var sum = 0;

// Get a random number between 0 and the total number of weights
var n = Test.rand(0, total);

// Loop until we've encountered the first weight greater than our random number
for (i = 0; i < names.length; i++) {
sum += weights[i];

if (n <= sum) {
return names[i];
}
}
}

static noop () {}

static rand (min, max) {
return Math.random() * (max - min) + min;
}

static storage () { return storage; }
}

export default Test;
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ab-tester",
"version": "0.1.2",
"version": "0.2.0",
"homepage": "https://github.com/dollarshaveclub/ab-tester.js",
"main": "./index.js",
"repository": {
Expand All @@ -13,13 +13,17 @@
"test": "./node_modules/mocha-phantomjs/bin/mocha-phantomjs --reporter spec test/runner.html"
},
"devDependencies": {
"babelify": "^7.2.0",
"browserify": "^12.0.1",
"chai": "3.2.0",
"gulp": "^3.9.0",
"gulp-jshint": "^1.11.2",
"gulp-rename": "^1.2.2",
"gulp-uglify": "^1.2.0",
"mocha": "2.2.5",
"mocha-phantomjs": "3.6.0",
"sinon": "1.15.4"
"sinon": "1.15.4",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
}
}
1 change: 1 addition & 0 deletions test/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<script src="../node_modules/chai/chai.js"></script>
<script>mocha.setup('bdd')</script>
<script src="../build/test.min.js"></script>

<script src="./test.js"></script>
<script>
if (window.mochaPhantomJS) { mochaPhantomJS.run(); }
Expand Down
Loading

0 comments on commit 5b613bc

Please sign in to comment.