使用 React Hook 客户端测试 Apollo Query



我正在尝试使用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);

我的测试文件如下

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();
});
});

我不断收到错误:TypeError:this.state.client.stop 不是一个函数

我还删除了包装器<MockedProvider>我得到了另一个错误不变违规:无法在上下文中找到"客户端"或作为道具传入。将根组件包装在 中,或将 ApolloClient 实例传入 通过道具。

有谁知道为什么我会收到此错误以及如何解决此问题?

我没有解决方案,但我有一些信息。

首先,我在这里遇到了同样的错误,用@testing-library/react渲染。

然后我尝试使用 ReactDOM 渲染,如下所示:

// inside the it() call with async function
const container = document.createElement("div");
ReactDOM.render(
< MockedProvider {...props}>
<MyComponent />
</MockedProvider>,
container
);
await wait(0);
expect(container).toMatchSnapshot();

并且还尝试使用酶渲染,如下所示:

// inside the it() call, with async function too
const wrapper = mount(
<MockedProvider {...props}>
<MyComponent />
</MemoryRouter>
);
await wait(0);
expect(wrapper.html()).toMatchSnapshot();

ReactDOM和Enzyme方法都工作正常。 关于我们得到的错误,我认为这可能与@testing-library/react有关=/

我没有尝试用react-test-renderer渲染,也许它也可以。

嗯,这就是我得到的...也许它以某种方式帮助你。

PS.:关于waait:https://www.apollographql.com/docs/react/development-testing/testing/#testing-final-state


编辑5 Feb 2020:

根据 https://github.com/apollographql/react-apollo/pull/2165#issuecomment-478865830,我找到了该解决方案(它看起来很丑但有效̄\_(ツ)_/̄(:

<MockedProvider {...props}>
<ApolloConsumer>
{client => {
client.stop = jest.fn();
return <MyComponent />;
}}
</ApolloConsumer>
</MockedProvider>

我遇到了同样的问题,并且能够解决它。我缺少同伴依赖。

您的package.json未显示,所以我不确定您的问题是否与我的相同,但我能够通过安装"apollo-client"来解决问题。

我正在为我的客户端使用 AWS Appsync,因此没有安装 apollo-client。

相关内容

  • 没有找到相关文章