Skip to content

Commit

Permalink
Extended experiments URL parser to handle originalHash (#4536)
Browse files Browse the repository at this point in the history
* Extended experiments URL parser to handle originalHash

* Intermediate state: Update in place, but working on tests.  Do not merge to master.

* Update method of hash parsing for URL params.

* Remove a debugging-only log message.

* Lint fixes.

* Removed unnecessary viewerFor call.

* Updated expectations for rand.called.

* Updated expectations for rand.called.
  • Loading branch information
Terran Lane authored and lannka committed Aug 24, 2016
1 parent cda8ef0 commit 7df92dd
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 21 deletions.
154 changes: 134 additions & 20 deletions ads/google/a4a/test/test-google-ads-a4a-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,29 @@ import {
isInManualExperiment,
} from '../traffic-experiments';
import {resetExperimentToggles_} from '../../../../src/experiments';
import {installViewerService} from '../../../../src/service/viewer-impl';
import {resetServiceForTesting} from '../../../../src/service';
import {documentStateFor} from '../../../../src/document-state';
import {platformFor} from '../../../../src/platform';
import * as sinon from 'sinon';

const EXP_ID = 'EXP_ID';
/** @type {!Branches} */
const EXTERNAL_BRANCHES = {
control: 'EXT_CONTROL',
experiment: 'EXT_EXPERIMENT',
};
/** @type {!Branches} */
const INTERNAL_BRANCHES = {
control: 'INT_CONTROL',
experiment: 'INT_EXPERIMENT',
};

describe('a4a_config', () => {
let sandbox;
let win;
let rand;
let events;

beforeEach(() => {
sandbox = sinon.sandbox.create();
Expand All @@ -38,32 +55,34 @@ describe('a4a_config', () => {
href: 'https://cdn.ampproject.org/fnord',
pathname: '/fnord',
origin: 'https://cdn.ampproject.org',
hash: '',
},
document: {
hidden: false,
cookie: null,
visibilityState: 'visible',
addEventListener: function(type, listener) {
events[type] = listener;
},
},
crypto: {
subtle: true,
webkitSubtle: true,
},
navigator: window.navigator,
};
events = {};
platformFor(win);
documentStateFor(win);
installViewerService(win);
});

afterEach(() => {
resetExperimentToggles_(); // Clear saved, page-level experiment state.
resetServiceForTesting(win, 'viewer');
sandbox.restore();
});

const EXP_ID = 'EXP_ID';
const EXTERNAL_BRANCHES = {
control: 'EXT_CONTROL',
experiment: 'EXT_EXPERIMENT',
};
const INTERNAL_BRANCHES = {
control: 'INT_CONTROL',
experiment: 'INT_EXPERIMENT',
};

it('should attach expt ID and return true when expt is on', () => {
rand.onFirstCall().returns(-1); // Force experiment on.
rand.onSecondCall().returns(0.75); // Select second branch.
Expand Down Expand Up @@ -107,7 +126,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.false;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called ever').to.be.false;
expect(rand).to.not.be.called;
expect(element.getAttribute('data-experiment-id')).to.not.be.ok;
});

Expand All @@ -119,7 +138,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.false;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called ever').to.be.false;
expect(rand).to.not.be.called;
expect(element.getAttribute('data-experiment-id')).to.not.be.ok;
});

Expand Down Expand Up @@ -155,7 +174,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.false;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.true;
expect(rand, 'rand called at least once').to.be.called;
expect(element.getAttribute('data-experiment-id')).to.not.be.ok;
});

Expand All @@ -167,7 +186,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.false;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.true;
expect(rand, 'rand called at least once').to.be.called;
expect(element.getAttribute('data-experiment-id')).to.not.be.ok;
});

Expand All @@ -181,7 +200,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.true;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.true;
expect(rand, 'rand called at least once').to.be.called;
expect(element.getAttribute('data-experiment-id')).to.equal(
INTERNAL_BRANCHES.experiment);
});
Expand All @@ -196,7 +215,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.true;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.true;
expect(rand, 'rand called at least once').to.be.called;
expect(element.getAttribute('data-experiment-id')).to.equal(
INTERNAL_BRANCHES.experiment);
});
Expand All @@ -211,7 +230,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.true;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.false;
expect(rand).to.not.be.called;
expect(element.getAttribute('data-experiment-id')).to.equal(
EXTERNAL_BRANCHES.experiment);
});
Expand All @@ -227,7 +246,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.false;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.false;
expect(rand).to.not.be.called;
expect(element.getAttribute('data-experiment-id')).to.equal(
EXTERNAL_BRANCHES.control);
});
Expand All @@ -243,7 +262,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.false;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.false;
expect(rand).to.not.be.called;
expect(element.getAttribute('data-experiment-id')).to.not.be.ok;
});

Expand All @@ -258,7 +277,7 @@ describe('a4a_config', () => {
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.true;
expect(win.document.cookie).to.be.null;
expect(rand.called, 'rand called at least once').to.be.false;
expect(rand).to.not.be.called;
expect(isInManualExperiment(element), 'element in manual experiment')
.to.be.true;
// And it shouldn't be in any *other* experiments.
Expand All @@ -272,5 +291,100 @@ describe('a4a_config', () => {
}
});
});
});

// These tests are separated because they need to invoke installViewerService
// within the test, rather than in the beforeEach().
describe('a4a_config hash param parsing', () => {
let sandbox;
let win;
let rand;
let events;

beforeEach(() => {
sandbox = sinon.sandbox.create();
rand = sandbox.stub(Math, 'random');
win = {
AMP_MODE: {
localDev: true,
},
location: {
href: 'https://cdn.ampproject.org/fnord',
pathname: '/fnord',
origin: 'https://cdn.ampproject.org',
hash: '',
search: 'somewhere=over&the=rainbow',
},
document: {
hidden: false,
cookie: null,
visibilityState: 'visible',
addEventListener: function(type, listener) {
events[type] = listener;
},
},
crypto: {
subtle: true,
webkitSubtle: true,
},
navigator: window.navigator,
};
events = {};
platformFor(win);
documentStateFor(win);
});
afterEach(() => {
resetExperimentToggles_(); // Clear saved, page-level experiment state.
resetServiceForTesting(win, 'viewer');
sandbox.restore();
});

const hashBaseConditions = ['#exp=PARAM',
'#p=blarg&exp=PARAM',
'#p=blarg&exp=PARAM&s=987',
'#p=blarg&exp=zort:123,PARAM,spaz:987&s=987'];

hashBaseConditions.forEach(hashBase => {
it(`should find viewer param when pattern is ${hashBase}`, () => {
win.location.hash = hashBase.replace('PARAM', 'a4a:-1');
installViewerService(win);
// Ensure that internal branches aren't attached, even if the PRNG
// would normally trigger them.
rand.onFirstCall().returns(-1);
const element = document.createElement('div');
// Should not register as 'A4A enabled', but should still attach the
// control experiment ID.
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.true;
expect(win.document.cookie).to.be.null;
expect(rand).to.not.be.called;
expect(isInManualExperiment(element), 'element in manual experiment')
.to.be.true;
// And it shouldn't be in any *other* experiments.
for (const branch in EXTERNAL_BRANCHES) {
expect(isInExperiment(element, EXTERNAL_BRANCHES[branch]),
'element in ', EXTERNAL_BRANCHES[branch]).to.be.false;
}
for (const branch in EXTERNAL_BRANCHES) {
expect(isInExperiment(element, INTERNAL_BRANCHES[branch]),
'element in ', EXTERNAL_BRANCHES[branch]).to.be.false;
}
});

it(`hash should trump search; pattern=${hashBase}`, () => {
win.location.search = hashBase.replace('PARAM', 'a4a:-1');
win.location.hash = hashBase.replace('PARAM', 'a4a:2');
installViewerService(win);
// Ensure that internal branches aren't attached, even if the PRNG
// would normally trigger them.
rand.onFirstCall().returns(-1);
const element = document.createElement('div');
expect(googleAdsIsA4AEnabled(win, element, EXP_ID, EXTERNAL_BRANCHES,
INTERNAL_BRANCHES), 'googleAdsIsA4AEnabled').to.be.true;
expect(win.document.cookie).to.be.null;
expect(rand).to.not.be.called;
expect(element.getAttribute('data-experiment-id')).to.equal(
EXTERNAL_BRANCHES.experiment);
});
});
});
4 changes: 3 additions & 1 deletion ads/google/a4a/traffic-experiments.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {isGoogleAdsA4AValidEnvironment} from './utils';
import {isExperimentOn, toggleExperiment} from '../../../src/experiments';
import {dev} from '../../../src/log';
import {getMode} from '../../../src/mode';
import {viewerFor} from '../../../src/viewer';
import {parseQueryString} from '../../../src/url';

/** @typedef {{string: {branches: !Branches}}} */
Expand Down Expand Up @@ -121,7 +122,8 @@ export function googleAdsIsA4AEnabled(win, element, experimentName,
*/
function maybeSetExperimentFromUrl(win, experimentName,
controlBranchId, treatmentBranchId, manualId) {
const expParam = parseQueryString(win.location.search)['exp'];
const expParam = viewerFor(win).getParam('exp') ||
parseQueryString(win.location.search)['exp'];
if (!expParam) {
return;
}
Expand Down

0 comments on commit 7df92dd

Please sign in to comment.