import { render } from '@testing-library/react-native';
import React from 'react';
import { Text, View } from 'react-native';
import { withCSQMask } from '../../../replay/csqMasking/withCSQMask';
// Mock dependencies
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('withCSQMask Higher-Order Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});
const TestComponent: React.FC<{
testProp?: string;
children?: React.ReactNode;
}> = ({ testProp, children }) => (
{testProp || 'Default'}
{children}
);
TestComponent.displayName = 'TestComponent';
describe('Basic HoC functionality', () => {
it('should wrap component with CSQMask functionality', () => {
const WrappedComponent = withCSQMask(TestComponent);
const { getByTestId } = render();
expect(getByTestId('heap-ignore')).toBeTruthy();
expect(getByTestId('test-component')).toBeTruthy();
});
it('should render masked by default', () => {
const WrappedComponent = withCSQMask(TestComponent);
const { getByTestId } = render();
expect(getByTestId('csq-masked-view')).toBeTruthy();
});
it('should render unmasked when isSessionReplayMasked is false', () => {
const WrappedComponent = withCSQMask(TestComponent, {
isSessionReplayMasked: false,
});
const { getByTestId } = render();
expect(getByTestId('csq-unmasked-view')).toBeTruthy();
});
it('should render masked when isSessionReplayMasked is true', () => {
const WrappedComponent = withCSQMask(TestComponent, {
isSessionReplayMasked: true,
});
const { getByTestId } = render();
expect(getByTestId('csq-masked-view')).toBeTruthy();
});
});
describe('Props forwarding', () => {
it('should forward props to wrapped component', () => {
const WrappedComponent = withCSQMask(TestComponent);
const { getByText } = render(
);
expect(getByText('Custom Value')).toBeTruthy();
});
it('should forward children to wrapped component', () => {
const WrappedComponent = withCSQMask(TestComponent);
const { getByText } = render(
Child Content
);
expect(getByText('Child Content')).toBeTruthy();
});
});
describe('HeapIgnore props configuration', () => {
it('should apply custom allow* props', () => {
const WrappedComponent = withCSQMask(TestComponent, {
allowText: true,
allowInnerHierarchy: true,
allowProps: false,
allowInteraction: true,
allowAccessibilityLabel: false,
});
const { getByTestId } = render();
const heapIgnore = getByTestId('heap-ignore');
expect(heapIgnore.props.allowText).toBe(true);
expect(heapIgnore.props.allowInnerHierarchy).toBe(true);
expect(heapIgnore.props.allowProps).toBe(false);
expect(heapIgnore.props.allowInteraction).toBe(true);
expect(heapIgnore.props.allowAccessibilityLabel).toBe(false);
});
});
describe('Display name', () => {
it('should generate correct display name from component name', () => {
const WrappedComponent = withCSQMask(TestComponent);
expect(WrappedComponent.displayName).toBe('withCSQMask(TestComponent)');
});
it('should use default name when component has no display name', () => {
const AnonymousComponent = () => ;
const WrappedComponent = withCSQMask(AnonymousComponent);
expect(WrappedComponent.displayName).toBe(
'withCSQMask(AnonymousComponent)'
);
});
it('should use component displayName if available', () => {
const ComponentWithDisplayName: React.FC = () => ;
ComponentWithDisplayName.displayName = 'MyCustomComponent';
const WrappedComponent = withCSQMask(ComponentWithDisplayName);
expect(WrappedComponent.displayName).toBe(
'withCSQMask(MyCustomComponent)'
);
});
});
describe('Ref forwarding', () => {
it('should forward ref to wrapped component', () => {
const RefComponent = React.forwardRef(
(props, ref) => (
{props.testProp}
)
);
RefComponent.displayName = 'RefComponent';
const WrappedComponent = withCSQMask(RefComponent);
const ref = React.createRef();
render();
expect(ref.current).toBeTruthy();
});
});
describe('Nested HoC', () => {
it('should handle multiple nested withCSQMask calls', () => {
const WrappedOnce = withCSQMask(TestComponent, {
isSessionReplayMasked: true,
});
const WrappedTwice = withCSQMask(WrappedOnce, {
isSessionReplayMasked: false,
});
const { getAllByTestId } = render();
// Should have two HeapIgnore components (nested)
const heapIgnores = getAllByTestId('heap-ignore');
expect(heapIgnores.length).toBeGreaterThanOrEqual(1);
});
});
describe('Component hierarchy', () => {
it('should maintain correct hierarchy: HeapIgnore > CSQComponent > WrappedComponent', () => {
const WrappedComponent = withCSQMask(TestComponent);
const { getByTestId } = render();
const heapIgnore = getByTestId('heap-ignore');
const csqView = getByTestId('csq-masked-view');
const testComponent = getByTestId('test-component');
// Verify hierarchy exists (all components rendered)
expect(heapIgnore).toBeTruthy();
expect(csqView).toBeTruthy();
expect(testComponent).toBeTruthy();
});
});
describe('Edge cases', () => {
it('should handle component with no children', () => {
const NoChildrenComponent: React.FC = () => ;
const WrappedComponent = withCSQMask(NoChildrenComponent);
const { getByTestId } = render();
expect(getByTestId('no-children')).toBeTruthy();
});
it('should handle component with complex children', () => {
const ComplexComponent: React.FC<{ children?: React.ReactNode }> = ({
children,
}) => (
Header
{children}
Footer
);
const WrappedComponent = withCSQMask(ComplexComponent);
const { getByText } = render(
Middle Content
);
expect(getByText('Header')).toBeTruthy();
expect(getByText('Middle Content')).toBeTruthy();
expect(getByText('Footer')).toBeTruthy();
});
});
});