@@ -10,6 +10,7 @@ import {
1010 marketplaceAnalyticsConfig ,
1111 marketplaceCanonicalPath ,
1212 marketplaceTrackingAllowed ,
13+ marketplaceUmamiBeforeSend ,
1314 trackMarketplaceAction ,
1415} from '../src/tracking/marketplace-tracking.mjs' ;
1516import {
@@ -126,6 +127,52 @@ test('allows only reviewed aggregate actions and public pack identifiers', () =>
126127 assert . equal ( trackMarketplaceAction ( element , { path : '/' , umami : undefined } ) , false ) ;
127128} ) ;
128129
130+ test ( 'sanitizes Umami event location without changing SDK metadata or event data' , ( ) => {
131+ const payload = {
132+ website : '00000000-0000-4000-8000-000000000001' ,
133+ hostname : 'marketplace.registrystack.org' ,
134+ screen : '1440x900' ,
135+ language : 'en-US' ,
136+ title : 'Professional licensing' ,
137+ url : 'https://marketplace.registrystack.org/packs/professional-licensing/?email=person@example.org#request-form' ,
138+ referrer : 'https://marketplace.registrystack.org/contribute/?message=private#form' ,
139+ name : 'marketplace_action' ,
140+ data : { action : 'download_intent' , source_path : '/packs/professional-licensing/' } ,
141+ } ;
142+ const sanitized = marketplaceUmamiBeforeSend ( 'event' , payload ) ;
143+ assert . deepEqual ( sanitized , {
144+ ...payload ,
145+ url : '/packs/professional-licensing/' ,
146+ referrer : 'https://marketplace.registrystack.org' ,
147+ } ) ;
148+ assert . notEqual ( sanitized , payload ) ;
149+ assert . match ( payload . url , / p e r s o n @ e x a m p l e \. o r g / ) ;
150+
151+ assert . equal ( marketplaceUmamiBeforeSend ( 'event' , {
152+ ...payload ,
153+ url : '/privacy/?email=person@example.org' ,
154+ referrer : 'not a URL' ,
155+ } ) . referrer , '' ) ;
156+ assert . equal ( marketplaceUmamiBeforeSend ( 'event' , {
157+ ...payload ,
158+ url : '/contribute/#private' ,
159+ referrer : 'http://partner.example/private?email=person@example.org' ,
160+ } ) . referrer , 'http://partner.example' ) ;
161+ } ) ;
162+
163+ test ( 'rejects non-events and Umami payloads for invalid or private routes' , ( ) => {
164+ const payload = { url : '/' , referrer : '' } ;
165+ for ( const [ type , candidate ] of [
166+ [ 'identify' , payload ] ,
167+ [ 'pageview' , payload ] ,
168+ [ 'event' , null ] ,
169+ [ 'event' , [ ] ] ,
170+ [ 'event' , { } ] ,
171+ [ 'event' , { ...payload , url : '/search/person@example.org/' } ] ,
172+ [ 'event' , { ...payload , url : 'https://other.example/privacy/' } ] ,
173+ ] ) assert . equal ( marketplaceUmamiBeforeSend ( type , candidate ) , false ) ;
174+ } ) ;
175+
129176test ( 'DNT and GPC keep optional Mautic tracking off' , ( ) => {
130177 assert . equal ( trackingDeniedByBrowser ( { navigator : { doNotTrack : '1' } } ) , true ) ;
131178 assert . equal ( trackingDeniedByBrowser ( { navigator : { doNotTrack : 'yes' } } ) , true ) ;
@@ -159,12 +206,20 @@ test('component separates aggregate events from consented pageviews and never re
159206 assert . match ( component , / T r a c k i n g i s d i s a b l e d i n l o c a l p r e v i e w / ) ;
160207 assert . match ( component , / P U B L I C _ S H A R E D _ T R A C K I N G _ E N A B L E D = = = ' t r u e ' / ) ;
161208 assert . match ( component , / s c r i p t \. d a t a s e t \. e x c l u d e H a s h = ' t r u e ' / ) ;
209+ assert . match ( component , / w i n d o w \. m a r k e t p l a c e U m a m i B e f o r e S e n d = m a r k e t p l a c e U m a m i B e f o r e S e n d / ) ;
210+ assert . match ( component , / s c r i p t \. d a t a s e t \. b e f o r e S e n d = ' m a r k e t p l a c e U m a m i B e f o r e S e n d ' / ) ;
211+ assert . ok (
212+ component . indexOf ( 'window.marketplaceUmamiBeforeSend = marketplaceUmamiBeforeSend' )
213+ < component . indexOf ( 'document.head.append(script)' ) ,
214+ 'The sanitizer must be installed before the Umami SDK executes.' ,
215+ ) ;
162216 assert . match ( component , / i f \( ! c a n o n i c a l R u n t i m e \| \| ! s h a r e d T r a c k i n g E n a b l e d \) / ) ;
163217 assert . match ( component , / O p t i o n a l v i s i t t r a c k i n g i s n o t e n a b l e d o n t h i s s i t e / ) ;
164218 assert . match ( component , / w e b s i t e a n d d o c u m e n t a t i o n / i) ;
165219 assert . match ( component , / M a r k e t p l a c e t r a c k i n g s t a r t s o n l y a f t e r y o u a c c e p t h e r e / ) ;
166220 assert . match ( privacy , / a c t i v i t y \s + a l r e a d y r e c o r d e d f r o m t h o s e s i t e s / ) ;
167221 assert . doesNotMatch ( component , / \. a s s o c i a t e \( | d e v i c e I d | m a u t i c _ d e v i c e _ i d | m t c _ i d / ) ;
222+ assert . doesNotMatch ( component , / \. i d e n t i f y \( / ) ;
168223 assert . match ( privacy , / f r e s h a c c e p t a n c e / i) ;
169224 assert . match ( privacy , / D o N o t T r a c k a n d G l o b a l P r i v a c y C o n t r o l / i) ;
170225 assert . match (
0 commit comments