Reactjs 使用React钩子客户端测试Apollo查询

Reactjs 使用React钩子客户端测试Apollo查询,reactjs,jestjs,react-hooks,react-apollo,Reactjs,Jestjs,React Hooks,React Apollo,我正在尝试使用jest为这个组件编写测试 import { useState, useRef } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { Query } from 'react-apollo'; import { updateYourDetails } from 'universal/domain/health/yourDetails/yo

我正在尝试使用jest为这个组件编写测试

import { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Query } from 'react-apollo';

import { updateYourDetails } from 'universal/domain/health/yourDetails/yourDetailsActions';
import Input from 'universal/components/input/input';
import InputNumber from 'universal/components/input/inputNumber/inputNumber';
import AsyncButton from 'universal/components/asyncButton/asyncButton';
import ErrorMessage from 'universal/components/errorMessage/errorMessage';
import Link from 'universal/components/link/link';
import analytics from 'universal/utils/analytics/analytics';
import { isChatAvailable } from 'universal/logic/chatLogic';
import { validators } from 'universal/utils/validation';
import { localTimezone, getWeekdays } from 'universal/utils/date';
import {
  CALL_ME_BACK_LOADING_MSG,
  CALL_ME_BACK_LABELS_SCHEDULE_TIME,
  CALL_ME_BACK_LABELS_SELECTED_DATE,
  CALL_ME_BACK_ERROR_MSG,
  CALL_ME_BACK_TEST_PARENT_WEEKDAY,
  CALL_ME_BACK_TEST_CHILD_WEEKDAY,
} from 'universal/constants/callMeBack';

import CallCenterAvailibility from './CallCenterAvailibility';
import SelectWrapper from './SelectWrapper';
import SelectOption from './SelectOption';
import styles from './callMeBackLightBox.css';
import { CALL_ME_BACK_QUERY } from './callMeBackQuery';
import postData from './postData';

export const CallMeForm = props => {
  const initSelectedDate = getWeekdays()
    .splice(0, 1)
    .reduce(acc => ({ ...acc }));

  const { onSubmissionComplete, className, variant } = props;
  const [hasSuccessfullySubmitted, setHasSuccessfullySubmitted] = useState(false);
  const [apiStatus, setApiStatus] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [cellNumber, setCallNumber] = useState(props.cellNumber || '');
  const [customerFirstName, setCustomerFirstName] = useState(props.customerFirstName || '');
  const [number, setNumber] = useState(props.Number || '');
  const [selectedDate, setSelectedDate] = useState(initSelectedDate || '');
  const [scheduledTime, setScheduledTime] = useState('');

  const weekdays = getWeekdays() || [];
  const timezone = localTimezone || '';
  const requestReceived = apiStatus === 'CALLBACK_ALREADY_EXIST';

  const cellNumberInput = useRef(null);
  const customerFirstNameInput = useRef(null);

  const getQuery = () => (
    <Query query={CALL_ME_BACK_QUERY} variables={{ weekday: selectedDate.weekday }}>
      {({ data, error, loading }) => {
        if (loading)
          return (
            <SelectWrapper disabled labelTitle={CALL_ME_BACK_LABELS_SCHEDULE_TIME} name="scheduledTime">
              <SelectOption label={CALL_ME_BACK_LOADING_MSG} />
            </SelectWrapper>
          );
        if (error) return <ErrorMessage hasError errorMessage={<p>{CALL_ME_BACK_ERROR_MSG}</p>} />;
        return (
          <CallCenterAvailibility
            selectedDate={selectedDate}
            callCenterBusinessHour={data.callCenterBusinessHour}
            onChange={val => setScheduledTime(val)}
          />
        );
      }}
    </Query>
  );

  const getPostSubmitMessage = (firstName: string, type: string) => {
    const messages = {
      callCentreClosed: `a`,
      requestReceived: `b`,
      default: `c`,
    };
    return `Thanks ${firstName}, ${messages[type] || messages.default}`;
  };

  const validate = () => {
    const inputs = [customerFirstNameInput, cellNumberInput];
    const firstInvalidIndex = inputs.map(input => input.current.validate()).indexOf(false);
    const isValid = firstInvalidIndex === -1;

    return isValid;
  };

  const onSubmitForm = event => {
    event.preventDefault();
    onSubmit();
  };

  const onSubmit = async () => {
    if (variant === '0' && !validate()) {
      return;
    }

    analytics.track(analytics.events.callMeBack.callMeBackSubmit, {
      trackingSource: 'Call Me Form',
    });

    setIsLoading(true);

    const srDescription = '';
    const response = await postData({
      cellNumber,
      customerFirstName,
      number,
      scheduledTime,
      timezone,
      srDescription,
    });
    const { status } = response;

    const updatedSubmissionFlag = status === 'CALLBACK_ALREADY_EXIST' || status === 'CALLBACK_ADDED_SUCCESSFULLY';

    // NOTE: add a slight delay for better UX
    setTimeout(() => {
      setApiStatus(apiStatus);
      setIsLoading(false);
      setHasSuccessfullySubmitted(updatedSubmissionFlag);
    }, 400);

    // Update Redux store
    updateYourDetails({
      mobile: cellNumber,
      firstName: customerFirstName,
    });

    if (onSubmissionComplete) {
      onSubmissionComplete();
    }
  };

  if (hasSuccessfullySubmitted) {
    return (
      <p aria-live="polite" role="status">
        {getPostSubmitMessage(
          customerFirstName,
          (!requestReceived && !isChatAvailable() && 'callCentreClosed') || (requestReceived && 'requestReceived')
        )}
      </p>
    );
  }

  return (
    <form onSubmit={onSubmitForm} className={className}>
      {variant !== '1' && (
        <>
          <label htmlFor="customerFirstName" className={styles.inputLabel}>
            First name
          </label>
          <Input
            className={styles.input}
            initialValue={customerFirstName}
            isMandatory
            maxLength={20}
            name="customerFirstName"
            onChange={val => setCustomerFirstName(val)}
            ref={customerFirstNameInput}
            value={customerFirstName}
            {...validators.plainCharacters}
          />
        </>
      )}
      {variant !== '1' && (
        <>
          <label htmlFor="cellNumber" className={styles.inputLabel}>
            Mobile number
          </label>
          <Input
            className={styles.input}
            initialValue={cellNumber}
            isMandatory
            maxLength={10}
            name="cellNumber"
            onChange={val => setCallNumber(val)}
            ref={cellNumberInput}
            type="tel"
            value={cellNumber}
            {...validators.tel}
          />
        </>
      )}
      {variant !== '1' && (
        <>
          {' '}
          <label htmlFor="number" className={styles.inputLabel}>
            Qantas Frequent Flyer number (optional)
          </label>
          <InputNumber
            className={styles.input}
            disabled={Boolean(props.number)}
            initialValue={number}
            name="number"
            onChange={val => setNumber(val)}
            value={number}
          />
        </>
      )}
      {weekdays && (
        <>
          <SelectWrapper
            testId={`${CALL_ME_BACK_TEST_PARENT_WEEKDAY}`}
            labelTitle={CALL_ME_BACK_LABELS_SELECTED_DATE}
            name="selectedDate"
            onChange={val =>
              setSelectedDate({
                ...weekdays.filter(({ value }) => value === val).reduce(acc => ({ ...acc })),
              })
            }
            tabIndex={0}
          >
            {weekdays.map(({ value, label }, i) => (
              <SelectOption
                testId={`${CALL_ME_BACK_TEST_CHILD_WEEKDAY}-${i}`}
                key={value}
                label={label}
                value={value}
              />
            ))}
          </SelectWrapper>
          {getQuery()}
        </>
      )}
      <AsyncButton className={styles.submitButton} onClick={onSubmit} isLoading={isLoading}>
        Call me
      </AsyncButton>
      <ErrorMessage
        hasError={(apiStatus >= 400 && apiStatus < 600) || apiStatus === 'Failed to fetch'}
        errorMessage={
          <p>
            There was an error submitting your request to call you back. Please try again or call us at{' '}
            <Link href="tel:134960">13 49 60</Link>.
          </p>
        }
      />
    </form>
  );
};

CallMeForm.propTypes = {
  cellNumber: PropTypes.string,
  customerFirstName: PropTypes.string,
  number: PropTypes.string,

  onSubmissionComplete: PropTypes.func,
  className: PropTypes.string,
  variant: PropTypes.string,
};

const mapStateToProps = state => {
  const { frequentFlyer, yourDetails } = state;

  return {
    cellNumber: yourDetails.mobile,
    customerFirstName: yourDetails.firstName,
    number: frequentFlyer.memberNumber,
  };
};

export default connect(mapStateToProps)(CallMeForm);
从'react'导入{useState,useRef};
从“道具类型”导入道具类型;
从'react redux'导入{connect};
从'react apollo'导入{Query};
从“universal/domain/health/yourDetails/yourDetailsActions”导入{updateYourDetails};
从“通用/组件/输入/输入”导入输入;
从“universal/components/input/InputNumber/InputNumber”导入InputNumber;
从“universal/components/AsyncButton/AsyncButton”导入AsyncButton;
从“universal/components/ErrorMessage/ErrorMessage”导入ErrorMessage;
从“通用/组件/链接/链接”导入链接;
从“universal/utils/analytics/analytics”导入分析;
从“universal/logic/chatLogic”导入{isChatAvailable};
从“universal/utils/validation”导入{validators};
从“universal/utils/date”导入{localTimezone,getWeekdays};
进口{
给我回电话,装邮件,
给我回个电话给我安排时间,
回拨\u我\u标签\u选定日期,
给我回电话错误消息,
在工作日给我回电话,考试,家长,
在工作日给我回电话,考试,孩子,
}来自“universal/constants/callMeBack”;
从“/CallCenterAvailability”导入CallCenterAvailability;
从“/SelectWrapper”导入SelectWrapper;
从“/SelectOption”导入SelectOption;
从“./callmebackgroundbox.css”导入样式;
从“./callMeBackQuery”导入{CALL_ME_BACK_QUERY};
从“/postData”导入postData;
导出常量CallMeForm=props=>{
const initSelectedDate=getWeekdays()
.拼接(0,1)
.reduce(acc=>({…acc}));
const{onSubmissionComplete,className,variant}=props;
const[HassSuccessfullySubmitted,SetHassSuccessfullySubmitted]=useState(false);
const[apiStatus,setApiStatus]=useState(“”);
const[isLoading,setIsLoading]=useState(false);
const[cellNumber,setCallNumber]=useState(props.cellNumber | |“”);
const[customerFirstName,setCustomerFirstName]=useState(props.customerFirstName | |“”);
const[number,setNumber]=useState(props.number | |“”);
const[selectedDate,setSelectedDate]=useState(initSelectedDate | |“”);
const[scheduledTime,setScheduledTime]=useState(“”);
const weekdays=getWeekdays()| 124;[];
常量时区=本地时区| |'';
const requestReceived=apiStatus==“回调已存在”;
const cellNumberInput=useRef(null);
const customerFirstNameInput=useRef(null);
常量getQuery=()=>(
{({数据,错误,加载})=>{
如果(装载)
返回(
);
如果(错误)返回;
返回(
setScheduledTime(val)}
/>
);
}}
);
const getPostSubmitMessage=(名字:string,类型:string)=>{
常量消息={
callCentreClosed:`a`,
requestReceived:`b`,
默认值:`c`,
};
返回`谢谢${firstName},${messages[type]| | messages.default}`;
};
常量验证=()=>{
常量输入=[customerFirstNameInput,cellNumberInput];
const firstInvalidIndex=inputs.map(input=>input.current.validate()).indexOf(false);
常量isValid=firstInvalidIndex==-1;
返回有效;
};
const onSubmitForm=事件=>{
event.preventDefault();
onSubmit();
};
const onSubmit=async()=>{
如果(变量=='0'&&!validate()){
返回;
}
analytics.track(analytics.events.callMeBack.callMeBackSubmit{
trackingSource:“呼叫我表单”,
});
设置加载(真);
常量srDescription='';
const response=等待postData({
手机号码,
customerFirstName,
数字,
预定时间,
时区,
描述,
});
const{status}=响应;
const updatedSubmissionFlag=状态=='CALLBACK_已经存在'| |状态=='CALLBACK_添加成功';
//注意:增加一点延迟以获得更好的用户体验
设置超时(()=>{
setApiStatus(apiStatus);
设置加载(假);
SetHassSuccessfullySubmited(更新的SubmissionFlag);
}, 400);
//更新Redux存储
更新您的详细信息({
手机号码:,
名字:customerFirstName,
});
如果(任务完成时){
onSubmissionComplete();
}
};
如果(已成功提交){
返回(

{getPostSubmitMessage( customerFirstName, (!requestReceived&&!isChatAvailable()&&“callCentreClosed”)| |(requestReceived&&“requestReceived”) )}

); } 返回( {variant!='1'&&( 名字 setCustomerFirstName(val)} ref={customerFirstNameInput} 值={customerFirstName} {…验证程序.纯字符} /> )} {variant!='1'&&( 手机号码 setCallNumber(val)} ref={cellNumberInput} type=“电话” 值={cellNumber} {…validators.tel} /> )} {variant!='1'&&( {' '} 澳航常客号码(可选) setNumber(val)} 值={number} /> )} {工作日&&( 选定日期({ …weekdays.filter(({value})=>value==val.reduce(acc=>({…acc})), }) } tabIndex={0} > {weekdays.map({value,label},i)=>( ))} {getQuery()} )} 打电话给我 =400&&apiStatus<600)| | apiStatus==='无法获取'} 错误信息={ 提交您的回拨请求时出错。请重试或致电{'} 13 49 60. import { render, cleanup } from '@testing-library/react'; import { MockedProvider } from 'react-apollo/test-utils'; import { shallow } from 'enzyme'; import MockDate from 'mockdate'; import { isChatAvailable } from 'universal/logic/chatLogic'; import { CALL_ME_BACK_QUERY } from './callMeBackQuery'; import { CallMeForm } from './CallMeForm'; import postData from './postData'; jest.mock('universal/components/input/input', () => 'Input'); jest.mock('universal/components/asyncButton/asyncButton', () => 'AsyncButton'); jest.mock('universal/components/errorMessage/errorMessage', () => 'ErrorMessage'); jest.mock('universal/logic/chatLogic'); jest.mock('./postData'); describe('CallMeForm', () => { let output; beforeEach(() => { jest.resetModules(); jest.resetAllMocks(); const mockQueryData = [ { client:{}, request: { query: CALL_ME_BACK_QUERY, variables: { weekday: '' }, }, result: { data: { callCenterBusinessHour: { timeStartHour: 9, timeStartMinute: 0, timeEndHour: 5, timeEndMinute: 0, closed: false, }, }, }, }, ]; const { container } = render(<MockedProvider mocks={mockQueryData} addTypename={false}><CallMeForm /></MockedProvider>); output = container; }); afterEach(cleanup); it('renders correctly', () => { expect(output).toMatchSnapshot(); }); });
<MockedProvider {...props}>
    <ApolloConsumer>
        {client => {
            client.stop = jest.fn();
            return <MyComponent />;
        }}
    </ApolloConsumer>
</MockedProvider>