132 lines
5.3 KiB
JavaScript
132 lines
5.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
import test from 'node:test';
|
|
import vm from 'node:vm';
|
|
import { parseHTML } from 'linkedom';
|
|
|
|
const controllerSource = await readFile('public/matomo-consent.js', 'utf8').catch(() => '');
|
|
const storageKey = 'incognito.analytics-consent.v1';
|
|
|
|
function queuedCommands(window) {
|
|
return JSON.parse(JSON.stringify(window._paq));
|
|
}
|
|
|
|
function runController({ decision, storageError = false } = {}) {
|
|
const { document } = parseHTML('<!doctype html><html><head></head><body></body></html>');
|
|
const values = new Map(decision ? [[storageKey, decision]] : []);
|
|
const localStorage = {
|
|
getItem(key) {
|
|
if (storageError) throw new Error('storage unavailable');
|
|
return values.get(key) ?? null;
|
|
},
|
|
setItem(key, value) {
|
|
if (storageError) throw new Error('storage unavailable');
|
|
values.set(key, value);
|
|
},
|
|
};
|
|
const window = { localStorage };
|
|
vm.runInNewContext(controllerSource, { window, document });
|
|
return { window, document, values };
|
|
}
|
|
|
|
test('first visit offers equal choices without initializing Matomo', () => {
|
|
const { window, document } = runController();
|
|
const banner = document.getElementById('incognito-analytics-consent');
|
|
const accept = document.getElementById('incognito-analytics-accept');
|
|
const decline = document.getElementById('incognito-analytics-decline');
|
|
|
|
assert.ok(banner, 'consent banner should exist');
|
|
assert.ok(accept, 'accept button should exist');
|
|
assert.ok(decline, 'decline button should exist');
|
|
assert.equal(window._paq, undefined);
|
|
assert.equal(document.querySelector('script[src*="analytics.msvincognito.nl"]'), null);
|
|
assert.equal(banner.hidden, false);
|
|
assert.equal(accept.className, decline.className);
|
|
assert.equal(
|
|
document.querySelector('#incognito-analytics-consent a').href,
|
|
'https://msvincognito.nl/privacy-policy',
|
|
);
|
|
});
|
|
|
|
test('accepting persists consent and initializes cookieless Matomo once', () => {
|
|
const { window, document, values } = runController();
|
|
const accept = document.getElementById('incognito-analytics-accept');
|
|
|
|
assert.ok(accept, 'accept button should exist');
|
|
accept.click();
|
|
accept.click();
|
|
|
|
assert.equal(values.get(storageKey), 'accepted');
|
|
assert.deepEqual(queuedCommands(window), [
|
|
['requireConsent'],
|
|
['disableCookies'],
|
|
['setConsentGiven'],
|
|
['setTrackerUrl', 'https://analytics.msvincognito.nl/matomo.php'],
|
|
['setSiteId', '1'],
|
|
['trackPageView'],
|
|
['enableLinkTracking'],
|
|
]);
|
|
const scripts = document.querySelectorAll('#incognito-matomo-script');
|
|
assert.equal(scripts.length, 1);
|
|
assert.equal(scripts[0].src, 'https://analytics.msvincognito.nl/matomo.js');
|
|
assert.equal(scripts[0].async, true);
|
|
});
|
|
|
|
test('declining persists the choice without creating Matomo state', () => {
|
|
const { window, document, values } = runController();
|
|
document.getElementById('incognito-analytics-decline').click();
|
|
|
|
assert.equal(values.get(storageKey), 'declined');
|
|
assert.equal(window._paq, undefined);
|
|
assert.equal(document.getElementById('incognito-matomo-script'), null);
|
|
assert.equal(document.getElementById('incognito-privacy-settings').hidden, false);
|
|
});
|
|
|
|
test('remembered acceptance tracks on a later page load', () => {
|
|
const { window, document } = runController({ decision: 'accepted' });
|
|
|
|
assert.ok(window._paq.some(([method]) => method === 'trackPageView'));
|
|
assert.equal(document.querySelectorAll('#incognito-matomo-script').length, 1);
|
|
assert.equal(document.getElementById('incognito-analytics-consent').hidden, true);
|
|
});
|
|
|
|
test('remembered decline never initializes Matomo', () => {
|
|
const { window, document } = runController({ decision: 'declined' });
|
|
|
|
assert.equal(window._paq, undefined);
|
|
assert.equal(document.getElementById('incognito-matomo-script'), null);
|
|
assert.equal(document.getElementById('incognito-privacy-settings').hidden, false);
|
|
});
|
|
|
|
test('storage failure defaults to no tracking and keeps consent available', () => {
|
|
const { window, document } = runController({ storageError: true });
|
|
|
|
assert.equal(window._paq, undefined);
|
|
assert.equal(document.getElementById('incognito-analytics-consent').hidden, false);
|
|
});
|
|
|
|
test('privacy settings allow an accepted visitor to withdraw consent', () => {
|
|
const { window, document, values } = runController({ decision: 'accepted' });
|
|
|
|
document.getElementById('incognito-privacy-settings').click();
|
|
document.getElementById('incognito-analytics-decline').click();
|
|
|
|
assert.equal(values.get(storageKey), 'declined');
|
|
assert.deepEqual(queuedCommands(window).slice(-2), [
|
|
['forgetConsentGiven'],
|
|
['deleteCookies'],
|
|
]);
|
|
|
|
const later = runController({ decision: values.get(storageKey) });
|
|
assert.equal(later.window._paq, undefined);
|
|
assert.equal(later.document.getElementById('incognito-matomo-script'), null);
|
|
});
|
|
|
|
test('running the local controller twice does not duplicate UI or tracking', () => {
|
|
const state = runController({ decision: 'accepted' });
|
|
vm.runInNewContext(controllerSource, { window: state.window, document: state.document });
|
|
|
|
assert.equal(state.document.querySelectorAll('#incognito-analytics-consent').length, 1);
|
|
assert.equal(state.document.querySelectorAll('#incognito-matomo-script').length, 1);
|
|
assert.equal(state.window._paq.filter(([method]) => method === 'trackPageView').length, 1);
|
|
});
|