diff --git a/packages/sui-segment-wrapper/README.md b/packages/sui-segment-wrapper/README.md index cd2cbf326..ff8a6017b 100644 --- a/packages/sui-segment-wrapper/README.md +++ b/packages/sui-segment-wrapper/README.md @@ -10,7 +10,6 @@ This package adds an abstraction layer on top of [segment.com](https://segment.c **Google Analytics 🔍** -- [x] Load GA4 if `googleAnalyticsMeasurementId` is provided. - [x] Retrieve `clientId` and `sessionId` automatically from GA4 and put in Segment tracks. **Adobe Marketing Cloud Visitor Id ☁️** @@ -149,9 +148,6 @@ Configure both values correctly before running the application to ensure proper You could put a special config in a the `window.__mpi` to change some behaviour of the wrapper. This config MUST somewhere before using the Segment Wrapper. -- `googleAnalyticsMeasurementId`: _(optional)_ If set, this value will be used for the Google Analytics Measurement API. It will load `gtag` to get the client id. -- `googleAnalyticsConfig`: _(optional)_ If set, this config will be passed when initializing the Google Analytics Measurement API. -- `googleAnalyticsInitEvent`: _(optional)_ If set, an event will be sent in order to initialize all the Google Analytics data. - `defaultContext`: _(optional)_ If set, properties will be merged and sent with every `track` and `page` in the **context object**. It's the ideal place to put the `site` and `vertical` info to make sure that static info will be sent along with all the tracking. - `defaultProperties`: _(optional)_ If set, properties will be merged and sent with every `track` and `page`. - `getCustomAdobeVisitorId`: _(optional)_ If set, the output of this function will be used as `marketingCloudVisitorId` in Adobe Analytics' integration. It must return a promise. @@ -168,7 +164,6 @@ Example: ```js window.__mpi = { segmentWrapper: { - googleAnalyticsMeasurementId: 'GA-123456789', universalId: '7ab9ddf3281d5d5458a29e8b3ae2864', defaultContext: { site: 'comprocasa', diff --git a/packages/sui-segment-wrapper/src/events.js b/packages/sui-segment-wrapper/src/events.js deleted file mode 100644 index 2cf38a2e1..000000000 --- a/packages/sui-segment-wrapper/src/events.js +++ /dev/null @@ -1,3 +0,0 @@ -export const EVENTS = { - GA4_INIT_EVENT_SENT: 'ga4InitEventSent' -} diff --git a/packages/sui-segment-wrapper/src/index.js b/packages/sui-segment-wrapper/src/index.js index 75d89163c..4bad2aa50 100644 --- a/packages/sui-segment-wrapper/src/index.js +++ b/packages/sui-segment-wrapper/src/index.js @@ -6,12 +6,7 @@ import {defaultContextProperties} from './middlewares/source/defaultContextPrope import {pageReferrer} from './middlewares/source/pageReferrer.js' import {userScreenInfo} from './middlewares/source/userScreenInfo.js' import {userTraits} from './middlewares/source/userTraits.js' -import { - DEFAULT_DATA_LAYER_NAME, - getCampaignDetails, - loadGoogleAnalytics, - sendGoogleConsents -} from './repositories/googleRepository.js' +import {initDataLayer} from './repositories/googleRepository.js' import {checkAnonymousId} from './utils/checkAnonymousId.js' import {getConfig, isClient} from './config.js' import analytics from './segmentWrapper.js' @@ -44,33 +39,7 @@ const addMiddlewares = () => { } if (isClient && window.analytics) { - // Initialize Google Analtyics if needed - const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') - const dataLayerName = getConfig('googleAnalyticsDataLayer') || DEFAULT_DATA_LAYER_NAME - const needsConsentManagement = getConfig('googleAnalyticsConsentManagement') - - if (googleAnalyticsMeasurementId) { - const googleAnalyticsConfig = getConfig('googleAnalyticsConfig') - - window[dataLayerName] = window[dataLayerName] || [] - window.gtag = - window.gtag || - function gtag() { - window[dataLayerName].push(arguments) - } - - window.gtag('js', new Date()) - if (needsConsentManagement) sendGoogleConsents() - window.gtag('config', googleAnalyticsMeasurementId, { - cookie_prefix: 'segment', - send_page_view: false, - ...googleAnalyticsConfig, - ...getCampaignDetails() - }) - loadGoogleAnalytics().catch(error => { - console.error(error) - }) - } + initDataLayer() window.analytics.ready(checkAnonymousId) window.analytics.addSourceMiddleware ? addMiddlewares() : window.analytics.ready(addMiddlewares) @@ -79,4 +48,3 @@ if (isClient && window.analytics) { export default analytics export {getAdobeVisitorData, getAdobeMCVisitorID} from './repositories/adobeRepository.js' export {getUniversalId} from './universalId.js' -export {EVENTS} from './events.js' diff --git a/packages/sui-segment-wrapper/src/middlewares/source/campaignContext.js b/packages/sui-segment-wrapper/src/middlewares/source/campaignContext.js index c3c1a5afe..91fd5f288 100644 --- a/packages/sui-segment-wrapper/src/middlewares/source/campaignContext.js +++ b/packages/sui-segment-wrapper/src/middlewares/source/campaignContext.js @@ -1,16 +1,11 @@ -import {getConfig} from '../../config.js' import {getCampaignDetails} from '../../repositories/googleRepository.js' export const campaignContext = ({payload, next}) => { - const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') + const campaignDetails = getCampaignDetails({needsTransformation: false}) - if (googleAnalyticsMeasurementId) { - const campaignDetails = getCampaignDetails({needsTransformation: false}) - - payload.obj.context = { - ...payload.obj.context, - ...campaignDetails - } + payload.obj.context = { + ...payload.obj.context, + ...campaignDetails } next(payload) diff --git a/packages/sui-segment-wrapper/src/repositories/googleRepository.js b/packages/sui-segment-wrapper/src/repositories/googleRepository.js index 343fb2650..4d534dbc4 100644 --- a/packages/sui-segment-wrapper/src/repositories/googleRepository.js +++ b/packages/sui-segment-wrapper/src/repositories/googleRepository.js @@ -1,16 +1,6 @@ -import {dispatchEvent} from '@s-ui/js/lib/events' - import {getConfig} from '../config.js' -import {EVENTS} from '../events.js' import {utils} from '../middlewares/source/pageReferrer.js' -const FIELDS = { - clientId: 'client_id', - sessionId: 'session_id' -} - -export const DEFAULT_DATA_LAYER_NAME = 'dataLayer' - export const CONSENT_STATES = { granted: 'granted', denied: 'denied' @@ -45,70 +35,26 @@ const STC_MEDIUM_TRANSFORMATIONS = { cs: 'cross-sites' } const STC_INVALID_CONTENT = 'na' -const DEFAULT_GA_INIT_EVENT = 'sui' - const EMPTY_STC = {medium: null, source: null, campaign: null} -const loadScript = async src => - new Promise(function (resolve, reject) { - const script = document.createElement('script') - - script.src = src - script.onload = resolve - script.onerror = reject - document.head.appendChild(script) - }) - -export const loadGoogleAnalytics = async () => { - const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') - const dataLayerName = getConfig('googleAnalyticsDataLayer') || DEFAULT_DATA_LAYER_NAME - - // Check we have the needed config to load the script - if (!googleAnalyticsMeasurementId) return Promise.resolve(false) - // Create the `gtag` script - const gtagScript = `https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsMeasurementId}&l=${dataLayerName}` - // Load it and retrieve the `clientId` from Google - return loadScript(gtagScript) -} - -// Trigger GA init event just once per session. -const triggerGoogleAnalyticsInitEvent = sessionId => { - const eventName = getConfig('googleAnalyticsInitEvent') ?? DEFAULT_GA_INIT_EVENT - const eventPrefix = `ga_event_${eventName}_` - const eventKey = `${eventPrefix}${sessionId}` - - if (typeof window.gtag === 'undefined') return - - // Check if the event has already been sent in this session. - if (!localStorage.getItem(eventKey)) { - // If not, send it. - window.gtag('event', eventName) - - // eslint-disable-next-line no-console - console.log(`Sending GA4 event "${eventName}" for the session "${sessionId}"`) - - // And then save a new GA session hit in local storage. - localStorage.setItem(eventKey, 'true') - dispatchEvent({eventName: EVENTS.GA4_INIT_EVENT_SENT, detail: {eventName, sessionId}}) - } +export const waitForGAData = () => { + return new Promise(resolve => { + if (window.__GA4_DATA) { + console.log('[segment-wrapper] GA4 data already available!') + resolve(window.__GA4_DATA) - // Clean old GA sessions hits from the storage. - Object.keys(localStorage).forEach(key => { - if (key.startsWith(eventPrefix) && key !== eventKey) { - localStorage.removeItem(key) + return } - }) -} -const getGoogleField = async field => { - const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') + console.log('[segment-wrapper] Waiting for GTM...') - // If `googleAnalyticsMeasurementId` is not present, don't load anything. - if (!googleAnalyticsMeasurementId) return Promise.resolve() - - return new Promise(resolve => { - // If it is, get it from `gtag`. - window.gtag?.('get', googleAnalyticsMeasurementId, field, resolve) + /** + * @param {{clientId: any, sessionId: any}} data + */ + window.resolveGAData = data => { + console.log('[segment-wrapper] GTM has delivered the data.') + resolve(data) + } }) } @@ -129,14 +75,12 @@ export const getCampaignDetails = ({needsTransformation = true} = {}) => { const needsContent = typeof content !== 'undefined' && content !== STC_INVALID_CONTENT return { - campaign: { - medium: (needsTransformation && STC_MEDIUM_TRANSFORMATIONS[medium]) || medium, - ...(typeof name !== 'undefined' && {id}), - name: name ?? id, - source, - ...(needsContent && {content}), - ...(typeof term !== 'undefined' && {term}) - } + campaign_medium: (needsTransformation && STC_MEDIUM_TRANSFORMATIONS[medium]) || medium, + ...(typeof name !== 'undefined' && {campaign_id: id}), + campaign_name: name ?? id, + campaign_source: source, + ...(needsContent && {campaign_content: content}), + ...(typeof term !== 'undefined' && {campaign_term: term}) } } @@ -183,15 +127,6 @@ function readFromUtm(searchParams) { } } -export const getGoogleClientId = async () => getGoogleField(FIELDS.clientId) -export const getGoogleSessionId = async () => { - const sessionId = await getGoogleField(FIELDS.sessionId) - - triggerGoogleAnalyticsInitEvent(sessionId) - - return sessionId -} - // Unified consent state getter. // Returns GRANTED, DENIED or undefined (default / unknown / unavailable). export function getGoogleConsentValue(consentType = 'analytics_storage') { @@ -212,35 +147,13 @@ export function getGoogleConsentValue(consentType = 'analytics_storage') { export const getConsentState = () => getGoogleConsentValue() ?? CONSENT_STATES.denied export const setGoogleUserId = userId => { - const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') - - if (!googleAnalyticsMeasurementId || !userId) return + if (!userId) return window.gtag?.('set', 'user_id', userId) } -/** - * Send consents to Google Consent Mode. - * - * @param {'default' | 'update'} mode Mode for the consent update - * @param {object} consents Consents object to be sent to Google Consent Mode. - * Defaults used when not provided: - * { - * analytics_storage: 'denied', - * ad_user_data: 'denied', - * ad_personalization: 'denied', - * ad_storage: 'denied' - * } - */ -export const sendGoogleConsents = (mode = 'default', consents) => { - window.gtag?.( - 'consent', - mode, - consents || { - analytics_storage: CONSENT_STATES.denied, - ad_user_data: CONSENT_STATES.denied, - ad_personalization: CONSENT_STATES.denied, - ad_storage: CONSENT_STATES.denied - } - ) +export function initDataLayer() { + window.dataLayer = window.dataLayer || [] + + window.dataLayer.push({...getCampaignDetails()}) } diff --git a/packages/sui-segment-wrapper/src/segmentWrapper.js b/packages/sui-segment-wrapper/src/segmentWrapper.js index 500c04e93..ca21cb73d 100644 --- a/packages/sui-segment-wrapper/src/segmentWrapper.js +++ b/packages/sui-segment-wrapper/src/segmentWrapper.js @@ -5,14 +5,12 @@ import { CONSENT_STATES, getConsentState, getGoogleConsentValue, - getGoogleClientId, - getGoogleSessionId, setGoogleUserId, - sendGoogleConsents + waitForGAData } from './repositories/googleRepository.js' import {getXandrId} from './repositories/xandrRepository.js' import {getConfig} from './config.js' -import {USER_GDPR, CMP_TRACK_EVENT, checkAnalyticsGdprIsAccepted, getGdprPrivacyValue} from './tcf.js' +import {USER_GDPR, checkAnalyticsGdprIsAccepted, getGdprPrivacyValue} from './tcf.js' /* Default properties to be sent on all trackings */ const DEFAULT_PROPERTIES = {platform: 'web'} @@ -57,15 +55,13 @@ export const getDefaultProperties = () => ({ const getTrackIntegrations = async ({gdprPrivacyValue, event}) => { const isGdprAccepted = checkAnalyticsGdprIsAccepted(gdprPrivacyValue) let marketingCloudVisitorId - let sessionId - let clientId + let sessionId, clientId try { if (isGdprAccepted) { marketingCloudVisitorId = await getAdobeMCVisitorID() } - sessionId = await getGoogleSessionId() - clientId = await getGoogleClientId() + ;({sessionId, clientId} = await waitForGAData()) } catch (error) { console.error(error) } @@ -223,12 +219,6 @@ const track = (event, properties, context = {}, callback) => } } - const needsConsentManagement = getConfig('googleAnalyticsConsentManagement') - - if (needsConsentManagement && event === CMP_TRACK_EVENT) { - sendGoogleConsents('update', newContext.google_consents) - } - window.analytics.track( event, newProperties, diff --git a/packages/sui-segment-wrapper/test/assertions.js b/packages/sui-segment-wrapper/test/assertions.js index 4e8749637..2263fd677 100644 --- a/packages/sui-segment-wrapper/test/assertions.js +++ b/packages/sui-segment-wrapper/test/assertions.js @@ -1,7 +1,6 @@ import {expect} from 'chai' import sinon from 'sinon' -import {setConfig} from '../src/config.js' import suiAnalytics from '../src/index.js' import {getCampaignDetails} from '../src/repositories/googleRepository.js' import {stubActualQueryString} from './stubs.js' @@ -12,7 +11,6 @@ export const assertCampaignDetails = async ({queryString, expectation}) => { try { const spy = sinon.stub() - setConfig('googleAnalyticsMeasurementId', 123) await simulateUserAcceptConsents() await suiAnalytics.track( 'fakeEvent', diff --git a/packages/sui-segment-wrapper/test/repositories/googleRepositorySpec.js b/packages/sui-segment-wrapper/test/repositories/googleRepositorySpec.js index 552ba77f6..8df427bd2 100644 --- a/packages/sui-segment-wrapper/test/repositories/googleRepositorySpec.js +++ b/packages/sui-segment-wrapper/test/repositories/googleRepositorySpec.js @@ -28,11 +28,11 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails() expect(details).to.not.be.null - expect(details.campaign).to.have.property('medium', 'email') - expect(details.campaign).to.have.property('name', 'campaign_name') - expect(details.campaign).to.have.property('source', 'source_type') - expect(details.campaign).to.have.property('content', 'content_type') - expect(details.campaign).to.have.property('term', 'term_type') + expect(details).to.have.property('campaign_medium', 'email') + expect(details).to.have.property('campaign_name', 'campaign_name') + expect(details).to.have.property('campaign_source', 'source_type') + expect(details).to.have.property('campaign_content', 'content_type') + expect(details).to.have.property('campaign_term', 'term_type') }) it('should get medium without transformation from STC', async () => { @@ -40,7 +40,7 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails({needsTransformation: false}) - expect(details.campaign).to.have.property('medium', 'custom_medium') + expect(details).to.have.property('campaign_medium', 'custom_medium') }) it('should get campaign id from STC', async () => { @@ -48,7 +48,7 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails() - expect(details.campaign).to.have.property('name', 'campaing_name') + expect(details).to.have.property('campaign_name', 'campaing_name') }) it('should not return content with invalid content type', async () => { @@ -56,7 +56,7 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails() - expect(details.campaign).to.not.have.property('content') + expect(details).to.not.have.property('content') }) it('should return null when no STC param present', async () => { @@ -91,11 +91,11 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails() expect(details).to.not.be.null - expect(details.campaign).to.have.property('medium', 'email') - expect(details.campaign).to.have.property('name', 'campaign_name') - expect(details.campaign).to.have.property('source', 'source_type') - expect(details.campaign).to.have.property('content', 'content_type') - expect(details.campaign).to.have.property('term', 'term_type') + expect(details).to.have.property('campaign_medium', 'email') + expect(details).to.have.property('campaign_name', 'campaign_name') + expect(details).to.have.property('campaign_source', 'source_type') + expect(details).to.have.property('campaign_content', 'content_type') + expect(details).to.have.property('campaign_term', 'term_type') }) it('should get medium without transformation from UTM', async () => { @@ -106,7 +106,7 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails({needsTransformation: false}) - expect(details.campaign).to.have.property('medium', 'custom_medium') + expect(details).to.have.property('campaign_medium', 'custom_medium') }) it('should get campaign id from UTM', async () => { @@ -117,7 +117,7 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails() - expect(details.campaign).to.have.property('name', 'campaign_name') + expect(details).to.have.property('campaign_name', 'campaign_name') }) it('should return null when no UTM mandatory params are present', async () => { @@ -135,9 +135,9 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails({needsTransformation: false}) - expect(details.campaign).to.have.property('medium', 'stc_medium') - expect(details.campaign).to.have.property('source', 'stc_source') - expect(details.campaign).to.have.property('name', 'stc_campaign') + expect(details).to.have.property('campaign_medium', 'stc_medium') + expect(details).to.have.property('campaign_source', 'stc_source') + expect(details).to.have.property('campaign_name', 'stc_campaign') }) it('should fallback to stc if utm_source is empty', async () => { @@ -146,9 +146,9 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails({needsTransformation: false}) - expect(details.campaign).to.have.property('medium', 'stc_medium') - expect(details.campaign).to.have.property('source', 'stc_source') - expect(details.campaign).to.have.property('name', 'stc_campaign') + expect(details).to.have.property('campaign_medium', 'stc_medium') + expect(details).to.have.property('campaign_source', 'stc_source') + expect(details).to.have.property('campaign_name', 'stc_campaign') }) it('should fallback to stc if utm_campaign is empty', async () => { @@ -157,9 +157,9 @@ describe('GoogleRepository', () => { const details = await getCampaignDetails({needsTransformation: false}) - expect(details.campaign).to.have.property('medium', 'stc_medium') - expect(details.campaign).to.have.property('source', 'stc_source') - expect(details.campaign).to.have.property('name', 'stc_campaign') + expect(details).to.have.property('campaign_medium', 'stc_medium') + expect(details).to.have.property('campaign_source', 'stc_source') + expect(details).to.have.property('campaign_name', 'stc_campaign') }) describe('getGoogleConsentValue', () => { diff --git a/packages/sui-segment-wrapper/test/segmentWrapperSpec.js b/packages/sui-segment-wrapper/test/segmentWrapperSpec.js index 30e019a5a..14da9946d 100644 --- a/packages/sui-segment-wrapper/test/segmentWrapperSpec.js +++ b/packages/sui-segment-wrapper/test/segmentWrapperSpec.js @@ -1,7 +1,7 @@ import {expect} from 'chai' import sinon from 'sinon' -import {getConfig, setConfig} from '../src/config.js' +import {setConfig} from '../src/config.js' import suiAnalytics from '../src/index.js' import {campaignContext} from '../src/middlewares/source/campaignContext.js' import {defaultContextProperties} from '../src/middlewares/source/defaultContextProperties.js' @@ -13,7 +13,7 @@ import {INTEGRATIONS_WHEN_NO_CONSENTS} from '../src/segmentWrapper.js' import initTcfTracking, {getGdprPrivacyValue, USER_GDPR} from '../src/tcf.js' import {assertCampaignDetails} from './assertions.js' import { - cleanWindowStubs, + cleanStubs, resetReferrerState, stubActualLocation, stubDocumentCookie, @@ -54,7 +54,7 @@ describe('Segment Wrapper', function () { window.analytics.addSourceMiddleware(pageReferrer) }) - afterEach(() => cleanWindowStubs()) + afterEach(() => cleanStubs()) describe('should use correct page referrer for tracks', () => { let referrerStub, locationStub @@ -220,172 +220,148 @@ describe('Segment Wrapper', function () { }) }) - describe('and gtag has been configured properly', () => { - it('should send Google Analytics integration with true if user declined consents', async () => { - // Add the needed config to enable Google Analytics - setConfig('googleAnalyticsMeasurementId', 123) - - await simulateUserDeclinedConsents() + it('should send Google Analytics integration with true if user declined consents', async () => { + await simulateUserDeclinedConsents() - await suiAnalytics.track( - 'fakeEvent', - {}, - { - integrations: {fakeIntegrationKey: 'fakeIntegrationValue'} - } - ) + await suiAnalytics.track( + 'fakeEvent', + {}, + { + integrations: {fakeIntegrationKey: 'fakeIntegrationValue'} + } + ) - const {context} = getDataFromLastTrack() + const {context} = getDataFromLastTrack() - expect(context.integrations).to.deep.includes({ - fakeIntegrationKey: 'fakeIntegrationValue', - 'Google Analytics 4': {clientId: 'fakeClientId', sessionId: 'fakeSessionId'} - }) + expect(context.integrations).to.deep.includes({ + fakeIntegrationKey: 'fakeIntegrationValue', + 'Google Analytics 4': {clientId: 'fakeClientId', sessionId: 'fakeSessionId'} }) + }) - it('should send ClientId on Google Analytics integration if user accepted consents', async () => { - // add needed config to enable Google Analytics - setConfig('googleAnalyticsMeasurementId', 123) - - await simulateUserAcceptConsents() + it('should send ClientId on Google Analytics integration if user accepted consents', async () => { + await simulateUserAcceptConsents() - await suiAnalytics.track( - 'fakeEvent', - {}, - { - integrations: {fakeIntegrationKey: 'fakeIntegrationValue'} - } - ) + await suiAnalytics.track( + 'fakeEvent', + {}, + { + integrations: {fakeIntegrationKey: 'fakeIntegrationValue'} + } + ) - const {context} = getDataFromLastTrack() + const {context} = getDataFromLastTrack() - expect(context.integrations).to.deep.includes({ - fakeIntegrationKey: 'fakeIntegrationValue', - 'Google Analytics 4': { - clientId: 'fakeClientId', - sessionId: 'fakeSessionId' - } - }) + expect(context.integrations).to.deep.includes({ + fakeIntegrationKey: 'fakeIntegrationValue', + 'Google Analytics 4': { + clientId: 'fakeClientId', + sessionId: 'fakeSessionId' + } }) + }) - it('should send mapped campaign details when the url query string is `?stc=em-mail-winter%20promo-honda`', async () => { - await assertCampaignDetails({ - queryString: '?stc=em-mail-winter%20promo-honda', - expectation: { - campaign: { - medium: 'email', - name: 'winter promo', - source: 'mail', - content: 'honda' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=em-mail-winter%20promo-honda`', async () => { + await assertCampaignDetails({ + queryString: '?stc=em-mail-winter%20promo-honda', + expectation: { + campaign_medium: 'email', + campaign_name: 'winter promo', + campaign_source: 'mail', + campaign_content: 'honda' + } }) + }) - it('should not send mapped campaing details when stc param is invalid', async () => { - await assertCampaignDetails({ - queryString: - '?stc=IJ_PUSH%7Celement~50220488692%7Cversion~pushmssearch_jobtitle_normalized&id_push=50220488692 ', - expectation: null - }) + it('should not send mapped campaing details when stc param is invalid', async () => { + await assertCampaignDetails({ + queryString: + '?stc=IJ_PUSH%7Celement~50220488692%7Cversion~pushmssearch_jobtitle_normalized&id_push=50220488692 ', + expectation: null }) + }) - it('should send mapped campaign details when the url query string is `?stc=sm-google-1234%3Aspring%20sale-aprilia-logolink`', async () => { - await assertCampaignDetails({ - queryString: '?stc=sm-google-1234%3Aspring%20sale-aprilia-logolink', - expectation: { - campaign: { - medium: 'social-media', - id: '1234', - name: 'spring sale', - source: 'google', - content: 'aprilia', - term: 'logolink' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=sm-google-1234%3Aspring%20sale-aprilia-logolink`', async () => { + await assertCampaignDetails({ + queryString: '?stc=sm-google-1234%3Aspring%20sale-aprilia-logolink', + expectation: { + campaign_medium: 'social-media', + campaign_id: '1234', + campaign_name: 'spring sale', + campaign_source: 'google', + campaign_content: 'aprilia', + campaign_term: 'logolink' + } }) + }) - it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn%20sale`', async () => { - await assertCampaignDetails({ - queryString: '?stc=sem-google-autumn%20sale', - expectation: { - campaign: { - medium: 'paid-search', - name: 'autumn sale', - source: 'google' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn%20sale`', async () => { + await assertCampaignDetails({ + queryString: '?stc=sem-google-autumn%20sale', + expectation: { + campaign_medium: 'paid-search', + campaign_name: 'autumn sale', + campaign_source: 'google' + } }) + }) - it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn sale`', async () => { - await assertCampaignDetails({ - queryString: '?stc=sem-google-autumn sale', - expectation: { - campaign: { - medium: 'paid-search', - name: 'autumn sale', - source: 'google' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn sale`', async () => { + await assertCampaignDetails({ + queryString: '?stc=sem-google-autumn sale', + expectation: { + campaign_medium: 'paid-search', + campaign_name: 'autumn sale', + campaign_source: 'google' + } }) + }) - it('should send mapped campaign details when the url query string is `?stc=sem-google-1234%3Aautumn%20sale`', async () => { - await assertCampaignDetails({ - queryString: '?stc=sem-google-1234%3Aautumn%20sale', - expectation: { - campaign: { - medium: 'paid-search', - id: '1234', - name: 'autumn sale', - source: 'google' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=sem-google-1234%3Aautumn%20sale`', async () => { + await assertCampaignDetails({ + queryString: '?stc=sem-google-1234%3Aautumn%20sale', + expectation: { + campaign_medium: 'paid-search', + campaign_id: '1234', + campaign_name: 'autumn sale', + campaign_source: 'google' + } }) + }) - it('should send mapped campaign details when the url query string is `?stc=sem-google-1234:autumn sale`', async () => { - await assertCampaignDetails({ - queryString: '?stc=sem-google-1234:autumn sale', - expectation: { - campaign: { - medium: 'paid-search', - id: '1234', - name: 'autumn sale', - source: 'google' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=sem-google-1234:autumn sale`', async () => { + await assertCampaignDetails({ + queryString: '?stc=sem-google-1234:autumn sale', + expectation: { + campaign_medium: 'paid-search', + campaign_id: '1234', + campaign_name: 'autumn sale', + campaign_source: 'google' + } }) + }) - it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn sale-aprilia`', async () => { - await assertCampaignDetails({ - queryString: '?stc=sem-google-autumn sale-aprilia', - expectation: { - campaign: { - medium: 'paid-search', - name: 'autumn sale', - source: 'google', - content: 'aprilia' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn sale-aprilia`', async () => { + await assertCampaignDetails({ + queryString: '?stc=sem-google-autumn sale-aprilia', + expectation: { + campaign_medium: 'paid-search', + campaign_name: 'autumn sale', + campaign_source: 'google', + campaign_content: 'aprilia' + } }) + }) - it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn sale-na-logolink`', async () => { - await assertCampaignDetails({ - queryString: '?stc=sem-google-autumn sale-na-logolink', - expectation: { - campaign: { - medium: 'paid-search', - name: 'autumn sale', - source: 'google', - term: 'logolink' - } - } - }) + it('should send mapped campaign details when the url query string is `?stc=sem-google-autumn sale-na-logolink`', async () => { + await assertCampaignDetails({ + queryString: '?stc=sem-google-autumn sale-na-logolink', + expectation: { + campaign_medium: 'paid-search', + campaign_name: 'autumn sale', + campaign_source: 'google', + campaign_term: 'logolink' + } }) }) @@ -515,24 +491,20 @@ describe('Segment Wrapper', function () { }) describe('and GA Measurment ID is set', () => { - beforeEach(() => { - setConfig('googleAnalyticsMeasurementId', 123) - }) - it('should set the user id into `gtag` properly', async function () { + const googleAnalyticsMeasurementId = 123 + await simulateUserAcceptConsents() await suiAnalytics.identify('myTestUserId') - const googleAnalyticsMeasurementId = getConfig('googleAnalyticsMeasurementId') - - const getGaUserId = async () => + const getGaUserId = async googleAnalyticsMeasurementId => new Promise(resolve => { window.gtag('get', googleAnalyticsMeasurementId, 'user_id', userId => { resolve(userId) }) }) - const gaUserId = await getGaUserId() + const gaUserId = await getGaUserId(googleAnalyticsMeasurementId) expect(gaUserId).to.equal('myTestUserId') }) @@ -811,6 +783,7 @@ describe('Segment Wrapper', function () { it('sends an event with the actual context and traits when the consents are declined', async () => { const spy = sinon.stub() + window.google_tag_data = { ics: { getConsentState: () => 2 @@ -835,7 +808,10 @@ describe('Segment Wrapper', function () { const integrations = { All: false, 'Adobe Analytics': true, - 'Google Analytics 4': true, + 'Google Analytics 4': { + clientId: 'fakeClientId', + sessionId: 'fakeSessionId' + }, Personas: false, Webhooks: true, Webhook: true @@ -861,6 +837,10 @@ describe('Segment Wrapper', function () { clientVersion: 'segment-wrapper@0.0.0' } const {traits} = spy.getCall(0).firstArg.obj.context + + console.log('[segment-wrapper] context.context.integrations', context.context.integrations) + console.log('[segment-wrapper] expectation.context.integrations', expectation.context.integrations) + expect(context).to.deep.equal(expectation) expect(traits).to.deep.equal({ anonymousId: 'fakeAnonymousId', diff --git a/packages/sui-segment-wrapper/test/stubs.js b/packages/sui-segment-wrapper/test/stubs.js index c387f2256..60f9be7a2 100644 --- a/packages/sui-segment-wrapper/test/stubs.js +++ b/packages/sui-segment-wrapper/test/stubs.js @@ -4,13 +4,22 @@ import {referrerState, updatePageReferrer, utils as referrerUtils} from '../src/ const IDENTIFY_TIMEOUT = 300 // from https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/#identify -export const cleanWindowStubs = () => { +const cleanWindowStubs = () => { delete window.analytics delete window.Visitor delete window.__borosTcf delete window.__mpi delete window.__tcfapi +} + +const cleanGAStubs = () => { delete window.gtag + delete window.__GA4_DATA +} + +export const cleanStubs = () => { + cleanWindowStubs() + cleanGAStubs() } export const stubTcfApi = ({success = true, eventStatus = 'cmpuishown', consents = {}} = {}) => { @@ -43,10 +52,7 @@ export const stubFetch = ({responses = [{urlRe: /^http/, fetchResponse: {}}]} = } export const stubGoogleAnalytics = () => { - const fakeFields = { - client_id: 'fakeClientId', - session_id: 'fakeSessionId' - } + const fakeFields = {clientId: 'fakeClientId', sessionId: 'fakeSessionId'} window.gtag = (key, id, field, done) => { if (key === 'get') { @@ -57,6 +63,9 @@ export const stubGoogleAnalytics = () => { fakeFields[id] = field } } + + // Set initial fake data + window.__GA4_DATA = fakeFields } export const stubWindowObjects = ({borosMock = true, borosSuccess = true, isDmpAccepted = true} = {}) => { diff --git a/packages/sui-segment-wrapper/test/universalIdSpec.js b/packages/sui-segment-wrapper/test/universalIdSpec.js index b4fbee5ea..d0cc4953f 100644 --- a/packages/sui-segment-wrapper/test/universalIdSpec.js +++ b/packages/sui-segment-wrapper/test/universalIdSpec.js @@ -2,7 +2,7 @@ import {expect} from 'chai' import {getConfig, setConfig} from '../src/config.js' import {getUniversalId, getUserDataAndNotify} from '../src/universalId.js' -import {cleanWindowStubs} from './stubs.js' +import {cleanStubs} from './stubs.js' const UNIVERSAL_ID_EXAMPLE = '043d36c36dad0741bdebce605d0ee4e6c1dea6e2eb6399864dec7a59432a20c4' const USER_EMAIL_EXAMPLE = 'test@sui.com' @@ -15,7 +15,7 @@ describe('Universal Id', () => { }) afterEach(() => { - cleanWindowStubs() + cleanStubs() }) it('could be retrieved as expected from getUniversalId', () => { @@ -30,7 +30,7 @@ describe('Universal Id', () => { }) afterEach(() => { - cleanWindowStubs() + cleanStubs() }) it('could be retrieved as expected from method getUniversalId and set to window', () => { @@ -60,7 +60,7 @@ describe('Universal Id', () => { }) afterEach(() => { - cleanWindowStubs() + cleanStubs() }) it('should set universalIdInitialized config accordingly', () => {