ACT(...)中未包装测试中的更新

发布于 2025-01-21 06:36:45 字数 8723 浏览 1 评论 0原文

在测试组件时,我没有被ACT错误包裹。有人知道如何解决吗?我已经尝试了很多事情,例如将FindbyTestattr用在Waitfor中,但它没有用。顺便说一句,我的测试不会失败,断言是正确的,我只会得到这些警告。我想知道这个错误根本显示出是我的错还是开玩笑的错误?

// 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;

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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

始终不够 2025-01-28 06:36:45

好的,似乎我通过包装请求解决了这个问题。整个行为错误非常烦人:/

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

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();
          });
      });
    });
  });
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文