import { stubExperiments } from 'helpers/experimentation_helper'; import { DEFAULT_VARIANT, CANDIDATE_VARIANT, TRACKING_CONTEXT_SCHEMA, } from '~/experimentation/constants'; import * as experimentUtils from '~/experimentation/utils'; describe('experiment Utilities', () => { const ABC_KEY = 'abc'; const DEF_KEY = 'def'; let origGon; let origGl; beforeEach(() => { origGon = window.gon; origGl = window.gl; window.gon.experiment = {}; window.gl.experiments = {}; }); afterEach(() => { window.gon = origGon; window.gl = origGl; }); describe('getExperimentData', () => { const ABC_DATA = '_abc_data_'; const ABC_DATA2 = '_updated_abc_data_'; const DEF_DATA = '_def_data_'; describe.each` gonData | glData | input | output ${[ABC_KEY, ABC_DATA]} | ${[]} | ${[ABC_KEY]} | ${{ experiment: ABC_KEY, variant: ABC_DATA }} ${[]} | ${[ABC_KEY, ABC_DATA]} | ${[ABC_KEY]} | ${{ experiment: ABC_KEY, variant: ABC_DATA }} ${[ABC_KEY, ABC_DATA]} | ${[DEF_KEY, DEF_DATA]} | ${[ABC_KEY]} | ${{ experiment: ABC_KEY, variant: ABC_DATA }} ${[ABC_KEY, ABC_DATA]} | ${[DEF_KEY, DEF_DATA]} | ${[DEF_KEY]} | ${{ experiment: DEF_KEY, variant: DEF_DATA }} ${[ABC_KEY, ABC_DATA]} | ${[ABC_KEY, ABC_DATA2]} | ${[ABC_KEY]} | ${{ experiment: ABC_KEY, variant: ABC_DATA2 }} ${[]} | ${[]} | ${[ABC_KEY]} | ${undefined} `('with input=$input, gon=$gonData, & gl=$glData', ({ gonData, glData, input, output }) => { beforeEach(() => { const [gonKey, gonVariant] = gonData; const [glKey, glVariant] = glData; if (gonKey) window.gon.experiment[gonKey] = { experiment: gonKey, variant: gonVariant }; if (glKey) window.gl.experiments[glKey] = { experiment: glKey, variant: glVariant }; }); it(`returns ${output}`, () => { expect(experimentUtils.getExperimentData(...input)).toEqual(output); }); }); it('only collects the data properties which are supported by the schema', () => { origGl = window.gl; window.gl.experiments = { my_experiment: { experiment: 'my_experiment', variant: 'control', key: 'randomization-unit-key', migration_keys: 'migration_keys object', excluded: false, other: 'foobar', }, }; expect(experimentUtils.getExperimentData('my_experiment')).toEqual({ experiment: 'my_experiment', variant: 'control', key: 'randomization-unit-key', migration_keys: 'migration_keys object', }); window.gl = origGl; }); }); describe('getAllExperimentContexts', () => { const schema = TRACKING_CONTEXT_SCHEMA; it('collects all of the experiment contexts into a single array', () => { const experiments = { [ABC_KEY]: 'candidate', [DEF_KEY]: 'control', ghi: 'blue' }; stubExperiments(experiments); expect(experimentUtils.getAllExperimentContexts()).toEqual( Object.entries(experiments).map(([experiment, variant]) => ({ schema, data: { experiment, variant }, })), ); }); it('returns an empty array if there are no experiments', () => { expect(experimentUtils.getAllExperimentContexts()).toEqual([]); }); }); describe('isExperimentVariant', () => { describe.each` experiment | variant | input | output ${ABC_KEY} | ${CANDIDATE_VARIANT} | ${[ABC_KEY]} | ${true} ${ABC_KEY} | ${DEFAULT_VARIANT} | ${[ABC_KEY, DEFAULT_VARIANT]} | ${true} ${ABC_KEY} | ${'_variant_name'} | ${[ABC_KEY, '_variant_name']} | ${true} ${ABC_KEY} | ${'_variant_name'} | ${[ABC_KEY, '_bogus_name']} | ${false} ${ABC_KEY} | ${'_variant_name'} | ${['boguskey', '_variant_name']} | ${false} ${undefined} | ${undefined} | ${[ABC_KEY, '_variant_name']} | ${false} `( 'with input=$input, experiment=$experiment, variant=$variant', ({ experiment, variant, input, output }) => { it(`returns ${output}`, () => { if (experiment) stubExperiments({ [experiment]: variant }); expect(experimentUtils.isExperimentVariant(...input)).toEqual(output); }); }, ); }); describe('experiment', () => { const experiment = 'marley'; const useSpy = jest.fn(); const controlSpy = jest.fn(); const trySpy = jest.fn(); const candidateSpy = jest.fn(); const getUpStandUpSpy = jest.fn(); const variants = { use: useSpy, try: trySpy, get_up_stand_up: getUpStandUpSpy, }; describe('when there is no experiment data', () => { it('calls the use variant', () => { experimentUtils.experiment(experiment, variants); expect(useSpy).toHaveBeenCalled(); }); describe("when 'control' is provided instead of 'use'", () => { it('calls the control variant', () => { experimentUtils.experiment(experiment, { control: controlSpy }); expect(controlSpy).toHaveBeenCalled(); }); }); }); describe('when experiment variant is "control"', () => { beforeEach(() => { stubExperiments({ [experiment]: DEFAULT_VARIANT }); }); it('calls the use variant', () => { experimentUtils.experiment(experiment, variants); expect(useSpy).toHaveBeenCalled(); }); describe("when 'control' is provided instead of 'use'", () => { it('calls the control variant', () => { experimentUtils.experiment(experiment, { control: controlSpy }); expect(controlSpy).toHaveBeenCalled(); }); }); }); describe('when experiment variant is "candidate"', () => { beforeEach(() => { stubExperiments({ [experiment]: CANDIDATE_VARIANT }); }); it('calls the try variant', () => { experimentUtils.experiment(experiment, variants); expect(trySpy).toHaveBeenCalled(); }); describe("when 'candidate' is provided instead of 'try'", () => { it('calls the candidate variant', () => { experimentUtils.experiment(experiment, { candidate: candidateSpy }); expect(candidateSpy).toHaveBeenCalled(); }); }); }); describe('when experiment variant is "get_up_stand_up"', () => { beforeEach(() => { stubExperiments({ [experiment]: 'get_up_stand_up' }); }); it('calls the get-up-stand-up variant', () => { experimentUtils.experiment(experiment, variants); expect(getUpStandUpSpy).toHaveBeenCalled(); }); }); }); describe('getExperimentVariant', () => { it.each` experiment | variant | input | output ${ABC_KEY} | ${DEFAULT_VARIANT} | ${ABC_KEY} | ${DEFAULT_VARIANT} ${ABC_KEY} | ${CANDIDATE_VARIANT} | ${ABC_KEY} | ${CANDIDATE_VARIANT} ${undefined} | ${undefined} | ${ABC_KEY} | ${DEFAULT_VARIANT} `( 'with input=$input, experiment=$experiment, & variant=$variant; returns $output', ({ experiment, variant, input, output }) => { stubExperiments({ [experiment]: variant }); expect(experimentUtils.getExperimentVariant(input)).toEqual(output); }, ); }); });