-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extended experiments URL parser to handle originalHash #4536
Changes from 7 commits
f0e64bd
8f092df
713d2a5
21011c6
a9d55b6
00689fa
e06f9c5
59ecd5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
|
@@ -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. | ||
|
@@ -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, 'rand called ever').to.not.be.called; | ||
expect(element.getAttribute('data-experiment-id')).to.not.be.ok; | ||
}); | ||
|
||
|
@@ -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, 'rand called ever').to.not.be.called; | ||
expect(element.getAttribute('data-experiment-id')).to.not.be.ok; | ||
}); | ||
|
||
|
@@ -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; | ||
}); | ||
|
||
|
@@ -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; | ||
}); | ||
|
||
|
@@ -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); | ||
}); | ||
|
@@ -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); | ||
}); | ||
|
@@ -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, 'rand never called').to.not.be.called; | ||
expect(element.getAttribute('data-experiment-id')).to.equal( | ||
EXTERNAL_BRANCHES.experiment); | ||
}); | ||
|
@@ -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, 'rand never called').to.not.be.called; | ||
expect(element.getAttribute('data-experiment-id')).to.equal( | ||
EXTERNAL_BRANCHES.control); | ||
}); | ||
|
@@ -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, 'rand never called').to.not.be.called; | ||
expect(isInManualExperiment(element), 'element in manual experiment') | ||
.to.be.true; | ||
// And it shouldn't be in any *other* experiments. | ||
|
@@ -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, 'rand never called').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.called, 'rand called at least once').to.be.false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one more left. I think you can also remove the message, as it will say something similar for you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. D'oh! Ok, this time I did a global search for |
||
expect(element.getAttribute('data-experiment-id')).to.equal( | ||
EXTERNAL_BRANCHES.experiment); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm..
googleAdsIsA4AEnabled
has a side effect toelement
. And all the following test actually depend on this side effect.Any way to improve the code? Or maybe just rename it to a better name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The side effect is deliberate. The attribute that's set on the element is to communicate the experiment ID to future processing, regardless of whether it's other A4A code or 3p iframe code that ends up processing the ad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understand, I saw it clearly documented in the JS doc. Still, the naming confused me again (I recall that I was confused when reviewing that function in the previous PR).
registerElementIfA4AEnabled()
how does this name sound to you?I'm totally fine to get this PR in and have a renaming afterwards, if you think it makes sense.