abby-client
Version:
[](https://coveralls.io/github/GreetzNL/abby-client) [](https://tra
365 lines (341 loc) • 14 kB
JavaScript
const chai = require('chai');
const spies = require('chai-spies');
const { expect, assert } = chai;
chai.use(spies);
const mocks = require('../../mocks');
const abby = require('../../lib/client');
const expressionEvaluator = require('expression-evaluator');
describe('client', () => {
let abbyClient,
synchroniser = mocks.synchroniser;
beforeEach(() => {
abbyClient = abby({
tags: 'test, production',
apiEndpoint: 'https://abby.io/api/v1/'
}, synchroniser);
});
describe('when there is already a session cookie', () => {
let request,
response,
props;
function initSpec(toggles = [], forced = [], rebalance = []) {
// create the request object having no cookies
request = {
headers: {
cookie: 'ABBY_SESSION=123-123;ABBY_TOGGLES=' + toggles.join(',')
},
url: '?abby_toggle=' + forced.join(',')
};
// create the response object for later reference
response = {
headers: {},
setHeader() {
},
getHeader() { }
};
props = {
device: 'MOBILE'
};
// return toggles from api
abbyClient(request, response, props, { rebalance });
}
it('should not set features to request', () => {
initSpec();
expect(request.toggles).to.be.empty;
});
it('should preserve current features', () => {
initSpec(['feature_abc|control|1']);
expect(request.toggles.feature_abc).to.not.undefined;
});
it('should add a forced toggle', () => {
initSpec(['feature_abc|control|1'], ['feature_def|control|1']);
expect(request.toggles.feature_def).to.not.undefined;
});
it('should rebalance a toggle', () => {
initSpec(['feature_rebalance|variant|1'], [], ['feature_rebalance']);
expect(request.toggles.feature_rebalance.variantCode).to.equal('control');
});
it('should rebalance a toggle when toggle set to true', () => {
initSpec(['feature_rebalance_toggle|variant|1']);
expect(request.toggles.feature_rebalance_toggle.variantCode).to.equal('control');
});
it('should preserve current development toggle', () => {
initSpec(['feature_development_toggle|false|1']);
expect(request.toggles.feature_development_toggle).to.not.undefined;
});
it('should enforce forced development toggle', () => {
initSpec(['feature_abc|variant|1'], ['feature_development_toggle|true|1']);
expect(request.toggles.feature_development_toggle).to.not.undefined;
});
});
describe('when there is no Abby cookie', () => {
let request,
response;
beforeEach(() => {
// create the request object having no cookies
request = {
headers: {
cookie: null
},
url: 'anything'
};
// create the response object for later reference
response = {
headers: {},
setHeader() {
// do something
response.headers.cookie = 'uhhh cookies!';
},
getHeader() { }
};
// return toggles from api
abbyClient(request, response);
});
it('should set all features to request', () => {
expect(request.toggles.feature_abc).to.not.undefined;
expect(request.toggles.feature_def).to.not.undefined;
expect(request.toggles.feature_development_toggle).to.not.undefined;
});
it('should store the evaluation result in Cookies', () => {
// check res.cookies
expect(response.headers.cookie).to.equal('uhhh cookies!');
});
it('should store variant and toggle per toggleCode', () => {
expect(request.toggles.feature_abc.toggle).to.not.undefined;
expect(request.toggles.feature_abc.variant).to.not.undefined;
});
it('should not set variant property when building a development toggle', () => {
expect(request.toggles.feature_development_toggle.variant).to.be.undefined;
});
});
describe('when there is a forced query toggle in request', () => {
let request,
response;
function initSpec({ isAbbyQuery = true, toggle = 'feature_ghi', variant = 'variant_1_ghi' } = {}) {
// create the request object having no cookies
request = {
headers: {
cookie: null
},
url: isAbbyQuery ? `?abby_toggle=${toggle}|${variant}` : `anything`
};
// create the response object for later reference
response = {
headers: {},
setHeader() {
// do something
response.headers.cookie = 'uhhh cookies!';
},
getHeader() { }
};
// return toggles from api
abbyClient(request, response);
}
it('should set feature_ghi to variant_1_ghi', () => {
initSpec();
expect(request.toggles.feature_ghi.variant.code).to.equal('variant_1_ghi');
});
});
describe('distribution of original/variant', () => {
let deviation;
function initSpec() {
let originalCount = 0,
variantCount = 0,
distributionRate = 10000,
originalPercent,
variantPercent,
request,
response;
// create the request object having no cookies
request = {
headers: {
cookie: null
},
url: 'anything'
};
// create the response object for later reference
response = {
headers: {},
setHeader() {
// do something
response.headers.cookie = 'uhhh cookies!';
},
getHeader() { }
};
for (let i = 0; i < distributionRate; i++) {
abbyClient(request, response);
request.toggles.feature_ghi &&
request.toggles.feature_ghi.variant.code === 'variant_1_ghi' ? variantCount++ : originalCount++;
}
originalPercent = (originalCount * 100) / distributionRate;
variantPercent = (variantCount * 100) / distributionRate;
deviation = originalPercent - variantPercent;
}
it('should have approximately same range', () => {
initSpec();
assert.isAtMost(deviation, 5);
}).timeout(10000);
});
describe('getTargetedExperiments', () => {
let targeting = { 'type': 'and' };
let userExperiments;
beforeEach(() => {
userExperiments = [{
toggle: {
tags: ['tag'],
targeting
},
variant: { original: false },
type: 'EXPERIMENT',
}];
});
afterEach(() => chai.spy.restore(expressionEvaluator));
describe('filtering', () => {
beforeEach(() => chai.spy.on(expressionEvaluator, 'evaluate', () => true));
it('should return empty array when tags do not match', () => {
let result = abbyClient.getTargetedExperiments(userExperiments, {}, { tags: ['whatever'] });
expect(result.length).to.equal(0);
});
it('should return 1 item when tag matches', () => {
let result = abbyClient.getTargetedExperiments(userExperiments, {}, { tags: ['tag'] });
expect(result.length).to.equal(1);
});
});
describe('targeting', () => {
let props = { 'a': '1' };
it('should return empty array when target does not match', () => {
let spy = chai.spy.on(expressionEvaluator, 'evaluate', () => false);
let result = abbyClient.getTargetedExperiments(userExperiments, props);
expect(spy).to.have.been.called.with(targeting, props);
expect(result.length).to.equal(0);
});
it('should return 1 item when target does match', () => {
let spy = chai.spy.on(expressionEvaluator, 'evaluate', () => true);
let result = abbyClient.getTargetedExperiments(userExperiments, props);
expect(spy).to.have.been.called.with(targeting, props);
expect(result.length).to.equal(1);
});
});
it('should return empty when variant is original', () => {
userExperiments[0].variant.original = true;
let result = abbyClient.getTargetedExperiments(userExperiments, {});
expect(result.length).to.equal(0);
});
});
describe('when there is a paused toggle', () => {
let request,
response,
addedToCookie;
function initSpec() {
addedToCookie = false;
// create the request object having no cookies
request = {
headers: {
cookie: ''
},
url: '/'
};
// create the response object for later reference
response = {
headers: {},
setHeader(key, value) {
if (Array.isArray(value) && value.join('').indexOf('feature_paused') > -1) {
addedToCookie = true;
}
},
getHeader() { }
};
// return toggles from api
abbyClient(request, response);
}
it('should not add the toggle to the request', () => {
initSpec();
expect(request.toggles.feature_paused).to.be.undefined;
});
it('should add the toggle to the cookie', () => {
initSpec();
expect(addedToCookie).to.be.true;
});
});
describe('areTogglesEnforcedToControl', () => {
let request;
let toggles;
function initSpec({ isEnforced = 'false' } = {}) {
request = {
headers: {
cookie: ''
},
url: `?abby_enforce_control=${isEnforced}`
};
toggles = abbyClient.areTogglesEnforcedToControl(request);
return toggles;
}
it('should return empty string with default parameters', () => {
initSpec();
expect(toggles).to.equal(false);
});
it('should return false when control enforcement is NOT true', () => {
initSpec({ isEnforced: 'something' });
expect(toggles).to.equal(false);
});
it('should return true when control enforcement is true', () => {
initSpec({ isEnforced: 'true' });
expect(toggles).to.equal(true);
});
});
describe('setTogglesToControl', () => {
let toggles = {
'toggle1': { variantCode: 'variant', toggleCode: 'toggle1', hash: '123456', type: 'EXPERIMENT' },
'toggle2': { variantCode: 'control', toggleCode: 'toggle2', hash: '789012', type: 'EXPERIMENT' },
};
function initSpec() {
abbyClient.setTogglesToControl(toggles);
}
it('should return toggles with all variant codes equal to "control" or "false"', () => {
initSpec();
expect(toggles['toggle1'].variantCode).to.equal('control');
expect(toggles['toggle2'].variantCode).to.equal('control');
});
});
describe('request handler when there is an enforcement of all toggles in request query', () => {
let request,
response;
const toggles = [
'feature_abc|control|134738',
'feature_def|variant|2323',
];
function initSpec({ isEnforced = 'false'} = {}) {
request = {
headers: {
cookie: 'ABBY_SESSION=123-123;ABBY_TOGGLES=' + toggles.join(',')
},
url: `?abby_enforce_control=${isEnforced}`
};
response = {
headers: {},
setHeader() {
// do something
response.headers.cookie = 'uhhh cookies!';
},
getHeader() { }
};
// return toggles from api
abbyClient(request, response);
}
it('should NOT change toggles variations when control enforcement is "false"', () => {
initSpec();
expect(request.toggles['feature_abc'].variant.code).to.equal('control');
expect(request.toggles['feature_def'].variant.code).to.equal('variant');
});
it('should NOT change toggles\' variations when control enforcement is NOT "true"', () => {
initSpec({ isEnforced: 'something' });
expect(request.toggles['feature_abc'].variantCode).to.equal('control');
expect(request.toggles['feature_def'].variantCode).to.equal('variant');
});
it('should set all toggles to "control" when control enforcement is "true"', () => {
initSpec({ isEnforced: 'true' });
expect(request.toggles['feature_abc'].variantCode).to.equal('control');
expect(request.toggles['feature_def'].variantCode).to.equal('control');
});
});
});