diff --git a/packages/analytics-js-integrations/__tests__/integrations/Mixpanel/browser.test.js b/packages/analytics-js-integrations/__tests__/integrations/Mixpanel/browser.test.js index f086279b08..daa385f0b2 100644 --- a/packages/analytics-js-integrations/__tests__/integrations/Mixpanel/browser.test.js +++ b/packages/analytics-js-integrations/__tests__/integrations/Mixpanel/browser.test.js @@ -76,6 +76,143 @@ describe('Init tests', () => { loaded: expect.any(Function), }); }); + + test('Session replay configuration', () => { + const analytics = { + loadOnlyIntegrations: { + Mixpanel: { + recordBlockClass: 'block-class', + recordCollectFonts: true, + recordIdleTimeout: 5000, + recordMaskTextClass: 'mask-text', + recordMaskTextSelector: '.sensitive', + recordMaxMs: 30000, + recordMinMs: 1000, + }, + }, + logLevel: 'debug', + }; + + mixpanel = new Mixpanel( + { + persistenceType: 'localStorage', + persistenceName: 'test', + sessionReplayPercentage: 50, + }, + analytics, + { logLevel: 'debug' }, + ); + mixpanel.init(); + + expect(window.mixpanel._i[0][1]).toEqual({ + cross_subdomain_cookie: false, + secure_cookie: false, + persistence: 'localStorage', + persistence_name: 'test', + record_block_class: 'block-class', + record_collect_fonts: true, + record_idle_timeout_ms: 5000, + record_mask_text_class: 'mask-text', + record_mask_text_selector: '.sensitive', + record_max_ms: 30000, + record_min_ms: 1000, + record_sessions_percent: 50, + loaded: expect.any(Function), + }); + }); + + test('Session replay configuration with partial options', () => { + const analytics = { + loadOnlyIntegrations: { + Mixpanel: { + recordBlockClass: 'block-class', + recordCollectFonts: true, + }, + }, + logLevel: 'debug', + }; + + mixpanel = new Mixpanel( + { + persistenceType: 'localStorage', + persistenceName: 'test', + }, + analytics, + { logLevel: 'debug' }, + ); + mixpanel.init(); + + expect(window.mixpanel._i[0][1]).toEqual({ + cross_subdomain_cookie: false, + secure_cookie: false, + persistence: 'localStorage', + persistence_name: 'test', + record_block_class: 'block-class', + record_collect_fonts: true, + loaded: expect.any(Function), + }); + }); + + test('Session replay configuration without loadOnlyIntegrations', () => { + const analytics = { + logLevel: 'debug', + }; + + mixpanel = new Mixpanel( + { + persistenceType: 'localStorage', + persistenceName: 'test', + sessionReplayPercentage: 75, + }, + analytics, + { logLevel: 'debug' }, + ); + mixpanel.init(); + + expect(window.mixpanel._i[0][1]).toEqual({ + cross_subdomain_cookie: false, + secure_cookie: false, + persistence: 'localStorage', + persistence_name: 'test', + record_sessions_percent: 75, + loaded: expect.any(Function), + }); + }); + + test('Session replay configuration with invalid percentage', () => { + const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + const analytics = { + logLevel: 'debug', + }; + + mixpanel = new Mixpanel( + { + persistenceType: 'localStorage', + persistenceName: 'test', + sessionReplayPercentage: '101', + }, + analytics, + { logLevel: 'debug' }, + ); + mixpanel.init(); + + expect(window.mixpanel._i[0][1]).toEqual({ + cross_subdomain_cookie: false, + secure_cookie: false, + persistence: 'localStorage', + persistence_name: 'test', + loaded: expect.any(Function), + }); + + expect(consoleSpy).toHaveBeenCalledWith( + '%c RS SDK - Mixpanel %c Invalid sessionReplayPercentage: 101. It should be a string matching the pattern "^(100|[1-9]?[0-9])$"', + 'font-weight: bold; background: black; color: white;', + 'font-weight: normal;', + ); + + consoleSpy.mockRestore(); + }); }); describe('isLoaded and isReady tests', () => { diff --git a/packages/analytics-js-integrations/src/integrations/Mixpanel/browser.js b/packages/analytics-js-integrations/src/integrations/Mixpanel/browser.js index 031e2233e5..e492742bb3 100644 --- a/packages/analytics-js-integrations/src/integrations/Mixpanel/browser.js +++ b/packages/analytics-js-integrations/src/integrations/Mixpanel/browser.js @@ -5,7 +5,12 @@ import { DISPLAY_NAME, } from '@rudderstack/analytics-js-common/constants/integrations/Mixpanel/constants'; import Logger from '../../utils/logger'; -import { pick, removeUndefinedAndNullValues, isNotEmpty } from '../../utils/commonUtils'; +import { + pick, + removeUndefinedAndNullValues, + isNotEmpty, + isDefinedAndNotNull, +} from '../../utils/commonUtils'; import { mapTraits, unionArrays, @@ -101,16 +106,26 @@ class Mixpanel { // ref : https://docs.mixpanel.com/docs/tracking-methods/sdks/javascript#session-replay if (mixpanelIntgConfig) { const sessionReplayConfig = { - record_sessions_percent : this.sessionReplayPercentage, - record_block_class : mixpanelIntgConfig?.recordBlockClass, - record_collect_fonts : mixpanelIntgConfig?.recordCollectFonts, - record_idle_timeout_ms : mixpanelIntgConfig?.recordIdleTimeout, - record_mask_text_class : mixpanelIntgConfig?.recordMaskTextClass, - record_mask_text_selector : mixpanelIntgConfig?.recordMaskTextSelector, - record_max_ms : mixpanelIntgConfig?.recordMaxMs, - record_min_ms : mixpanelIntgConfig?.recordMinMs + record_block_class: mixpanelIntgConfig?.recordBlockClass, + record_collect_fonts: mixpanelIntgConfig?.recordCollectFonts, + record_idle_timeout_ms: mixpanelIntgConfig?.recordIdleTimeout, + record_mask_text_class: mixpanelIntgConfig?.recordMaskTextClass, + record_mask_text_selector: mixpanelIntgConfig?.recordMaskTextSelector, + record_max_ms: mixpanelIntgConfig?.recordMaxMs, + record_min_ms: mixpanelIntgConfig?.recordMinMs, }; - options = {...options,...removeUndefinedAndNullValues(sessionReplayConfig)} + options = { ...options, ...removeUndefinedAndNullValues(sessionReplayConfig) }; + } + + if (isDefinedAndNotNull(this.sessionReplayPercentage)) { + const percentageInt = parseInt(this.sessionReplayPercentage, 10); + if (percentageInt >= 0 && percentageInt <= 100) { + options.record_sessions_percent = percentageInt; + } else { + logger.warn( + `Invalid sessionReplayPercentage: ${this.sessionReplayPercentage}. It should be a string matching the pattern "^(100|[1-9]?[0-9])$"`, + ); + } } options.loaded = () => { this.isNativeSDKLoaded = true;