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(''); 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); });