Skip to content

Commit 4c0f1f5

Browse files
committed
improve test coverage
1 parent 78150f6 commit 4c0f1f5

File tree

4 files changed

+232
-0
lines changed

4 files changed

+232
-0
lines changed

src/__tests__/fire-event-async.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,3 +641,21 @@ describe('React.Suspense integration', () => {
641641
expect(screen.getByText('Loading data...')).toBeTruthy();
642642
});
643643
});
644+
645+
test('should handle unmounted elements gracefully in async mode', async () => {
646+
const onPress = jest.fn();
647+
const result = render(
648+
<TouchableOpacity onPress={onPress}>
649+
<Text>Test</Text>
650+
</TouchableOpacity>,
651+
);
652+
653+
const element = screen.getByText('Test');
654+
655+
// Unmount the component
656+
result.unmount();
657+
658+
// Firing async event on unmounted element should not crash
659+
await fireEventAsync.press(element);
660+
expect(onPress).not.toHaveBeenCalled();
661+
});

src/__tests__/fire-event.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,3 +553,47 @@ describe('native events', () => {
553553
expect(onMomentumScrollEndSpy).toHaveBeenCalled();
554554
});
555555
});
556+
557+
test('should handle unmounted elements gracefully', () => {
558+
const onPress = jest.fn();
559+
const result = render(
560+
<TouchableOpacity onPress={onPress}>
561+
<Text>Test</Text>
562+
</TouchableOpacity>,
563+
);
564+
565+
const element = screen.getByText('Test');
566+
567+
// Unmount the component
568+
result.unmount();
569+
570+
// Firing event on unmounted element should not crash
571+
fireEvent.press(element);
572+
expect(onPress).not.toHaveBeenCalled();
573+
});
574+
575+
test('should handle invalid scroll event data gracefully', () => {
576+
const onScrollSpy = jest.fn();
577+
render(<ScrollView testID="scroll-view" onScroll={onScrollSpy} />);
578+
579+
const scrollView = screen.getByTestId('scroll-view');
580+
581+
// Test with malformed event data that would cause an error in tryGetContentOffset
582+
fireEvent.scroll(scrollView, { malformed: 'data' });
583+
expect(onScrollSpy).toHaveBeenCalled();
584+
});
585+
586+
test('should handle scroll event with invalid contentOffset', () => {
587+
const onScrollSpy = jest.fn();
588+
render(<ScrollView testID="scroll-view" onScroll={onScrollSpy} />);
589+
590+
const scrollView = screen.getByTestId('scroll-view');
591+
592+
// Test with event data that has invalid contentOffset structure
593+
fireEvent.scroll(scrollView, {
594+
nativeEvent: {
595+
contentOffset: { x: 'invalid', y: null },
596+
},
597+
});
598+
expect(onScrollSpy).toHaveBeenCalled();
599+
});

src/__tests__/render-async.test.tsx

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import * as React from 'react';
2+
import { Text, View } from 'react-native';
3+
4+
import { renderAsync, screen } from '..';
5+
6+
class Banana extends React.Component<any, { fresh: boolean }> {
7+
state = {
8+
fresh: false,
9+
};
10+
11+
componentDidUpdate() {
12+
if (this.props.onUpdate) {
13+
this.props.onUpdate();
14+
}
15+
}
16+
17+
componentWillUnmount() {
18+
if (this.props.onUnmount) {
19+
this.props.onUnmount();
20+
}
21+
}
22+
23+
changeFresh = () => {
24+
this.setState((state) => ({
25+
fresh: !state.fresh,
26+
}));
27+
};
28+
29+
render() {
30+
return (
31+
<View>
32+
<Text>Is the banana fresh?</Text>
33+
<Text testID="bananaFresh">{this.state.fresh ? 'fresh' : 'not fresh'}</Text>
34+
</View>
35+
);
36+
}
37+
}
38+
39+
test('renderAsync renders component asynchronously', async () => {
40+
await renderAsync(<View testID="test" />);
41+
expect(screen.getByTestId('test')).toBeOnTheScreen();
42+
});
43+
44+
test('renderAsync with wrapper option', async () => {
45+
const WrapperComponent = ({ children }: { children: React.ReactNode }) => (
46+
<View testID="wrapper">{children}</View>
47+
);
48+
49+
await renderAsync(<View testID="inner" />, {
50+
wrapper: WrapperComponent,
51+
});
52+
53+
expect(screen.getByTestId('wrapper')).toBeTruthy();
54+
expect(screen.getByTestId('inner')).toBeTruthy();
55+
});
56+
57+
test('renderAsync supports concurrent rendering option', async () => {
58+
await renderAsync(<View testID="test" />, { concurrentRoot: true });
59+
expect(screen.root).toBeOnTheScreen();
60+
});
61+
62+
test('renderAsync supports legacy rendering option', async () => {
63+
await renderAsync(<View testID="test" />, { concurrentRoot: false });
64+
expect(screen.root).toBeOnTheScreen();
65+
});
66+
67+
test('update function updates component synchronously', async () => {
68+
const fn = jest.fn();
69+
const result = await renderAsync(<Banana onUpdate={fn} />);
70+
71+
result.update(<Banana onUpdate={fn} />);
72+
73+
expect(fn).toHaveBeenCalledTimes(1);
74+
});
75+
76+
test('updateAsync function updates component asynchronously', async () => {
77+
const fn = jest.fn();
78+
const result = await renderAsync(<Banana onUpdate={fn} />);
79+
80+
await result.updateAsync(<Banana onUpdate={fn} />);
81+
82+
expect(fn).toHaveBeenCalledTimes(1);
83+
});
84+
85+
test('rerender is an alias for update', async () => {
86+
const fn = jest.fn();
87+
const result = await renderAsync(<Banana onUpdate={fn} />);
88+
89+
result.rerender(<Banana onUpdate={fn} />);
90+
91+
expect(fn).toHaveBeenCalledTimes(1);
92+
});
93+
94+
test('rerenderAsync is an alias for updateAsync', async () => {
95+
const fn = jest.fn();
96+
const result = await renderAsync(<Banana onUpdate={fn} />);
97+
98+
await result.rerenderAsync(<Banana onUpdate={fn} />);
99+
100+
expect(fn).toHaveBeenCalledTimes(1);
101+
});
102+
103+
test('unmount function unmounts component synchronously', async () => {
104+
const fn = jest.fn();
105+
const result = await renderAsync(<Banana onUnmount={fn} />);
106+
107+
result.unmount();
108+
109+
expect(fn).toHaveBeenCalled();
110+
});
111+
112+
test('unmountAsync function unmounts component asynchronously', async () => {
113+
const fn = jest.fn();
114+
const result = await renderAsync(<Banana onUnmount={fn} />);
115+
116+
await result.unmountAsync();
117+
118+
expect(fn).toHaveBeenCalled();
119+
});
120+
121+
test('container property displays deprecation message', async () => {
122+
await renderAsync(<View testID="inner" />);
123+
124+
expect(() => (screen as any).container).toThrowErrorMatchingInlineSnapshot(`
125+
"'container' property has been renamed to 'UNSAFE_root'.
126+
127+
Consider using 'root' property which returns root host element."
128+
`);
129+
});
130+
131+
test('debug function handles null JSON', async () => {
132+
const result = await renderAsync(<View testID="test" />);
133+
134+
// Mock toJSON to return null to test the debug edge case
135+
const originalToJSON = result.toJSON;
136+
(result as any).toJSON = jest.fn().mockReturnValue(null);
137+
138+
// This should not throw and handle null JSON gracefully
139+
expect(() => result.debug()).not.toThrow();
140+
141+
// Restore original toJSON
142+
(result as any).toJSON = originalToJSON;
143+
});

src/__tests__/render.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,30 @@ test('supports concurrent rendering', () => {
243243
render(<View testID="test" />, { concurrentRoot: true });
244244
expect(screen.root).toBeOnTheScreen();
245245
});
246+
247+
test('updateAsync updates the component asynchronously', async () => {
248+
const fn = jest.fn();
249+
const result = render(<Banana onUpdate={fn} />);
250+
251+
await result.updateAsync(<Banana onUpdate={fn} />);
252+
253+
expect(fn).toHaveBeenCalledTimes(1);
254+
});
255+
256+
test('rerenderAsync is an alias for updateAsync', async () => {
257+
const fn = jest.fn();
258+
const result = render(<Banana onUpdate={fn} />);
259+
260+
await result.rerenderAsync(<Banana onUpdate={fn} />);
261+
262+
expect(fn).toHaveBeenCalledTimes(1);
263+
});
264+
265+
test('unmountAsync unmounts the component asynchronously', async () => {
266+
const fn = jest.fn();
267+
const result = render(<Banana onUnmount={fn} />);
268+
269+
await result.unmountAsync();
270+
271+
expect(fn).toHaveBeenCalled();
272+
});

0 commit comments

Comments
 (0)