1+ import cookie from 'js-cookie'
2+ import assert from 'assert'
13import { Analytics } from '../../../core/analytics'
24import { pageEnrichment , pageDefaults } from '..'
35import { pick } from '../../../lib/pick'
6+ import { SegmentioSettings } from '../../segmentio'
7+ import { version } from '../../../generated/version'
8+ import { CoreExtraContext } from '@segment/analytics-core'
49
510let ajs : Analytics
611
@@ -16,6 +21,20 @@ const helpers = {
1621 } ,
1722}
1823
24+ /**
25+ * Filters out the calls made for probing cookie availability
26+ */
27+ const ignoreProbeCookieWrites = (
28+ fn : jest . SpyInstance <
29+ string | undefined ,
30+ [
31+ name : string ,
32+ value : string | object ,
33+ options ?: cookie . CookieAttributes | undefined
34+ ]
35+ >
36+ ) => fn . mock . calls . filter ( ( c ) => c [ 0 ] !== 'ajs_cookies_check' )
37+
1938describe ( 'Page Enrichment' , ( ) => {
2039 beforeEach ( async ( ) => {
2140 ajs = new Analytics ( {
@@ -239,3 +258,310 @@ describe('pageDefaults', () => {
239258 expect ( defs . url ) . toEqual ( window . location . href )
240259 } )
241260} )
261+
262+ describe ( 'Other visitor metadata' , ( ) => {
263+ let options : SegmentioSettings
264+ let analytics : Analytics
265+
266+ const amendSearchParams = ( search ?: any ) : CoreExtraContext => ( {
267+ page : { search } ,
268+ } )
269+
270+ beforeEach ( async ( ) => {
271+ options = { apiKey : 'foo' }
272+ analytics = new Analytics ( { writeKey : options . apiKey } )
273+
274+ await analytics . register ( pageEnrichment )
275+ } )
276+
277+ afterEach ( ( ) => {
278+ analytics . reset ( )
279+ Object . keys ( cookie . get ( ) ) . map ( ( k ) => cookie . remove ( k ) )
280+
281+ if ( window . localStorage ) {
282+ window . localStorage . clear ( )
283+ }
284+ } )
285+
286+ it ( 'should add .library' , async ( ) => {
287+ const ctx = await analytics . track ( 'test' )
288+ assert ( ctx . event . context ?. library )
289+ assert ( ctx . event . context ?. library . name === 'analytics.js' )
290+ assert ( ctx . event . context ?. library . version === `npm:next-${ version } ` )
291+ } )
292+
293+ it ( 'should allow override of .library' , async ( ) => {
294+ const customContext = {
295+ library : {
296+ name : 'analytics-wordpress' ,
297+ version : '1.0.3' ,
298+ } ,
299+ }
300+
301+ const ctx = await analytics . track ( 'test' , { } , { context : customContext } )
302+
303+ assert ( ctx . event . context ?. library )
304+ assert ( ctx . event . context ?. library . name === 'analytics-wordpress' )
305+ assert ( ctx . event . context ?. library . version === '1.0.3' )
306+ } )
307+
308+ it ( 'should add .userAgent' , async ( ) => {
309+ const ctx = await analytics . track ( 'test' )
310+ const removeVersionNum = ( agent : string ) => agent . replace ( / j s d o m \/ .* / , '' )
311+ const userAgent1 = removeVersionNum ( ctx . event . context ?. userAgent as string )
312+ const userAgent2 = removeVersionNum ( navigator . userAgent )
313+ assert ( userAgent1 === userAgent2 )
314+ } )
315+
316+ it ( 'should add .locale' , async ( ) => {
317+ const ctx = await analytics . track ( 'test' )
318+ assert ( ctx . event . context ?. locale === navigator . language )
319+ } )
320+
321+ it ( 'should not replace .locale if provided' , async ( ) => {
322+ const customContext = {
323+ locale : 'foobar' ,
324+ }
325+
326+ const ctx = await analytics . track ( 'test' , { } , { context : customContext } )
327+ assert ( ctx . event . context ?. locale === 'foobar' )
328+ } )
329+
330+ it ( 'should add .campaign' , async ( ) => {
331+ const ctx = await analytics . track (
332+ 'test' ,
333+ { } ,
334+ {
335+ context : amendSearchParams (
336+ 'utm_source=source&utm_medium=medium&utm_term=term&utm_content=content&utm_campaign=name'
337+ ) ,
338+ }
339+ )
340+
341+ assert ( ctx . event )
342+ assert ( ctx . event . context )
343+ assert ( ctx . event . context . campaign )
344+ assert ( ctx . event . context . campaign . source === 'source' )
345+ assert ( ctx . event . context . campaign . medium === 'medium' )
346+ assert ( ctx . event . context . campaign . term === 'term' )
347+ assert ( ctx . event . context . campaign . content === 'content' )
348+ assert ( ctx . event . context . campaign . name === 'name' )
349+ } )
350+
351+ it ( 'should decode query params' , async ( ) => {
352+ const ctx = await analytics . track (
353+ 'test' ,
354+ { } ,
355+ {
356+ context : amendSearchParams ( '?utm_source=%5BFoo%5D' ) ,
357+ }
358+ )
359+
360+ assert ( ctx . event )
361+ assert ( ctx . event . context )
362+ assert ( ctx . event . context . campaign )
363+ assert ( ctx . event . context . campaign . source === '[Foo]' )
364+ } )
365+
366+ it ( 'should guard against undefined utm params' , async ( ) => {
367+ const ctx = await analytics . track (
368+ 'test' ,
369+ { } ,
370+ {
371+ context : amendSearchParams ( '?utm_source' ) ,
372+ }
373+ )
374+
375+ assert ( ctx . event )
376+ assert ( ctx . event . context )
377+ assert ( ctx . event . context . campaign )
378+ assert ( ctx . event . context . campaign . source === '' )
379+ } )
380+
381+ it ( 'should guard against empty utm params' , async ( ) => {
382+ const ctx = await analytics . track (
383+ 'test' ,
384+ { } ,
385+ {
386+ context : amendSearchParams ( '?utm_source=' ) ,
387+ }
388+ )
389+
390+ assert ( ctx . event )
391+ assert ( ctx . event . context )
392+ assert ( ctx . event . context . campaign )
393+ assert ( ctx . event . context . campaign . source === '' )
394+ } )
395+
396+ it ( 'only parses utm params suffixed with _' , async ( ) => {
397+ const ctx = await analytics . track (
398+ 'test' ,
399+ { } ,
400+ {
401+ context : amendSearchParams ( '?utm' ) ,
402+ }
403+ )
404+
405+ assert ( ctx . event )
406+ assert ( ctx . event . context )
407+ assert . deepStrictEqual ( ctx . event . context . campaign , { } )
408+ } )
409+
410+ it ( 'should guard against short utm params' , async ( ) => {
411+ const ctx = await analytics . track (
412+ 'test' ,
413+ { } ,
414+ {
415+ context : amendSearchParams ( '?utm_' ) ,
416+ }
417+ )
418+
419+ assert ( ctx . event )
420+ assert ( ctx . event . context )
421+ assert . deepStrictEqual ( ctx . event . context . campaign , { } )
422+ } )
423+
424+ it ( 'should allow override of .campaign' , async ( ) => {
425+ const ctx = await analytics . track (
426+ 'test' ,
427+ { } ,
428+ {
429+ context : {
430+ ...amendSearchParams (
431+ '?utm_source=source&utm_medium=medium&utm_term=term&utm_content=content&utm_campaign=name'
432+ ) ,
433+ campaign : {
434+ source : 'overrideSource' ,
435+ medium : 'overrideMedium' ,
436+ term : 'overrideTerm' ,
437+ content : 'overrideContent' ,
438+ name : 'overrideName' ,
439+ } ,
440+ } ,
441+ }
442+ )
443+
444+ assert ( ctx . event )
445+ assert ( ctx . event . context )
446+ assert ( ctx . event . context . campaign )
447+ assert ( ctx . event . context . campaign . source === 'overrideSource' )
448+ assert ( ctx . event . context . campaign . medium === 'overrideMedium' )
449+ assert ( ctx . event . context . campaign . term === 'overrideTerm' )
450+ assert ( ctx . event . context . campaign . content === 'overrideContent' )
451+ assert ( ctx . event . context . campaign . name === 'overrideName' )
452+ } )
453+
454+ it ( 'should allow override of .search with object' , async ( ) => {
455+ const ctx = await analytics . track (
456+ 'test' ,
457+ { } ,
458+ {
459+ context : amendSearchParams ( {
460+ someObject : 'foo' ,
461+ } ) ,
462+ }
463+ )
464+ assert ( ctx . event )
465+ assert ( ctx . event . context )
466+ assert ( ctx . event . context . campaign === undefined )
467+ assert ( ctx . event . context . referrer === undefined )
468+ } )
469+
470+ it ( 'should add .referrer.id and .referrer.type (cookies)' , async ( ) => {
471+ const ctx = await analytics . track (
472+ 'test' ,
473+ { } ,
474+ {
475+ context : amendSearchParams ( '?utm_source=source&urid=medium' ) ,
476+ }
477+ )
478+
479+ assert ( ctx . event )
480+ assert ( ctx . event . context )
481+ assert ( ctx . event . context . referrer )
482+ expect ( ctx . event . context . referrer . id ) . toBe ( 'medium' )
483+ assert ( ctx . event . context . referrer . type === 'millennial-media' )
484+ expect ( cookie . get ( 's:context.referrer' ) ) . toEqual (
485+ JSON . stringify ( {
486+ id : 'medium' ,
487+ type : 'millennial-media' ,
488+ } )
489+ )
490+ } )
491+
492+ it ( 'should add .referrer.id and .referrer.type (cookieless)' , async ( ) => {
493+ const setCookieSpy = jest . spyOn ( cookie , 'set' )
494+ analytics = new Analytics (
495+ { writeKey : options . apiKey } ,
496+ { disableClientPersistence : true }
497+ )
498+
499+ await analytics . register ( pageEnrichment )
500+
501+ const ctx = await analytics . track (
502+ 'test' ,
503+ { } ,
504+ {
505+ context : amendSearchParams ( 'utm_source=source&urid=medium' ) ,
506+ }
507+ )
508+
509+ assert ( ctx . event )
510+ assert ( ctx . event . context )
511+ assert ( ctx . event . context . referrer )
512+ expect ( ctx . event . context . referrer . id ) . toEqual ( 'medium' )
513+ assert ( ctx . event . context . referrer . type === 'millennial-media' )
514+ expect ( cookie . get ( 's:context.referrer' ) ) . toBeUndefined ( )
515+ expect ( ignoreProbeCookieWrites ( setCookieSpy ) . length ) . toBe ( 0 )
516+ } )
517+
518+ it ( 'should add .referrer.id and .referrer.type from cookie' , async ( ) => {
519+ cookie . set ( 's:context.referrer' , '{"id":"baz","type":"millennial-media"}' )
520+ const ctx = await analytics . track ( 'test' )
521+
522+ assert ( ctx . event )
523+ assert ( ctx . event . context )
524+ assert ( ctx . event . context . referrer )
525+ assert ( ctx . event . context . referrer . id === 'baz' )
526+ assert ( ctx . event . context . referrer . type === 'millennial-media' )
527+ } )
528+
529+ it ( 'should add .referrer.id and .referrer.type from cookie when no query is given' , async ( ) => {
530+ cookie . set (
531+ 's:context.referrer' ,
532+ '{"id":"medium","type":"millennial-media"}'
533+ )
534+ const ctx = await analytics . track ( 'test' )
535+
536+ assert ( ctx . event )
537+ assert ( ctx . event . context )
538+ assert ( ctx . event . context . referrer )
539+ assert ( ctx . event . context . referrer . id === 'medium' )
540+ assert ( ctx . event . context . referrer . type === 'millennial-media' )
541+ } )
542+
543+ it ( 'shouldnt add non amp ga cookie' , async ( ) => {
544+ cookie . set ( '_ga' , 'some-nonamp-id' )
545+ const ctx = await analytics . track ( 'test' )
546+ assert ( ctx . event )
547+ assert ( ctx . event . context )
548+ assert ( ! ctx . event . context . amp )
549+ } )
550+
551+ it ( 'should add .amp.id from store' , async ( ) => {
552+ cookie . set ( '_ga' , 'amp-foo' )
553+ const ctx = await analytics . track ( 'test' )
554+ assert ( ctx . event )
555+ assert ( ctx . event . context )
556+ assert ( ctx . event . context . amp )
557+ assert ( ctx . event . context . amp . id === 'amp-foo' )
558+ } )
559+
560+ it ( 'should not add .amp if theres no _ga' , async ( ) => {
561+ cookie . remove ( '_ga' )
562+ const ctx = await analytics . track ( 'test' )
563+ assert ( ctx . event )
564+ assert ( ctx . event . context )
565+ assert ( ! ctx . event . context . amp )
566+ } )
567+ } )
0 commit comments