import React from 'react' import { render, fireEvent } from '@testing-library/react-native' import Text from './Text' describe('Text Component', () => { describe('Basic Rendering', () => { it('should render basic text with default dark theme color', () => { const { getByText } = render(Hello World) const textElement = getByText('Hello World') expect(textElement).toBeTruthy() expect(textElement.props.style).toEqual( expect.objectContaining({ color: '#F5F5F5', }) ) }) it('should render children correctly', () => { const { getByText } = render(Test Content) expect(getByText('Test Content')).toBeTruthy() }) it('should render nested text elements', () => { const { getByText } = render( Parent Child ) // In React Native, nested Text components are flattened into a single text node expect(getByText('Parent Child')).toBeTruthy() expect(getByText('Child')).toBeTruthy() }) it('should have correct displayName', () => { expect(Text.displayName).toBe('Text') }) }) describe('Style Merging', () => { it('should merge custom object style with default style', () => { const customStyle = { fontSize: 16, fontWeight: '600' as const } const { getByText } = render(Styled Text) const textElement = getByText('Styled Text') expect(textElement.props.style).toEqual( expect.objectContaining({ color: '#F5F5F5', fontSize: 16, fontWeight: '600', }) ) }) it('should merge custom array style with default style', () => { const styleArray = [{ fontSize: 14 }, { fontWeight: '500' as const }] const { getByText } = render(Array Style) const textElement = getByText('Array Style') // Array style should be merged as [defaultStyle, ...styleArray] expect(textElement.props.style).toContainEqual({ color: '#F5F5F5' }) expect(textElement.props.style).toContainEqual({ fontSize: 14 }) expect(textElement.props.style).toContainEqual({ fontWeight: '500' }) }) it('should allow overriding default color', () => { const { getByText } = render( Red Text ) const textElement = getByText('Red Text') expect(textElement.props.style.color).toBe('#FF0000') }) it('should pass className through correctly', () => { const { getByText } = render(Class Text) const textElement = getByText('Class Text') expect(textElement.props.className).toBe('text-primary') }) }) describe('onClick Handler', () => { it('should create TouchableOpacity wrapper when onClick is provided', () => { const mockOnClick = jest.fn() const { getByText } = render(Clickable Text) const textElement = getByText('Clickable Text') // Parent should be TouchableOpacity (or TouchableWithoutFeedback depending on implementation) expect(textElement.parent).toBeTruthy() }) it('should call onClick handler when pressed', () => { const mockOnClick = jest.fn() const { getByText } = render(Click Me) const textElement = getByText('Click Me') fireEvent.press(textElement) expect(mockOnClick).toHaveBeenCalledTimes(1) }) it('should pass touchProps to TouchableOpacity', () => { const mockOnClick = jest.fn() const touchProps = { testID: 'touchable-test', activeOpacity: 0.5, } const { getByTestId } = render( Touchable Text ) expect(getByTestId('touchable-test')).toBeTruthy() }) it('should not interfere with normal text rendering when onClick is not provided', () => { const { getByText } = render(Normal Text) const textElement = getByText('Normal Text') expect(textElement).toBeTruthy() // Should be plain Text element, not wrapped expect(textElement.parent.type).not.toBe('TouchableOpacity') }) }) describe('Animated Text', () => { it('should render Animated.Text when animated prop is true', () => { const { getByText } = render(Animated Text) const textElement = getByText('Animated Text') expect(textElement).toBeTruthy() // Animated.Text should still render the content }) it('should pass style to Animated.Text component', () => { const customStyle = { fontSize: 20 } const { getByText } = render( Animated Styled ) const textElement = getByText('Animated Styled') expect(textElement.props.style).toEqual( expect.objectContaining({ color: '#F5F5F5', fontSize: 20, }) ) }) }) describe('Props Forwarding', () => { it('should forward standard RN Text props', () => { const { getByText } = render( Long text that should be truncated ) const textElement = getByText(/Long text/) expect(textElement.props.numberOfLines).toBe(2) expect(textElement.props.ellipsizeMode).toBe('tail') }) it('should forward accessibility props', () => { const { getByText } = render( Content ) const textElement = getByText('Content') expect(textElement.props.accessibilityLabel).toBe('Label text') expect(textElement.props.accessible).toBe(true) }) it('should forward testID prop', () => { const { getByTestId } = render(Test Content) expect(getByTestId('text-test-id')).toBeTruthy() }) it('should forward onPress through onClick', () => { const mockOnPress = jest.fn() const { getByText } = render(Press Me) const textElement = getByText('Press Me') fireEvent.press(textElement) expect(mockOnPress).toHaveBeenCalled() }) }) describe('Edge Cases', () => { it('should handle empty string children', () => { const { getByText } = render() // Empty text should still render const textElement = getByText('') expect(textElement).toBeTruthy() }) it('should handle null children gracefully', () => { // null should not cause crash, component renders but with no text content const result = render({null}) expect(result).toBeTruthy() }) it('should handle style as undefined', () => { const { getByText } = render(No Style) const textElement = getByText('No Style') expect(textElement.props.style).toEqual({ color: '#F5F5F5' }) }) it('should handle className as undefined', () => { const { getByText } = render(No Class) const textElement = getByText('No Class') expect(textElement.props.className).toBe('') }) it('should handle complex style objects', () => { const complexStyle = { fontSize: 16, fontWeight: 'bold' as const, lineHeight: 24, letterSpacing: 0.5, textTransform: 'uppercase' as const, } const { getByText } = render(Complex Style) const textElement = getByText('Complex Style') expect(textElement.props.style).toEqual( expect.objectContaining({ color: '#F5F5F5', fontSize: 16, fontWeight: 'bold', lineHeight: 24, letterSpacing: 0.5, textTransform: 'uppercase', }) ) }) it('should prioritize onClick over animated when both are provided', () => { const mockOnClick = jest.fn() const { getByText } = render( Both Props ) const textElement = getByText('Both Props') // animated prop takes precedence in the conditional rendering // onClick is ignored when animated is true based on the implementation }) }) describe('Integration with Other Components', () => { it('should work within a View component', () => { const { getByText } = render( Nested Text ) // In React Native, nested Text components are flattened into a single text node expect(getByText('Nested Text')).toBeTruthy() expect(getByText('Nested')).toBeTruthy() }) it('should handle multiple onClick handlers correctly', () => { const onClick1 = jest.fn() const onClick2 = jest.fn() const { getByText } = render( <> First Second ) fireEvent.press(getByText('First')) fireEvent.press(getByText('Second')) expect(onClick1).toHaveBeenCalledTimes(1) expect(onClick2).toHaveBeenCalledTimes(1) }) }) })