Skip to content

Instantly share code, notes, and snippets.

@jmporchet
Created April 1, 2019 16:57
Show Gist options
  • Save jmporchet/4cf816bdb6125c8675e4821a0bfa8a5a to your computer and use it in GitHub Desktop.
Save jmporchet/4cf816bdb6125c8675e4821a0bfa8a5a to your computer and use it in GitHub Desktop.
How to test componentDidUpdate with react-native-testing-library
import React from 'react';
import { render, fireEvent } from 'react-native-testing-library';
import { _EditPlanDetails as EditPlanDetails } from '../Settings/EditPlanDetails';
// Scrollviews are bugged in the current Expo/RN release and won't render in tests https://github.com/expo/expo/issues/2806#issuecomment-465373231
jest.mock('ScrollView', () => require.requireMock('ScrollViewMock'));
describe('EditPlanDetails', () => {
const props = {
subscription: {
membershipStartedAt: '2019-03-10',
nextBillingAt: '2019-04-10'
},
isFetching: false,
cancelSubscription: jest.fn(),
navigation: { navigate: jest.fn(), goBack: () => jest.fn() }
};
// react-testing-library doesn't have "update" as an utility
const { update, getByText, getByTestId } = render(
<EditPlanDetails {...props} />
);
it('should cancel subscription when clicking on cancel button', () => {
fireEvent.press(getByText(/change/i));
fireEvent.press(getByTestId('cancelbutton'));
expect(props.cancelSubscription).toHaveBeenCalledTimes(1);
// the magic happens here :)
const newProps = { ...props, subscription: {} };
update(<EditPlanDetails {...newProps} />);
expect(props.navigation.navigate).toHaveBeenCalledWith('Canceled');
});
});
import _ from 'lodash';
import React from 'react';
import { Component } from 'react';
import { connect } from 'react-redux';
import { Icon } from 'react-native-elements';
import { NavigationScreenProp } from 'react-navigation';
import {
View,
Text,
SafeAreaView,
StyleSheet,
Modal,
TouchableOpacity,
ScrollView,
TextStyle,
ViewStyle
} from 'react-native';
import Button from '../../components/SubmitButton';
import { cancelSubscription } from '../../actions/user';
import colors from '../../constants/Colors';
import DateTimeService from '../../services/DateTimeService';
import { ScreenHeader } from '../../components/ScreenHeader';
import { Subscription } from '../../types';
interface Props {
navigation: NavigationScreenProp<any, any>;
subscription: Subscription;
isFetching: Boolean;
cancelSubscription: () => any;
}
interface State {
modal: boolean;
}
export class _EditPlanDetails extends Component<Props, State> {
state = {
modal: false
};
componentDidUpdate = (prevProps: Props, prevState: State) => {
if (
prevProps.subscription &&
Object.keys(prevProps.subscription).length > 0 &&
Object.keys(this.props.subscription).length < 1
) {
this.props.navigation.navigate('Canceled');
}
};
closeModal = () => {
this.setState({ modal: false });
};
openModal = () => {
this.setState({ modal: true });
};
changeMySubscriptionCycle = () => {
this.props.navigation.navigate('EditSubscriptionCycle');
};
render() {
const membershipStartedAt = _.get(
this.props,
'subscription.membershipStartedAt',
null
);
const nextBillingAt = _.get(this.props, 'subscription.nextBillingAt', null);
const plan = _.get(this.props, 'subscription.plan', null);
return (
<SafeAreaView>
<ScreenHeader
header={'Edit my plan details'}
onPress={this.props.navigation.goBack}
/>
<ScrollView>
<View style={styles.header}>
<Text style={styles.textHeader}>YOUR CURRENT PLAN</Text>
</View>
<View>
<View style={[styles.item, { borderBottomWidth: 0 }]}>
<Text style={[styles.itemText, styles.boldText]}>
Subscription rate:
</Text>
<Text style={styles.itemText}>
{_.get(plan, 'intervalCount', '-')}
</Text>
</View>
<View style={[styles.item, { borderBottomWidth: 0 }]}>
<Text style={[styles.itemText, styles.boldText]}>
Next cycle start date:
</Text>
<Text style={styles.itemText}>
{nextBillingAt
? DateTimeService.formatSubscriptionDate(nextBillingAt)
: '-'}
</Text>
</View>
<View style={styles.item}>
<Text style={[styles.itemText, styles.boldText]}>
Member since:
</Text>
<Text style={styles.itemText}>
{membershipStartedAt
? DateTimeService.formatSubscriptionDate(membershipStartedAt)
: '-'}
</Text>
</View>
</View>
<View style={[styles.buttonBlock, { marginHorizontal: 30 }]}>
<Button
style={styles.button}
onPress={this.changeMySubscriptionCycle}
textStyle={{ fontSize: 12 }}
title="CHANGE MY SUBSCRIPTION CYCLE"
/>
<Button
light
style={styles.button}
onPress={this.openModal}
title="CANCEL MY SUBSCRIPTION"
/>
</View>
<Modal
animationType="fade"
transparent={true}
visible={this.state.modal}
onRequestClose={() => {}}
>
<View style={styles.modalWrapper}>
<View style={styles.modalWindow}>
<TouchableOpacity
testID="crossbutton"
onPress={this.closeModal}
style={styles.closeButton}
>
<Icon name={'close'} size={12} />
</TouchableOpacity>
<View style={{ marginBottom: 20 }}>
<Text
style={{
fontFamily: 'barlow-condensed',
fontSize: 18,
textAlign: 'center',
fontWeight: '300'
}}
>
Want to cancel your subscription?
</Text>
<Text
style={{
fontFamily: 'openSans-light',
fontSize: 13,
marginVertical: 20
}}
>
Please know that by canceling your subscription your profile
will be locked from any Mixfit device on the next renewal
date, but you can still use the other elements of the app.
</Text>
</View>
<View style={styles.buttonBlock}>
<Button
style={styles.button}
onPress={this.closeModal}
title="NEVERMIND"
/>
<Button
light
testID="cancelbutton"
style={styles.button}
onPress={() => {
this.closeModal();
this.props.cancelSubscription();
}}
title="CANCEL SUBSCRIPTION"
/>
</View>
</View>
</View>
</Modal>
</ScrollView>
</SafeAreaView>
);
}
}
interface StylesProps {
header: ViewStyle;
textHeader: TextStyle;
modalWrapper: ViewStyle;
modalWindow: ViewStyle;
item: ViewStyle;
itemText: TextStyle;
boldText: TextStyle;
button: ViewStyle;
buttonReg: ViewStyle;
buttonEdit: ViewStyle;
buttonBlock: ViewStyle;
checkBox: ViewStyle;
purchaseItem: ViewStyle;
counter: ViewStyle;
counterButton: ViewStyle;
closeButton: ViewStyle;
}
const styles = StyleSheet.create<StylesProps>({
header: {
backgroundColor: colors.xtra_light_gray,
paddingHorizontal: 30,
paddingVertical: 10
},
textHeader: {
fontSize: 23,
fontFamily: 'barlow-condensed',
color: colors.black
},
modalWrapper: {
backgroundColor: 'rgba(0,0,0,.8)',
flex: 1,
alignItems: 'center',
paddingTop: '10%'
},
modalWindow: {
height: '70%',
width: '80%',
borderColor: '#000',
borderWidth: 1,
backgroundColor: '#fff',
paddingHorizontal: 30,
paddingVertical: 30
},
item: {
paddingHorizontal: 30,
paddingVertical: 20,
borderColor: colors.grayBorder,
borderWidth: 1,
flexDirection: 'row',
justifyContent: 'space-between'
},
itemText: {
fontSize: 16
},
boldText: {
fontWeight: '500'
},
buttonReg: {
backgroundColor: colors.white,
borderColor: colors.blackGray,
borderWidth: 1,
marginTop: 15
},
// button: {
// width: 275,
// height: 56,
// marginHorizontal: 7
// },
button: {
flex: 1,
marginTop: 10,
minHeight: 40,
minWidth: 200
},
buttonEdit: {
maxWidth: 340,
marginVertical: 20,
borderColor: colors.grayBorder,
borderWidth: 1
},
buttonBlock: {
flexDirection: 'column',
justifyContent: 'center',
paddingTop: 10
},
checkBox: {
marginLeft: -10,
marginTop: 5,
borderRadius: 50
},
purchaseItem: {
paddingHorizontal: 30,
paddingVertical: 20,
borderColor: colors.grayBorder,
borderWidth: 1
},
counter: {
borderWidth: 1,
borderColor: colors.grayBorder,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderRadius: 50,
padding: 5,
marginLeft: '25%'
},
counterButton: {
borderRadius: 50,
backgroundColor: colors.aquamarine
},
closeButton: {
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
top: -10,
right: -5,
backgroundColor: colors.xtra_light_gray,
height: 20,
width: 20,
borderRadius: 10
}
});
interface StateProps {
isFetching: any;
subscription: Subscription;
}
const mapStateToProps = (state: any): StateProps => ({
isFetching: state.user.isFetching,
subscription:
state.user.subscriptions[0] &&
Object.keys(state.user.subscriptions[0]).length > 1
? state.user.subscriptions[0]
: {}
});
const mapDispatchToProps = (dispatch: any): any => ({
cancelSubscription: () => dispatch(cancelSubscription())
});
export default connect<StateProps, any>(
mapStateToProps,
mapDispatchToProps
)(_EditPlanDetails);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment