UNPKG

react-ab-test

Version:

A/B testing React components and debug tools. Isomorphic with a simple, universal interface. Well documented and lightweight. Tested in popular browsers and Node.js. Includes helpers for Mixpanel and Segment.com.

102 lines (96 loc) 3.31 kB
import React from "react"; import ReactDOM from "react-dom"; import Experiment from "../../src/Experiment.jsx"; import Variant from "../../src/Variant.jsx"; import emitter from "../../src/emitter.jsx"; import assert from "assert"; import co from "co"; import UUID from "node-uuid"; import TestUtils from 'react-dom/test-utils'; import ES6Promise from 'es6-promise'; ES6Promise.polyfill(); let store; let noopStore = { getItem: function(){}, setItem: function(){}, clear: function(){} }; if(typeof window !== 'undefined' && 'localStorage' in window && window['localStorage'] !== null) { try { let key = '__pushtell_react__'; window.localStorage.setItem(key, key); if (window.localStorage.getItem(key) !== key) { store = noopStore; } else { window.localStorage.removeItem(key); store = window.localStorage; } } catch(e) { store = noopStore; } } else { store = noopStore; } function add(a, b) { return a + b; } describe("Weighted Experiment", function() { this.timeout(10000); let container; before(function(){ container = document.createElement("div"); container.id = "react"; document.getElementsByTagName('body')[0].appendChild(container); }); after(function(){ document.getElementsByTagName('body')[0].removeChild(container); emitter._reset(); }); it("should choose a weighted variants.", co.wrap(function *(){ const experimentName = UUID.v4(); const variantNames = []; const variantWeights = []; const playCount = {}; for(let i = 0; i < 5; i++) { variantNames.push(UUID.v4()); variantWeights.push(Math.floor(Math.random() * 100)); } const weightSum = variantWeights.reduce(add, 0); emitter.defineVariants(experimentName, variantNames, variantWeights); assert.equal(emitter.getSortedVariantWeights(experimentName).reduce(add, 0), weightSum); let App = React.createClass({ render: function(){ return <Experiment name={experimentName}> {variantNames.map(name => { return <Variant key={name} name={name}><div id={'variant-' + name}></div></Variant> })} </Experiment>; } }); let chosenVariant; emitter.addListener("play", function(experimentName, variantName){ playCount[variantName] = playCount[variantName] || 0; playCount[variantName] += 1; }); for(let i = 0; i < 1000; i++) { yield new Promise(function(resolve, reject){ ReactDOM.render(<App />, container, resolve); }); ReactDOM.unmountComponentAtNode(container); store.clear(); emitter._resetPlayedExperiments(); } const playSum = Object.keys(playCount).map(function(variantName){ return playCount[variantName] || 0; }).reduce(add, 0); const playCountToWeightRatios = variantNames.map(function(variantName, index){ return (playCount[variantName] / playSum) / (variantWeights[index] / weightSum) }); const ratioMean = playCountToWeightRatios.reduce(add, 0) / playCountToWeightRatios.length; const ratioVariance = playCountToWeightRatios.map(function(ratio){ return Math.pow(ratioMean - ratio, 2); }).reduce(add, 0); const ratioStandardDeviation = Math.sqrt(ratioVariance); assert(ratioStandardDeviation < 0.6); })); });