'An update inside a test was not wrapped in act(...)
I get not wrapped in act error while testing my component. Has someone any idea how to solve it? I've already tried many things like wrapping findByTestAttr in waitFor but it didn't work. And btw my test doesn't fail, assertion is correct, i only get these warnings. I wonder if it's actually my fault or it's jest fault that this error shows at all?
// 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:\Users\jacek\Desktop\moje-strony\CurrencyCenter\src\components\home\infoCard\currencyConverter\CurrencyConverter.tsx:47:55)
at WrapperComponent (C:\Users\jacek\Desktop\moje-strony\CurrencyCenter\node_modules\@wojtekmaj\enzyme-adapter-utils\src\createMountWrapper.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:\Users\jacek\Desktop\moje-strony\CurrencyCenter\src\components\home\infoCard\currencyConverter\CurrencyConverter.tsx:47:55)
at WrapperComponent (C:\Users\jacek\Desktop\moje-strony\CurrencyCenter\node_modules\@wojtekmaj\enzyme-adapter-utils\src\createMountWrapper.jsx:46:26)
130 |
131 | const setter = status.last === HAVE ? setReceiveInputValues : setHaveInputValues;
Solution 1:[1]
Ok, it seems like I solved this problem by wrapping request.respondWith with waitFor. This entire act error is very annoying :/
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();
});
});
});
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Jacek Krajewski |
