React Native PayModal 组件 Jest 单元测试 (无 Enzyme)
import React from 'react'; import { View, Text, AppState, Linking } from 'react-native'; import { UIActivityIndicator } from 'react-native-indicators'; import { getList, doPayment, checkPay } from '@api/Order'; import ACTION_TYPE from '../Order/OrderAction'; import LTouchableOpacity from '../TouchAble/LTouchableOpacity'; import { aliPay, wxPay } from '@utils/native'; import TimerText from './TimerText'; import { Base64 } from 'js-base64'; import DialogContainer from '@common/UIComponents/Dialog/DialogContainer'; import { track } from '@utils/dataAnalytics'; import IconFont from '@assets/iconfont'; import LNumberText from 'imou-rn/dist/components/LNumberText'; import LToast from 'imou-rn/dist/components/LToast'; import { getImageUrlForPath } from '@utils/layout'; import LNetImage from '../LNetImage'; import { log, toast } from '@common/Utils/LogUtil';
// Import the component to be tested import PayModal from './PayModal';
// Mock the imported dependencies jest.mock('react-native', () => ({ StyleSheet: { hairlineWidth: 1, }, View: 'View', Text: 'Text', AppState: { currentState: 'active', addEventListener: jest.fn(), removeEventListener: jest.fn(), }, Linking: { canOpenURL: jest.fn(() => Promise.resolve(true)), }, })); jest.mock('react-native-indicators', () => ({ UIActivityIndicator: 'UIActivityIndicator', })); jest.mock('@api/Order', () => ({ getList: jest.fn(), doPayment: jest.fn(), checkPay: jest.fn(), })); jest.mock('../TouchAble/LTouchableOpacity', () => 'LTouchableOpacity'); jest.mock('@utils/native', () => ({ aliPay: jest.fn(), wxPay: jest.fn(), })); jest.mock('./TimerText', () => 'TimerText'); jest.mock('js-base64', () => ({ Base64: { encode: jest.fn(), }, })); jest.mock('@common/UIComponents/Dialog/DialogContainer', () => 'DialogContainer'); jest.mock('@utils/dataAnalytics', () => ({ track: jest.fn(), })); jest.mock('@assets/iconfont', () => ({ name: jest.fn(), })); jest.mock('imou-rn/dist/components/LNumberText', () => 'LNumberText'); jest.mock('imou-rn/dist/components/LToast', () => 'LToast'); jest.mock('@utils/layout', () => ({ getImageUrlForPath: jest.fn(), })); jest.mock('../LNetImage', () => 'LNetImage'); jest.mock('@common/Utils/LogUtil', () => ({ log: jest.fn(), toast: jest.fn(), }));
describe('PayModal', () => { let wrapper; let instance;
beforeEach(() => {
wrapper = shallow(
afterEach(() => { jest.clearAllMocks(); });
describe('constructor', () => { it('should initialize state correctly', () => { expect(wrapper.state()).toEqual({ data: null, selPayWayIndex: 0, time: '', isLoading: true, isNetError: false, leftTime: 10000, confirmModalVisible: true, appState: 'active', paymentId: '', }); }); });
describe('_handleAppStateChange', () => { it('should call doWhileGetPayResult if app state changes to active', () => { instance.doWhileGetPayResult = jest.fn(); instance._handleAppStateChange('active'); expect(instance.doWhileGetPayResult).toHaveBeenCalled(); }); });
describe('doWhileGetPayResult', () => { it('should not call _checkPay if paying is false or visible is false', () => { instance.isChecking = false; instance._checkPay = jest.fn(); instance.setState({ paying: false }); instance.doWhileGetPayResult(); expect(instance._checkPay).not.toHaveBeenCalled();
instance.setState({ paying: true, visible: false });
instance.doWhileGetPayResult();
expect(instance._checkPay).not.toHaveBeenCalled();
});
it('should call _checkPay and set isChecking to true', () => {
instance._checkPay = jest.fn();
instance.setState({ paying: true, visible: true });
instance.doWhileGetPayResult();
expect(instance._checkPay).toHaveBeenCalled();
expect(instance.isChecking).toBe(true);
});
it('should call clearInterval and set isChecking to false if times reaches 6', () => {
instance._checkPay = jest.fn();
instance.isChecking = false;
const interval = setInterval(() => {}, 500);
instance.doWhileGetPayResult();
expect(clearInterval).toHaveBeenCalledWith(interval);
expect(instance.isChecking).toBe(false);
});
});
describe('_checkPay', () => { it('should return if visible is false', () => { instance._fetchData = jest.fn(); instance.setState({ visible: false }); instance._checkPay(); expect(instance._fetchData).not.toHaveBeenCalled(); });
it('should call _fetchData if paymentId is not set', () => {
instance._fetchData = jest.fn();
instance.setState({ paymentId: '' });
instance._checkPay();
expect(instance._fetchData).toHaveBeenCalled();
});
it('should call checkPay with correct paymentId', () => {
instance.setState({ paymentId: 'paymentId' });
checkPay.mockResolvedValue({ code: 10000, data: { payStatus: '1' } });
instance._checkPay();
expect(checkPay).toHaveBeenCalledWith({ paymentId: 'paymentId' });
});
it('should handle success response from checkPay', () => {
instance.setState({ paymentId: 'paymentId' });
instance.updateCallback = jest.fn();
instance.timerText = { stop: jest.fn() };
instance.props.navigation = { navigate: jest.fn(), replace: jest.fn() };
checkPay.mockResolvedValue({ code: 10000, data: { payStatus: '1' } });
instance._checkPay();
expect(instance.timerText.stop).toHaveBeenCalled();
expect(instance.updateCallback).toHaveBeenCalledWith(ACTION_TYPE.PAY);
expect(instance.props.navigation.replace).toHaveBeenCalledWith('PaySuccessScreen', { data: { orderInfo: { payStatus: '1' } } });
});
it('should handle failure response from checkPay', () => {
instance.setState({ paymentId: 'paymentId' });
checkPay.mockResolvedValue({ code: 50000 });
instance._checkPay();
expect(toast).toHaveBeenCalledWith('服务器开小差啦~');
});
});
describe('_handleBack', () => { it('should call _handleOnClose and navigate to correct screen', () => { instance._handleOnClose = jest.fn(); instance.props.navigation = { replace: jest.fn() }; instance.props.from = 'confirm'; instance.setState({ data: { orderId: 'orderId' } }); instance._handleBack(); expect(instance._handleOnClose).toHaveBeenCalled(); expect(instance.props.navigation.replace).toHaveBeenCalledWith('OrderDtlScreen', { orderID: 'orderId' }); }); });
describe('_handleOnClose', () => { it('should call props.onClose and setState correctly', () => { instance.props.onClose = jest.fn(); instance.props.from = 'orderList'; instance.updateCallback = jest.fn(); instance.timerText = { stop: jest.fn() }; instance._handleOnClose(); expect(instance.props.onClose).toHaveBeenCalled(); expect(instance.updateCallback).toHaveBeenCalledWith(ACTION_TYPE.PAY); expect(instance.timerText.stop).toHaveBeenCalled(); expect(wrapper.state('visible')).toBe(false); expect(wrapper.state('isLoading')).toBe(false); expect(wrapper.state('paying')).toBe(false); }); });
describe('show', () => { it('should return if data is not provided', () => { instance._fetchData = jest.fn(); instance.show(); expect(instance._fetchData).not.toHaveBeenCalled(); });
it('should setState correctly and call _fetchData', () => {
instance._fetchData = jest.fn();
instance.updateCallback = jest.fn();
const data = { orderId: 'orderId', updateCallback: jest.fn() };
instance.show(data);
expect(wrapper.state('visible')).toBe(true);
expect(wrapper.state('data')).toEqual(data);
expect(instance._fetchData).toHaveBeenCalled();
expect(instance.updateCallback).toHaveBeenCalledWith(data.updateCallback);
});
});
describe('_fetchData', () => { it('should return if data is not set', () => { getList.mockResolvedValue({ code: 10000, data: {} }); instance._handleNetResult = jest.fn(); instance._fetchData(); expect(getList).not.toHaveBeenCalled(); });
it('should call getList with correct params', () => {
instance.setState({ data: { orderId: 'orderId' } });
getList.mockResolvedValue({ code: 10000, data: {} });
instance._handleNetResult = jest.fn();
instance._fetchData();
expect(getList).toHaveBeenCalledWith({ orderId: 'orderId', endPay: 0, platform: 'appnative', version: '5.5.0' });
});
it('should call _handleNetResult on success response', () => {
instance.setState({ data: { orderId: 'orderId' } });
getList.mockResolvedValue({ code: 10000, data: {} });
instance._handleNetResult = jest.fn();
instance._fetchData();
expect(instance._handleNetResult).toHaveBeenCalled();
});
it('should call toast on failure response', () => {
instance.setState({ data: { orderId: 'orderId' } });
getList.mockResolvedValue({ code: 50000 });
instance._handleNetResult = jest.fn();
instance._fetchData();
expect(toast).toHaveBeenCalledWith('服务器开小差啦~');
});
});
describe('_handleNetResult', () => { it('should return if isGoToSuccess is true', () => { instance.isGoToSuccess = true; instance.comfirmModal = { hide: jest.fn() }; instance.timerText = { stop: jest.fn() }; instance.props.navigation = { navigate: jest.fn(), replace: jest.fn() }; instance._handleNetResult({ data: { payStatus: '1' } }); expect(instance.comfirmModal.hide).not.toHaveBeenCalled(); expect(instance.timerText.stop).not.toHaveBeenCalled(); expect(instance.props.navigation.navigate).not.toHaveBeenCalled(); });
it('should handle success response and navigate to PaySuccessScreen', () => {
instance.isGoToSuccess = false;
instance.comfirmModal = { hide: jest.fn() };
instance.timerText = { stop: jest.fn() };
instance.props.navigation = { navigate: jest.fn(), replace: jest.fn() };
instance.updateCallback = jest.fn();
instance._handleNetResult({ data: { payStatus: '1' } });
expect(instance.comfirmModal.hide).toHaveBeenCalled();
expect(instance.timerText.stop).toHaveBeenCalled();
expect(instance.updateCallback).toHaveBeenCalledWith(ACTION_TYPE.PAY);
expect(instance.props.navigation.replace).toHaveBeenCalledWith('PaySuccessScreen', { data: { payStatus: '1' } });
});
it('should handle failure response', () => {
instance.isGoToSuccess = false;
instance.setState({ isLoading: true, isNetError: true });
instance.timerText = { stop: jest.fn() };
instance._handleNetResult({ code: 50000 });
expect(instance.timerText.stop).toHaveBeenCalled();
expect(wrapper.state('isLoading')).toBe(false);
expect(wrapper.state('isNetError')).toBe(false);
});
});
describe('_handlePayResult', () => { it('should call doWhileGetPayResult if result is empty or invalid', () => { instance.doWhileGetPayResult = jest.fn(); instance._handlePayResult({}); expect(instance.doWhileGetPayResult).toHaveBeenCalled();
instance._handlePayResult(null);
expect(instance.doWhileGetPayResult).toHaveBeenCalled();
});
it('should handle different error codes from pay result', () => {
instance._fetchData = jest.fn();
instance.comfirmModal = { hide: jest.fn() };
instance._handlePayResult({ code: 6001 });
expect(toast).toHaveBeenCalledWith('取消支付');
expect(instance._fetchData).toHaveBeenCalled();
expect(instance.comfirmModal.hide).not.toHaveBeenCalled();
instance._handlePayResult({ code: -1 });
expect(toast).toHaveBeenCalledWith('其他错误');
expect(instance._fetchData).toHaveBeenCalled();
expect(instance.comfirmModal.hide).not.toHaveBeenCalled();
instance._handlePayResult({ code: 8000 });
expect(toast).not.toHaveBeenCalled();
expect(instance._fetchData).not.toHaveBeenCalled();
expect(instance.comfirmModal.hide).not.toHaveBeenCalled();
});
});
describe('_renderPayWayRow', () => { it('should render the pay way row correctly', () => { instance.setState({ data: { payable: '1' } }); const row = instance._renderPayWayRow({ title: '支付宝', appId: 'alipayapp', index: 0 }); expect(row.type).toBe('View'); expect(row.props.style).toEqual(styles.rowBack); expect(row.props.children[0].type).toBe('View'); expect(row.props.children[1].type).toBe('LTouchableOpacity'); expect(row.props.children[1].props.disabled).toBe(false); expect(row.props.children[1].props.trackInfo.elementInfo.$element_id).toBe('click_pay_alipayapp'); expect(row.props.children[1].props.sensorsDataParams.$element_id).toBe('m_orderDtl_payWayByalipayapp_rpckn'); expect(row.props.children[1].props.children.length).toBe(1); });
it('should render a divider for index greater than 0', () => {
instance.setState({ data: { payable: '1' } });
const row = instance._renderPayWayRow({ title: '微信支付', appId: 'wxpayapp', index: 1 });
expect(row.props.children[0].type).toBe('View');
});
});
describe('_doPayment', () => { it('should call Linking.canOpenURL and show alertModal if appId is wxpayapp and cannot open WeChat', () => { instance.setState({ data: { orderId: 'orderId', payMoney: 100 } }); instance.alertModal = { show: jest.fn() }; Linking.canOpenURL.mockResolvedValue(false); instance._doPayment({ appId: 'wxpayapp' }); expect(Linking.canOpenURL).toHaveBeenCalledWith('weixin://'); expect(instance.alertModal.show).toHaveBeenCalled(); });
it('should call doPayment with correct params', () => {
instance.setState({ data: { orderId: 'orderId', payMoney: 100 } });
doPayment.mockResolvedValue({ code: 10000, data: { html: 'html', paymentId: 'paymentId' } });
instance._handlePayResult = jest.fn();
instance._doPayment({ appId: 'alipayapp' });
expect(doPayment).toHaveBeenCalledWith({ orderId: 'orderId', payMoney: 100, payAppId: 'alipayapp' });
});
it('should handle success response from doPayment and call aliPay or wxPay', () => {
instance.setState({ data: { orderId: 'orderId', payMoney: 100 } });
instance.comfirmModal = { show: jest.fn() };
instance._handlePayResult = jest.fn();
doPayment.mockResolvedValue({ code: 10000, data: { html: 'html', paymentId: 'paymentId' } });
Base64.encode.mockReturnValue('base64Str');
aliPay.mockResolvedValue({ code: 10000 });
instance._doPayment({ appId: 'alipayapp' });
expect(aliPay).toHaveBeenCalledWith('base64Str');
expect(instance._handlePayResult).toHaveBeenCalledWith({ code: 10000 });
expect(instance.comfirmModal.show).toHaveBeenCalled();
doPayment.mockResolvedValue({ code: 10000, data: { html: 'html', paymentId: 'paymentId' } });
wxPay.mockResolvedValue({ code: 10000 });
instance._doPayment({ appId: 'wxpayapp' });
expect(wxPay).toHaveBeenCalledWith('base64Str');
expect(instance._handlePayResult).toHaveBeenCalledWith({ code: 10000 });
expect(instance.comfirmModal.show).toHaveBeenCalled();
});
it('should handle failure response from doPayment', () => {
instance.setState({ data: { orderId: 'orderId', payMoney: 100 } });
doPayment.mockResolvedValue({ code: 50000 });
instance._handlePayResult = jest.fn();
instance._doPayment({ appId: 'alipayapp' });
expect(toast).toHaveBeenCalledWith('服务器开小差啦~');
expect(instance._handlePayResult).not.toHaveBeenCalled();
});
});
describe('render', () => {
it('should render the loading view if isLoading is true', () => {
instance.setState({ visible: true, isLoading: true });
const wrapper = shallow(
it('should render the content view if isLoading is false', () => {
instance.setState({ visible: true, isLoading: false, leftTime: 10000, data: { payMoney: 100 } });
const wrapper = shallow(<PayModal />);
expect(wrapper.find('View').at(0).props.style).toEqual(styles.container);
expect(wrapper.find('LNumberText')).toHaveLength(1);
expect(wrapper.find('TimerText')).toHaveLength(1);
expect(wrapper.find('LNetImage')).toHaveLength(0);
});
it('should render the timeout view if leftTime is 0', () => {
instance.setState({ visible: true, isLoading: false, leftTime: 0, data: { payMoney: 100 } });
const wrapper = shallow(<PayModal />);
expect(wrapper.find('View').at(0).props.style).toEqual(styles.container);
expect(wrapper.find('LNumberText')).toHaveLength(0);
expect(wrapper.find('TimerText')).toHaveLength(0);
expect(wrapper.find('LNetImage')).toHaveLength(1);
});
}); });
原文地址: https://www.cveoy.top/t/topic/f3DZ 著作权归作者所有。请勿转载和采集!