From 097f83cf537047446aed1e3892b4facb5ca47261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 03:09:07 -0300 Subject: [PATCH 1/9] feat: track canonical completed registrations --- assets/hmp-commerce.js | 13 ++++ assets/meta-pixel.js | 30 ++++++++ tests/hmp-commerce.test.js | 57 +++++++++++++- tests/meta-pixel.test.js | 127 +++++++++++++++++++++++++++++++ tests/registration-pages.test.js | 10 +++ 5 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 tests/meta-pixel.test.js diff --git a/assets/hmp-commerce.js b/assets/hmp-commerce.js index bb817f7..c87d5cd 100644 --- a/assets/hmp-commerce.js +++ b/assets/hmp-commerce.js @@ -241,6 +241,7 @@ const result = await response.json(); if ( result.schema_version !== 'registration.response.v1' || + result.registration_status !== 'REGISTERED' || !REGISTRATION_ID_PATTERN.test(result.registration_id || '') || !STATUS_TOKEN_PATTERN.test(result.commerce_status_token || '') || !validCheckoutUrl(result.checkout && result.checkout.widget_url, payload, result.checkout) @@ -255,6 +256,18 @@ session_code: payload.session_code })); storage().removeItem(IDEMPOTENCY_KEY); + const completedRegistration = Object.freeze({ + registrationId: result.registration_id, + sessionCode: payload.session_code + }); + window.__hbCompletedRegistration = completedRegistration; + if (typeof window.CustomEvent === 'function' && typeof window.dispatchEvent === 'function') { + try { + window.dispatchEvent(new window.CustomEvent('hb:registration-completed', { + detail: completedRegistration + })); + } catch (_) {} + } return result; } diff --git a/assets/meta-pixel.js b/assets/meta-pixel.js index 577845b..c42c5cc 100644 --- a/assets/meta-pixel.js +++ b/assets/meta-pixel.js @@ -5,6 +5,9 @@ const CONSENT_KEY = 'hb-meta-consent'; const GRANTED = 'granted'; const DENIED = 'denied'; + const REGISTRATION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + const SESSION_CODE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/; + const sentRegistrationIds = new Set(); let pixelLoaded = false; const readConsent = () => { @@ -64,12 +67,39 @@ window.fbq('consent', 'grant'); window.fbq('init', PIXEL_ID); window.fbq('track', 'PageView'); + if (window.__hbCompletedRegistration) trackCompletedRegistration(window.__hbCompletedRegistration); + } + + function trackCompletedRegistration(detail) { + if ( + !detail || + !REGISTRATION_ID_PATTERN.test(detail.registrationId || '') || + !SESSION_CODE_PATTERN.test(detail.sessionCode || '') + ) return; + const registrationKey = `hb-meta-registration-${detail.registrationId}`; + if (sentRegistrationIds.has(detail.registrationId)) return; + try { + if (sessionStorage.getItem(registrationKey)) return; + sessionStorage.setItem(registrationKey, 'sent'); + } catch (_) {} + sentRegistrationIds.add(detail.registrationId); + + window.fbq('track', 'CompleteRegistration', { + content_name: 'Harmonic Myth Projection', + content_ids: [detail.sessionCode], + content_type: 'product' + }); } // Purchase intentionally fails closed until commerce-status exposes an // authoritative paid conversion, real amount/currency and stable opaque ID. // registrationId + ACCESS_READY alone can also represent a zero-value access. + window.addEventListener('hb:registration-completed', (event) => { + window.__hbCompletedRegistration = event.detail; + if (pixelLoaded) trackCompletedRegistration(event.detail); + }); + function revokePixel() { if (typeof window.fbq === 'function') window.fbq('consent', 'revoke'); ['_fbp', '_fbc'].forEach((name) => { diff --git a/tests/hmp-commerce.test.js b/tests/hmp-commerce.test.js index 73830e7..b8aaaf4 100644 --- a/tests/hmp-commerce.test.js +++ b/tests/hmp-commerce.test.js @@ -22,6 +22,12 @@ function runtime(fetchImpl, overrides = {}, registrationOpen = true) { fetch: fetchImpl, sessionStorage, setTimeout, + CustomEvent: class CustomEvent { + constructor(type, options = {}) { + this.type = type; + this.detail = options.detail; + } + }, ...overrides }; const context = vm.createContext({URL, window}); @@ -33,7 +39,7 @@ function runtime(fetchImpl, overrides = {}, registrationOpen = true) { ? source : source.replace('const REGISTRATION_OPEN = true;', 'const REGISTRATION_OPEN = false;'); vm.runInContext(evaluatedSource, context); - return {api: window.HMPCommerce, values}; + return {api: window.HMPCommerce, values, window}; } function widgetDom() { @@ -258,6 +264,7 @@ test('stores a separate status token only after a valid registration response', ok: true, json: async () => ({ schema_version: 'registration.response.v1', + registration_status: 'REGISTERED', registration_id: '20000000-0000-4000-8000-000000000001', commerce_status_token: statusToken, checkout @@ -274,6 +281,54 @@ test('stores a separate status token only after a valid registration response', assert.match(result.checkout.widget_url, /^https:\/\/tickets\.harmonicbeacon\.com\//); }); +test('announces CompleteRegistration only after a canonical REGISTERED response', async () => { + const events = []; + const {api, window} = runtime(async () => ({ + ok: true, + json: async () => ({ + schema_version: 'registration.response.v1', + registration_status: 'REGISTERED', + registration_id: '20000000-0000-4000-8000-000000000001', + commerce_status_token: statusToken, + checkout + }) + }), { + dispatchEvent: event => { events.push(event); return true; } + }); + + await api.register(payload); + + assert.equal(events.length, 1); + assert.equal(events[0].type, 'hb:registration-completed'); + assert.deepEqual( + JSON.parse(JSON.stringify(events[0].detail)), + { + registrationId: '20000000-0000-4000-8000-000000000001', + sessionCode: 'es-0830-cr' + } + ); + assert.equal(window.__hbCompletedRegistration.registrationId, events[0].detail.registrationId); +}); + +test('rejects a response without canonical REGISTERED status and emits no event', async () => { + const events = []; + const {api} = runtime(async () => ({ + ok: true, + json: async () => ({ + schema_version: 'registration.response.v1', + registration_status: 'VERIFYING', + registration_id: '20000000-0000-4000-8000-000000000001', + commerce_status_token: statusToken, + checkout + }) + }), { + dispatchEvent: event => { events.push(event); return true; } + }); + + await assert.rejects(api.register(payload), /invalid_registration_response/); + assert.equal(events.length, 0); +}); + test('rejects malformed registration ids and status tokens before storing context', async () => { const malformed = async () => ({ ok: true, diff --git a/tests/meta-pixel.test.js b/tests/meta-pixel.test.js new file mode 100644 index 0000000..1089e08 --- /dev/null +++ b/tests/meta-pixel.test.js @@ -0,0 +1,127 @@ +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const test = require('node:test'); +const vm = require('node:vm'); + +const source = fs.readFileSync(path.join(__dirname, '..', 'assets', 'meta-pixel.js'), 'utf8'); +const registration = { + registrationId: '20000000-0000-4000-8000-000000000001', + sessionCode: 'es-0830-cr' +}; + +function pixelRuntime(initialConsent = null) { + const localValues = new Map(initialConsent ? [['hb-meta-consent', initialConsent]] : []); + const sessionValues = new Map(); + const listeners = new Map(); + const controls = new Map(); + const insertedScripts = []; + const localStorage = { + getItem: key => localValues.has(key) ? localValues.get(key) : null, + setItem: (key, value) => localValues.set(key, String(value)) + }; + const sessionStorage = { + getItem: key => sessionValues.has(key) ? sessionValues.get(key) : null, + setItem: (key, value) => sessionValues.set(key, String(value)) + }; + const button = name => ({ + addEventListener: (type, callback) => controls.set(`${name}:${type}`, callback), + focus() {} + }); + const grant = button('grant'); + const deny = button('deny'); + const document = { + body: {append() {}}, + cookie: '', + documentElement: {lang: 'es'}, + head: {appendChild() {}}, + readyState: 'complete', + querySelector: () => null, + createElement: tag => { + if (tag === 'section') { + return { + hidden: false, + setAttribute() {}, + querySelector: selector => selector.includes('grant') ? grant : deny + }; + } + if (tag === 'button') { + return { + hidden: false, + addEventListener: (type, callback) => controls.set(`settings:${type}`, callback) + }; + } + return {}; + }, + getElementsByTagName: () => [{ + parentNode: {insertBefore: script => insertedScripts.push(script)} + }], + addEventListener() {} + }; + const window = { + addEventListener: (type, callback) => listeners.set(type, callback), + localStorage, + sessionStorage + }; + vm.runInNewContext(source, { + document, + localStorage, + navigator: {language: 'es'}, + sessionStorage, + window + }); + const registrationHandler = listeners.get('hb:registration-completed'); + return { + calls: () => window.fbq?.queue || [], + controls, + insertedScripts, + sessionValues, + triggerRegistration: detail => registrationHandler({detail}), + window + }; +} + +test('consent granted tracks CompleteRegistration once without PII', () => { + const runtime = pixelRuntime('granted'); + + runtime.triggerRegistration(registration); + runtime.triggerRegistration(registration); + + const events = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(events.length, 1); + assert.deepEqual(Array.from(events[0][2].content_ids), ['es-0830-cr']); + assert.equal(events[0].length, 3); + assert.equal(JSON.stringify(events[0]).includes('email'), false); + assert.equal(JSON.stringify(events[0]).includes('Alma'), false); +}); + +test('declined consent never loads Meta or tracks the registration', () => { + const runtime = pixelRuntime('denied'); + + runtime.triggerRegistration(registration); + + assert.equal(runtime.insertedScripts.length, 0); + assert.equal(runtime.window.fbq, undefined); + assert.equal(runtime.sessionValues.size, 0); +}); + +test('registration completed before consent is sent once after explicit grant', () => { + const runtime = pixelRuntime(); + runtime.triggerRegistration(registration); + + assert.equal(runtime.window.fbq, undefined); + runtime.controls.get('grant:click')(); + + const events = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(events.length, 1); +}); + +test('malformed identifiers never produce CompleteRegistration', () => { + const runtime = pixelRuntime('granted'); + + runtime.triggerRegistration({registrationId: 'not-a-uuid', sessionCode: 'es-0830-cr'}); + runtime.triggerRegistration({registrationId: registration.registrationId, sessionCode: '../private'}); + + const events = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(events.length, 0); +}); diff --git a/tests/registration-pages.test.js b/tests/registration-pages.test.js index bb1ac7c..64c6f60 100644 --- a/tests/registration-pages.test.js +++ b/tests/registration-pages.test.js @@ -69,6 +69,16 @@ test('Meta Purchase fails closed without canonical paid conversion facts', () => assert.doesNotMatch(pixel, /tt_order_id/); }); +test('Meta CompleteRegistration is gated by the canonical registration event', () => { + const commerce = read('assets/hmp-commerce.js'); + const pixel = read('assets/meta-pixel.js'); + assert.match(commerce, /result\.registration_status !== 'REGISTERED'/); + assert.match(commerce, /hb:registration-completed/); + assert.match(pixel, /trackCompletedRegistration/); + assert.match(pixel, /'CompleteRegistration'/); + assert.doesNotMatch(pixel, /detail\.email|detail\.firstName|detail\.lastName/); +}); + test('registration-v3 remains byte-exact as immutable historical evidence', () => { const bytes = fs.readFileSync(path.join(root, 'legal/terms/registration-v3.html')); assert.equal( From 88890186c2692cd7c4b4559deda3d86bcfd51849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 03:51:09 -0300 Subject: [PATCH 2/9] fix: honor Meta consent changes --- assets/meta-pixel.js | 9 ++++++++- tests/meta-pixel.test.js | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/assets/meta-pixel.js b/assets/meta-pixel.js index c42c5cc..9008047 100644 --- a/assets/meta-pixel.js +++ b/assets/meta-pixel.js @@ -51,7 +51,12 @@ }; function loadPixel() { - if (pixelLoaded || readConsent() !== GRANTED) return; + if (readConsent() !== GRANTED) return; + if (pixelLoaded) { + if (typeof window.fbq === 'function') window.fbq('consent', 'grant'); + if (window.__hbCompletedRegistration) trackCompletedRegistration(window.__hbCompletedRegistration); + return; + } pixelLoaded = true; /* Meta Pixel base code */ @@ -72,6 +77,8 @@ function trackCompletedRegistration(detail) { if ( + readConsent() !== GRANTED || + typeof window.fbq !== 'function' || !detail || !REGISTRATION_ID_PATTERN.test(detail.registrationId || '') || !SESSION_CODE_PATTERN.test(detail.sessionCode || '') diff --git a/tests/meta-pixel.test.js b/tests/meta-pixel.test.js index 1089e08..3703100 100644 --- a/tests/meta-pixel.test.js +++ b/tests/meta-pixel.test.js @@ -116,6 +116,32 @@ test('registration completed before consent is sent once after explicit grant', assert.equal(events.length, 1); }); +test('consent can be revoked and granted again without reloading the page', () => { + const runtime = pixelRuntime('granted'); + runtime.triggerRegistration(registration); + + runtime.controls.get('deny:click')(); + runtime.controls.get('grant:click')(); + + const consentCalls = runtime.calls().filter(call => call[0] === 'consent'); + const events = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.deepEqual(Array.from(consentCalls, call => call[1]), ['grant', 'revoke', 'grant']); + assert.equal(events.length, 1); +}); + +test('a registration completed while consent is revoked is sent after a later grant', () => { + const runtime = pixelRuntime('granted'); + runtime.controls.get('deny:click')(); + runtime.triggerRegistration(registration); + + let events = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(events.length, 0); + + runtime.controls.get('grant:click')(); + events = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(events.length, 1); +}); + test('malformed identifiers never produce CompleteRegistration', () => { const runtime = pixelRuntime('granted'); From 697c335c1eed994039b636824a4377755db25eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 03:52:13 -0300 Subject: [PATCH 3/9] test: prove registration analytics resilience --- tests/hmp-commerce.test.js | 20 ++++++++++++++++++++ tests/meta-pixel.test.js | 17 +++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/hmp-commerce.test.js b/tests/hmp-commerce.test.js index b8aaaf4..e947bd7 100644 --- a/tests/hmp-commerce.test.js +++ b/tests/hmp-commerce.test.js @@ -310,6 +310,26 @@ test('announces CompleteRegistration only after a canonical REGISTERED response' assert.equal(window.__hbCompletedRegistration.registrationId, events[0].detail.registrationId); }); +test('an analytics listener failure never rejects a valid registration response', async () => { + const {api} = runtime(async () => ({ + ok: true, + json: async () => ({ + schema_version: 'registration.response.v1', + registration_status: 'REGISTERED', + registration_id: '20000000-0000-4000-8000-000000000001', + commerce_status_token: statusToken, + checkout + }) + }), { + dispatchEvent: () => { throw new Error('analytics unavailable'); } + }); + + const result = await api.register(payload); + + assert.equal(result.registration_status, 'REGISTERED'); + assert.equal(result.checkout.widget_url, checkout.widget_url); +}); + test('rejects a response without canonical REGISTERED status and emits no event', async () => { const events = []; const {api} = runtime(async () => ({ diff --git a/tests/meta-pixel.test.js b/tests/meta-pixel.test.js index 3703100..c486615 100644 --- a/tests/meta-pixel.test.js +++ b/tests/meta-pixel.test.js @@ -10,9 +10,8 @@ const registration = { sessionCode: 'es-0830-cr' }; -function pixelRuntime(initialConsent = null) { +function pixelRuntime(initialConsent = null, sessionValues = new Map()) { const localValues = new Map(initialConsent ? [['hb-meta-consent', initialConsent]] : []); - const sessionValues = new Map(); const listeners = new Map(); const controls = new Map(); const insertedScripts = []; @@ -95,6 +94,20 @@ test('consent granted tracks CompleteRegistration once without PII', () => { assert.equal(JSON.stringify(events[0]).includes('Alma'), false); }); +test('refresh does not resend a completed registration in the same browser tab', () => { + const sessionValues = new Map(); + const firstPage = pixelRuntime('granted', sessionValues); + firstPage.triggerRegistration(registration); + + const refreshedPage = pixelRuntime('granted', sessionValues); + refreshedPage.triggerRegistration(registration); + + const firstEvents = firstPage.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + const refreshedEvents = refreshedPage.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(firstEvents.length, 1); + assert.equal(refreshedEvents.length, 0); +}); + test('declined consent never loads Meta or tracks the registration', () => { const runtime = pixelRuntime('denied'); From 07829ffd604fc11d369aa2d75d288905175a746a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 04:30:10 -0300 Subject: [PATCH 4/9] test: pin PageView consent lifecycle --- tests/meta-pixel.test.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/meta-pixel.test.js b/tests/meta-pixel.test.js index c486615..8dcc82b 100644 --- a/tests/meta-pixel.test.js +++ b/tests/meta-pixel.test.js @@ -94,6 +94,19 @@ test('consent granted tracks CompleteRegistration once without PII', () => { assert.equal(JSON.stringify(events[0]).includes('Alma'), false); }); +test('PageView fires once per page load and is not repeated by consent re-grant', () => { + const runtime = pixelRuntime('granted'); + + let pageViews = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'PageView'); + assert.equal(pageViews.length, 1); + + runtime.controls.get('deny:click')(); + runtime.controls.get('grant:click')(); + + pageViews = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'PageView'); + assert.equal(pageViews.length, 1); +}); + test('refresh does not resend a completed registration in the same browser tab', () => { const sessionValues = new Map(); const firstPage = pixelRuntime('granted', sessionValues); From 88be3712875e44e0f1f906353d086eb16993c788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 04:32:14 -0300 Subject: [PATCH 5/9] fix: dedupe registration analytics across tabs --- assets/meta-pixel.js | 14 ++++++++++---- tests/meta-pixel.test.js | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/assets/meta-pixel.js b/assets/meta-pixel.js index 9008047..5138cca 100644 --- a/assets/meta-pixel.js +++ b/assets/meta-pixel.js @@ -85,10 +85,16 @@ ) return; const registrationKey = `hb-meta-registration-${detail.registrationId}`; if (sentRegistrationIds.has(detail.registrationId)) return; - try { - if (sessionStorage.getItem(registrationKey)) return; - sessionStorage.setItem(registrationKey, 'sent'); - } catch (_) {} + for (const browserStorage of [sessionStorage, localStorage]) { + try { + if (browserStorage.getItem(registrationKey)) return; + } catch (_) {} + } + for (const browserStorage of [sessionStorage, localStorage]) { + try { + browserStorage.setItem(registrationKey, 'sent'); + } catch (_) {} + } sentRegistrationIds.add(detail.registrationId); window.fbq('track', 'CompleteRegistration', { diff --git a/tests/meta-pixel.test.js b/tests/meta-pixel.test.js index 8dcc82b..a666638 100644 --- a/tests/meta-pixel.test.js +++ b/tests/meta-pixel.test.js @@ -10,8 +10,8 @@ const registration = { sessionCode: 'es-0830-cr' }; -function pixelRuntime(initialConsent = null, sessionValues = new Map()) { - const localValues = new Map(initialConsent ? [['hb-meta-consent', initialConsent]] : []); +function pixelRuntime(initialConsent = null, sessionValues = new Map(), localValues = new Map()) { + if (initialConsent) localValues.set('hb-meta-consent', initialConsent); const listeners = new Map(); const controls = new Map(); const insertedScripts = []; @@ -121,6 +121,20 @@ test('refresh does not resend a completed registration in the same browser tab', assert.equal(refreshedEvents.length, 0); }); +test('an idempotent response in another browser tab does not resend CompleteRegistration', () => { + const localValues = new Map(); + const firstTab = pixelRuntime('granted', new Map(), localValues); + firstTab.triggerRegistration(registration); + + const secondTab = pixelRuntime('granted', new Map(), localValues); + secondTab.triggerRegistration(registration); + + const firstEvents = firstTab.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + const secondEvents = secondTab.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(firstEvents.length, 1); + assert.equal(secondEvents.length, 0); +}); + test('declined consent never loads Meta or tracks the registration', () => { const runtime = pixelRuntime('denied'); From 59b9c82a5838b7b25f2d6a90cea5733575d333f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 04:38:27 -0300 Subject: [PATCH 6/9] fix: migrate registration analytics dedupe --- assets/meta-pixel.js | 14 ++++++++++---- tests/meta-pixel.test.js | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/assets/meta-pixel.js b/assets/meta-pixel.js index 5138cca..083f7b3 100644 --- a/assets/meta-pixel.js +++ b/assets/meta-pixel.js @@ -85,10 +85,16 @@ ) return; const registrationKey = `hb-meta-registration-${detail.registrationId}`; if (sentRegistrationIds.has(detail.registrationId)) return; - for (const browserStorage of [sessionStorage, localStorage]) { - try { - if (browserStorage.getItem(registrationKey)) return; - } catch (_) {} + let sentInCurrentTab = false; + try { + sentInCurrentTab = Boolean(sessionStorage.getItem(registrationKey)); + } catch (_) {} + try { + if (localStorage.getItem(registrationKey)) return; + if (sentInCurrentTab) localStorage.setItem(registrationKey, 'sent'); + } catch (_) {} + if (sentInCurrentTab) { + return; } for (const browserStorage of [sessionStorage, localStorage]) { try { diff --git a/tests/meta-pixel.test.js b/tests/meta-pixel.test.js index a666638..54c0bef 100644 --- a/tests/meta-pixel.test.js +++ b/tests/meta-pixel.test.js @@ -135,6 +135,24 @@ test('an idempotent response in another browser tab does not resend CompleteRegi assert.equal(secondEvents.length, 0); }); +test('a legacy same-tab marker is migrated before another tab receives the response', () => { + const key = `hb-meta-registration-${registration.registrationId}`; + const localValues = new Map(); + const legacySessionValues = new Map([[key, 'sent']]); + const migratedTab = pixelRuntime('granted', legacySessionValues, localValues); + + migratedTab.triggerRegistration(registration); + assert.equal(localValues.get(key), 'sent'); + + const secondTab = pixelRuntime('granted', new Map(), localValues); + secondTab.triggerRegistration(registration); + + const migratedEvents = migratedTab.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + const secondEvents = secondTab.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(migratedEvents.length, 0); + assert.equal(secondEvents.length, 0); +}); + test('declined consent never loads Meta or tracks the registration', () => { const runtime = pixelRuntime('denied'); From ecaa6eef478707b7f4108ec1de711554ba23c68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 06:16:00 -0300 Subject: [PATCH 7/9] test: keep checkout widget independent from analytics --- tests/hmp-commerce.test.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/hmp-commerce.test.js b/tests/hmp-commerce.test.js index e947bd7..66bc93f 100644 --- a/tests/hmp-commerce.test.js +++ b/tests/hmp-commerce.test.js @@ -330,6 +330,33 @@ test('an analytics listener failure never rejects a valid registration response' assert.equal(result.checkout.widget_url, checkout.widget_url); }); +test('an analytics listener failure never blocks the validated checkout widget', async () => { + const dom = widgetDom(); + const {api} = runtime(async () => ({ + ok: true, + json: async () => ({ + schema_version: 'registration.response.v1', + registration_status: 'REGISTERED', + registration_id: '20000000-0000-4000-8000-000000000001', + commerce_status_token: statusToken, + checkout + }) + }), { + document: dom.document, + dispatchEvent: () => { throw new Error('analytics unavailable'); } + }); + + const result = await api.register(payload); + const script = api.mountCheckoutWidget(dom.container, payload, result.checkout); + + assert.equal(dom.container.children.length, 1); + assert.equal(script.src, 'https://cdn.tickettailor.com/js/widgets/min/widget.js'); + assert.equal( + new URL(script.attributes.get('data-url')).hash, + `#p[meta_registration_context]=${checkoutContext}` + ); +}); + test('rejects a response without canonical REGISTERED status and emits no event', async () => { const events = []; const {api} = runtime(async () => ({ From 77604b29737e74304813f5b0b0e25fcf25f9bc02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 06:20:30 -0300 Subject: [PATCH 8/9] test: cover English registration analytics --- tests/meta-pixel.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/meta-pixel.test.js b/tests/meta-pixel.test.js index 54c0bef..fb416ef 100644 --- a/tests/meta-pixel.test.js +++ b/tests/meta-pixel.test.js @@ -94,6 +94,21 @@ test('consent granted tracks CompleteRegistration once without PII', () => { assert.equal(JSON.stringify(events[0]).includes('Alma'), false); }); +test('English canonical session tracks the selected product without locale-derived values', () => { + const runtime = pixelRuntime('granted'); + + runtime.triggerRegistration({ + registrationId: '20000000-0000-4000-8000-000000000002', + sessionCode: 'en-1400-cr' + }); + + const events = runtime.calls().filter(call => call[0] === 'track' && call[1] === 'CompleteRegistration'); + assert.equal(events.length, 1); + assert.deepEqual(Array.from(events[0][2].content_ids), ['en-1400-cr']); + assert.equal('value' in events[0][2], false); + assert.equal('currency' in events[0][2], false); +}); + test('PageView fires once per page load and is not repeated by consent re-grant', () => { const runtime = pixelRuntime('granted'); From df77ef3e907ce8979522708f9c8f7c9658f6e740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ech=C3=A1niz?= Date: Tue, 4 Aug 2026 15:09:27 -0300 Subject: [PATCH 9/9] test: preserve checkout prefill with analytics failure --- tests/hmp-commerce.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/hmp-commerce.test.js b/tests/hmp-commerce.test.js index 66bc93f..08228f2 100644 --- a/tests/hmp-commerce.test.js +++ b/tests/hmp-commerce.test.js @@ -348,13 +348,15 @@ test('an analytics listener failure never blocks the validated checkout widget', const result = await api.register(payload); const script = api.mountCheckoutWidget(dom.container, payload, result.checkout); + const mountedUrl = new URL(script.attributes.get('data-url')); + const mountedHash = new URLSearchParams(mountedUrl.hash.slice(1)); assert.equal(dom.container.children.length, 1); assert.equal(script.src, 'https://cdn.tickettailor.com/js/widgets/min/widget.js'); - assert.equal( - new URL(script.attributes.get('data-url')).hash, - `#p[meta_registration_context]=${checkoutContext}` - ); + assert.equal(mountedHash.get('p[meta_registration_context]'), checkoutContext); + assert.equal(mountedHash.get('p[first_name]'), payload.first_name); + assert.equal(mountedHash.get('p[last_name]'), payload.last_name); + assert.equal(mountedHash.get('p[email]'), payload.email); }); test('rejects a response without canonical REGISTERED status and emits no event', async () => {