fix: isolate tracker credentials and count static download intent
This commit is contained in:
+56
-4
@@ -10,13 +10,15 @@ const site = player ? 'dc1b14a6-d1ba-4802-b895-553b3c81c677' : 'e00bb594-4c6e-44
|
||||
const host = player ? 'player.nenasets.com' : 'tools.nenasets.com';
|
||||
const route = player ? '/' : '/private/onvif/';
|
||||
function boot(url) {
|
||||
const scripts = [], tracked = [];
|
||||
const window = { location: new URL(url), umami: {track: () => tracked.push(true)} };
|
||||
const document = { createElement: () => ({setAttribute(k,v) { this[k] = v; }}), head: {appendChild: s => scripts.push(s)} };
|
||||
const scripts = [], tracked = [], listeners = [];
|
||||
const window = { location: new URL(url), umami: {track: (...args) => tracked.push(args)} };
|
||||
const document = { addEventListener: (type, fn, capture) => listeners.push({type, fn, capture}),
|
||||
createElement: () => ({setAttribute(k,v) { this[k] = v; }}),
|
||||
head: {appendChild: s => { assert.equal(typeof window.altaSafePageview, 'function', 'hook installed before load'); scripts.push(s); }} };
|
||||
const context = vm.createContext({window, document});
|
||||
const source = fs.readFileSync(path.join(root, 'static/privacy-pageview.js'), 'utf8');
|
||||
vm.runInContext(source, context);
|
||||
return {window, scripts, tracked, context, source};
|
||||
return {window, scripts, tracked, listeners, context, source};
|
||||
}
|
||||
test('production pageviews use one manually loaded no-referrer tracker', () => {
|
||||
const b = boot('https://' + host + route + '?customer=PRIVATE#PRIVATE');
|
||||
@@ -24,6 +26,7 @@ test('production pageviews use one manually loaded no-referrer tracker', () => {
|
||||
const s = b.scripts[0];
|
||||
assert.equal(s.src, 'https://umami.nenasets.com/script.js');
|
||||
assert.equal(s.referrerPolicy, 'no-referrer');
|
||||
assert.equal(s.crossOrigin, 'anonymous');
|
||||
assert.equal(s['data-auto-track'], 'false');
|
||||
assert.equal(s['data-exclude-search'], 'true');
|
||||
assert.equal(s['data-exclude-hash'], 'true');
|
||||
@@ -57,6 +60,55 @@ test('fail closed on DEV, previous, unknown, API, port, alternate host and HTTP
|
||||
'https://' + host + '/customer-name', 'https://' + host + '/api/verify-cert'];
|
||||
for (const url of excluded) assert.equal(boot(url).scripts.length, 0, url);
|
||||
});
|
||||
|
||||
const downloads = player ? [['releaseAction', 'download_desktop_app', {tool:'alta-export-player',format:'desktop'}]]
|
||||
: [['download-template', 'download_csv_template', {tool:'onvif',format:'csv',kind:'template'}],
|
||||
['download-report', 'download_csv_report', {tool:'onvif',format:'csv',kind:'report'}]];
|
||||
test('static download clicks count once without changing native navigation or reading user data', () => {
|
||||
const b = boot('https://' + host + route);
|
||||
b.scripts[0].onload();
|
||||
vm.runInContext(b.source, b.context);
|
||||
assert.equal(b.listeners.length, 1);
|
||||
assert.equal(b.listeners[0].type, 'click');
|
||||
assert.equal(b.listeners[0].capture, true);
|
||||
for (const [id, name, data] of downloads) {
|
||||
const control = {id,tagName:'A',disabled:false,getAttribute:()=>null,
|
||||
form:{checkValidity:()=>false},closest:()=>control};
|
||||
const event = {target:{closest:()=>control},button:0,preventDefault:()=>assert.fail('navigation intercepted')};
|
||||
const n=b.tracked.length;
|
||||
b.listeners[0].fn(event);
|
||||
assert.equal(b.tracked.length,n+1);
|
||||
assert.equal(b.tracked.at(-1)[0],name);
|
||||
assert.deepEqual(JSON.parse(JSON.stringify(b.tracked.at(-1)[1])),data);
|
||||
const safe=b.window.altaSafePageview('event',{name,data:{url:'PRIVATE',filename:'PRIVATE'},id:'PRIVATE',tag:'PRIVATE'});
|
||||
assert.equal(safe.name,name);
|
||||
assert.deepEqual(JSON.parse(JSON.stringify(safe.data)),data);
|
||||
assert.ok(!JSON.stringify(safe).includes('PRIVATE'));
|
||||
control.disabled=true;b.listeners[0].fn(event);assert.equal(b.tracked.length,n+1);
|
||||
}
|
||||
b.window.location=new URL('https://'+host+'/private/previous/');
|
||||
const n=b.tracked.length;
|
||||
b.listeners[0].fn({target:{closest:()=>({id:downloads[0][0],tagName:'A'})},button:0});
|
||||
assert.equal(b.tracked.length,n);
|
||||
});
|
||||
test('analytics missing or throwing does not prevent download clicks', () => {
|
||||
const b=boot('https://'+host+route);
|
||||
const e={target:{closest:()=>({id:downloads[0][0],tagName:'A',getAttribute:()=>null})},button:0};
|
||||
delete b.window.umami;assert.doesNotThrow(()=>b.listeners[0].fn(e));
|
||||
b.window.umami={track:()=>{throw Error('offline');}};assert.doesNotThrow(()=>b.listeners[0].fn(e));
|
||||
});
|
||||
test('known events use an own-property whitelist, native desktop modes are excluded', () => {
|
||||
const b=boot('https://'+host+route);
|
||||
for(const name of ['__proto__','constructor','toString','PRIVATE', {toString:()=>downloads[0][1]}])
|
||||
assert.equal(b.window.altaSafePageview('event',{name}),null);
|
||||
if(player){
|
||||
b.window.webavpNative={checkForUpdates:()=>{}};
|
||||
const n=b.tracked.length;
|
||||
b.listeners[0].fn({target:{closest:()=>({id:'releaseAction',tagName:'A',getAttribute:()=>null})},button:0});
|
||||
assert.equal(b.tracked.length,n);
|
||||
}
|
||||
});
|
||||
|
||||
test('only native application document routes are eligible', () => {
|
||||
for(const suffix of (player ? [''] : ['', 'upload', 'results', 'process', 'clear']))
|
||||
assert.equal(boot('https://' + host + route + suffix).scripts.length, 1, suffix);
|
||||
|
||||
Reference in New Issue
Block a user