2022-01-26 22:10:35 +00:00
|
|
|
const AB = require( '../../resources/skins.vector.es6/AB.js' );
|
2022-03-17 23:01:17 +00:00
|
|
|
const NAME_OF_EXPERIMENT = 'name-of-experiment';
|
|
|
|
const TOKEN = 'token';
|
|
|
|
const MW_EXPERIMENT_PARAM = {
|
|
|
|
name: NAME_OF_EXPERIMENT,
|
|
|
|
enabled: true,
|
|
|
|
buckets: {
|
|
|
|
unsampled: 0.5,
|
|
|
|
control: 0.25,
|
|
|
|
treatment: 0.25
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// eslint-disable-next-line jsdoc/require-returns
|
|
|
|
/**
|
|
|
|
* @param {Object} props
|
|
|
|
*/
|
|
|
|
function createInstance( props = {} ) {
|
|
|
|
const mergedProps = /** @type {AB.WebABTestProps} */ ( Object.assign( {
|
|
|
|
name: NAME_OF_EXPERIMENT,
|
|
|
|
buckets: {
|
|
|
|
unsampled: {
|
|
|
|
samplingRate: 0.5
|
|
|
|
},
|
|
|
|
control: {
|
|
|
|
samplingRate: 0.25
|
|
|
|
},
|
|
|
|
treatment: {
|
|
|
|
samplingRate: 0.25
|
|
|
|
}
|
|
|
|
},
|
|
|
|
token: TOKEN
|
|
|
|
}, props ) );
|
|
|
|
|
|
|
|
return AB( mergedProps );
|
|
|
|
}
|
2022-01-26 22:10:35 +00:00
|
|
|
|
|
|
|
describe( 'AB.js', () => {
|
2022-03-17 23:01:17 +00:00
|
|
|
const bucket = 'treatment';
|
2022-01-26 22:10:35 +00:00
|
|
|
const getBucketMock = jest.fn().mockReturnValue( bucket );
|
|
|
|
mw.experiments.getBucket = getBucketMock;
|
|
|
|
|
2022-03-17 23:01:17 +00:00
|
|
|
afterEach( () => {
|
|
|
|
document.body.removeAttribute( 'class' );
|
|
|
|
} );
|
2022-01-26 22:10:35 +00:00
|
|
|
|
2022-03-17 23:01:17 +00:00
|
|
|
describe( 'initialization when body tag does not contain bucket', () => {
|
2022-08-16 21:09:02 +00:00
|
|
|
let /** @type {jest.SpyInstance} */ hookMock;
|
2022-03-17 23:01:17 +00:00
|
|
|
|
|
|
|
beforeEach( () => {
|
2022-08-16 21:09:02 +00:00
|
|
|
hookMock = jest.spyOn( mw, 'hook' );
|
2022-03-17 23:01:17 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
it( 'sends data to WikimediaEvents when the bucket is part of sample (e.g. control)', () => {
|
|
|
|
getBucketMock.mockReturnValueOnce( 'control' );
|
|
|
|
createInstance();
|
|
|
|
expect( hookMock ).toHaveBeenCalled();
|
|
|
|
} );
|
|
|
|
it( 'sends data to WikimediaEvents when the bucket is part of sample (e.g. treatment)', () => {
|
|
|
|
getBucketMock.mockReturnValueOnce( 'treatment' );
|
|
|
|
createInstance();
|
2022-01-26 22:10:35 +00:00
|
|
|
expect( hookMock ).toHaveBeenCalled();
|
|
|
|
} );
|
2022-03-17 23:01:17 +00:00
|
|
|
it( 'does not send data to WikimediaEvents when the bucket is unsampled ', () => {
|
|
|
|
getBucketMock.mockReturnValueOnce( 'unsampled' );
|
|
|
|
createInstance();
|
2022-01-26 22:10:35 +00:00
|
|
|
expect( hookMock ).not.toHaveBeenCalled();
|
|
|
|
} );
|
2022-03-17 23:01:17 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'initialization when body tag contains bucket', () => {
|
2022-08-16 21:09:02 +00:00
|
|
|
let /** @type {jest.SpyInstance} */ hookMock;
|
2022-03-17 23:01:17 +00:00
|
|
|
|
|
|
|
beforeEach( () => {
|
2022-08-16 21:09:02 +00:00
|
|
|
hookMock = jest.spyOn( mw, 'hook' );
|
2022-03-17 23:01:17 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
it( 'sends data to WikimediaEvents when the bucket is part of sample (e.g. control)', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-control' );
|
|
|
|
createInstance();
|
|
|
|
expect( hookMock ).toHaveBeenCalled();
|
|
|
|
} );
|
|
|
|
it( 'sends data to WikimediaEvents when the bucket is part of sample (e.g. treatment)', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-treatment' );
|
|
|
|
createInstance();
|
|
|
|
expect( hookMock ).toHaveBeenCalled();
|
|
|
|
} );
|
|
|
|
it( 'does not send data to WikimediaEvents when the bucket is unsampled ', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-unsampled' );
|
|
|
|
createInstance();
|
2022-01-26 22:10:35 +00:00
|
|
|
expect( hookMock ).not.toHaveBeenCalled();
|
|
|
|
} );
|
|
|
|
} );
|
2022-03-17 23:01:17 +00:00
|
|
|
|
|
|
|
describe( 'initialization when token is undefined', () => {
|
|
|
|
it( 'throws an error', () => {
|
|
|
|
expect( () => {
|
|
|
|
createInstance( { token: undefined } );
|
|
|
|
} ).toThrow( 'Tried to call `getBucket`' );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'getBucket when body tag does not contain AB class', () => {
|
|
|
|
it( 'calls mw.experiments.getBucket with config data', () => {
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( getBucketMock ).toBeCalledWith( MW_EXPERIMENT_PARAM, TOKEN );
|
|
|
|
expect( experiment.getBucket() ).toBe( bucket );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'getBucket when body tag contains AB class that is in the sample', () => {
|
|
|
|
it( 'returns the bucket on the body tag', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-control' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( getBucketMock ).not.toHaveBeenCalled();
|
|
|
|
expect( experiment.getBucket() ).toBe( 'control' );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'getBucket when body tag contains AB class that is not in the sample', () => {
|
|
|
|
it( 'returns the bucket on the body tag', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-unsampled' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( getBucketMock ).not.toHaveBeenCalled();
|
|
|
|
expect( experiment.getBucket() ).toBe( 'unsampled' );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInBucket', () => {
|
|
|
|
it( 'compares assigned bucket with passed in bucket', () => {
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInBucket( 'treatment' ) ).toBe( true );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInTreatmentBucket when assigned to unsampled bucket (from server)', () => {
|
|
|
|
it( 'returns false', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-unsampled' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInTreatmentBucket() ).toBe( false );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInTreatmentBucket when assigned to control bucket (from server)', () => {
|
|
|
|
it( 'returns false', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-control' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInTreatmentBucket() ).toBe( false );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInTreatmentBucket when assigned to treatment bucket (from server)', () => {
|
|
|
|
it( 'returns true', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-treatment' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInTreatmentBucket() ).toBe( true );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInTreatmentBucket when assigned to unsampled bucket (from client)', () => {
|
|
|
|
it( 'returns false', () => {
|
|
|
|
getBucketMock.mockReturnValueOnce( 'unsampled' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInTreatmentBucket() ).toBe( false );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInTreatmentBucket when assigned to control bucket (from client)', () => {
|
|
|
|
it( 'returns false', () => {
|
|
|
|
getBucketMock.mockReturnValueOnce( 'control' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInTreatmentBucket() ).toBe( false );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInTreatmentBucket when assigned to treatment bucket (from client)', () => {
|
|
|
|
it( 'returns true', () => {
|
|
|
|
getBucketMock.mockReturnValueOnce( 'treatment' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInTreatmentBucket() ).toBe( true );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInTreatmentBucket when assigned to treatment bucket (is case insensitive)', () => {
|
|
|
|
it( 'returns true', () => {
|
|
|
|
getBucketMock.mockReturnValueOnce( 'StickyHeaderVisibleTreatment' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInTreatmentBucket() ).toBe( true );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInSample when in unsampled bucket', () => {
|
|
|
|
it( 'returns false', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-unsampled' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInSample() ).toBe( false );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInSample when in control bucket', () => {
|
|
|
|
it( 'returns true', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-control' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInSample() ).toBe( true );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
describe( 'isInSample when in treatment bucket', () => {
|
|
|
|
it( 'returns true', () => {
|
|
|
|
document.body.classList.add( 'name-of-experiment-treatment' );
|
|
|
|
const experiment = createInstance();
|
|
|
|
|
|
|
|
expect( experiment.isInSample() ).toBe( true );
|
|
|
|
} );
|
|
|
|
} );
|
2022-01-26 22:10:35 +00:00
|
|
|
} );
|