Monday, May 20, 2024
58
rated 0 times [  60] [ 2]  / answers: 1 / hits: 18113  / 7 Years ago, thu, february 16, 2017, 12:00:00

I try to test an Animated.View with Jest for React-Native. When I set a property visible to true, it supposed to animate my view from opacity 0 to opacity 1.



This is what my component renders:



<Animated.View
style={{
opacity: opacityValue,
}}
>
<Text>{message}</Text>
</Animated.View>


Where opacityValue gets updated when the props visible changes:



Animated.timing(
this.opacityValue, {
toValue: this.props.visible ? 1 : 0,
duration: 350,
},
).start(),


I want to make sure my view is visible when I set it the property visible=true. Although it takes some time for the view to become visible and as the test runs, the opacity is equal to 0.



This is my test it:



it('Becomes visible when visible=true', () => {
const tree = renderer.create(
<MessageBar
visible={true}
/>
).toJSON();
expect(tree).toMatchSnapshot();
});


I was wondering how I could have Jest to wait? Or how I could test this to make sure the view becomes visible when I set the props to true?



Thanks.


More From » unit-testing

 Answers
27

I solved this problem by creating an Animated stub for tests.



I see you are using visible as a property, so an working example is:



Components code



import React from 'react';                                                                                                                                                                            
import { Animated, Text, View, TouchableOpacity } from 'react-native';

// This class will control the visible prop
class AnimatedOpacityController extends React.Component {

constructor(props, ctx) {
super(props, ctx);
this.state = {
showChild: false,
};
}

render() {
const { showChild } = this.state;
return (
<View>
<AnimatedOpacity visible={this.state.showChild} />
<TouchableOpacity onPress={() => this.setState({ showChild: !showChild })}>
<Text>{showChild ? 'Hide' : 'Show' } greeting</Text>
</TouchableOpacity>
</View>
);
}

}

// This is your animated Component
class AnimatedOpacity extends React.Component {

constructor(props, ctx) {
super(props, ctx);
this.state = {
opacityValue: new Animated.Value(props.visible ? 1 : 0),
};
}

componentWillReceiveProps(nextProps) {
if (nextProps.visible !== this.props.visible) {
this._animate(nextProps.visible);
}
}

_animate(visible) {
Animated.timing(this.state.opacityValue, {
toValue: visible ? 1 : 0,
duration: 350,
}).start();
}

render() {
return (
<Animated.View style={{ opacity: this.state.opacityValue }}>
<Text>Hello World</Text>
</Animated.View>
);

}

}


export { AnimatedOpacityController, AnimatedOpacity };


Now moving to tests



import React from 'react';                                                                                                                                                                            
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';

import { AnimatedOpacityController, AnimatedOpacity } from '../AnimatedOpacity';


jest.mock('Animated', () => {
const ActualAnimated = require.requireActual('Animated');
return {
...ActualAnimated,
timing: (value, config) => {
return {
start: (callback) => {
value.setValue(config.toValue);
callback && callback()
},
};
},
};
});

it('renders visible', () => {
expect(
renderer.create(
<AnimatedOpacity visible={true} />
).toJSON()
).toMatchSnapshot();
});

it('renders invisible', () => {
expect(
renderer.create(
<AnimatedOpacity visible={false} />
).toJSON()
).toMatchSnapshot();
});

it('makes transition', () => {
const component = shallow(<AnimatedOpacityController />);
expect(renderer.create(component.node).toJSON()).toMatchSnapshot();
component.find('TouchableOpacity').simulate('press');
expect(renderer.create(component.node).toJSON()).toMatchSnapshot();
component.find('TouchableOpacity').simulate('press');
expect(renderer.create(component.node).toJSON()).toMatchSnapshot();
});


Now the generated snapshots will have opacity values as expected.
If you are using animated a lot you can move you mock to js/config/jest and edit you package.json to use it in all your tests, then any change made to your stub will be available to all tests.



EDITED:



The solution above solves only to go from beginning to end. A more granular solution is:




  1. Don't mock Animated

  2. In jest config make global.requestAnimationFrame = null

  3. Use mockdate do mock the date

  4. Use jest.runTimersToTime for time travel



A time travel function would be



const timeTravel = (ms, step = 100) => {                                                                                                                                                                              

const tickTravel = v => {
jest.runTimersToTime(v);
const now = Date.now();
MockDate.set(new Date(now + v));
}

let done = 0;
while (ms - done > step) {
tickTravel(step);
done += step;
}
tickTravel(ms - done);
};


Breaking steps in small chunks is importante because of Animated internal behavior.


[#58914] Tuesday, February 14, 2017, 7 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
calicinthias

Total Points: 447
Total Questions: 101
Total Answers: 118

Location: Botswana
Member since Sat, Dec 31, 2022
1 Year ago
;