All files tcfTracking.js

96.3% Statements 26/27
85.71% Branches 12/14
100% Functions 6/6
100% Lines 25/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109            1x           1x           1x           1x           1x 4x 4x 4x               1x 3x         1x                   1x                         1x 3x                   4x         3x   3x 1x 1x     2x 2x   2x   2x       2x 2x          
import analytics from './index'
 
/**
 * TCF Api Version to use
 * @type {number}
 */
const TCF_API_VERSION = 2
 
/**
 * List of purpose ids needed to be able to track with all info
 * @type {Array<number>}
 */
const NEEDED_PURPOSES = [8, 10]
 
/**
 * Event that determines that the user has performed an action
 * @type {string}
 */
const TCF_EVENT_USER_ACTION_COMPLETE = 'useractioncomplete'
 
/**
 * Event that determines that the user is seeing the UI to give consent
 * @type {string}
 */
const TCF_EVENT_SHOW_UI = 'cmpuishown'
 
/**
 * Check if we're on client and tcfApi is available on window object
 * @returns {Boolean}
 */
const checkTcfIsAvailable = () => {
  const isAvailable = typeof window !== 'undefined' && !!window.__tcfapi
  !isAvailable && console.warn('[tcfTracking] window.__tcfapi is not available')
  return isAvailable
}
 
/**
 * Check from a list of consents if user has accepted being tracked
 * @param {{[purposeId: string]: boolean}} userConsents
 * @returns {Boolean}
 */
const checkHasUserConsentedTracking = userConsents =>
  NEEDED_PURPOSES.every(purposeId => userConsents[`${purposeId}`])
 
/**
 * State of user according to GDPR regarding tracking
 */
const USER_GDPR = {
  ACCEPTED: 'ACCEPTED',
  DECLINED: 'DECLINED',
  UNKNOWN: 'UNKNOWN'
}
 
/**
 * Events info according
 * @type {{ [event: string]: [gdprValue: string, eventId: string]}}
 */
const EVENT_INFO = {
  [USER_GDPR.ACCEPTED]: ['Privacy Accepted', 'accepted'],
  [USER_GDPR.DECLINED]: ['Privacy Declined', 'declined'],
  [USER_GDPR.UNKNOWN]: ['Privacy Impression', 'declined']
}
 
/**
 * Track a specific TCF event
 * @param {object} params
 * @param {string} params.eventId
 * @param {string} params.gdprValue
 * @return {Promise}
 */
const trackTcf = ({eventId, gdprValue}) =>
  analytics.track('Event Fired', {
    channel: 'GDPR',
    event_id: eventId,
    gdpr_privacy: gdprValue
  })
 
/**
 * Init TCF Tracking User Consents with Segment
 */
export default function initTcfTracking() {
  checkTcfIsAvailable() &&
    window.__tcfapi(
      'addEventListener',
      TCF_API_VERSION,
      ({eventStatus, purpose}, success) => {
        Iif (!success) return Promise.resolve()
 
        if (eventStatus === TCF_EVENT_SHOW_UI) {
          const [eventId, gdprValue] = EVENT_INFO.UNKNOWN
          return trackTcf({eventId, gdprValue})
        }
 
        Eif (eventStatus === TCF_EVENT_USER_ACTION_COMPLETE) {
          const {consents} = purpose
 
          const hasConsents = checkHasUserConsentedTracking(consents)
 
          const userGdprState = hasConsents
            ? USER_GDPR.ACCEPTED
            : USER_GDPR.DECLINED
 
          const [eventId, gdprValue] = EVENT_INFO[userGdprState]
          return trackTcf({eventId, gdprValue})
        }
      }
    )
}