Skip to content

Commit 40e3931

Browse files
chore: Mock of SVG to test it with Reanimated (#8387)
## Summary We had no way to test react-native-svg components before, but after user adds this mock it'll work :") to-do - [x] rename it to test - [x] Integrate Svg into our mock ## Test plan Run the tests
1 parent 0297d29 commit 40e3931

File tree

5 files changed

+191
-206
lines changed

5 files changed

+191
-206
lines changed

packages/react-native-reanimated/__tests__/__snapshots__/animatedProps.test.tsx.snap

Lines changed: 1 addition & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -2,180 +2,6 @@
22

33
exports[`animatedProps Custom animated component 1`] = `
44
<View>
5-
<RNSVGSvgView
6-
bbHeight="100%"
7-
bbWidth="100%"
8-
focusable={false}
9-
style={
10-
[
11-
{
12-
"backgroundColor": "transparent",
13-
"borderWidth": 0,
14-
},
15-
{
16-
"flex": 0,
17-
"height": "100%",
18-
"width": "100%",
19-
},
20-
]
21-
}
22-
>
23-
<RNSVGGroup
24-
fill={
25-
{
26-
"payload": 4278190080,
27-
"type": 0,
28-
}
29-
}
30-
>
31-
<RNSVGCircle
32-
cx="50%"
33-
cy="50%"
34-
fill={
35-
{
36-
"payload": 4290088433,
37-
"type": 0,
38-
}
39-
}
40-
propList={
41-
[
42-
"fill",
43-
]
44-
}
45-
r={20}
46-
testID="circle"
47-
/>
48-
</RNSVGGroup>
49-
</RNSVGSvgView>
50-
<TextInput
51-
collapsable={false}
52-
defaultValue="Box width: 20"
53-
jestAnimatedProps={
54-
{
55-
"value": {
56-
"defaultValue": "Box width: 20",
57-
"text": "Box width: 20",
58-
},
59-
}
60-
}
61-
jestAnimatedStyle={
62-
{
63-
"value": {},
64-
}
65-
}
66-
nativeID="7"
67-
testID="text"
68-
text="Box width: 20"
69-
/>
70-
<View
71-
accessibilityRole="button"
72-
accessibilityState={
73-
{
74-
"busy": undefined,
75-
"checked": undefined,
76-
"disabled": undefined,
77-
"expanded": undefined,
78-
"selected": undefined,
79-
}
80-
}
81-
accessibilityValue={
82-
{
83-
"max": undefined,
84-
"min": undefined,
85-
"now": undefined,
86-
"text": undefined,
87-
}
88-
}
89-
accessible={true}
90-
collapsable={false}
91-
focusable={true}
92-
onClick={[Function]}
93-
onResponderGrant={[Function]}
94-
onResponderMove={[Function]}
95-
onResponderRelease={[Function]}
96-
onResponderTerminate={[Function]}
97-
onResponderTerminationRequest={[Function]}
98-
onStartShouldSetResponder={[Function]}
99-
style={
100-
{
101-
"opacity": 1,
102-
}
103-
}
104-
testID="button"
105-
>
106-
<View
107-
style={
108-
[
109-
{},
110-
]
111-
}
112-
>
113-
<Text
114-
style={
115-
[
116-
{
117-
"color": "#007AFF",
118-
"fontSize": 18,
119-
"margin": 8,
120-
"textAlign": "center",
121-
},
122-
]
123-
}
124-
>
125-
Click me
126-
</Text>
127-
</View>
128-
</View>
129-
</View>
130-
`;
131-
132-
exports[`animatedProps SVG component cannot be tested 1`] = `
133-
<View>
134-
<RNSVGSvgView
135-
bbHeight="100%"
136-
bbWidth="100%"
137-
focusable={false}
138-
style={
139-
[
140-
{
141-
"backgroundColor": "transparent",
142-
"borderWidth": 0,
143-
},
144-
{
145-
"flex": 0,
146-
"height": "100%",
147-
"width": "100%",
148-
},
149-
]
150-
}
151-
>
152-
<RNSVGGroup
153-
fill={
154-
{
155-
"payload": 4278190080,
156-
"type": 0,
157-
}
158-
}
159-
>
160-
<RNSVGCircle
161-
cx="50%"
162-
cy="50%"
163-
fill={
164-
{
165-
"payload": 4290088433,
166-
"type": 0,
167-
}
168-
}
169-
propList={
170-
[
171-
"fill",
172-
]
173-
}
174-
r={20}
175-
testID="circle"
176-
/>
177-
</RNSVGGroup>
178-
</RNSVGSvgView>
1795
<TextInput
1806
collapsable={false}
1817
defaultValue="Box width: 20"
@@ -192,7 +18,7 @@ exports[`animatedProps SVG component cannot be tested 1`] = `
19218
"value": {},
19319
}
19420
}
195-
nativeID="3"
21+
nativeID="1"
19622
testID="text"
19723
text="Box width: 20"
19824
/>

packages/react-native-reanimated/__tests__/animatedProps.test.tsx

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,10 @@ import { Button, TextInput, View } from 'react-native';
44
import Animated, {
55
useAnimatedProps,
66
useSharedValue,
7-
withTiming,
87
} from 'react-native-reanimated';
9-
import { Circle, Svg } from 'react-native-svg';
108

119
const animationDuration = 100;
1210

13-
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
14-
1511
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
1612

1713
export default function AnimatedComponent() {
@@ -23,10 +19,6 @@ export default function AnimatedComponent() {
2319
width.value += 10;
2420
};
2521

26-
const animatedProps = useAnimatedProps(() => ({
27-
r: withTiming(r.value, { duration: animationDuration }),
28-
}));
29-
3022
const textAnimatedProps = useAnimatedProps(() => {
3123
return {
3224
text: `Box width: ${width.value}`,
@@ -36,16 +28,6 @@ export default function AnimatedComponent() {
3628

3729
return (
3830
<View>
39-
<Svg>
40-
{/* SVG components strip our jest props and cannot be tested */}
41-
<AnimatedCircle
42-
cx="50%"
43-
cy="50%"
44-
fill="#b58df1"
45-
testID={'circle'}
46-
animatedProps={animatedProps}
47-
/>
48-
</Svg>
4931
<AnimatedTextInput testID={'text'} animatedProps={textAnimatedProps} />
5032
<Button testID={'button'} onPress={handlePress} title="Click me" />
5133
</View>
@@ -62,16 +44,6 @@ describe('animatedProps', () => {
6244
jest.useRealTimers();
6345
});
6446

65-
test('SVG component cannot be tested', () => {
66-
const { getByTestId } = render(<AnimatedComponent />);
67-
const circle = getByTestId('circle');
68-
69-
expect(circle).toHaveAnimatedProps({});
70-
71-
const rendered = render(<AnimatedComponent />).toJSON();
72-
expect(rendered).toMatchSnapshot();
73-
});
74-
7547
test('Custom animated component', () => {
7648
const { getByTestId } = render(<AnimatedComponent />);
7749
const textInput = getByTestId('text');
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { fireEvent, render } from '@testing-library/react-native';
2+
import React from 'react';
3+
import { Pressable, Text } from 'react-native';
4+
import Animated, {
5+
useAnimatedProps,
6+
useAnimatedStyle,
7+
useSharedValue,
8+
withTiming,
9+
} from 'react-native-reanimated';
10+
import { Circle, Svg } from 'react-native-svg';
11+
12+
// eslint-disable-next-line @typescript-eslint/no-require-imports
13+
jest.mock('react-native-svg', () => require('../mock'));
14+
15+
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
16+
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
17+
18+
function AnimatedSvgWrapper() {
19+
const opacity = useSharedValue(0.3);
20+
21+
const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value }));
22+
23+
const handlePress = () => {
24+
opacity.value = withTiming(opacity.value === 0.3 ? 1 : 0.3, {
25+
duration: 300,
26+
});
27+
};
28+
29+
return (
30+
<AnimatedPressable
31+
style={animatedStyle}
32+
testID={'wrapper'}
33+
onPress={handlePress}>
34+
<Svg height="100" width="100">
35+
<Circle cx="50" cy="50" r="40" fill="blue" />
36+
</Svg>
37+
<Text>Button</Text>
38+
</AnimatedPressable>
39+
);
40+
}
41+
42+
function AnimatedExample() {
43+
const pressed = useSharedValue(0);
44+
45+
const handlePress = () => {
46+
pressed.value = pressed.value === 0 ? 1 : 0;
47+
};
48+
49+
const animatedProps = useAnimatedProps(() => {
50+
return { r: withTiming(pressed.value === 0 ? 20 : 40, { duration: 300 }) };
51+
});
52+
53+
const animatedStyle = useAnimatedStyle(() => {
54+
return {
55+
width: withTiming(pressed.value === 0 ? 100 : 200, { duration: 300 }),
56+
};
57+
});
58+
59+
return (
60+
<>
61+
<Svg height="100" width="100">
62+
<AnimatedCircle
63+
testID="circle"
64+
cx="50"
65+
cy="50"
66+
style={animatedStyle}
67+
animatedProps={animatedProps}
68+
/>
69+
</Svg>
70+
<Pressable testID={'pressable'} onPress={handlePress}>
71+
<Text>Button</Text>
72+
</Pressable>
73+
</>
74+
);
75+
}
76+
77+
describe('Animating SVG', () => {
78+
beforeEach(() => {
79+
jest.useFakeTimers();
80+
});
81+
82+
test('Should animate radius - Circle', () => {
83+
const { getByTestId } = render(<AnimatedExample />);
84+
const circle = getByTestId('circle');
85+
const pressable = getByTestId('pressable');
86+
87+
expect(circle).toHaveAnimatedProps({ r: 20 });
88+
89+
fireEvent.press(pressable);
90+
jest.advanceTimersByTime(600);
91+
92+
expect(circle).toHaveAnimatedProps({ r: 40 });
93+
});
94+
95+
test('Should animate width - Circle', () => {
96+
const { getByTestId } = render(<AnimatedExample />);
97+
const pressable = getByTestId('pressable');
98+
const circle = getByTestId('circle');
99+
100+
expect(circle).toHaveAnimatedStyle({ width: 100 });
101+
102+
fireEvent.press(pressable);
103+
jest.advanceTimersByTime(600);
104+
105+
expect(circle).toHaveAnimatedStyle({ width: 200 });
106+
});
107+
});
108+
109+
describe('Animating wrapper of SVG', () => {
110+
test('Should animate opacity - Wrapper', () => {
111+
const { getByTestId } = render(<AnimatedSvgWrapper />);
112+
const wrapper = getByTestId('wrapper');
113+
114+
expect(wrapper).toHaveAnimatedStyle({ opacity: 0.3 });
115+
116+
fireEvent.press(wrapper);
117+
jest.advanceTimersByTime(600);
118+
119+
expect(wrapper).toHaveAnimatedStyle({ opacity: 1 });
120+
});
121+
});
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
const Reanimated = require('./src/mock');
2+
const SVG = require('./src/mock-svg');
23
// @ts-expect-error
34
const Animated = Reanimated.default;
45

56
module.exports = {
67
...Reanimated,
8+
...SVG,
79

8-
default: {
9-
...Animated,
10-
},
10+
default: { ...Animated },
1111
};

0 commit comments

Comments
 (0)