Skip to content
This repository was archived by the owner on Jul 30, 2020. It is now read-only.

Commit ad41bb6

Browse files
authored
feat: Support React Native 0.63.x (#136)
* update react-navigation test example to use v4 * support react-native 0.63.0 * fix RefreshControl mock * lint * add back disabled touchable test BREAKING CHANGE: removed deprecated methods
1 parent 6de7d48 commit ad41bb6

File tree

13 files changed

+247
-36
lines changed

13 files changed

+247
-36
lines changed

examples/__tests__/react-navigation.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import '@testing-library/jest-native/extend-expect';
22
import React from 'react';
33
import { Button, Text, View } from 'react-native';
4-
import { createStackNavigator, createAppContainer, withNavigation } from 'react-navigation';
4+
import { createStackNavigator } from 'react-navigation-stack';
5+
import { createAppContainer, withNavigation } from 'react-navigation';
56

6-
import { render, fireEvent } from '../../src';
7+
import { render, fireEvent, cleanup } from '../../src';
78

89
jest
910
.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper')
@@ -56,6 +57,8 @@ function renderWithNavigation({ screens = {}, navigatorConfig = {} } = {}) {
5657
return { ...render(<App detached />), navigationTestRenderer: App };
5758
}
5859

60+
afterEach(cleanup);
61+
5962
test('full app rendering/navigating', async () => {
6063
const { findByText, getByTestId, getByTitle } = renderWithNavigation();
6164

package.json

+10-8
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,30 @@
4444
},
4545
"devDependencies": {
4646
"@babel/cli": "7.2.3",
47-
"@babel/core": "7.4.0",
47+
"@babel/core": "7.8.4",
4848
"@babel/runtime": "7.4.0",
49-
"@testing-library/jest-native": "^3.0.0",
49+
"@testing-library/jest-native": "^3.1.0",
5050
"commitizen": "^3.0.7",
5151
"cz-conventional-changelog": "^2.1.0",
5252
"husky": "^1.3.1",
5353
"intl": "^1.2.5",
54-
"jest": "24.5.0",
54+
"jest": "25.1.0",
5555
"jest-fetch-mock": "^2.1.1",
5656
"jest-in-case": "^1.0.2",
57-
"metro-react-native-babel-preset": "^0.52.0",
57+
"metro-react-native-babel-preset": "^0.59.0",
5858
"prettier": "^1.16.4",
5959
"pretty-quick": "^1.10.0",
60-
"react": "16.9.0",
60+
"react": "16.13.1",
6161
"react-hooks-testing-library": "^0.5.0",
6262
"react-intl": "^2.8.0",
6363
"react-intl-native": "^2.1.2",
64-
"react-native": "^0.61.1",
64+
"react-native": "^0.63.0",
6565
"react-native-gesture-handler": "^1.1.0",
66-
"react-navigation": "^3.5.1",
66+
"react-native-screens": "^2.9.0",
67+
"react-navigation": "^4.4.0",
68+
"react-navigation-stack": "^1.7.3",
6769
"react-redux": "^7.0.3",
68-
"react-test-renderer": "16.9.0",
70+
"react-test-renderer": "16.13.1",
6971
"redux": "^4.0.1",
7072
"semantic-release": "^15.13.3",
7173
"snapshot-diff": "0.5.1"

src/__tests__/__snapshots__/fetch.js.snap

+1-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ exports[`Fetch makes an API call and displays the greeting when load-greeting is
1111
}
1212
>
1313
<View>
14-
<TouchableOpacity
15-
activeOpacity={0.2}
16-
>
14+
<TouchableOpacity>
1715
<Text>
1816
Fetch
1917
</Text>

src/__tests__/events.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,18 @@ test('calling a handler if a Button is disabled does not work', () => {
120120
test('calling a handler if a Touchable is disabled does not work', () => {
121121
const handleEvent = jest.fn();
122122
const { getByText } = render(
123-
<TouchableHighlight disabled onPress={jest.fn()}>
123+
<TouchableHighlight disabled onPress={handleEvent}>
124124
<Text>touchable</Text>
125125
</TouchableHighlight>,
126126
);
127-
expect(() => fireEvent.press(getByText('touchable'))).not.toThrow();
127+
fireEvent.press(getByText('touchable'));
128128
expect(handleEvent).toBeCalledTimes(0);
129129
});
130130

131131
test('calling an event that has no defined handler throws', () => {
132132
const { getByText } = render(<Text>test</Text>);
133-
expect(() => fireEvent.press(getByText('test'))).not.toThrow();
133+
const text = getByText('test');
134+
expect(() => fireEvent.changeText(text).toThrow());
134135
});
135136

136137
test('calling an event sets nativeEvent properly', () => {

src/__tests__/misc.js

+120-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
1-
import React from 'react';
2-
import { Button, Picker, Switch, Text, View, TextInput } from 'react-native';
1+
import React, { useEffect, useRef } from 'react';
2+
import {
3+
Button,
4+
Picker,
5+
Pressable,
6+
ScrollView,
7+
Switch,
8+
Text,
9+
TextInput,
10+
TouchableHighlight,
11+
TouchableNativeFeedback,
12+
TouchableOpacity,
13+
TouchableWithoutFeedback,
14+
View,
15+
} from 'react-native';
316
import { toMatchDiffSnapshot } from 'snapshot-diff';
417

518
import { cleanup, fireEvent, render } from '../';
619

720
afterEach(cleanup);
821

22+
test('<Pressable /> works', () => {
23+
const fireZeMissiles = jest.fn();
24+
25+
function Wrapper() {
26+
return (
27+
<Pressable onPress={fireZeMissiles}>
28+
<Text>missiles</Text>
29+
</Pressable>
30+
);
31+
}
32+
const { getByText } = render(<Wrapper />);
33+
34+
fireEvent.press(getByText('missiles'));
35+
expect(fireZeMissiles).toBeCalledTimes(1);
36+
});
37+
938
test('<Picker /> works', () => {
1039
function Wrapper() {
1140
const [value, setValue] = React.useState('js');
@@ -23,6 +52,95 @@ test('<Picker /> works', () => {
2352
expect(() => findByDisplayValue('js')).not.toThrow();
2453
});
2554

55+
test('<ScrollView /> instance methods are mocked', () => {
56+
function Wrapper() {
57+
const ref = useRef();
58+
59+
useEffect(() => {
60+
ref.current.scrollTo(0);
61+
}, []);
62+
63+
return (
64+
<ScrollView ref={ref}>
65+
<Text>Some content</Text>
66+
</ScrollView>
67+
);
68+
}
69+
const { getByText, debug } = render(<Wrapper />);
70+
71+
expect(() => getByText('Some content')).not.toThrow();
72+
});
73+
74+
test('<TextInput /> instance methods are mocked', () => {
75+
function Wrapper() {
76+
const ref = useRef();
77+
78+
useEffect(() => {
79+
ref.current.clear();
80+
}, []);
81+
82+
return <TextInput ref={ref} value={'yo'} />;
83+
}
84+
const { getByDisplayValue } = render(<Wrapper />);
85+
86+
expect(() => getByDisplayValue('yo')).not.toThrow();
87+
});
88+
89+
test('calling a handler if a Touchable is disabled does not work', () => {
90+
const handleEvent = jest.fn();
91+
const { getByText } = render(
92+
<Pressable disabled onPress={handleEvent}>
93+
<Text>touchable</Text>
94+
</Pressable>,
95+
);
96+
expect(() => fireEvent.press(getByText('touchable'))).not.toThrow();
97+
expect(handleEvent).toBeCalledTimes(0);
98+
});
99+
100+
test('calling a TouchableHighlight handler works', () => {
101+
const handleEvent = jest.fn();
102+
const { getByText } = render(
103+
<TouchableHighlight onPress={handleEvent}>
104+
<Text>touchable</Text>
105+
</TouchableHighlight>,
106+
);
107+
expect(() => fireEvent.press(getByText('touchable'))).not.toThrow();
108+
expect(handleEvent).toBeCalledTimes(1);
109+
});
110+
111+
test('calling a TouchableNativeFeedback handler works', () => {
112+
const handleEvent = jest.fn();
113+
const { getByText } = render(
114+
<TouchableNativeFeedback onPress={handleEvent}>
115+
<Text>touchable</Text>
116+
</TouchableNativeFeedback>,
117+
);
118+
expect(() => fireEvent.press(getByText('touchable'))).not.toThrow();
119+
expect(handleEvent).toBeCalledTimes(1);
120+
});
121+
122+
test('calling a TouchableOpacity handler works', () => {
123+
const handleEvent = jest.fn();
124+
const { getByText } = render(
125+
<TouchableOpacity onPress={handleEvent}>
126+
<Text>touchable</Text>
127+
</TouchableOpacity>,
128+
);
129+
expect(() => fireEvent.press(getByText('touchable'))).not.toThrow();
130+
expect(handleEvent).toBeCalledTimes(1);
131+
});
132+
133+
test('calling a TouchableWithoutFeedback handler works ', () => {
134+
const handleEvent = jest.fn();
135+
const { getByText } = render(
136+
<TouchableWithoutFeedback onPress={handleEvent}>
137+
<Text>touchable</Text>
138+
</TouchableWithoutFeedback>,
139+
);
140+
expect(() => fireEvent.press(getByText('touchable'))).not.toThrow();
141+
expect(handleEvent).toBeCalledTimes(1);
142+
});
143+
26144
test('fragments can show diffs', () => {
27145
function TestComponent() {
28146
const [count, setCount] = React.useState(0);

src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function render(ui, { options = {}, wrapper: WrapperComponent, queries } = {}) {
3535
renderers.add(testRenderer);
3636

3737
const wrappers = proxyElement(testRenderer.root).findAll(n => n.type === 'View');
38-
const baseElement = wrappers[0]; // Includes YellowBox and your render
38+
const baseElement = wrappers[0];
3939
const container = wrappers[1]; // Includes only your render
4040

4141
return {

src/lib/__tests__/queries.find.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ test('find asynchronously finds elements', async () => {
3333
<Button title="button" />
3434
<TextInput placeholder="placeholder" />
3535
<TextInput value="value" />
36-
<TextInput accessibilityStates={['disabled']} />
36+
<TextInput accessibilityState={{ disabled: false }} />
3737
<Image accessibilityLabel="test-label" src="/lucy-ricardo.png" />
3838
<Image accessibilityHint="test-hint" src="/lucy-ricardo.png" />
3939
<View accessibilityRole="dialog" />

src/lib/events.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const eventMap = {
2828
Image: ['error', 'layout', 'load', 'loadEnd', 'loadStart', 'partialLoad', 'progress'],
2929
Modal: ['dismiss', 'orientationChange', 'requestClose', 'show'],
3030
Picker: [...viewEvents, 'valueChange'],
31+
Pressable: ['longPress', 'press', 'pressIn', 'pressOut'],
3132
RefreshControl: [...viewEvents, 'refresh'],
3233
SafeAreaView: [...viewEvents],
3334
ScrollView: [
@@ -108,8 +109,8 @@ function isValidTarget(element, event) {
108109
}
109110

110111
function isDisabled(element) {
111-
const { accessibilityStates = [], disabled } = element.props;
112-
return disabled || accessibilityStates.includes('disabled');
112+
const { accessibilityState = {}, disabled } = element.props;
113+
return disabled || accessibilityState.disabled;
113114
}
114115

115116
function findEventTarget(element, event) {

src/preset/configure.js

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ configureNTL({
2222
'react-native/Libraries/Image/Image',
2323
'react-native/Libraries/Modal/Modal',
2424
'react-native/Libraries/Components/Picker/Picker',
25+
'react-native/Libraries/Components/Pressable/Pressable',
2526
'react-native/Libraries/Components/RefreshControl/RefreshControl',
2627
'react-native/Libraries/Components/SafeAreaView/SafeAreaView',
2728
'react-native/Libraries/Components/ScrollView/ScrollView',

src/preset/mock-component.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import React from 'react';
22

33
import { mockNativeMethods } from './mock-native-methods';
44

5-
function mockComponent(component, path = component) {
5+
function mockComponent(path, { instanceMethods, displayName: customDisplayName } = {}) {
66
const RealComponent = jest.requireActual(path);
77

88
const displayName =
9+
customDisplayName ||
910
RealComponent.displayName ||
1011
RealComponent.name ||
1112
(RealComponent.render // handle React.forwardRef
@@ -19,7 +20,6 @@ function mockComponent(component, path = component) {
1920

2021
render() {
2122
const props = Object.assign({}, RealComponent.defaultProps);
22-
2323
if (this.props) {
2424
Object.keys(this.props).forEach(prop => {
2525
// We can't just assign props on top of defaultProps
@@ -43,6 +43,10 @@ function mockComponent(component, path = component) {
4343

4444
Object.assign(Component.prototype, mockNativeMethods);
4545

46+
if (instanceMethods != null) {
47+
Object.assign(Component.prototype, instanceMethods);
48+
}
49+
4650
return Component;
4751
}
4852

src/preset/mock-modules.js

+53-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { getConfig } from '../lib';
22
import { mockComponent } from './mock-component';
3-
import { mockNativeMethods } from './mock-native-methods';
3+
4+
import { mockScrollView } from './mock-scroll-view';
5+
import RefreshControlMock from './mock-refresh-control';
46

57
// Un-mock the react-native components so we can do it ourselves
68
getConfig('coreComponents').forEach(component => {
@@ -30,16 +32,55 @@ jest.doMock('react-native/Libraries/Components/Picker/Picker', () => {
3032
return Picker;
3133
});
3234

33-
// Re-mock ReactNative with native methods mocked
34-
jest
35-
.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper')
36-
.doMock('react-native/Libraries/Renderer/shims/ReactNative', () => {
37-
const ReactNative = jest.requireActual('react-native/Libraries/Renderer/shims/ReactNative');
38-
const NativeMethodsMixin =
39-
ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.NativeMethodsMixin;
35+
// Mock some other tricky ones
36+
jest.doMock('react-native/Libraries/Components/ScrollView/ScrollView', () => {
37+
const baseComponent = mockComponent('react-native/Libraries/Components/ScrollView/ScrollView', {
38+
instanceMethods: {
39+
getScrollResponder: jest.fn(),
40+
getScrollableNode: jest.fn(),
41+
getInnerViewNode: jest.fn(),
42+
getInnerViewRef: jest.fn(),
43+
getNativeScrollRef: jest.fn(),
44+
scrollTo: jest.fn(),
45+
scrollToEnd: jest.fn(),
46+
flashScrollIndicators: jest.fn(),
47+
scrollResponderZoomTo: jest.fn(),
48+
scrollResponderScrollNativeHandleToKeyboard: jest.fn(),
49+
},
50+
});
51+
return mockScrollView(baseComponent);
52+
});
4053

41-
Object.assign(NativeMethodsMixin, mockNativeMethods);
42-
Object.assign(ReactNative.NativeComponent.prototype, mockNativeMethods);
54+
jest.doMock(
55+
'react-native/Libraries/Components/RefreshControl/RefreshControl',
56+
() => RefreshControlMock,
57+
);
4358

44-
return ReactNative;
45-
});
59+
jest.doMock('react-native/Libraries/Components/TextInput/TextInput', () =>
60+
mockComponent('react-native/Libraries/Components/TextInput/TextInput', {
61+
instanceMethods: {
62+
isFocused: jest.fn(),
63+
clear: jest.fn(),
64+
getNativeRef: jest.fn(),
65+
},
66+
}),
67+
);
68+
69+
jest.doMock('react-native/Libraries/Components/Touchable/TouchableHighlight', () =>
70+
mockComponent('react-native/Libraries/Components/Touchable/TouchableHighlight', {
71+
displayName: 'TouchableHighlight',
72+
}),
73+
);
74+
75+
jest.doMock('react-native/Libraries/Components/Touchable/TouchableOpacity', () =>
76+
mockComponent('react-native/Libraries/Components/Touchable/TouchableOpacity', {
77+
displayName: 'TouchableOpacity',
78+
}),
79+
);
80+
81+
// Re-mock ReactNative
82+
jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');
83+
jest.mock('react-native/Libraries/Renderer/shims/ReactNative');
84+
85+
// Mock LogBox
86+
jest.mock('react-native/Libraries/LogBox/LogBox');

0 commit comments

Comments
 (0)