import { render } from '@testing-library/react-native'; import { Text, View } from 'react-native'; import Logger from '../../../core/logging/logger'; import CSQMethodFilter from '../../../csq/CSQMethodFilter'; import { CSQMask } from '../../../replay/csqMasking/CSQMask'; // Mock dependencies jest.mock('../../../csq/CSQMethodFilter', () => ({ warnIfDisabled: jest.fn(), })); jest.mock('../../../core/logging/logger', () => ({ error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn(), })); jest.mock('@contentsquare/react-native-autocapture', () => ({ HeapIgnore: ({ children, ...props }: any) => { // eslint-disable-next-line @typescript-eslint/no-var-requires const React = require('react'); return React.createElement( 'View', { testID: 'heap-ignore', ...props, }, children ); }, })); // Mock iOS codegen components jest.mock('../../../core/specs/CSQMaskedViewNativeComponent', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const React = require('react'); return { __esModule: true, default: React.forwardRef((props: any, ref: any) => { return React.createElement( 'View', { ...props, ref, testID: 'csq-masked-view', }, props.children ); }), }; }); jest.mock('../../../core/specs/CSQUnmaskedViewNativeComponent', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const React = require('react'); return { __esModule: true, default: React.forwardRef((props: any, ref: any) => { return React.createElement( 'View', { ...props, ref, testID: 'csq-unmasked-view', }, props.children ); }), }; }); // Mock Android wrapper components jest.mock('../../../replay/csqMasking/CSQMaskedView', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const React = require('react'); return React.forwardRef((props: any, ref: any) => { return React.createElement( 'View', { ...props, ref, testID: 'csq-masked-view', }, props.children ); }); }); jest.mock('../../../replay/csqMasking/CSQUnmaskedView', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const React = require('react'); return React.forwardRef((props: any, ref: any) => { return React.createElement( 'View', { ...props, ref, testID: 'csq-unmasked-view', }, props.children ); }); }); describe('CSQMask Component', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('CSQMethodFilter integration', () => { it('should call CSQMethodFilter.warnIfDisabled on mount', () => { render( Test ); expect(CSQMethodFilter.warnIfDisabled).toHaveBeenCalledWith( 'CSQ', 'CSQMask' ); expect(CSQMethodFilter.warnIfDisabled).toHaveBeenCalledTimes(1); }); }); describe('Basic rendering with isSessionReplayMasked', () => { it('should render CSQMaskedView when isSessionReplayMasked is true', () => { const { getByTestId } = render( Masked Content ); expect(getByTestId('csq-masked-view')).toBeTruthy(); expect(() => getByTestId('csq-unmasked-view')).toThrow(); }); it('should render CSQUnmaskedView when isSessionReplayMasked is false', () => { const { getByTestId } = render( Unmasked Content ); expect(getByTestId('csq-unmasked-view')).toBeTruthy(); expect(() => getByTestId('csq-masked-view')).toThrow(); }); it('should default to CSQMaskedView when isSessionReplayMasked is not provided', () => { const { getByTestId } = render( Default Content ); expect(getByTestId('csq-masked-view')).toBeTruthy(); }); }); describe('HeapIgnore integration', () => { it('should wrap content with HeapIgnore component', () => { const { getByTestId } = render( Test ); expect(getByTestId('heap-ignore')).toBeTruthy(); }); it('should pass correct HeapIgnore props when allow* props are specified', () => { const { getByTestId } = render( Test ); const heapIgnore = getByTestId('heap-ignore'); expect(heapIgnore.props.allowInnerHierarchy).toBe(true); expect(heapIgnore.props.allowProps).toBe(true); expect(heapIgnore.props.allowText).toBe(true); expect(heapIgnore.props.allowInteraction).toBe(true); expect(heapIgnore.props.allowAccessibilityLabel).toBe(true); }); it('should default HeapIgnore props to false when not specified', () => { const { getByTestId } = render( Test ); const heapIgnore = getByTestId('heap-ignore'); expect(heapIgnore.props.allowInnerHierarchy).toBe(false); expect(heapIgnore.props.allowProps).toBe(false); expect(heapIgnore.props.allowText).toBe(false); expect(heapIgnore.props.allowInteraction).toBe(false); expect(heapIgnore.props.allowAccessibilityLabel).toBe(false); }); }); describe('Children rendering', () => { it('should render children correctly', () => { const { getByText } = render( Child Content ); expect(getByText('Child Content')).toBeTruthy(); }); it('should render multiple children', () => { const { getByText } = render( First Child Second Child Nested Child ); expect(getByText('First Child')).toBeTruthy(); expect(getByText('Second Child')).toBeTruthy(); expect(getByText('Nested Child')).toBeTruthy(); }); it('should handle null children', () => { const { getByTestId } = render({null}); expect(getByTestId('heap-ignore')).toBeTruthy(); }); }); describe('Component switching', () => { it('should switch from masked to unmasked when isSessionReplayMasked changes', () => { const { getByTestId, rerender, queryByTestId } = render( Test ); expect(getByTestId('csq-masked-view')).toBeTruthy(); expect(queryByTestId('csq-unmasked-view')).toBeNull(); rerender( Test ); expect(getByTestId('csq-unmasked-view')).toBeTruthy(); expect(queryByTestId('csq-masked-view')).toBeNull(); }); }); describe('ignoreTextOnly mode', () => { it('should render children correctly', () => { const { getByText } = render( Test Content ); expect(getByText('Test Content')).toBeTruthy(); }); it('should apply fixed HeapIgnore props for ignoring text', () => { const { getByTestId } = render( Test ); const heapIgnore = getByTestId('heap-ignore'); expect(heapIgnore.props.allowInnerHierarchy).toBe(true); expect(heapIgnore.props.allowProps).toBe(true); expect(heapIgnore.props.allowText).toBe(false); expect(heapIgnore.props.allowInteraction).toBe(true); expect(heapIgnore.props.allowAccessibilityLabel).toBe(false); }); it('should use CSQMaskedView by default', () => { const { getByTestId } = render( Test ); expect(getByTestId('csq-masked-view')).toBeTruthy(); }); it('should respect isSessionReplayMasked prop', () => { const { getByTestId } = render( Test ); expect(getByTestId('csq-unmasked-view')).toBeTruthy(); }); it('should ignore custom allow* props when ignoreTextOnly is true', () => { const { getByTestId } = render( Test ); const heapIgnore = getByTestId('heap-ignore'); // Should use fixed props, not custom ones expect(heapIgnore.props.allowText).toBe(false); expect(heapIgnore.props.allowInnerHierarchy).toBe(true); expect(heapIgnore.props.allowAccessibilityLabel).toBe(false); }); it('should log error when ignoreTextOnly is true and allow* props are set', () => { render( Test ); expect(Logger.error).toHaveBeenCalledWith( 'CSQMask: When ignoreTextOnly is true, allow* props (allowInnerHierarchy, allowProps, allowText, allowInteraction, allowAccessibilityLabel) are ignored and should not be set.' ); }); it('should log error when ignoreTextOnly is true and allowInnerHierarchy is set', () => { render( Test ); expect(Logger.error).toHaveBeenCalled(); }); it('should log error when ignoreTextOnly is true and allowProps is set', () => { render( Test ); expect(Logger.error).toHaveBeenCalled(); }); it('should log error when ignoreTextOnly is true and allowInteraction is set', () => { render( Test ); expect(Logger.error).toHaveBeenCalled(); }); it('should log error when ignoreTextOnly is true and allowAccessibilityLabel is set', () => { render( Test ); expect(Logger.error).toHaveBeenCalled(); }); it('should NOT log error when ignoreTextOnly is true and no allow* props are set', () => { render( Test ); expect(Logger.error).not.toHaveBeenCalled(); }); it('should NOT log error when ignoreTextOnly is false with allow* props', () => { render( Test ); expect(Logger.error).not.toHaveBeenCalled(); }); }); });