Skip to content

Commit 003b92e

Browse files
authored
Add ophan tracking for Admiral events (#2141)
* install ophan-tracker-js as dev dependency * add ophan record function and track INSERT, DETECT, VIEW, CLOSE actions for adblock recovery * add test for ophan tracking
1 parent 9a2b997 commit 003b92e

9 files changed

Lines changed: 156 additions & 22 deletions

File tree

bundle/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"web-vitals": "^4.2.4"
4949
},
5050
"devDependencies": {
51+
"@guardian/ophan-tracker-js": "catalog:",
5152
"@babel/cli": "^7.26.4",
5253
"@babel/core": "^7.26.0",
5354
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { isUserInVariant } from '../../experiments/ab';
2+
import { recordAdmiralOphanEvent } from './admiral';
3+
4+
jest.mock('../../experiments/ab');
5+
6+
// Mock Ophan
7+
window.guardian.ophan = {
8+
trackComponentAttention: jest.fn(),
9+
viewId: '',
10+
pageViewId: '',
11+
record: jest.fn(),
12+
};
13+
14+
describe('Admiral functions', () => {
15+
describe('recordAdmiralOphanEvent', () => {
16+
it('calls ophan.record with expected params for action provided', () => {
17+
jest.mocked(isUserInVariant).mockReturnValue(true);
18+
19+
recordAdmiralOphanEvent({ action: 'INSERT' });
20+
const expectedComponentEvent = {
21+
component: {
22+
componentType: 'AD_BLOCK_RECOVERY',
23+
id: 'admiral-adblock-recovery',
24+
},
25+
action: 'INSERT',
26+
abTest: {
27+
name: 'AdmiralAdblockRecovery',
28+
variant: 'variant-detect',
29+
},
30+
};
31+
expect(window.guardian.ophan?.record).toHaveBeenCalledWith({
32+
componentEvent: expectedComponentEvent,
33+
});
34+
});
35+
36+
it('calls ophan.record with expected params for action and value provided', () => {
37+
jest.mocked(isUserInVariant).mockReturnValue(true);
38+
39+
recordAdmiralOphanEvent({ action: 'DETECT', value: 'whitelisted' });
40+
const expectedComponentEvent = {
41+
component: {
42+
componentType: 'AD_BLOCK_RECOVERY',
43+
id: 'admiral-adblock-recovery',
44+
},
45+
action: 'DETECT',
46+
value: 'whitelisted',
47+
abTest: {
48+
name: 'AdmiralAdblockRecovery',
49+
variant: 'variant-detect',
50+
},
51+
};
52+
expect(window.guardian.ophan?.record).toHaveBeenCalledWith({
53+
componentEvent: expectedComponentEvent,
54+
});
55+
});
56+
57+
it('omits the abTest key value pair if not in a variant', () => {
58+
jest.mocked(isUserInVariant).mockReturnValue(false);
59+
60+
recordAdmiralOphanEvent({ action: 'DETECT', value: 'whitelisted' });
61+
const expectedComponentEvent = {
62+
component: {
63+
componentType: 'AD_BLOCK_RECOVERY',
64+
id: 'admiral-adblock-recovery',
65+
},
66+
action: 'DETECT',
67+
value: 'whitelisted',
68+
};
69+
expect(window.guardian.ophan?.record).toHaveBeenCalledWith({
70+
componentEvent: expectedComponentEvent,
71+
});
72+
});
73+
});
74+
});

bundle/src/init/consented/admiral.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ComponentEvent } from '@guardian/ophan-tracker-js';
12
import { isUserInVariant } from '../../experiments/ab';
23
import { admiralAdblockRecovery } from '../../experiments/tests/admiral-adblocker-recovery';
34

@@ -17,6 +18,41 @@ const getAdmiralAbTestVariant = (): string | undefined => {
1718
return undefined;
1819
};
1920

21+
/**
22+
* Sends component events to Ophan with the componentType of `AD_BLOCK_RECOVERY`
23+
* as well as sending the AB test participation
24+
*
25+
* @param overrides allows overriding / setting values for `action` and `value`
26+
*/
27+
const recordAdmiralOphanEvent = ({
28+
action,
29+
value,
30+
}: {
31+
action: ComponentEvent['action'];
32+
value?: ComponentEvent['value'];
33+
}): void => {
34+
const abTestVariant = getAdmiralAbTestVariant();
35+
36+
const componentEvent: ComponentEvent = {
37+
component: {
38+
componentType: 'AD_BLOCK_RECOVERY',
39+
id: 'admiral-adblock-recovery',
40+
},
41+
action,
42+
...(value ? { value } : {}),
43+
...(abTestVariant
44+
? {
45+
abTest: {
46+
name: 'AdmiralAdblockRecovery',
47+
variant: abTestVariant,
48+
},
49+
}
50+
: {}),
51+
};
52+
53+
window.guardian.ophan?.record({ componentEvent });
54+
};
55+
2056
/**
2157
* Sets targeting on the Admiral object
2258
*
@@ -26,4 +62,8 @@ const getAdmiralAbTestVariant = (): string | undefined => {
2662
const setAdmiralTargeting = (key: string, value: string): void =>
2763
window.admiral?.('targeting', 'set', key, value);
2864

29-
export { getAdmiralAbTestVariant, setAdmiralTargeting };
65+
export {
66+
getAdmiralAbTestVariant,
67+
recordAdmiralOphanEvent,
68+
setAdmiralTargeting,
69+
};

bundle/src/init/consented/prepare-admiral.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Admiral, AdmiralEvent } from '@guardian/commercial-core/types';
22
import { log } from '@guardian/libs';
3+
import { recordAdmiralOphanEvent } from './admiral';
34

45
type MeasureDetectedEvent = {
56
adblocking: boolean;
@@ -31,12 +32,14 @@ const handleMeasureDetectedEvent = (event: AdmiralEvent): void => {
3132
'commercial',
3233
'🛡️ Admiral - user has an adblocker and it is enabled',
3334
);
35+
recordAdmiralOphanEvent({ action: 'DETECT', value: 'blocked' });
3436
}
3537
if (event.whitelisted) {
3638
log(
3739
'commercial',
3840
'🛡️ Admiral - user has seen Engage and subsequently disabled their adblocker',
3941
);
42+
recordAdmiralOphanEvent({ action: 'DETECT', value: 'whitelisted' });
4043
}
4144
if (event.subscribed) {
4245
log(
@@ -64,6 +67,7 @@ const handleCandidateShownEvent = (event: AdmiralEvent): void => {
6467
'commercial',
6568
`🛡️ Admiral - Launching candidate ${event.candidateID}`,
6669
);
70+
recordAdmiralOphanEvent({ action: 'VIEW', value: event.candidateID });
6771
} else {
6872
log(
6973
'commercial',
@@ -83,6 +87,7 @@ const handleCandidateDismissedEvent = (event: AdmiralEvent): void => {
8387
'commercial',
8488
`🛡️ Admiral - Candidate ${event.candidateID} was dismissed`,
8589
);
90+
recordAdmiralOphanEvent({ action: 'CLOSE', value: event.candidateID });
8691
} else {
8792
log(
8893
'commercial',

bundle/src/lib/third-party-tags/admiral.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { isInUsa } from '@guardian/commercial-core/geo/geo-utils';
22
import { cmp, log } from '@guardian/libs';
33
import {
44
getAdmiralAbTestVariant,
5+
recordAdmiralOphanEvent,
56
setAdmiralTargeting,
67
} from '../../init/consented/admiral';
78
import type { GetThirdPartyTag } from '../types';
@@ -54,6 +55,7 @@ const admiralTag: ReturnType<GetThirdPartyTag> = {
5455
url: `${BASE_AJAX_URL}/commercial/admiral-bootstrap.js`,
5556
beforeLoad: () => {
5657
log('commercial', '🛡️ Admiral - loading script on the page');
58+
recordAdmiralOphanEvent({ action: 'INSERT' });
5759

5860
/** Send targeting to Admiral for AB test variants */
5961
if (isInVariant && abTestVariant) {

core/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@
5050
"@types/googletag": "catalog:"
5151
},
5252
"devDependencies": {
53-
"@types/node": "catalog:",
54-
"typescript": "catalog:",
53+
"@guardian/ophan-tracker-js": "catalog:",
5554
"@types/jest": "catalog:",
55+
"@types/node": "catalog:",
5656
"jest": "catalog:",
5757
"jest-environment-jsdom": "catalog:",
5858
"jest-environment-jsdom-global": "catalog:",
5959
"ts-jest": "catalog:",
60+
"typescript": "catalog:",
6061
"type-fest": "catalog:"
6162
},
6263
"publishConfig": {

core/src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { EventPayload } from '@guardian/ophan-tracker-js';
12
import type { AdSize, SizeMapping } from './ad-sizes';
23
import type { PageTargeting } from './targeting/build-page-targeting';
34
import 'googletag';
@@ -57,7 +58,7 @@ interface NetworkInformation extends EventTarget {
5758
}
5859

5960
type OphanRecordFunction = (
60-
event: Record<string, unknown> & {
61+
event: EventPayload & {
6162
/**
6263
* the experiences key will override previously set values.
6364
* Use `recordExperiences` instead.

pnpm-lock.yaml

Lines changed: 26 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ packages:
55
catalog:
66
'@guardian/ab-core': '8.0.1'
77
'@guardian/libs': '25.2.0'
8-
'typescript': '5.8.3'
8+
'@guardian/ophan-tracker-js': '2.4.0'
99
'@types/node': '24.2.0'
1010
'@types/jest': '30.0.0'
1111
'@types/googletag': '~3.3.0'
1212
'jest': '^30.0.5'
1313
'jest-environment-jsdom': '^30.0.5'
1414
'jest-environment-jsdom-global': '~4.0.0'
1515
'ts-jest': '^29.4.1'
16+
'typescript': '5.8.3'
1617
'type-fest': '^4.41.0'

0 commit comments

Comments
 (0)