-
Notifications
You must be signed in to change notification settings - Fork 230
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
WSTEAM1-1514: Add click tracking at top level #12360
base: latest
Are you sure you want to change the base?
Conversation
} | ||
} | ||
|
||
if (!atUserIdValue && crypto.randomUUID) { |
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.
Do we need to worry about Opera Mini + Lite, or does this work? Or is the point of Opera Mini that all users have the same ID, so it would be impossible for us to detect unique users?
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.
We'll check this on Opera Mini on preview
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.
I have a feeling that using the opera mini proxy, the userId was the same, so there was little/no value including this value - in the canonical / full fat solution, we don't bother generating the uuid. We might need to do the same thing here?
simorgh/src/app/lib/analyticsUtils/index.js
Lines 198 to 204 in b340e53
export const getAtUserId = () => { | |
if (!onClient()) return null; | |
// Users accessing the site on opera "extreme data saving mode" have the pages rendered by an intermediate service | |
// Attempting to track these users is just tracking that proxy, causing all opera mini visitors to have the same id | |
if (isOperaProxy()) return null; | |
} | ||
|
||
if (!atUserIdValue && crypto.randomUUID) { | ||
atUserIdValue = crypto.randomUUID(); |
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.
Similarly here, crypto
may not be natively supported in Opera Mini. We have a polyfill from IE here:
Lines 15 to 21 in 2662a70
import getRandomValues from 'polyfill-crypto.getrandomvalues'; | |
if (!window.crypto && !window.msCrypto) { | |
window.crypto = { | |
getRandomValues, | |
}; | |
} |
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.
I've been working on a PR which replaces uuid with crypto - wonder if we could borrow the generateUUID code from there? https://github.com/bbc/simorgh/blob/38995753d1d339c9f1f937d896610094e9f0ea48/src/app/lib/utilities/getUUID/index.ts
Also, crypto.randomUUID
doesn't work on http, only https, meaning it wouldn't work properly on local... not a massive issue I suppose?
Looks like a neat solution! Just some comments on Opera Mini, as ultimately the Lite site will become its default case. We'll just need to be careful writing plain ol' JS as it won't be getting transpiled down to syntax that Opera Mini supports. EDIT: The way this is implemented does let it transpile down, so the syntax should be ok 👍 |
if (atiURL == null) { | ||
return; | ||
} |
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.
Will this not prevent links from proceeding if they don't have the ATI data-attribute?
@@ -0,0 +1,117 @@ | |||
import { LITE_ATI_TRACKING } from '#app/hooks/useClickTrackerHandler'; | |||
import trackingScript from '.'; |
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.
import trackingScript from '.'; | |
import liteATIClickTracking from '.'; |
function mockTrackingScript() { | ||
return 'Tracking script placeholder'; | ||
} | ||
jest.mock('#src/server/utilities/liteATIClickTracking', () => | ||
mockTrackingScript.toString(), | ||
); |
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.
function mockTrackingScript() { | |
return 'Tracking script placeholder'; | |
} | |
jest.mock('#src/server/utilities/liteATIClickTracking', () => | |
mockTrackingScript.toString(), | |
); | |
function liteATIClickTracking() { | |
return 'Tracking script placeholder'; | |
} | |
jest.mock('#src/server/utilities/liteATIClickTracking', () => | |
liteATIClickTracking.toString(), | |
); |
}); | ||
beforeEach(() => { |
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.
nitpick: newline
}); | |
beforeEach(() => { | |
}); | |
beforeEach(() => { |
Object.defineProperty(document, 'cookie', { | ||
get() { | ||
return mockCookie; | ||
}, | ||
set(cookieValue) { | ||
mockCookie = cookieValue; | ||
}, | ||
}); | ||
Object.defineProperty(window, 'location', { | ||
value: { | ||
assign: jest.fn(), | ||
}, | ||
}); | ||
}); |
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.
Should we use mocking here instead of Object.defineProperty?
const anchorElement = document.createElement('a'); | ||
anchorElement.setAttribute( | ||
LITE_ATI_TRACKING, | ||
'https://logws1363.ati-host.net/?', | ||
); |
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.
We seem to use this in a number of places - is it worth extracting into a separate function & reusing in the tests which need it?
let clientSideAtiURL = atiURL | ||
.concat('&', 'r=', rValue) | ||
.concat('&', 're=', reValue) | ||
.concat('&', 'hl=', hlValue); |
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.
Can we use URLSearchParams here? It should be supported on all browsers..? Although we may need to check: https://caniuse.com/?search=URLSearchParams
let clientSideAtiURL = atiURL | |
.concat('&', 'r=', rValue) | |
.concat('&', 're=', reValue) | |
.concat('&', 'hl=', hlValue); | |
const params = new URLSearchParams(); | |
params.append('r', screenResolutionColourDepth); | |
params.append('re', browserViewportResolution); | |
params.append('hl', timestamp); |
If not, could we do something which is a bit more readable - using object key values before appending them all at the bottom - this will make it a bit more future proof if we ever decide to add new params.
let clientSideAtiURL = atiURL | |
.concat('&', 'r=', rValue) | |
.concat('&', 're=', reValue) | |
.concat('&', 'hl=', hlValue); | |
const params = { | |
r: screenResolutionColourDepth, | |
re: browserViewportResolution, | |
... | |
...(navigator.language && { lng: navigator.language }) | |
} |
); | ||
} | ||
|
||
window.sendBeaconLite(clientSideAtiURL); |
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.
Following on from above, if URLSearchParams is supported:
window.sendBeaconLite(clientSideAtiURL); | |
window.sendBeaconLite(${atiUrl}`&${params.toString()}`) |
If not, and we have gone down the params as an object route, then we can do this:
window.sendBeaconLite(clientSideAtiURL); | |
const paramValues = Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&') | |
window.sendBeaconLite(${atiUrl}`&${paramValues}`) |
} | ||
} | ||
|
||
if (!atUserIdValue && crypto.randomUUID) { |
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.
I have a feeling that using the opera mini proxy, the userId was the same, so there was little/no value including this value - in the canonical / full fat solution, we don't bother generating the uuid. We might need to do the same thing here?
simorgh/src/app/lib/analyticsUtils/index.js
Lines 198 to 204 in b340e53
export const getAtUserId = () => { | |
if (!onClient()) return null; | |
// Users accessing the site on opera "extreme data saving mode" have the pages rendered by an intermediate service | |
// Attempting to track these users is just tracking that proxy, causing all opera mini visitors to have the same id | |
if (isOperaProxy()) return null; | |
} | ||
|
||
if (!atUserIdValue && crypto.randomUUID) { | ||
atUserIdValue = crypto.randomUUID(); |
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.
I've been working on a PR which replaces uuid with crypto - wonder if we could borrow the generateUUID code from there? https://github.com/bbc/simorgh/blob/38995753d1d339c9f1f937d896610094e9f0ea48/src/app/lib/utilities/getUUID/index.ts
Also, crypto.randomUUID
doesn't work on http, only https, meaning it wouldn't work properly on local... not a massive issue I suppose?
Resolves JIRA WSTEAM1-1514 & WORLDSERVICE-91
Overall changes
Implements changes from #12332
Code changes
Testing
Helpful Links
Add Links to useful resources related to this PR if applicable.
Coding Standards
Repository use guidelines