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.
265 lines (262 loc) • 10.2 kB
JSX
import React from "react";
import ReactDOM from "react-dom";
import Experiment from "../../src/CoreExperiment.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();
describe("Emitter", function() {
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 throw an error when passed an invalid name argument.", function (){
assert.throws(function(){emitter.emitWin(1)}, /type \'string\'/);
});
it("should emit when a variant is played.", co.wrap(function *(){
let experimentName = UUID.v4();
let playedVariantName = null;
let playCallback = function(experimentName, variantName){
playedVariantName = variantName;
};
let experimentNameGlobal = null;
let playedVariantNameGlobal = null;
let playCallbackGlobal = function(experimentName, variantName){
experimentNameGlobal = experimentName;
playedVariantNameGlobal = variantName;
};
let playSubscription = emitter.addPlayListener(experimentName, playCallback);
let playSubscriptionGlobal = emitter.addPlayListener(playCallbackGlobal);
let App = React.createClass({
render: function(){
return <Experiment name={experimentName} value="A">
<Variant name="A"><div id="variant-a"/></Variant>
<Variant name="B"><div id="variant-b"/></Variant>
</Experiment>;
}
});
yield new Promise(function(resolve, reject){
ReactDOM.render(<App />, container, resolve);
});
assert.equal(playedVariantName, "A");
assert.equal(experimentNameGlobal, experimentName);
assert.equal(playedVariantNameGlobal, "A");
playSubscription.remove();
playSubscriptionGlobal.remove();
ReactDOM.unmountComponentAtNode(container);
}));
it("should emit when a variant wins.", co.wrap(function *(){
let experimentName = UUID.v4();
let winningVariantName = null;
let winCallback = function(experimentName, variantName){
winningVariantName = variantName;
};
let experimentNameGlobal = null;
let winningVariantNameGlobal = null;
let winCallbackGlobal = function(experimentName, variantName){
experimentNameGlobal = experimentName;
winningVariantNameGlobal = variantName;
};
let winSubscription = emitter.addWinListener(experimentName, winCallback);
let winSubscriptionGlobal = emitter.addWinListener(winCallbackGlobal);
let App = React.createClass({
render: function(){
return <Experiment name={experimentName} value="A">
<Variant name="A"><div id="variant-a"/></Variant>
<Variant name="B"><div id="variant-b"/></Variant>
</Experiment>;
}
});
yield new Promise(function(resolve, reject){
ReactDOM.render(<App />, container, resolve);
});
emitter.emitWin(experimentName);
assert.equal(winningVariantName, "A");
assert.equal(experimentNameGlobal, experimentName);
assert.equal(winningVariantNameGlobal, "A");
winSubscription.remove();
winSubscriptionGlobal.remove();
ReactDOM.unmountComponentAtNode(container);
}));
it("should emit when a variant is clicked.", co.wrap(function *(){
let experimentName = UUID.v4();
let winningVariantName = null;
let winCallback = function(experimentName, variantName){
winningVariantName = variantName;
};
let experimentNameGlobal = null;
let winningVariantNameGlobal = null;
let winCallbackGlobal = function(experimentName, variantName){
experimentNameGlobal = experimentName;
winningVariantNameGlobal = variantName;
};
let winSubscription = emitter.addWinListener(experimentName, winCallback);
let winSubscriptionGlobal = emitter.addWinListener(winCallbackGlobal);
let App = React.createClass({
onClickVariant: function(e){
this.refs.experiment.win();
},
render: function(){
return <Experiment ref="experiment" name={experimentName} value="A">
<Variant name="A"><a id="variant-a" href="#A" onClick={this.onClickVariant}>A</a></Variant>
<Variant name="B"><a id="variant-b" href="#B" onClick={this.onClickVariant}>B</a></Variant>
</Experiment>;
}
});
yield new Promise(function(resolve, reject){
ReactDOM.render(<App />, container, resolve);
});
let elementA = document.getElementById('variant-a');
TestUtils.Simulate.click(elementA);
assert.equal(winningVariantName, "A");
assert.equal(experimentNameGlobal, experimentName);
assert.equal(winningVariantNameGlobal, "A");
winSubscription.remove();
winSubscriptionGlobal.remove();
ReactDOM.unmountComponentAtNode(container);
}));
it("should emit when a variant is chosen.", co.wrap(function *(){
let experimentName = UUID.v4();
let activeVariantName = null;
let activeVariantCallback = function(experimentName, variantName){
activeVariantName = variantName;
};
let experimentNameGlobal = null;
let activeVariantNameGlobal = null;
let activeVariantCallbackGlobal = function(experimentName, variantName){
experimentNameGlobal = experimentName;
activeVariantNameGlobal = variantName;
};
let activeVariantSubscription = emitter.addActiveVariantListener(experimentName, activeVariantCallback);
let activeVariantSubscriptionGlobal = emitter.addActiveVariantListener(activeVariantCallbackGlobal);
let App = React.createClass({
render: function(){
return <Experiment ref="experiment" name={experimentName} value="A">
<Variant name="A"><a id="variant-a" href="#A">A</a></Variant>
<Variant name="B"><a id="variant-b" href="#B">B</a></Variant>
</Experiment>;
}
});
yield new Promise(function(resolve, reject){
ReactDOM.render(<App />, container, resolve);
});
assert.equal(activeVariantName, "A");
assert.equal(experimentNameGlobal, experimentName);
assert.equal(activeVariantNameGlobal, "A");
activeVariantSubscription.remove();
activeVariantSubscriptionGlobal.remove();
ReactDOM.unmountComponentAtNode(container);
}));
it("should get the experiment value.", co.wrap(function *(){
let experimentName = UUID.v4();
let App = React.createClass({
render: function(){
return <Experiment ref="experiment" name={experimentName} value="A">
<Variant name="A"><a id="variant-a" href="#A">A</a></Variant>
<Variant name="B"><a id="variant-b" href="#B">B</a></Variant>
</Experiment>;
}
});
yield new Promise(function(resolve, reject){
ReactDOM.render(<App />, container, resolve);
});
assert.equal(emitter.getActiveVariant(experimentName), "A");
ReactDOM.unmountComponentAtNode(container);
}));
it("should update the rendered component.", co.wrap(function *(){
let experimentName = UUID.v4();
let App = React.createClass({
render: function(){
return <Experiment name={experimentName} value="A">
<Variant name="A"><div id="variant-a" /></Variant>
<Variant name="B"><div id="variant-b" /></Variant>
</Experiment>;
}
});
yield new Promise(function(resolve, reject){
ReactDOM.render(<App />, container, resolve);
});
let elementA = document.getElementById('variant-a');
let elementB = document.getElementById('variant-b');
assert.notEqual(elementA, null);
assert.equal(elementB, null);
emitter.setActiveVariant(experimentName, "B");
elementA = document.getElementById('variant-a');
elementB = document.getElementById('variant-b');
assert.equal(elementA, null);
assert.notEqual(elementB, null);
ReactDOM.unmountComponentAtNode(container);
}));
it("should report active components.", co.wrap(function *(){
let experimentNameA = UUID.v4();
let experimentNameB = UUID.v4();
let AppA = React.createClass({
render: function(){
return <Experiment name={experimentNameA} value="A">
<Variant name="A"><div id="variant-a" /></Variant>
<Variant name="B"><div id="variant-b" /></Variant>
</Experiment>;
}
});
let AppB = React.createClass({
render: function(){
return <Experiment name={experimentNameB} value="C">
<Variant name="C"><div id="variant-c" /></Variant>
<Variant name="D"><div id="variant-d" /></Variant>
</Experiment>;
}
});
let AppCombined = React.createClass({
render: function(){
return <div>
<AppA />
<AppB />
</div>;
}
});
yield new Promise(function(resolve, reject){
ReactDOM.render(<AppA />, container, resolve);
});
let activeExperiments = {};
activeExperiments[experimentNameA] = {
"A": true,
"B": false
};
assert.deepEqual(emitter.getActiveExperiments(), activeExperiments);
ReactDOM.unmountComponentAtNode(container);
yield new Promise(function(resolve, reject){
ReactDOM.render(<AppB />, container, resolve);
});
activeExperiments = {};
activeExperiments[experimentNameB] = {
"C": true,
"D": false
};
assert.deepEqual(emitter.getActiveExperiments(), activeExperiments);
ReactDOM.unmountComponentAtNode(container);
yield new Promise(function(resolve, reject){
ReactDOM.render(<AppCombined />, container, resolve);
});
activeExperiments = {};
activeExperiments[experimentNameA] = {
"A": true,
"B": false
};
activeExperiments[experimentNameB] = {
"C": true,
"D": false
};
assert.deepEqual(emitter.getActiveExperiments(), activeExperiments);
ReactDOM.unmountComponentAtNode(container);
}));
});