diff --git a/.gitignore b/.gitignore index adfe36ad..1c05a798 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ test/integrations/requirejs/test-requirejs-bundle.js browserstack.err local.log test/cross-browser-testing/CBT-tests-es5.js -test/cross-browser-testing/CBT-tests.js \ No newline at end of file +test/cross-browser-testing/CBT-tests.js +coverage/ \ No newline at end of file diff --git a/src/configAPIClient.ts b/src/configAPIClient.ts index 91b7db53..fda5b2f7 100644 --- a/src/configAPIClient.ts +++ b/src/configAPIClient.ts @@ -13,6 +13,7 @@ import { FetchUploader, XHRUploader, } from './uploaders'; +import { IPixelConfiguration } from './cookieSyncManager.interfaces'; export interface IKitConfigs extends IKitFilterSettings { name: string; @@ -65,17 +66,6 @@ export interface IConsentRuleValue { hasConsented: boolean; } -export interface IPixelConfig { - name: string; - moduleId: number; - esId: number; - isDebug: boolean; - isProduction: boolean; - settings: Dictionary; - frequencyCap: number; - pixelUrl: string; - redirectUrl: string; -} export interface IConfigResponse { appName: string; @@ -85,7 +75,7 @@ export interface IConfigResponse { secureServiceUrl: string; minWebviewBridgeVersion: number; workspaceToken: string; - pixelConfigs: IPixelConfig[]; + pixelConfigs: IPixelConfiguration[]; flags: SDKEventCustomFlags; } diff --git a/src/consent.ts b/src/consent.ts index 6ff45c21..3dcac6af 100644 --- a/src/consent.ts +++ b/src/consent.ts @@ -38,6 +38,10 @@ export interface SDKConsentApi { createConsentState: (consentState?: ConsentState) => ConsentState; ConsentSerialization: IConsentSerialization; createPrivacyConsent: ICreatePrivacyConsentFunction; + isEnabledForUserConsent: ( + consentRules: IConsentRules, + user: IMParticleUser + ) => boolean; } export interface IConsentSerialization { diff --git a/src/cookieSyncManager.interfaces.ts b/src/cookieSyncManager.interfaces.ts index 1f471a5b..54b363b5 100644 --- a/src/cookieSyncManager.interfaces.ts +++ b/src/cookieSyncManager.interfaces.ts @@ -1,23 +1,39 @@ -import { MPID } from "@mparticle/web-sdk"; -import { Dictionary } from "./utils"; -import { IConsentRules } from "./consent"; +import { MPID } from '@mparticle/web-sdk'; +import { Dictionary } from './utils'; +import { IConsentRules } from './consent'; +export type CookieSyncDates = Dictionary; + +export interface IPixelConfiguration { + name?: string; + moduleId: number; + esId?: number; + isDebug?: boolean; + isProduction?: boolean; + settings: Dictionary; + frequencyCap: number; + pixelUrl: string; + redirectUrl: string; + filteringConsentRuleValues?: IConsentRules; +} export interface ICookieSyncManager { - attemptCookieSync: (previousMPID: MPID, mpid: MPID, mpidIsNotInCookies: boolean) => void; + attemptCookieSync: ( + previousMPID: MPID, + mpid: MPID, + mpidIsNotInCookies?: boolean + ) => void; performCookieSync: ( url: string, - moduleId: number, + moduleId: string, mpid: MPID, - cookieSyncDates: Dictionary, + cookieSyncDates: CookieSyncDates, filteringConsentRuleValues: IConsentRules, mpidIsNotInCookies: boolean, requiresConsent: boolean ) => void; - replaceAmpWithAmpersand: (string: string) => string; - replaceMPID: (string: string, mpid: MPID) => string; - - /** - * @deprecated replaceAmp has been deprecated, use replaceAmpersandWithAmp instead - */ - replaceAmp: (string: string) => string; -} \ No newline at end of file + combineUrlWithRedirect: ( + mpid: MPID, + pixelUrl: string, + redirectUrl: string + ) => string; +} diff --git a/src/cookieSyncManager.js b/src/cookieSyncManager.js deleted file mode 100644 index b5026149..00000000 --- a/src/cookieSyncManager.js +++ /dev/null @@ -1,204 +0,0 @@ -import Constants from './constants'; - -var Messages = Constants.Messages; - -export default function cookieSyncManager(mpInstance) { - var self = this; - - // Public - this.attemptCookieSync = function(previousMPID, mpid, mpidIsNotInCookies) { - // TODO: These should move inside the for loop - var pixelConfig, - lastSyncDateForModule, - url, - redirect, - urlWithRedirect, - requiresConsent; - - // TODO: Make this exit quicker instead of nested - if (mpid && !mpInstance._Store.webviewBridgeEnabled) { - mpInstance._Store.pixelConfigurations.forEach(function( - pixelSettings - ) { - // set requiresConsent to false to start each additional pixel configuration - // set to true only if filteringConsenRuleValues.values.length exists - requiresConsent = false; - - // TODO: Replace with isEmpty - if ( - pixelSettings.filteringConsentRuleValues && - pixelSettings.filteringConsentRuleValues.values && - pixelSettings.filteringConsentRuleValues.values.length - ) { - requiresConsent = true; - } - - pixelConfig = { - // Kit Module ID - moduleId: pixelSettings.moduleId, - - // Tells you how often we should do a cookie sync (in days) - frequencyCap: pixelSettings.frequencyCap, - - // Url for cookie sync pixel - pixelUrl: self.replaceAmp(pixelSettings.pixelUrl), - - // TODO: Document requirements for redirectUrl - redirectUrl: pixelSettings.redirectUrl - ? self.replaceAmp(pixelSettings.redirectUrl) - : null, - - // Filtering rules as defined in UI - filteringConsentRuleValues: - pixelSettings.filteringConsentRuleValues, - }; - - // TODO: combine replaceMPID and replaceAmp into sanitizeUrl function - url = self.replaceMPID(pixelConfig.pixelUrl, mpid); - redirect = pixelConfig.redirectUrl - ? self.replaceMPID(pixelConfig.redirectUrl, mpid) - : ''; - urlWithRedirect = url + encodeURIComponent(redirect); - - // TODO: Refactor so that Persistence is only called once - // outside of the loop - var persistence = mpInstance._Persistence.getPersistence(); - - // TODO: Is there a historic reason for checking for previousMPID? - // it does not appear to be passed in anywhere - if (previousMPID && previousMPID !== mpid) { - if (persistence && persistence[mpid]) { - if (!persistence[mpid].csd) { - persistence[mpid].csd = {}; - } - self.performCookieSync( - urlWithRedirect, - pixelConfig.moduleId, - mpid, - persistence[mpid].csd, - pixelConfig.filteringConsentRuleValues, - mpidIsNotInCookies, - requiresConsent - ); - } - return; - } else { - // TODO: Refactor to check for the inverse and exit early - // rather than nesting - if (persistence[mpid]) { - if (!persistence[mpid].csd) { - persistence[mpid].csd = {}; - } - lastSyncDateForModule = persistence[mpid].csd[ - pixelConfig.moduleId.toString() - ] - ? persistence[mpid].csd[ - pixelConfig.moduleId.toString() - ] - : null; - - if (lastSyncDateForModule) { - // Check to see if we need to refresh cookieSync - if ( - // TODO: Turn this into a convenience method for readability? - // We use similar comparisons elsewhere in the SDK, - // so perhaps we can make a time comparison convenience method - new Date().getTime() > - new Date(lastSyncDateForModule).getTime() + - pixelConfig.frequencyCap * - // TODO: Turn these numbers into a constant so - // we can remember what this number is for - 60 * - 1000 * - 60 * - 24 - ) { - self.performCookieSync( - urlWithRedirect, - pixelConfig.moduleId, - mpid, - persistence[mpid].csd, - pixelConfig.filteringConsentRuleValues, - mpidIsNotInCookies, - requiresConsent - ); - } - } else { - self.performCookieSync( - urlWithRedirect, - pixelConfig.moduleId, - mpid, - persistence[mpid].csd, - pixelConfig.filteringConsentRuleValues, - mpidIsNotInCookies, - requiresConsent - ); - } - } - } - }); - } - }; - - // Private - this.replaceMPID = function(string, mpid) { - return string.replace('%%mpid%%', mpid); - }; - - // Private - /** - * @deprecated replaceAmp has been deprecated, use replaceAmpersandWithAmp instead - */ - this.replaceAmp = function(string) { - return this.replaceAmpWithAmpersand(string); - }; - - // Private - this.replaceAmpWithAmpersand = function(string) { - return string.replace(/&/g, '&'); - }; - - // Private - this.performCookieSync = function( - url, - moduleId, - mpid, - cookieSyncDates, - filteringConsentRuleValues, - mpidIsNotInCookies, - requiresConsent - ) { - // if MPID is new to cookies, we should not try to perform the cookie sync - // because a cookie sync can only occur once a user either consents or doesn't - // we should not check if its enabled if the user has a blank consent - // TODO: We should do this check outside of this function - if (requiresConsent && mpidIsNotInCookies) { - return; - } - - // TODO: Refactor so that check is made outside of the function. - // Cache or store the boolean so that it only gets called once per - // cookie sync attempt per module. - // Currently, attemptCookieSync is called as a loop and therefore this - // function polls the user object and consent multiple times. - if ( - mpInstance._Consent.isEnabledForUserConsent( - filteringConsentRuleValues, - mpInstance.Identity.getCurrentUser() - ) - ) { - var img = document.createElement('img'); - - mpInstance.Logger.verbose(Messages.InformationMessages.CookieSync); - img.onload = function() { - // TODO: Break this out into a convenience method so we can unit test - cookieSyncDates[moduleId.toString()] = new Date().getTime(); - mpInstance._Persistence.saveUserCookieSyncDatesToPersistence( - mpid, - cookieSyncDates - ); - }; - img.src = url; - } - }; -} diff --git a/src/cookieSyncManager.ts b/src/cookieSyncManager.ts new file mode 100644 index 00000000..9f056fed --- /dev/null +++ b/src/cookieSyncManager.ts @@ -0,0 +1,168 @@ +import { isEmpty, replaceAmpWithAmpersand, replaceMPID } from './utils'; +import Constants from './constants'; +import { CookieSyncDates, ICookieSyncManager, IPixelConfiguration } from './cookieSyncManager.interfaces'; +import { MParticleWebSDK } from './sdkRuntimeModels'; +import { MPID } from '@mparticle/web-sdk'; +import { IConsentRules } from './consent'; + +const { Messages } = Constants; +const { InformationMessages } = Messages; + +export const DAYS_IN_MILLISECONDS = 1000 * 60 * 60 * 24; + +const hasFrequencyCapExpired = ( + frequencyCap: number, + lastSyncDate?: number, +): boolean => { + if (!lastSyncDate) { + return true; + } + + return ( + new Date().getTime() > + new Date(lastSyncDate).getTime() + frequencyCap * DAYS_IN_MILLISECONDS + ); +} + +export default function CookieSyncManager( + this: ICookieSyncManager, + mpInstance: MParticleWebSDK +) { + const self = this; + + // Public + this.attemptCookieSync = ( + previousMPID: MPID, + mpid: MPID, + mpidIsNotInCookies?: boolean + ): void => { + if (!mpid || mpInstance._Store.webviewBridgeEnabled) { + return; + } + + const { pixelConfigurations } = mpInstance._Store; + const persistence = mpInstance._Persistence.getPersistence(); + + pixelConfigurations.forEach((pixelSettings: IPixelConfiguration) => { + // set requiresConsent to false to start each additional pixel configuration + // set to true only if filteringConsenRuleValues.values.length exists + let requiresConsent = false; + + // Filtering rules as defined in UI + const { filteringConsentRuleValues } = pixelSettings; + const { values } = filteringConsentRuleValues || {}; + + if (!isEmpty(values)) { + requiresConsent = true; + } + + // Kit Module ID + const moduleId = pixelSettings.moduleId.toString(); + + // Tells you how often we should do a cookie sync (in days) + const frequencyCap = pixelSettings.frequencyCap; + + // Url for cookie sync pixel + const pixelUrl = replaceAmpWithAmpersand(pixelSettings.pixelUrl); + + const redirectUrl = pixelSettings.redirectUrl + ? replaceAmpWithAmpersand(pixelSettings.redirectUrl) + : null; + + const urlWithRedirect = this.combineUrlWithRedirect( + mpid, + pixelUrl, + redirectUrl + ); + + if (previousMPID && previousMPID !== mpid) { + if (persistence && persistence[mpid]) { + if (!persistence[mpid].csd) { + persistence[mpid].csd = {}; + } + self.performCookieSync( + urlWithRedirect, + moduleId, + mpid, + persistence[mpid].csd, + filteringConsentRuleValues, + mpidIsNotInCookies, + requiresConsent + ); + } + return; + } else { + if (!persistence || !persistence[mpid]) { + return; + } + + if (!persistence[mpid].csd) { + persistence[mpid].csd = {}; + } + + const lastSyncDateForModule = persistence[mpid].csd[moduleId] || null; + + // Check to see if we need to refresh cookieSync + if (hasFrequencyCapExpired(frequencyCap, lastSyncDateForModule)) { + self.performCookieSync( + urlWithRedirect, + moduleId, + mpid, + persistence[mpid].csd, + filteringConsentRuleValues, + mpidIsNotInCookies, + requiresConsent + ); + } + } + }); + }; + + this.combineUrlWithRedirect = ( + mpid: MPID, + pixelUrl: string, + redirectUrl: string, + ): string => { + const url = replaceMPID(pixelUrl, mpid); + const redirect = redirectUrl ? replaceMPID(redirectUrl, mpid) : ''; + return url + encodeURIComponent(redirect); + }; + + // Private + this.performCookieSync = ( + url: string, + moduleId: string, + mpid: MPID, + cookieSyncDates: CookieSyncDates, + filteringConsentRuleValues: IConsentRules, + mpidIsNotInCookies: boolean, + requiresConsent: boolean + ): void => { + // if MPID is new to cookies, we should not try to perform the cookie sync + // because a cookie sync can only occur once a user either consents or doesn't + // we should not check if its enabled if the user has a blank consent + if (requiresConsent && mpidIsNotInCookies) { + return; + } + + if ( + // https://go.mparticle.com/work/SQDSDKS-5009 + mpInstance._Consent.isEnabledForUserConsent( + filteringConsentRuleValues, + mpInstance.Identity.getCurrentUser() + ) + ) { + const img = document.createElement('img'); + + mpInstance.Logger.verbose(InformationMessages.CookieSync); + img.onload = function() { + cookieSyncDates[moduleId] = new Date().getTime(); + mpInstance._Persistence.saveUserCookieSyncDatesToPersistence( + mpid, + cookieSyncDates + ); + }; + img.src = url; + } + }; +} diff --git a/src/persistence.interfaces.ts b/src/persistence.interfaces.ts index 59a1bd76..4c89ec69 100644 --- a/src/persistence.interfaces.ts +++ b/src/persistence.interfaces.ts @@ -14,6 +14,7 @@ import { import { Dictionary } from './utils'; import { IMinifiedConsentJSONObject } from './consent'; import { UserAttributes } from './identity-user-interfaces'; +import { CookieSyncDates } from './cookieSyncManager.interfaces'; export type UploadsTable = Dictionary; export interface iForwardingStatsBatches { @@ -79,10 +80,8 @@ export interface IPersistenceMinified extends Dictionary { // }; } -export type CookieSyncDate = Dictionary; - export interface IUserPersistenceMinified extends Dictionary { - csd: CookieSyncDate; // Cookie Sync Dates // list of timestamps for last cookie sync + csd: CookieSyncDates; // list of timestamps for last cookie sync per module con: IMinifiedConsentJSONObject; // Consent State ui: UserIdentities; // User Identities ua: UserAttributes; // User Attributes @@ -121,7 +120,7 @@ export interface IPersistence { getDomain(doc: string, locationHostname: string): string; getCartProducts(mpid: MPID): Product[]; setCartProducts(allProducts: Product[]): void; - saveUserCookieSyncDatesToPersistence(mpid: MPID, csd: CookieSyncDate): void; + saveUserCookieSyncDatesToPersistence(mpid: MPID, csd: CookieSyncDates): void; savePersistence(persistance: IPersistenceMinified): void; getPersistence(): IPersistenceMinified; getFirstSeenTime(mpid: MPID): string | null; diff --git a/src/sdkRuntimeModels.ts b/src/sdkRuntimeModels.ts index 16110538..ae3e29b5 100644 --- a/src/sdkRuntimeModels.ts +++ b/src/sdkRuntimeModels.ts @@ -32,7 +32,7 @@ import { import { IIdentityType } from './types.interfaces'; import IntegrationCapture from './integrationCapture'; import { INativeSdkHelpers } from './nativeSdkHelpers.interfaces'; -import { ICookieSyncManager } from './cookieSyncManager.interfaces'; +import { ICookieSyncManager, IPixelConfiguration } from './cookieSyncManager.interfaces'; // TODO: Resolve this with version in @mparticle/web-sdk export type SDKEventCustomFlags = Dictionary; @@ -231,6 +231,7 @@ export interface SDKInitConfig sideloadedKits?: MPForwarder[]; dataPlanOptions?: KitBlockerOptions; flags?: Dictionary; + pixelConfigs?: IPixelConfiguration[]; aliasMaxWindow?: number; deviceId?: string; diff --git a/src/store.ts b/src/store.ts index d48158e1..91f6fd51 100644 --- a/src/store.ts +++ b/src/store.ts @@ -37,6 +37,7 @@ import { IGlobalStoreV2MinifiedKeys, IPersistenceMinified, } from './persistence.interfaces'; +import { CookieSyncDates, IPixelConfiguration } from './cookieSyncManager.interfaces'; // This represents the runtime configuration of the SDK AFTER // initialization has been complete and all settings and @@ -118,7 +119,6 @@ function createSDKConfig(config: SDKInitConfig): SDKConfig { // TODO: Placeholder Types to be filled in as we migrate more modules // to TypeScript -export type PixelConfiguration = Dictionary; export type ServerSettings = Dictionary; export type SessionAttributes = Dictionary; export type IntegrationAttributes = Dictionary>; @@ -168,7 +168,7 @@ export interface IStore { identifyCalled: boolean; isLoggedIn: boolean; sideloadedKitsCount?: number; - cookieSyncDates: Dictionary; + cookieSyncDates: CookieSyncDates; integrationAttributes: IntegrationAttributes; requireDelay: boolean; isLocalStorageAvailable: boolean | null; @@ -178,7 +178,7 @@ export interface IStore { kits: Dictionary; sideloadedKits: MPForwarder[]; configuredForwarders: MPForwarder[]; - pixelConfigurations: PixelConfiguration[]; + pixelConfigurations: IPixelConfiguration[]; integrationDelayTimeoutStart: number; // UNIX Timestamp webviewBridgeEnabled?: boolean; wrapperSDKInfo: WrapperSDKInfo; diff --git a/src/utils.ts b/src/utils.ts index f25b7e35..d5350587 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,4 @@ +import { MPID } from '@mparticle/web-sdk'; import Constants from './constants'; const { Messages } = Constants; @@ -175,6 +176,10 @@ const replaceApostrophesWithQuotes = (value: string): string => const replaceQuotesWithApostrophes = (value: string): string => value.replace(/\"/g, "'"); +const replaceMPID = (value: string, mpid: MPID): string => value.replace('%%mpid%%', mpid); + +const replaceAmpWithAmpersand = (value: string): string => value.replace(/&/g, '&'); + // FIXME: REFACTOR for V3 // only used in store.js to sanitize server-side formatting of // booleans when checking for `isDevelopmentMode` @@ -366,4 +371,6 @@ export { queryStringParser, getCookies, getHref, + replaceMPID, + replaceAmpWithAmpersand, }; diff --git a/test/jest/cookieSyncManager.spec.ts b/test/jest/cookieSyncManager.spec.ts new file mode 100644 index 00000000..860251ab --- /dev/null +++ b/test/jest/cookieSyncManager.spec.ts @@ -0,0 +1,638 @@ +import CookieSyncManager, { DAYS_IN_MILLISECONDS } from '../../src/cookieSyncManager'; +import { IPixelConfiguration } from '../../src/cookieSyncManager.interfaces'; +import { MParticleWebSDK } from '../../src/sdkRuntimeModels'; +import { testMPID } from '../src/config/constants'; + +const pixelSettings: IPixelConfiguration = { + name: 'TestPixel', + moduleId: 5, + esId: 24053, + isDebug: true, + isProduction: true, + settings: {}, + frequencyCap: 14, + pixelUrl: '', + redirectUrl: '', +}; + +describe('CookieSyncManager', () => { + describe('#attemptCookieSync', () => { + // https://go.mparticle.com/work/SQDSDKS-6915 + it('should perform a cookie sync with defaults', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: {} + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + {}, + undefined, + true, + false, + ); + }); + + it('should return early if mpid is not defined', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: {} + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, null, true); + + expect(cookieSyncManager.performCookieSync).not.toHaveBeenCalled(); + }); + + it('should return early if webviewBridgeEnabled is true', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: true, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: {} + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).not.toHaveBeenCalled(); + }); + + it('should toggle requiresConsent value if filtering filteringConsentRuleValues are defined', () => { + const myPixelSettings: IPixelConfiguration = { + filteringConsentRuleValues: { + values: ['test'], + }, + } as unknown as IPixelConfiguration; + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [{...pixelSettings, ...myPixelSettings}], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: {} + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + {}, + { values: ['test'] }, + true, + true, + ); + }); + + it('should update the urlWithRedirect if a redirectUrl is defined', () => { + const myPixelSettings: IPixelConfiguration = { + pixelUrl: 'https://test.com', + redirectUrl: '?redirect=https://redirect.com&mpid=%%mpid%%', + } as unknown as IPixelConfiguration; + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [{...pixelSettings, ...myPixelSettings}], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: {} + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + 'https://test.com%3Fredirect%3Dhttps%3A%2F%2Fredirect.com%26mpid%3DtestMPID', + '5', + testMPID, + {}, + undefined, + true, + false, + ); + }); + + // https://go.mparticle.com/work/SQDSDKS-6915 + it('should call performCookieSync with mpid if previousMpid and mpid do not match', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync('other-mpid', testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + {}, + undefined, + true, + false, + ); + }); + + it('should include mpid AND csd when calling performCookieSync if previousMpid and mpid do not match', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: { 5: 1234567890 } + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync('other-mpid', testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + { + 5: 1234567890 + }, + undefined, + true, + false, + ); + }); + + it('should call performCookieSync with mpid if previousMpid and mpid match', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(testMPID, testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + {}, + undefined, + true, + false, + ); + }); + + it('should perform a cookie sync if lastSyncDateForModule is null', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: {}}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + {}, + undefined, + true, + false, + ); + }); + + it('should perform a cookie sync if lastSyncDateForModule has passed the frequency cap', () => { + const now = new Date().getTime(); + + // Rev the date by one + const cookieSyncDateInPast = now - ( pixelSettings.frequencyCap + 1 ) * DAYS_IN_MILLISECONDS; + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: { 5: cookieSyncDateInPast } + }}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + { + 5: cookieSyncDateInPast + }, + undefined, + true, + false, + ); + }); + + it('should perform a cookie sync if lastSyncDateForModule is past the frequency cap even if csd is empty', () => { + const now = new Date().getTime(); + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + // This will make lastSyncDateForModule undefined, which bypasses the + // actual time check, but still performs a cookie sync + getPersistence: () => ({testMPID: {}}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + '', + '5', + testMPID, + {}, + undefined, + true, + false, + ); + }); + + it('should sync cookies when there was not a previous cookie-sync', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + + cookieSyncManager.attemptCookieSync(null, '456', true); + expect(mockMPInstance._Store.pixelConfigurations.length).toBe(1); + expect(mockMPInstance._Store.pixelConfigurations[0].moduleId).toBe(5); + }); + + it('should not perform a cookie sync if persistence is empty', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + getPersistence: () => ({}), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(null, testMPID, true); + + expect(cookieSyncManager.performCookieSync).not.toHaveBeenCalled(); + }); + }); + + describe('#performCookieSync', () => { + it('should add cookie sync dates to a tracking pixel', () => { + const mockImage = { + onload: jest.fn(), + src: '', + }; + jest.spyOn(document, 'createElement').mockReturnValue( + (mockImage as unknown) as HTMLImageElement + ); + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + setCookieSyncDates: jest.fn(), + getPersistence: jest.fn(), + saveUserCookieSyncDatesToPersistence: jest.fn(), + }, + _Consent: { + isEnabledForUserConsent: jest.fn().mockReturnValue(true), + }, + Identity: { + getCurrentUser: jest.fn().mockReturnValue({ + getMPID: () => '123', + }), + }, + Logger: { + verbose: jest.fn(), + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + + let cookieSyncDates = []; + cookieSyncManager.performCookieSync( + 'https://test.com', + 42, + '1234', + cookieSyncDates, + null, + false, + false + ); + + // Simulate image load + mockImage.onload(); + + expect(mockImage.src).toBe('https://test.com'); + expect(cookieSyncDates[42]).toBeDefined(); + expect(cookieSyncDates[42]).toBeGreaterThan(0); + }); + + it('should log a verbose message when a cookie sync is performed', () => { + const mockImage = { + onload: jest.fn(), + src: '', + }; + jest.spyOn(document, 'createElement').mockReturnValue( + (mockImage as unknown) as HTMLImageElement + ); + + const loggerSpy = jest.fn(); + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + setCookieSyncDates: jest.fn(), + getPersistence: jest.fn(), + saveUserCookieSyncDatesToPersistence: jest.fn(), + }, + _Consent: { + isEnabledForUserConsent: jest.fn().mockReturnValue(true), + }, + Identity: { + getCurrentUser: jest.fn().mockReturnValue({ + getMPID: () => '123', + }), + }, + Logger: { + verbose: loggerSpy, + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + + let cookieSyncDates = []; + cookieSyncManager.performCookieSync( + 'https://test.com', + 42, + '1234', + cookieSyncDates, + null, + false, + false + ); + + // Simulate image load + mockImage.onload(); + + expect(loggerSpy).toHaveBeenCalledWith('Performing cookie sync'); + }); + + it('should return early if the user has not consented to the cookie sync', () => { + const mockImage = { + onload: jest.fn(), + src: '', + }; + jest.spyOn(document, 'createElement').mockReturnValue( + (mockImage as unknown) as HTMLImageElement + ); + + const loggerSpy = jest.fn(); + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + setCookieSyncDates: jest.fn(), + getPersistence: jest.fn(), + saveUserCookieSyncDatesToPersistence: jest.fn(), + }, + _Consent: { + isEnabledForUserConsent: jest.fn().mockReturnValue(false), + }, + Identity: { + getCurrentUser: jest.fn().mockReturnValue({ + getMPID: () => '123', + }), + }, + Logger: { + verbose: loggerSpy, + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + + let cookieSyncDates = []; + cookieSyncManager.performCookieSync( + 'https://test.com', + 42, + '1234', + cookieSyncDates, + null, + false, + false, + ); + + // Simulate image load + mockImage.onload(); + + expect(mockImage.src).toBe(''); + expect(cookieSyncDates[42]).toBeUndefined(); + }); + + it('should return early if requiresConsent and mpidIsNotInCookies are both true', () => { + const mockImage = { + onload: jest.fn(), + src: '', + }; + jest.spyOn(document, 'createElement').mockReturnValue( + (mockImage as unknown) as HTMLImageElement + ); + + const loggerSpy = jest.fn(); + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + _Persistence: { + setCookieSyncDates: jest.fn(), + getPersistence: jest.fn(), + saveUserCookieSyncDatesToPersistence: jest.fn(), + }, + _Consent: { + isEnabledForUserConsent: jest.fn().mockReturnValue(true), + }, + Identity: { + getCurrentUser: jest.fn().mockReturnValue({ + getMPID: () => '123', + }), + }, + Logger: { + verbose: loggerSpy, + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + + let cookieSyncDates = []; + cookieSyncManager.performCookieSync( + 'https://test.com', + 42, + '1234', + cookieSyncDates, + null, + true, + true, + ); + + // Simulate image load + mockImage.onload(); + + expect(mockImage.src).toBe(''); + expect(cookieSyncDates[42]).toBeUndefined(); + }); + }); + + describe('#combineUrlWithRedirect', () => { + it('should combine a pixelUrl with a redirectUrl', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + + // Note: We expect that the input of the pixelUrl will include a `?` + // (ideally at the end of the string) + // so that the redirectUrl can be processed by the backend correctly + const result = cookieSyncManager.combineUrlWithRedirect( + '1234', + 'https://test.com/some/path?', + 'https://redirect.mparticle.com/v1/sync?esid=1234&MPID=%%mpid%%&ID=$UID&Key=testMPID&env=2' + ); + + expect(result).toBe('https://test.com/some/path?https%3A%2F%2Fredirect.mparticle.com%2Fv1%2Fsync%3Fesid%3D1234%26amp%3BMPID%3D1234%26amp%3BID%3D%24UID%26amp%3BKey%3DtestMPID%26amp%3Benv%3D2'); + }); + + it('should return the pixelUrl if no redirectUrl is defined', () => { + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [pixelSettings], + }, + } as unknown) as MParticleWebSDK; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + + const result = cookieSyncManager.combineUrlWithRedirect( + '1234', + 'https://test.com', + ); + + expect(result).toBe('https://test.com'); + }); + }); +}); diff --git a/test/jest/utils.spec.ts b/test/jest/utils.spec.ts index 8ae27d15..f9ac67dc 100644 --- a/test/jest/utils.spec.ts +++ b/test/jest/utils.spec.ts @@ -1,7 +1,12 @@ -import { queryStringParser, getCookies, getHref } from '../../src/utils'; +import { + queryStringParser, + getCookies, + getHref, + replaceMPID, + replaceAmpWithAmpersand, +} from '../../src/utils'; import { deleteAllCookies } from './utils'; - describe('Utils', () => { describe('getCookies', () => { beforeEach(() => { @@ -98,7 +103,6 @@ describe('Utils', () => { }); }); - describe('without URLSearchParams', () => { beforeEach(() => { URL = undefined; @@ -159,4 +163,24 @@ describe('Utils', () => { }); }); + describe('#replaceMPID', () => { + it('replaces the MPID in a string', () => { + const mpid = '1234'; + const string = 'https://www.google.com?mpid=%%mpid%%&foo=bar'; + + expect(replaceMPID(string, mpid)).toEqual( + 'https://www.google.com?mpid=1234&foo=bar' + ); + }); + }); + + describe('#replaceAmpWithAmpersand', () => { + it('replaces all instances of amp with ampersand', () => { + const string = 'https://www.google.com?mpid=%%mpid%%&foo=bar'; + + expect(replaceAmpWithAmpersand(string)).toEqual( + 'https://www.google.com?mpid=%%mpid%%&foo=bar' + ); + }); + }); }); diff --git a/test/src/tests-cookie-syncing.js b/test/src/tests-cookie-syncing.ts similarity index 94% rename from test/src/tests-cookie-syncing.js rename to test/src/tests-cookie-syncing.ts index 036b070d..e30e27aa 100644 --- a/test/src/tests-cookie-syncing.js +++ b/test/src/tests-cookie-syncing.ts @@ -1,11 +1,16 @@ +import { expect } from 'chai'; import Utils from './config/utils'; import fetchMock from 'fetch-mock/esm/client'; import { urls, testMPID, MPConfig, v4LSKey, apiKey } from './config/constants'; +import { MParticleWebSDK } from '../../src/sdkRuntimeModels'; +import { IMParticleUser } from '../../src/identity-user-interfaces'; +import { IPixelConfiguration } from '../../src/cookieSyncManager.interfaces'; +import { IConsentRules } from '../../src/consent'; const { fetchMockSuccess, waitForCondition, hasIdentifyReturned } = Utils; const { setLocalStorage, MockForwarder, getLocalStorage } = Utils; -let pixelSettings = { +const pixelSettings: IPixelConfiguration = { name: 'TestPixel', moduleId: 5, esId: 24053, @@ -17,6 +22,15 @@ let pixelSettings = { redirectUrl: '', }; +declare global { + interface Window { + mParticle: MParticleWebSDK; + fetchMock: any; + } +} + +const mParticle = window.mParticle; + describe('cookie syncing', function() { const timeout = 100; // Have a reference to createElement function to reset after all cookie sync @@ -27,8 +41,8 @@ describe('cookie syncing', function() { // Mock the img create onload method // https://raminmousavi.medium.com/mock-img-element-in-jest-3341c495ca8b window.document.createElement = (function(create) { - return function() { - const element = create.apply(this, arguments); + return function(this: Document) { + const element = create.apply(this, arguments as unknown as [string, ElementCreationOptions?]); if (element.tagName === 'IMG') { setTimeout(() => { @@ -68,9 +82,9 @@ describe('cookie syncing', function() { waitForCondition(hasIdentifyReturned) .then(() => { setTimeout(function() { - Should( + expect( mParticle.getInstance()._Store.pixelConfigurations.length - ).equal(1); + ).to.equal(1); const data = mParticle.getInstance()._Persistence.getLocalStorage(); data[testMPID].csd.should.have.property('5'); @@ -94,14 +108,14 @@ describe('cookie syncing', function() { mParticle.init(apiKey, window.mParticle.config); setTimeout(function() { - Should( + expect( mParticle.getInstance()._Store.pixelConfigurations.length - ).equal(1); + ).to.equal(1); const data = mParticle.getInstance()._Persistence.getLocalStorage(); const updated = data[testMPID].csd['5'] > 500; - Should(updated).be.ok(); + expect(updated).to.be.ok; done(); }, timeout); @@ -126,9 +140,9 @@ describe('cookie syncing', function() { mParticle.getInstance()._Persistence.getLocalStorage().testMPID .csd['5'] ); - Should( + expect( mParticle.getInstance()._Store.pixelConfigurations.length - ).equal(1); + ).to.equal(1); done(); }, timeout); @@ -163,11 +177,11 @@ describe('cookie syncing', function() { const data2 = mParticle .getInstance() ._Persistence.getLocalStorage(); - data1[testMPID].csd[5].should.be.ok(); - data2['otherMPID'].csd[5].should.be.ok(); - Should( + expect(data1[testMPID].csd[5]).to.be.ok; + expect(data2['otherMPID'].csd[5]).to.be.ok; + expect( mParticle.getInstance()._Store.pixelConfigurations.length - ).equal(1); + ).to.equal(1); done(); }, timeout); @@ -201,9 +215,9 @@ describe('cookie syncing', function() { ._Persistence.getLocalStorage(); Object.keys(data1[testMPID]).should.not.have.property('csd'); - Should( + expect( mParticle.getInstance()._Store.pixelConfigurations.length - ).equal(0); + ).to.equal(0); done(); }, 500); @@ -233,39 +247,14 @@ describe('cookie syncing', function() { .getInstance() ._Persistence.getLocalStorage(); data1[testMPID].should.not.have.property('csd'); - Should( + expect( mParticle.getInstance()._Store.pixelConfigurations.length - ).equal(0); + ).to.equal(0); done(); }, timeout); }); - it('should replace mpID properly', function(done) { - const result = mParticle - .getInstance() - ._CookieSyncManager.replaceMPID( - 'www.google.com?mpid=%%mpid%%?foo=bar', - 123 - ); - - result.should.equal('www.google.com?mpid=123?foo=bar'); - - done(); - }); - - it("should remove 'amp;' from the URLs", function(done) { - const result = mParticle - .getInstance() - ._CookieSyncManager.replaceAmp( - 'www.google.com?mpid=%%mpid%%&foo=bar' - ); - - result.should.equal('www.google.com?mpid=%%mpid%%&foo=bar'); - - done(); - }); - it('parse and capture pixel settings properly from backend', function(done) { mParticle._resetForTests(MPConfig); window.mParticle.config.requestConfig = true; @@ -355,15 +344,15 @@ describe('cookie syncing', function() { it('should perform a cookiesync when consent is not configured on the cookiesync setting', function(done) { mParticle._resetForTests(MPConfig); - pixelSettings.filteringConsentRuleValues = {}; + pixelSettings.filteringConsentRuleValues = {} as unknown as IConsentRules; window.mParticle.config.pixelConfigs = [pixelSettings]; mParticle.init(apiKey, window.mParticle.config); setTimeout(function() { - Should( + expect( mParticle.getInstance()._Store.pixelConfigurations.length - ).equal(1); + ).to.equal(1); const data = mParticle.getInstance()._Persistence.getLocalStorage(); data[testMPID].csd.should.have.property('5'); @@ -391,7 +380,7 @@ describe('cookie syncing', function() { .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, null); - enabled.should.not.be.ok(); + expect(enabled).to.not.be.ok; done(); }); @@ -422,12 +411,12 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.not.be.ok(); + expect(enabled).to.not.be.ok; done(); }); @@ -456,13 +445,13 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.not.be.ok(); + expect(enabled).to.not.be.ok; done(); }); @@ -493,13 +482,13 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -530,12 +519,12 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -566,12 +555,12 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsented) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.not.be.ok(); + expect(enabled).to.not.be.ok; done(); }); @@ -602,12 +591,12 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsented) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -638,12 +627,12 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsented) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -674,12 +663,12 @@ describe('cookie syncing', function() { 'foo purpose 1', mParticle.getInstance().Consent.createGDPRConsent(userConsented) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -709,12 +698,12 @@ describe('cookie syncing', function() { .setCCPAConsentState( mParticle.getInstance().Consent.createCCPAConsent(ccpaPresent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.not.be.ok(); + expect(enabled).to.not.be.ok; done(); }); @@ -744,12 +733,12 @@ describe('cookie syncing', function() { .setCCPAConsentState( mParticle.getInstance().Consent.createCCPAConsent(ccpaPresent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.not.be.ok(); + expect(enabled).to.not.be.ok; done(); }); @@ -779,12 +768,12 @@ describe('cookie syncing', function() { .setCCPAConsentState( mParticle.getInstance().Consent.createCCPAConsent(ccpaPresent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -814,12 +803,12 @@ describe('cookie syncing', function() { .setCCPAConsentState( mParticle.getInstance().Consent.createCCPAConsent(ccpaPresent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -849,12 +838,12 @@ describe('cookie syncing', function() { .setCCPAConsentState( mParticle.getInstance().Consent.createCCPAConsent(ccpaPresent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.not.be.ok(); + expect(enabled).to.not.be.ok; done(); }); @@ -884,12 +873,12 @@ describe('cookie syncing', function() { .setCCPAConsentState( mParticle.getInstance().Consent.createCCPAConsent(ccpaPresent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -919,12 +908,12 @@ describe('cookie syncing', function() { .setCCPAConsentState( mParticle.getInstance().Consent.createCCPAConsent(ccpaPresent) ); - const user = MockUser(); + const user = MockUser() as IMParticleUser; user.setConsentState(consentState); const enabled = mParticle .getInstance() ._Consent.isEnabledForUserConsent(filteringConsentRuleValues, user); - enabled.should.be.ok(); + expect(enabled).to.be.ok; done(); }); @@ -1225,7 +1214,7 @@ describe('cookie syncing', function() { mParticle.config.isDevelopmentMode = false; // pixelSetting1 has consent required, and so should only perform a cookiesync after consent is saved to the user - const pixelSettings1 = { + const pixelSettings1: IPixelConfiguration = { name: 'TestPixel', moduleId: 1, esId: 24053,