All files tcf.js

75% Statements 39/52
50% Branches 10/20
62.5% Functions 10/16
80.85% Lines 38/47

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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191          5x         5x                 5x           5x           5x         5x                   5x                   5x       18x   3x 3x               5x   6x 6x     6x 6x       6x               5x 5x                 5x 4x                             5x                                   5x   18x 18x                               5x             6x         4x   4x 1x 1x     3x 3x 3x     3x       3x     3x 3x          
import analytics from './segmentWrapper'
/**
 * TCF Api Version to use
 * @type {number}
 */
const TCF_API_VERSION = 2
 
/**
 * Default properties to send with every TCF tracking event
 */
const TCF_TRACK_PROPERTIES = {
  channel: 'GDPR',
  language: 'es'
}
 
/**
 * 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'
 
/**
 * State of user according to GDPR regarding tracking
 */
const USER_GDPR = {
  ACCEPTED: 'accepted',
  DECLINED: 'declined',
  UNKNOWN: 'unknown'
}
 
/**
 * Events info according
 * @type {{ [event: string]: [eventId: string, gdprValue: string]}}
 */
const EVENT_INFO = {
  [USER_GDPR.ACCEPTED]: ['Privacy Accepted', USER_GDPR.ACCEPTED],
  [USER_GDPR.DECLINED]: ['Privacy Declined', USER_GDPR.DECLINED],
  [USER_GDPR.UNKNOWN]: ['Privacy Impression', USER_GDPR.UNKNOWN]
}
 
/**
 * Define the user GDPR consents state. This value will be updated with new values
 * when the consents of the users changes.
 */
const gdprState = {
  _onChange: value => {},
  value: USER_GDPR.UNKNOWN,
  onChange: callback => (gdprState._onChange = callback),
  get: () => gdprState.value,
  set: value => {
    gdprState.value = value
    gdprState._onChange(value)
  }
}
 
/**
 * Check if we're on client and tcfApi is available on window object
 * @returns {Boolean}
 */
const checkTcfIsAvailable = () => {
  // first check if we're on server, as this doesn't work on server-side
  const isClient = typeof window !== 'undefined'
  Iif (!isClient) return false
 
  // if we're on client, check if we have the tcfapi available
  const isTcfApiAvailable = !!window.__tcfapi
  !isTcfApiAvailable &&
    console.warn(
      "[tcfTracking] window.__tcfapi is not available on client and TCF won't be tracked."
    )
  return isTcfApiAvailable
}
 
/**
 * 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}`])
 
/**
 * Track a specific TCF event
 * @param {object} params
 * @param {string} params.eventId Event ID to be sent with the TCF Tracking
 * @param {string} params.gdprPrivacy Send a string telling if the gdpr has been accepted or reject
 * @return {Promise}
 */
const trackTcf = ({eventId, gdprPrivacy}) =>
  analytics.track(
    'Event Fired',
    {
      ...TCF_TRACK_PROPERTIES,
      event_id: eventId
    },
    {
      gdpr_privacy: gdprPrivacy
    }
  )
 
/**
 * Check if DMP is consented by the user using boros API
 * @return {Promise<boolean>}
 */
export const checkIsDMPReady = () => {
  return new Promise(resolve => {
    if (typeof window === 'undefined') return resolve()
    if (window.__borosTcf === undefined) return resolve()
 
    window.__borosTcf.push(api => {
      api('isDmpAccepted', ({success, value}) => {
        const isDMPReady = success && value
        return resolve(isDMPReady)
      })
    })
  })
}
 
/**
 * Get if we have user consents
 * @return {Promise<string>}
 */
export const getGdprPrivacyValue = () => {
  // try to get the actual gdprPrivacyValue and just return it
  const gdprPrivacyValue = gdprState.get()
  return Promise.resolve(gdprPrivacyValue)
 
  // TODO: Disabled for now. It will be added on the next iteration
  // if (gdprPrivacyValue !== undefined) return Promise.resolve(gdprPrivacyValue)
 
  // // if we don't have a gdprPrivacyValue, then subscribe to it until we have a value
  // return new Promise(resolve => {
  //   gdprPrivacyValueState.onChange = gdprPrivacyValue =>
  //     resolve(gdprPrivacyValue)
  // })
}
 
/**
 * Check if gdprPrivacyValue is accepted
 * @return {boolean}
 */
export const checkGdprIsAccepted = gdprPrivacyValue =>
  gdprPrivacyValue === USER_GDPR.ACCEPTED
 
/**
 * 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, gdprPrivacy] = EVENT_INFO[USER_GDPR.UNKNOWN]
          return trackTcf({eventId, gdprPrivacy})
        }
 
        Eif (eventStatus === TCF_EVENT_USER_ACTION_COMPLETE) {
          const {consents} = purpose
          const hasConsents = checkHasUserConsentedTracking(consents)
 
          // get the state key according to the gdprPrivacyValue
          const gdprStateKey = hasConsents
            ? USER_GDPR.ACCEPTED
            : USER_GDPR.DECLINED
 
          gdprState.set(gdprStateKey)
 
          // extract the eventId and gdprPrivacy string to send with the track
          const [eventId, gdprPrivacy] = EVENT_INFO[gdprStateKey]
          return trackTcf({eventId, gdprPrivacy})
        }
      }
    )
}