From 27757737ac56a812436feb14c7c90dc48c971bbe Mon Sep 17 00:00:00 2001 From: Jacob Kelley Date: Fri, 11 Dec 2015 11:27:49 -0800 Subject: [PATCH 1/5] New API for chosen tests --- index.js | 22 ++++++++++++++++------ test/test.js | 28 ++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 86c2649..82bcde8 100644 --- a/index.js +++ b/index.js @@ -57,11 +57,14 @@ */ var Test = function(name, data, options) { + if(!name) { throw new Error('Tests must have a name'); } + // Ensure options exists options = options || {}; options = { persist: typeof options.persist !== "undefined" ? options.persist : true, - active: typeof options.active !== "undefined" ? options.active : true + active: typeof options.active !== "undefined" ? options.active : true, + chosen: typeof options.chosen !== "undefined" ? options.chosen : false }; // Return if inactive @@ -103,12 +106,23 @@ document.body.classList.add(name); document.body.classList.add(bucket); + var info = { + bucket: bucket, + data: data[bucket], + active: true + }; + + if(options.chosen) console.log(options.chosen); + // 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 = { @@ -119,11 +133,7 @@ } // Return - return { - bucket: bucket, - data: data[bucket], - active: true - }; + return info; }; Test.noop = function() {}; diff --git a/test/test.js b/test/test.js index b6d5a2e..7efc759 100644 --- a/test/test.js +++ b/test/test.js @@ -31,6 +31,22 @@ describe('Tester', function() { expect(document.body.classList.contains('foo')).to.equal(true); }); + it('should call `chosen` on an AB test', function() { + var chosen = false; + + new Test('test-1', { + foo: { weight: 1 }, + bar: { weight: 0 } + }, { + chosen: function (info) { + chosen = true; + expect(info.bucket).to.equal('foo'); + } + }); + + expect(chosen).to.equal(true); + + }); it('should create a persistent AB test', function() { @@ -43,7 +59,7 @@ describe('Tester', function() { var selected = {}; for(var i = 0; i Date: Mon, 14 Dec 2015 10:45:09 -0800 Subject: [PATCH 2/5] Major updates, polyfill, API additions, es6 --- .babelrc | 3 + gulpfile.babel.js | 26 +++++++++ gulpfile.js | 21 ------- index.js | 143 +--------------------------------------------- lib/index.js | 119 ++++++++++++++++++++++++++++++++++++++ package.json | 6 +- vendor/storage.js | 94 ++++++++++++++++++++++++++++++ 7 files changed, 248 insertions(+), 164 deletions(-) create mode 100644 .babelrc create mode 100644 gulpfile.babel.js delete mode 100644 gulpfile.js create mode 100644 lib/index.js create mode 100644 vendor/storage.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..c13c5f6 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/gulpfile.babel.js b/gulpfile.babel.js new file mode 100644 index 0000000..afa9d3a --- /dev/null +++ b/gulpfile.babel.js @@ -0,0 +1,26 @@ +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(gulp.dest('build')); +}); + +gulp.task('do-watch', () => { + gulp.watch('lib/**/*.js', ['default']); +}); + + +gulp.task('watch', ['default', 'do-watch']); diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index f51725b..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,21 +0,0 @@ -var gulp = require('gulp'); -var jshint = require('gulp-jshint'); -var rename = require("gulp-rename"); -var uglify = require('gulp-uglify'); - -var pkg = require('./package.json'); - -gulp.task('default', function() { - return gulp.src('index.js') - .pipe(jshint()) - .pipe(jshint.reporter('default', { verbose: true })) - .pipe(uglify()) - .pipe(rename('test.min.js')) - .pipe(gulp.dest('build')) -}); - -gulp.task('do-watch', function() { - gulp.watch('index.js', ['default']); -}); - -gulp.task('watch', ['default', 'do-watch']); diff --git a/index.js b/index.js index 82bcde8..4d5e21a 100644 --- a/index.js +++ b/index.js @@ -1,142 +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) { - - if(!name) { throw new Error('Tests must have a name'); } - - // Ensure options exists - options = 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 - }; - - // 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 +