Skip to content

Commit c1f3dec

Browse files
committed
test: add comprehensive tests for HeaderBackButton component
1 parent 6ece6e0 commit c1f3dec

File tree

2 files changed

+244
-0
lines changed

2 files changed

+244
-0
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ module.exports = {
4848
},
4949
},
5050
],
51+
ignorePatterns: ['coverage/**/*', 'lib/**/*', 'docs/**/*'],
5152
};
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { fireEvent, render } from '@testing-library/react-native';
3+
import { PixelRatio } from 'react-native';
4+
import { HeaderBackButton, ICON_MARGIN, ICON_SIZE } from './HeaderBackButton';
5+
6+
describe('HeaderBackButton', () => {
7+
beforeEach(() => {
8+
jest.clearAllMocks();
9+
});
10+
11+
describe('Rendering', () => {
12+
it('should render without crashing', () => {
13+
const { getByTestId } = render(<HeaderBackButton testID="back-button" />);
14+
expect(getByTestId('back-button')).toBeTruthy();
15+
});
16+
17+
it('should render with default back arrow image', () => {
18+
const { UNSAFE_getByType } = render(<HeaderBackButton />);
19+
const image = UNSAFE_getByType('Image' as any);
20+
expect(image).toBeTruthy();
21+
expect(image.props.source).toMatchObject({
22+
uri: expect.stringContaining('data:image/png;base64'),
23+
width: PixelRatio.getPixelSizeForLayoutSize(ICON_SIZE),
24+
height: PixelRatio.getPixelSizeForLayoutSize(ICON_SIZE),
25+
});
26+
});
27+
28+
it('should render without label by default', () => {
29+
const { queryByText } = render(<HeaderBackButton />);
30+
expect(queryByText(/./)).toBeNull();
31+
});
32+
33+
it('should render with label when provided', () => {
34+
const label = 'Back';
35+
const { getByText } = render(<HeaderBackButton label={label} />);
36+
expect(getByText(label)).toBeTruthy();
37+
});
38+
39+
it('should render with custom label text', () => {
40+
const customLabel = 'Go Back to Home';
41+
const { getByText } = render(<HeaderBackButton label={customLabel} />);
42+
expect(getByText(customLabel)).toBeTruthy();
43+
});
44+
});
45+
46+
describe('Custom Image Props', () => {
47+
it('should render with custom imageUri', () => {
48+
const customUri = 'https://example.com/custom-back-icon.png';
49+
const { UNSAFE_getByType } = render(
50+
<HeaderBackButton imageUri={customUri} />
51+
);
52+
const image = UNSAFE_getByType('Image' as any);
53+
expect(image.props.source).toMatchObject({
54+
uri: customUri,
55+
});
56+
});
57+
58+
it('should render with custom imageSource', () => {
59+
const customSource = { uri: 'https://example.com/icon.png' };
60+
const { UNSAFE_getByType } = render(
61+
<HeaderBackButton imageSource={customSource} />
62+
);
63+
const image = UNSAFE_getByType('Image' as any);
64+
expect(image.props.source).toEqual(customSource);
65+
});
66+
67+
it('should prioritize imageSource over imageUri when both are provided', () => {
68+
const customUri = 'https://example.com/custom-back-icon.png';
69+
const customSource = { uri: 'https://example.com/icon.png' };
70+
const { UNSAFE_getByType } = render(
71+
<HeaderBackButton imageUri={customUri} imageSource={customSource} />
72+
);
73+
const image = UNSAFE_getByType('Image' as any);
74+
expect(image.props.source).toEqual(customSource);
75+
});
76+
});
77+
78+
describe('Image Properties', () => {
79+
it('should render image with correct properties', () => {
80+
const { UNSAFE_getByType } = render(<HeaderBackButton />);
81+
const image = UNSAFE_getByType('Image' as any);
82+
83+
expect(image.props.resizeMode).toBe('contain');
84+
expect(image.props.fadeDuration).toBe(0);
85+
expect(image.props.height).toBe(ICON_SIZE);
86+
expect(image.props.width).toBe(ICON_SIZE);
87+
expect(image.props.resizeMethod).toBe('scale');
88+
expect(image.props.tintColor).toBeTruthy();
89+
});
90+
91+
it('should apply correct style to image', () => {
92+
const { UNSAFE_getByType } = render(<HeaderBackButton />);
93+
const image = UNSAFE_getByType('Image' as any);
94+
95+
expect(image.props.style).toMatchObject({
96+
height: ICON_SIZE,
97+
margin: ICON_MARGIN,
98+
width: ICON_SIZE,
99+
});
100+
});
101+
});
102+
103+
describe('Touch Interaction', () => {
104+
it('should call onPress when button is pressed', () => {
105+
const onPressMock = jest.fn();
106+
const { getByTestId } = render(
107+
<HeaderBackButton testID="back-button" onPress={onPressMock} />
108+
);
109+
110+
fireEvent.press(getByTestId('back-button'));
111+
expect(onPressMock).toHaveBeenCalledTimes(1);
112+
});
113+
114+
it('should call onPressIn when touch starts', () => {
115+
const onPressInMock = jest.fn();
116+
const { getByTestId } = render(
117+
<HeaderBackButton testID="back-button" onPressIn={onPressInMock} />
118+
);
119+
120+
fireEvent(getByTestId('back-button'), 'pressIn');
121+
expect(onPressInMock).toHaveBeenCalledTimes(1);
122+
});
123+
124+
it('should call onPressOut when touch ends', () => {
125+
const onPressOutMock = jest.fn();
126+
const { getByTestId } = render(
127+
<HeaderBackButton testID="back-button" onPressOut={onPressOutMock} />
128+
);
129+
130+
fireEvent(getByTestId('back-button'), 'pressOut');
131+
expect(onPressOutMock).toHaveBeenCalledTimes(1);
132+
});
133+
134+
it('should not trigger onPress when disabled', () => {
135+
const onPressMock = jest.fn();
136+
const { getByTestId } = render(
137+
<HeaderBackButton
138+
testID="back-button"
139+
onPress={onPressMock}
140+
disabled={true}
141+
/>
142+
);
143+
144+
fireEvent.press(getByTestId('back-button'));
145+
expect(onPressMock).not.toHaveBeenCalled();
146+
});
147+
});
148+
149+
describe('Platform-specific behavior', () => {
150+
it('should export correct icon size constant', () => {
151+
// ICON_SIZE is evaluated at module load time based on Platform.OS
152+
expect(ICON_SIZE).toBeDefined();
153+
expect([21, 24]).toContain(ICON_SIZE);
154+
});
155+
156+
it('should export correct icon margin constant', () => {
157+
// ICON_MARGIN is evaluated at module load time based on Platform.OS
158+
expect(ICON_MARGIN).toBeDefined();
159+
expect([3, 8]).toContain(ICON_MARGIN);
160+
});
161+
162+
it('should use consistent icon size in image props', () => {
163+
const { UNSAFE_getByType } = render(<HeaderBackButton />);
164+
const image = UNSAFE_getByType('Image' as any);
165+
166+
expect(image.props.height).toBe(ICON_SIZE);
167+
expect(image.props.width).toBe(ICON_SIZE);
168+
});
169+
});
170+
171+
describe('Accessibility', () => {
172+
it('should accept accessibility props', () => {
173+
const { getByTestId } = render(
174+
<HeaderBackButton
175+
testID="back-button"
176+
accessible={true}
177+
accessibilityLabel="Navigate back"
178+
accessibilityHint="Returns to previous screen"
179+
/>
180+
);
181+
182+
const button = getByTestId('back-button');
183+
expect(button.props.accessible).toBe(true);
184+
expect(button.props.accessibilityLabel).toBe('Navigate back');
185+
expect(button.props.accessibilityHint).toBe('Returns to previous screen');
186+
});
187+
});
188+
189+
describe('Component Structure', () => {
190+
it('should render View with correct flex direction', () => {
191+
const { UNSAFE_getAllByType } = render(<HeaderBackButton label="Back" />);
192+
const views = UNSAFE_getAllByType('View' as any);
193+
194+
// Find the view with returnButton style
195+
const returnButtonView = views.find(
196+
(view) =>
197+
view.props.style?.flexDirection === 'row' &&
198+
view.props.style?.alignItems === 'center'
199+
);
200+
expect(returnButtonView).toBeTruthy();
201+
});
202+
203+
it('should render label text with correct style when provided', () => {
204+
const { getByText } = render(<HeaderBackButton label="Back" />);
205+
const labelElement = getByText('Back');
206+
207+
expect(labelElement.props.style).toMatchObject({
208+
fontSize: 20,
209+
});
210+
});
211+
});
212+
213+
describe('Edge Cases', () => {
214+
it('should handle empty string label', () => {
215+
const { queryByText } = render(<HeaderBackButton label="" />);
216+
// Empty string should not render text
217+
expect(queryByText('')).toBeNull();
218+
});
219+
220+
it('should handle multiple props correctly', () => {
221+
const onPressMock = jest.fn();
222+
const label = 'Custom Back';
223+
const customUri = 'https://example.com/icon.png';
224+
225+
const { getByText, getByTestId } = render(
226+
<HeaderBackButton
227+
testID="back-button"
228+
label={label}
229+
imageUri={customUri}
230+
onPress={onPressMock}
231+
accessible={true}
232+
accessibilityLabel="Go back"
233+
/>
234+
);
235+
236+
expect(getByText(label)).toBeTruthy();
237+
expect(getByTestId('back-button').props.accessible).toBe(true);
238+
239+
fireEvent.press(getByTestId('back-button'));
240+
expect(onPressMock).toHaveBeenCalledTimes(1);
241+
});
242+
});
243+
});

0 commit comments

Comments
 (0)