测试内部的更新未封装在act(..)中



在测试组件时,我没有被act错误包裹。有人知道怎么解决吗?我已经尝试过很多方法,比如在waitFor中包装findByTestAttr,但都不起作用。顺便说一句,我的测试没有失败,断言是正确的,我只收到了这些警告。我想知道这个错误到底是我的错还是玩笑的错?

// CurrencyConverter.tsx
const HAVE = 'have';
const RECEIVE = 'receive';
const CurrencyConverter: React.FC = () => {
const [haveInputValues, setHaveInputValues] = React.useState<CurrencyInputValues>({ currency: Currencies.PLN, value: 100 });
const [receiveInputValues, setReceiveInputValues] = React.useState<CurrencyInputValues>({ currency: Currencies.USD, value: '' });
const [inputsSwapped, setInputsSwapped] = React.useState<boolean>(false);
const [rate, setRate] = React.useState<number>(0.0);
const [status, setStatus] = React.useState<Status>({ wasChangedByUser: true, last: HAVE });
const iconsStyle = useStyles();
const getNewCurrencies = (currency: Currencies, type: string) => {
const isHaveType = type === HAVE;
const notChangedCurrency = isHaveType ? receiveInputValues.currency : haveInputValues.currency;
let newHaveCurrency: Currencies, newReceiveCurrency: Currencies;
if (currency === Currencies.PLN && notChangedCurrency === Currencies.PLN) {
newHaveCurrency = isHaveType ? Currencies.PLN : receiveInputValues.currency;
newReceiveCurrency = isHaveType ? haveInputValues.currency : Currencies.PLN;
} else {
newHaveCurrency = isHaveType ? currency : Currencies.PLN;
newReceiveCurrency = isHaveType ? Currencies.PLN : currency;
}
return { newHaveCurrency, newReceiveCurrency };
};
const changeCurrency = (currency: Currencies, type: string) => {
const { newHaveCurrency, newReceiveCurrency } = getNewCurrencies(currency, type);
setReceiveInputValues((currState) => ({ ...currState, currency: newReceiveCurrency }));
setHaveInputValues((currState) => ({ ...currState, currency: newHaveCurrency }));
setStatus({ last: type, wasChangedByUser: true });
};
const changeValue = (value: number | '', type: string) => {
const setter = type === HAVE ? setHaveInputValues : setReceiveInputValues;
setter((currState) => ({ ...currState, value }));
setStatus({ last: type, wasChangedByUser: true });
};
const swapInputs = () => {
setHaveInputValues(receiveInputValues);
setReceiveInputValues(haveInputValues);
setInputsSwapped((currState) => !currState);
setStatus({ last: status.last === HAVE ? HAVE : RECEIVE, wasChangedByUser: true });
};
React.useEffect(() => {
if (!status.wasChangedByUser) return;
const getPropsToCompare = () => {
const isHaveStatus = status.last === HAVE;
const value = isHaveStatus ? haveInputValues.value : receiveInputValues.value;
const fromCurrency = isHaveStatus ? haveInputValues.currency : receiveInputValues.currency;
const toCurrency = isHaveStatus ? receiveInputValues.currency : haveInputValues.currency;
return { value, fromCurrency, toCurrency };
};
const getNewComparisonData = async () => {
const { value, fromCurrency, toCurrency } = getPropsToCompare();
if (value) return await axios.get<CurrencyComparison>(`${Endpoints.COMAPRE_CURRENCIES}/${value}/${fromCurrency}/${toCurrency}/`);
const onEmptyInputData = {
data: {
result: {
exchangeAmount: 0,
exchangeRate: rate,
},
},
};
return onEmptyInputData;
};
const updateComparison = async () => {
const { data } = await getNewComparisonData();
setRate(+data.result.exchangeRate);
const setter = status.last === HAVE ? setReceiveInputValues : setHaveInputValues;
setter((currState: CurrencyInputValues) => ({ ...currState, value: +data.result.exchangeAmount }));
};
updateComparison();
setStatus((currState) => ({ ...currState, wasChangedByUser: false }));
}, [status.wasChangedByUser]);
return (
<div className={classes.currencyConverter}>
<div className={classes.inputsWithConnector}>
<CurrencyInput label={HAVE} values={{ ...haveInputValues, changeCurrency, changeValue }} />
<div className={classes.inputsConnector}>
<div className={classes.connectorLine}></div>
<SwapHorizIcon className={classnames(iconsStyle.swap, inputsSwapped && iconsStyle.swapRotated)} onClick={swapInputs} />
</div>
<CurrencyInput label={RECEIVE} values={{ ...receiveInputValues, changeCurrency, changeValue }} />
</div>
<p className={classes.rate}>
Current rate:{' '}
<span className={classes.rateValue} data-test='currency-rate'>
{rate}
</span>
</p>
</div>
);
};
export default CurrencyConverter;

// test
const setup = () => {
return mount(<CurrencyConverter />);
};
describe('<CurrencyConverter />', () => {
let wrapper: ReactWrapper;
beforeEach(() => {
moxios.install(axiosInstance);
});
afterEach(() => {
moxios.uninstall(axiosInstance);
});
it('displays value in receive input and correct rate on page init', (done) => {
wrapper = setup();
moxios.wait(() => {
const request = moxios.requests.mostRecent();
request
.respondWith({
status: 200,
response: {
result: {
exchangeRate: '4',
exchangeAmount: '25',
},
},
})
.then(async () => {
wrapper.update();
const receiveValueInput = findByTestAttr(wrapper, 'receive-value-input');
const rate = findByTestAttr(wrapper, 'currency-rate');
expect(rate.text()).toEqual('4');
expect(receiveValueInput.prop('value')).toEqual(25);
done();
});
});
});
});

// error
console.error
Warning: An update to CurrencyConverter inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
/* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act      
at CurrencyConverter (C:UsersjacekDesktopmoje-stronyCurrencyCentersrccomponentshomeinfoCardcurrencyConverterCurrencyConverter.tsx:47:55)
at WrapperComponent (C:UsersjacekDesktopmoje-stronyCurrencyCenternode_modules@wojtekmajenzyme-adapter-utilssrccreateMountWrapper.jsx:46:26)
127 |       const { data } = await getNewComparisonData();
128 |
> 129 |       setRate(+data.result.exchangeRate);
|       ^
130 |
131 |       const setter = status.last === HAVE ? setReceiveInputValues : setHaveInputValues;
132 |       setter((currState: CurrencyInputValues) => ({ ...currState, value: +data.result.exchangeAmount }));
at printWarning (node_modules/react-dom/cjs/react-dom.development.js:67:30)
at error (node_modules/react-dom/cjs/react-dom.development.js:43:5)
at warnIfNotCurrentlyActingUpdatesInDEV (node_modules/react-dom/cjs/react-dom.development.js:24064:9)
at setRate (node_modules/react-dom/cjs/react-dom.development.js:16135:9)
at _callee2$ (src/components/home/infoCard/currencyConverter/CurrencyConverter.tsx:129:7)
at tryCatch (node_modules/regenerator-runtime/runtime.js:63:40)
at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:294:22)
at Generator.next (node_modules/regenerator-runtime/runtime.js:119:21)
console.error
Warning: An update to CurrencyConverter inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
/* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act      
at CurrencyConverter (C:UsersjacekDesktopmoje-stronyCurrencyCentersrccomponentshomeinfoCardcurrencyConverterCurrencyConverter.tsx:47:55)
at WrapperComponent (C:UsersjacekDesktopmoje-stronyCurrencyCenternode_modules@wojtekmajenzyme-adapter-utilssrccreateMountWrapper.jsx:46:26)
130 |
131 |       const setter = status.last === HAVE ? setReceiveInputValues : setHaveInputValues;

好的,我似乎通过用waitFor包装request.respondWith来解决这个问题。整个动作错误非常令人讨厌:/

it('displays value in receive input and correct rate on page init', (done) => {
wrapper = setup();
moxios.wait(async () => {
const request = moxios.requests.mostRecent();
await waitFor(() => {
request
.respondWith({
status: 200,
response: {
result: {
exchangeRate: '4',
exchangeAmount: '25',
},
},
})
.then(() => {
wrapper.update();
const receiveValueInput = findByTestAttr(wrapper, 'receive-value-input');
const rate = findByTestAttr(wrapper, 'currency-rate');
expect(rate.text()).toEqual('4');
expect(receiveValueInput.prop('value')).toEqual(25);
done();
});
});
});
});

最新更新