// Mock the currency constants first const mockCurrencyConstants = { CURRENCY_AFN: 1, CURRENCY_EUR: 2, CURRENCY_ALL: 8, CURRENCY_DZD: 12, CURRENCY_USD: 840, // Add other currencies as needed }; // Mock the @heap/heap-react-native-autocapture package jest.mock('@contentsquare/react-native-autocapture', () => ({ normalizeOptions: jest.fn(options => options || {}), register: jest.fn(), HeapIgnore: ({ children }: any) => children, })); // Mock the heap module jest.mock('../heap', () => ({ Heap: { logToConsole: jest.fn(), }, HeapLogger: jest.fn(), })); // Mock react-native Platform before other imports jest.mock('react-native', () => ({ NativeModules: { ContentsquareModule: { ...mockCurrencyConstants, resumeTracking: jest.fn(), stopTracking: jest.fn(), send: jest.fn(), sendTransaction: jest.fn(), sendTransactionWithStringCurrency: jest.fn(), sendDynamicStringVar: jest.fn(), sendDynamicIntVar: jest.fn(), initComponents: jest.fn(), }, }, Platform: { OS: 'ios', select: jest.fn(obj => obj.ios || obj.default), }, requireNativeComponent: jest.fn(() => jest.fn()), // Mock requireNativeComponent })); // Mock codegen native components for iOS jest.mock('../core/specs/CSMaskedViewNativeComponent', () => ({ __esModule: true, default: jest.fn(() => null), })); jest.mock('../core/specs/CSUnmaskedViewNativeComponent', () => ({ __esModule: true, default: jest.fn(() => null), })); jest.mock('../core/specs/CSQMaskedViewNativeComponent', () => ({ __esModule: true, default: jest.fn(() => null), })); jest.mock('../core/specs/CSQUnmaskedViewNativeComponent', () => ({ __esModule: true, default: jest.fn(() => null), })); // Mock wrapper components for Android jest.mock('../replay/masking/CSMaskedView', () => ({ __esModule: true, default: jest.fn(() => null), })); jest.mock('../replay/masking/CSUnmaskedView', () => ({ __esModule: true, default: jest.fn(() => null), })); jest.mock('../replay/csqMasking/CSQMaskedView', () => ({ __esModule: true, default: jest.fn(() => null), })); jest.mock('../replay/csqMasking/CSQUnmaskedView', () => ({ __esModule: true, default: jest.fn(() => null), })); // Mock the TurboModule Registry jest.mock('react-native/Libraries/TurboModule/TurboModuleRegistry', () => ({ getEnforcing: jest.fn(name => { if (name === 'ContentsquareModule') { return { getConstants: jest.fn(() => mockCurrencyConstants), resumeTracking: jest.fn(), stopTracking: jest.fn(), send: jest.fn(), sendTransaction: jest.fn(), sendTransactionWithStringCurrency: jest.fn(), sendDynamicStringVar: jest.fn(), sendDynamicIntVar: jest.fn(), initComponents: jest.fn(), }; } throw new Error(`Mock for TurboModule ${name} not found`); }), })); // Mock the NativeContentsquareModule spec jest.mock('../core/specs/NativeContentsquareModule', () => ({ __esModule: true, default: { getConstants: jest.fn(() => mockCurrencyConstants), resumeTracking: jest.fn(), stopTracking: jest.fn(), send: jest.fn(), sendTransaction: jest.fn(), sendTransactionWithStringCurrency: jest.fn(), sendDynamicStringVar: jest.fn(), sendDynamicIntVar: jest.fn(), initComponents: jest.fn(), }, })); // Mock the nativeModules jest.mock('../core/nativeModules', () => ({ ContentsquareModule: { ...mockCurrencyConstants, resumeTracking: jest.fn(), stopTracking: jest.fn(), send: jest.fn(), sendTransaction: jest.fn(), sendTransactionWithStringCurrency: jest.fn(), sendDynamicStringVar: jest.fn(), sendDynamicIntVar: jest.fn(), initComponents: jest.fn(), }, })); jest.mock('../core/featureFlags/feature', () => ({ Feature: { checkFeatureFlagImplementation: 'checkFeatureFlagImplementation', crashReporter: 'crashReporter', }, })); jest.mock('../core/csEventSubscriber', () => ({ csEventSubscriber: jest.fn(), })); jest.mock('../core/featureFlags/featureFlagHandler', () => ({ handleFeatureFlags: jest.fn(), })); jest.mock('../types/types', () => ({ Feature: { checkFeatureFlagImplementation: 'checkFeatureFlagImplementation', crashReporter: 'crashReporter', }, FeatureFlags: jest.fn(), EventSubscriberType: { GET_FEATURE_FLAG: 'getFeatureFlags', }, CustomVar: jest.fn(), })); describe('Contentsquare Module', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let Contentsquare: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any let ContentsquareInternals: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any let CSMask: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any let CSReliableTarget: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any let CSWebView: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any let Currency: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any let ErrorAnalysis: any; beforeAll(() => { jest.resetModules(); // Import after all mocks are defined const moduleExports = require('../index'); Contentsquare = moduleExports.default; ContentsquareInternals = moduleExports.ContentsquareInternals; CSMask = moduleExports.CSMask; CSReliableTarget = moduleExports.CSReliableTarget; CSWebView = moduleExports.CSWebView; Currency = moduleExports.Currency; ErrorAnalysis = moduleExports.ErrorAnalysis; }); beforeEach(() => { jest.clearAllMocks(); }); // TODO: Unable to test this due to the way the module is initialized, // MOBILE-12983, https://contentsquare.atlassian.net/browse/MOBILE-12983 // it('should subscribe to GET_FEATURE_FLAG events on initialization', () => { // expect(csEventSubscriber).toHaveBeenCalledWith( // EventSubscriberType.GET_FEATURE_FLAG, // handleFeatureFlags // ); // }); describe('Default Export', () => { it('should contain all public methods', () => { console.warn('Contentsquare:', Contentsquare); expect(Contentsquare).toEqual( expect.objectContaining({ start: expect.any(Function), handleUrl: expect.any(Function), getUserId: expect.any(Function), optIn: expect.any(Function), optOut: expect.any(Function), resumeTracking: expect.any(Function), stopTracking: expect.any(Function), send: expect.any(Function), sendTransaction: expect.any(Function), sendDynamicVar: expect.any(Function), setDefaultMasking: expect.any(Function), onSessionReplayLinkChange: expect.any(Function), sendUserIdentifier: expect.any(Function), }) ); }); }); describe('Named Exports', () => { it('should export CSMask', () => { expect(CSMask).toBeDefined(); }); it('should export CSReliableTarget', () => { expect(CSReliableTarget).toBeDefined(); }); it('should export CSWebView', () => { expect(CSWebView).toBeDefined(); }); it('should export Currency', () => { expect(Currency).toBeDefined(); expect(Currency.AFN).toBe(1); expect(Currency.EUR).toBe(2); }); it('should export ErrorAnalysis', () => { expect(ErrorAnalysis).toBeDefined(); }); }); describe('Internal API', () => { it('should expose ContentsquareInternals with internal methods', () => { expect(ContentsquareInternals).toEqual( expect.objectContaining({ getLogLevel: expect.any(Function), setLogLevel: expect.any(Function), }) ); }); }); });