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();
});
});
});