diff --git a/src/store.ts b/src/store.ts index 35a02787c..8f7177b8c 100644 --- a/src/store.ts +++ b/src/store.ts @@ -740,9 +740,21 @@ export default function Store( this.SDKConfig[baseUrlKeys] = baseUrls[baseUrlKeys]; } + const { noFunctional, noTargeting } = config?.launcherOptions ?? {}; + + if (noFunctional != null) { + this.setNoFunctional(noFunctional); + } + + if (noTargeting != null) { + this.setNoTargeting(noTargeting); + } + if (workspaceToken) { this.SDKConfig.workspaceToken = workspaceToken; - mpInstance._timeOnSiteTimer = new ForegroundTimer(workspaceToken); + if (!this.getPrivacyFlag('TimeOnSite')) { + mpInstance._timeOnSiteTimer = new ForegroundTimer(workspaceToken); + } } else { mpInstance.Logger.warning( 'You should have a workspaceToken on your config object for security purposes.' @@ -752,15 +764,6 @@ export default function Store( this.storageName = createMainStorageName(workspaceToken); this.prodStorageName = createProductStorageName(workspaceToken); - // Extract privacy flags directly from config into Store - if (config.hasOwnProperty('noFunctional')) { - this.setNoFunctional(config.noFunctional); - } - - if (config.hasOwnProperty('noTargeting')) { - this.setNoTargeting(config.noTargeting); - } - this.SDKConfig.requiredWebviewBridgeName = requiredWebviewBridgeName || workspaceToken; diff --git a/test/jest/foregroundTimeTracker.spec.ts b/test/jest/foregroundTimeTracker.spec.ts index 7f4ab1ee7..c4941bb9c 100644 --- a/test/jest/foregroundTimeTracker.spec.ts +++ b/test/jest/foregroundTimeTracker.spec.ts @@ -1,4 +1,7 @@ import ForegroundTimeTracker from '../../src/foregroundTimeTracker'; +import Store, { IStore } from '../../src/store'; +import { SDKInitConfig } from '../../src/sdkRuntimeModels'; +import { IMParticleWebSDKInstance } from '../../src/mp-instance'; describe('ForegroundTimeTracker', () => { let foregroundTimeTracker: ForegroundTimeTracker; @@ -490,4 +493,65 @@ describe('ForegroundTimeTracker', () => { expect(updatePersistenceSpy).toHaveBeenCalled(); }); }); + + describe('#privacy flags', () => { + const workspaceToken = 'abcdef'; + const tosKey = `mprtcl-tos-${workspaceToken}`; + let mockMPInstance: IMParticleWebSDKInstance; + let store: IStore; + + beforeEach(() => { + jest.useFakeTimers({ now: Date.now(), advanceTimers: true }); + localStorage.clear(); + + mockMPInstance = { + _Helpers: { + createMainStorageName: () => `mprtcl-v4_${workspaceToken}`, + createProductStorageName: () => `mprtcl-prodv4_${workspaceToken}`, + Validators: { isFunction: () => true }, + extend: Object.assign, + }, + _NativeSdkHelpers: { isWebviewEnabled: () => false }, + _Persistence: { + update: jest.fn(), + savePersistence: jest.fn(), + getPersistence: () => ({ gs: {} }), + }, + Logger: { verbose: jest.fn(), warning: jest.fn(), error: jest.fn() }, + Identity: { getCurrentUser: () => ({ getMPID: () => 'mpid' }) }, + _timeOnSiteTimer: undefined as any, + } as unknown as IMParticleWebSDKInstance; + + store = {} as IStore; + (Store as any).call(store, {} as SDKInitConfig, mockMPInstance, 'apikey'); + mockMPInstance._Store = store; + }); + + afterEach(() => { + jest.useRealTimers(); + localStorage.clear(); + }); + + it('should track time on site when noTargeting is false by default', () => { + store.processConfig({ workspaceToken } as SDKInitConfig); + expect(mockMPInstance._timeOnSiteTimer).toBeDefined(); + jest.advanceTimersByTime(1000); + mockMPInstance._timeOnSiteTimer.getTimeInForeground(); + expect(localStorage.getItem(tosKey)).not.toBeNull(); + }); + + it('should NOT track time on site when noTargeting is true', () => { + store.processConfig({ workspaceToken, launcherOptions: { noTargeting: true } } as SDKInitConfig); + expect(mockMPInstance._timeOnSiteTimer).toBeUndefined(); + expect(localStorage.getItem(tosKey)).toBeNull(); + }); + + it('should track time on site when noTargeting is false', () => { + store.processConfig({ workspaceToken, launcherOptions: { noTargeting: false } } as SDKInitConfig); + expect(mockMPInstance._timeOnSiteTimer).toBeDefined(); + jest.advanceTimersByTime(1000); + mockMPInstance._timeOnSiteTimer.getTimeInForeground(); + expect(localStorage.getItem(tosKey)).not.toBeNull(); + }); + }); }); \ No newline at end of file diff --git a/test/jest/store.flags.spec.ts b/test/jest/store.flags.spec.ts index cc980a3ff..ce383ce5b 100644 --- a/test/jest/store.flags.spec.ts +++ b/test/jest/store.flags.spec.ts @@ -46,8 +46,7 @@ describe('Store privacy flags', () => { it('should set noFunctional as true and noTargeting as true from config', () => { const cfg: SDKInitConfig = { - noFunctional: true, - noTargeting: true, + launcherOptions: { noFunctional: true, noTargeting: true }, flags: {}, } as any; @@ -59,8 +58,7 @@ describe('Store privacy flags', () => { it('should set noFunctional as true and noTargeting as false from config', () => { const cfg: SDKInitConfig = { - noFunctional: true, - noTargeting: false, + launcherOptions: { noFunctional: true, noTargeting: false }, flags: {}, } as any; @@ -72,13 +70,12 @@ describe('Store privacy flags', () => { it('should set noFunctional as false and noTargeting as true from config', () => { const cfg: SDKInitConfig = { - noFunctional: false, - noTargeting: true, + launcherOptions: { noFunctional: false, noTargeting: true }, flags: {}, } as any; store.processConfig(cfg); - + expect(store.getNoFunctional()).toBe(false); expect(store.getNoTargeting()).toBe(true); }); diff --git a/test/src/tests-batchUploader.ts b/test/src/tests-batchUploader.ts index f15fd42a5..533b182bc 100644 --- a/test/src/tests-batchUploader.ts +++ b/test/src/tests-batchUploader.ts @@ -1744,7 +1744,7 @@ describe('batch uploader', () => { offlineStorage: '100', ...enableBatchingConfigFlags, }; - window.mParticle.config.noFunctional = true; + window.mParticle.config.launcherOptions = { noFunctional: true }; window.mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); const mpInstance = window.mParticle.getInstance(); @@ -1758,7 +1758,7 @@ describe('batch uploader', () => { offlineStorage: '100', ...enableBatchingConfigFlags, }; - window.mParticle.config.noFunctional = false; + window.mParticle.config.launcherOptions = { noFunctional: false }; window.mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); const mpInstance = window.mParticle.getInstance(); @@ -1783,7 +1783,7 @@ describe('batch uploader', () => { offlineStorage: '100', ...enableBatchingConfigFlags, }; - window.mParticle.config.noFunctional = true; + window.mParticle.config.launcherOptions = { noFunctional: true }; window.mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); const mpInstance = window.mParticle.getInstance(); @@ -1798,7 +1798,7 @@ describe('batch uploader', () => { offlineStorage: '100', ...enableBatchingConfigFlags, }; - window.mParticle.config.noFunctional = false; + window.mParticle.config.launcherOptions = { noFunctional: false }; window.mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); const mpInstance = window.mParticle.getInstance(); diff --git a/test/src/tests-store.ts b/test/src/tests-store.ts index 2c68e78a1..c3ca59671 100644 --- a/test/src/tests-store.ts +++ b/test/src/tests-store.ts @@ -1817,8 +1817,7 @@ describe('Store', () => { it('should set noFunctional and noTargeting from init config', () => { window.mParticle._resetForTests(MPConfig); window.mParticle.config = window.mParticle.config || {}; - window.mParticle.config.noFunctional = true; - window.mParticle.config.noTargeting = true; + window.mParticle.config.launcherOptions = { noFunctional: true, noTargeting: true }; window.mParticle.init(apiKey, window.mParticle.config);