Skip to content

Commit 61c9a99

Browse files
authored
feat(test): migrate to testing-library (#306)
1 parent a7c3837 commit 61c9a99

File tree

7 files changed

+247
-250
lines changed

7 files changed

+247
-250
lines changed
Lines changed: 57 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import React from 'react';
2-
import renderer from 'react-test-renderer';
3-
import { FlatList, Text, TextInput, View } from 'react-native';
1+
import React, { type ReactElement } from 'react';
2+
import { render, screen, within } from '@testing-library/react-native';
3+
import { FlatList, TextInput, type FlatListProps } from 'react-native';
4+
45
import Autocomplete from '../index';
56

67
const ITEMS = [
@@ -12,111 +13,85 @@ const ITEMS = [
1213
'Revenge of the Sith',
1314
] as const;
1415

16+
const suggestionListTestId = 'suggestionListTestId';
17+
function TestSuggestionList<T>(props: FlatListProps<T>): ReactElement {
18+
return <FlatList {...props} testID={suggestionListTestId} />;
19+
}
20+
1521
describe('<AutocompleteInput />', () => {
1622
it('should hide suggestion list on initial render', () => {
17-
const r = renderer.create(<Autocomplete data={[]} />);
18-
const autocomplete = r.root;
19-
20-
expect(autocomplete.findAllByType(FlatList)).toHaveLength(0);
23+
render(<Autocomplete data={[]} renderResultList={TestSuggestionList} />);
24+
const suggestionList = screen.queryByTestId(suggestionListTestId);
25+
expect(suggestionList).not.toBeOnTheScreen();
2126
});
2227

23-
it('should show suggestion list when data gets updated with length > 0', () => {
24-
const testRenderer = renderer.create(<Autocomplete data={[]} />);
25-
const autocomplete = testRenderer.root;
28+
it('should show suggestion list with suggestions when data gets updated with length > 0', () => {
29+
const { rerender } = render(<Autocomplete data={[]} renderResultList={TestSuggestionList} />);
2630

27-
expect(autocomplete.findAllByType(FlatList)).toHaveLength(0);
31+
const hiddenSuggestionList = screen.queryByTestId(suggestionListTestId);
32+
expect(hiddenSuggestionList).not.toBeOnTheScreen();
2833

29-
testRenderer.update(<Autocomplete data={ITEMS} />);
34+
rerender(<Autocomplete data={ITEMS} renderResultList={TestSuggestionList} />);
3035

31-
const list = autocomplete.findByType(FlatList);
32-
expect(list.props.data).toEqual(ITEMS);
36+
const suggestionList = screen.getByTestId(suggestionListTestId);
37+
expect(suggestionList).toBeOnTheScreen();
3338

34-
const texts = list.findAllByType(Text);
35-
expect(texts).toHaveLength(ITEMS.length);
39+
const suggestions = within(suggestionList).getAllByRole('text');
40+
suggestions.forEach((suggestion, index) => {
41+
expect(suggestion).toHaveTextContent(ITEMS[index]);
42+
});
3643
});
3744

38-
it('should hide suggestion list when data gets updates with length < 1', () => {
39-
const props = { data: ITEMS };
40-
const testRenderer = renderer.create(<Autocomplete {...props} />);
41-
const autocomplete = testRenderer.root;
45+
it('should apply default render list function', () => {
46+
render(<Autocomplete data={ITEMS} renderResultList={undefined} />);
47+
const suggestions = screen.getAllByRole('text');
48+
suggestions.forEach((suggestion, index) => {
49+
expect(suggestion).toHaveTextContent(ITEMS[index]);
50+
});
51+
});
52+
53+
it('should hide suggestion list when data gets updated with length < 1', () => {
54+
const { rerender } = render(
55+
<Autocomplete data={ITEMS} renderResultList={TestSuggestionList} />,
56+
);
4257

43-
expect(autocomplete.findAllByType(FlatList)).toHaveLength(1);
44-
testRenderer.update(<Autocomplete data={[]} />);
58+
const suggestionList = screen.getByTestId(suggestionListTestId);
59+
expect(suggestionList).toBeOnTheScreen();
4560

46-
expect(autocomplete.findAllByType(FlatList)).toHaveLength(0);
61+
rerender(<Autocomplete data={[]} renderResultList={TestSuggestionList} />);
62+
63+
const hiddenSuggestionList = screen.queryByTestId(suggestionListTestId);
64+
expect(hiddenSuggestionList).not.toBeOnTheScreen();
4765
});
4866

4967
it('should render custom text input', () => {
50-
const text = 'Custom Text Input';
51-
const testRenderer = renderer.create(
68+
const customTextInputTestId = 'customTextInput';
69+
render(
5270
<Autocomplete
5371
data={[]}
54-
foo="bar"
55-
renderTextInput={(props) => <Text {...props}>{text}</Text>}
72+
renderTextInput={(props) => <TextInput {...props} testID={customTextInputTestId} />}
5673
/>,
5774
);
5875

59-
const autocomplete = testRenderer.root;
60-
const customTextInput = autocomplete.findByType(Text);
61-
62-
expect((customTextInput.children[0] as { children: unknown[] }).children).toEqual([text]);
63-
expect(autocomplete.findAllByType(TextInput)).toHaveLength(0);
76+
const textInput = screen.getByTestId(customTextInputTestId);
77+
expect(textInput).toBeOnTheScreen();
6478
});
6579

6680
it('should render default <TextInput /> if no custom one is supplied', () => {
67-
const props = { foo: 'bar' };
68-
const testRenderer = renderer.create(<Autocomplete data={[]} {...props} />);
69-
const autocomplete = testRenderer.root;
70-
const textInput = autocomplete.findByType(TextInput);
71-
72-
expect(textInput.props).toEqual(expect.objectContaining(props));
73-
});
74-
75-
it('should render default <FlatList /> if no custom one is supplied', () => {
76-
const testRenderer = renderer.create(<Autocomplete data={ITEMS} />);
77-
const autocomplete = testRenderer.root;
78-
const list = autocomplete.findByType(FlatList);
79-
80-
expect(list.props.data).toEqual(ITEMS);
81-
});
82-
83-
it('should only pass props in flatListProps to <FlatList />', () => {
84-
// Using keyExtractor isn't important for the test, but prevents a warning
85-
const keyExtractor = (_, index) => `key-${index}`;
86-
const flatListProps = { foo: 'bar', keyExtractor };
87-
const otherProps = { baz: 'qux' };
88-
const testRenderer = renderer.create(
89-
<Autocomplete data={ITEMS} flatListProps={flatListProps} {...otherProps} />,
90-
);
91-
const autocomplete = testRenderer.root;
92-
const list = autocomplete.findByType(FlatList);
93-
94-
expect(list.props).toEqual(expect.objectContaining(flatListProps));
95-
expect(list.props).toEqual(expect.not.objectContaining(otherProps));
96-
});
97-
98-
it('should render a custom result list', () => {
99-
const testRenderer = renderer.create(
100-
<Autocomplete
101-
data={ITEMS}
102-
renderResultList={({ data, style }) => (
103-
<View style={style}>{data?.map((item, index) => <Text key={index}>{item}</Text>)}</View>
104-
)}
105-
/>,
106-
);
107-
108-
const autocomplete = testRenderer.root;
109-
expect(autocomplete.findAllByType(FlatList)).toHaveLength(0);
81+
render(<Autocomplete data={[]} placeholder="Enter search" />);
11082

111-
const texts = autocomplete.findAllByType(Text);
112-
expect(texts).toHaveLength(ITEMS.length);
83+
const input = screen.getByPlaceholderText('Enter search');
84+
expect(input).toBeOnTheScreen();
11385
});
11486

115-
it('should forward the ref to the input', () => {
116-
const inputRef = React.createRef();
87+
it('should forward the ref to the text input', async () => {
88+
let ref: React.RefObject<TextInput>;
89+
function TestForwardRefComponent() {
90+
ref = React.useRef<TextInput>(null);
91+
return <Autocomplete data={ITEMS} placeholder="TestText" ref={ref} />;
92+
}
11793

118-
renderer.create(<Autocomplete data={ITEMS} ref={inputRef} />);
119-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
120-
expect((inputRef.current as any)._reactInternals.elementType.displayName).toBe('TextInput');
94+
render(<TestForwardRefComponent />);
95+
expect(ref!.current?.constructor.name).toBe('TextInput');
12196
});
12297
});

index.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function DefaultTextInput(props: TextInputProps): React.ReactElement {
3434
return <TextInput {...props} />;
3535
}
3636

37-
function AutocompleteInputComponent<Item, Ref>(
37+
export const AutocompleteInput = React.forwardRef(function AutocompleteInputComponent<Item, Ref>(
3838
props: AutocompleteInputProps<Item>,
3939
ref: React.ForwardedRef<Ref>,
4040
): React.ReactElement {
@@ -61,7 +61,7 @@ function AutocompleteInputComponent<Item, Ref>(
6161
ref,
6262
};
6363

64-
return renderFunction?.(textProps);
64+
return renderFunction(textProps);
6565
}
6666

6767
const {
@@ -90,7 +90,7 @@ function AutocompleteInputComponent<Item, Ref>(
9090
)}
9191
</View>
9292
);
93-
}
93+
});
9494

9595
const border = {
9696
borderColor: '#b9b9b9',
@@ -150,13 +150,9 @@ const styles = StyleSheet.create({
150150
}),
151151
});
152152

153-
export const AutocompleteInput = React.forwardRef(
154-
AutocompleteInputComponent,
155-
) as typeof AutocompleteInputComponent;
156-
157153
export default AutocompleteInput;
158154

159-
(AutocompleteInput as React.FC).propTypes = {
155+
AutocompleteInput.propTypes = {
160156
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
161157
// @ts-ignore
162158
...TextInput.propTypes,

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export default {
22
preset: 'react-native',
3+
setupFilesAfterEnv: ['@testing-library/react-native/extend-expect'],
34
verbose: true,
45
};

0 commit comments

Comments
 (0)