проблема при работе с react-testing-library с помощью material-ui

Пытаюсь написать свой первый тест с помощью react-testing-library, но похоже, что он не захватит правильный элемент material-ui.

https://codesandbox.io/s/lx5nl1839z

Я получаю сообщение об ошибке, в котором говорится, что событие никогда не запускалось.

Ожидается, что фиктивная функция будет вызываться один раз, но она вызывалась ноль раз.

Когда я меняю button на обычную кнопку вместо Button из материала ui, тогда он работает.

Вот мой тест

test('calls with user email and password', () => {
  const login = jest.fn();
  const error = null as any;

  const { getByLabelText, getByText } = render(
    <LoginForm login={login} error={error} />
  );

  const email = getByLabelText('Email');
  const password = getByLabelText('Password');
  (email as any).value = '[email protected]';
  (password as any).value = 'password';
  fireEvent.click(getByText('Login'));


  expect(login).toHaveBeenCalledTimes(1);
  expect(login).toHaveBeenCalledWith({
      email: '[email protected]',
      password: 'password',
  });
});

Вот моя составляющая:

export const LoginForm: FunctionComponent<IProps> = ({ login, error }) => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  return (
    <Paper style={{ width: '400px', padding: '30px', margin: '0 auto' }}>
      <form
        onSubmit={e => {
          e.preventDefault();
          return login({ email, password });
        }}
      >
        <TextField
          id="email"
          label="Email"
          onChange={e => setEmail(e.target.value)}
          fullWidth
          variant="outlined"
          error={Boolean(getEmailError(email))}
          helperText={getEmailError(email)}
        />
        <TextField
          id="password"
          label="Password"
          type="password"
          style={{ marginTop: '10px' }}
          onChange={e => setPassword(e.target.value)}
          fullWidth
          variant="outlined"
        />
        <LoginError error={error} />
        <Button
          variant="contained"
          color="primary"
          type="submit"
          fullWidth
          // disabled={isDisabled(email, password)}
          style={{
            margin: '20px 0px',
          }}
        >
          Login
        </Button>
      </form>
      <Divider />
      <Typography style={{ margin: '10px auto' }}>Forgot password?</Typography>
    </Paper>
  );
};

person leogoesger    schedule 05.12.2018    source источник
comment
Вы проверили, что действительно вернуло getByLabelText? Не могли бы вы очистить свой код, чтобы его можно было просто вставить в codeandbox и проверить, что отрисовывается?   -  person epsilon    schedule 06.12.2018
comment
@epsilon codeandbox.io/s/lx5nl1839z   -  person leogoesger    schedule 07.12.2018


Ответы (4)


Во-первых, codeandbox ненадежна ...

codeandbox каким-то образом запускает тесты в браузере, а не в jsdom.

Вот проблема: https://github.com/kentcdodds/react-testing-library/issues/234

Суть в том, что он щелкает диапазон submit, а не кнопку. Он должен был пузыриться, но это не так.

Вот рабочий пример с моим локальным сервером с Typescript.

test('calls with user email and password', () => {
    const login = jest.fn();

    const { getByLabelText, container } = render(
      <LoginForm login={login} error={null} />
    );

    const emailNode = getByLabelText('Email');
    const passwordNode = getByLabelText('Password');

    if (
      emailNode instanceof HTMLInputElement &&
      passwordNode instanceof HTMLInputElement
    ) {
      fireEvent.change(emailNode, { target: { value: '[email protected]' } });
      fireEvent.change(passwordNode, { target: { value: 'password' } });
      const form = container.querySelector('form') as HTMLFormElement;
      form.dispatchEvent(new Event('submit'));
      expect(login).toHaveBeenCalledTimes(1);
      expect(login).toHaveBeenCalledWith({
        email: emailNode.value,
        password: passwordNode.value,
      });
    } else {
      expect(false);
    }
  });
person leogoesger    schedule 07.12.2018

Ваши текстовые поля не контролируются. Вы должны передать фактическое значение и установить фактическое значение в событии изменения. В настоящее время вы запускаете submit дважды в своих тестах.

Запуск тестов с этими исправлениями дает правильный вывод на консоль, но по какой-то причине codeandbox игнорирует сопоставление jest. Я не знаю, что там происходит, но с приведенными выше исправлениями суть работает.

person epsilon    schedule 07.12.2018
comment
это форма, ее необязательно отслеживать с помощью состояния. - person leogoesger; 07.12.2018
comment
Если вы вызываете обратный вызов `login со значениями состояния вашего компонента Login, тогда вам нужно сохранить значения из входов в state. Вы делаете это с помощью контролируемых входов. В противном случае ваш тест никогда не пройдет, независимо от того, какие компоненты ввода используются. - person epsilon; 07.12.2018
comment
github.com/kentcdodds/react-testing-library/issues/234 Я отправил запрос, так как вы видите, что это проблема jsdom. @epsilon - person leogoesger; 07.12.2018
comment
Мой тест прошел без использования контролируемого ввода, как и ожидалось. Форма не требует контролируемого ввода. - person leogoesger; 07.12.2018
comment
Нет, это не так, но ваш обратный вызов login был бесполезен без управляемого компонента. Как вы думаете, какое состояние в вашем Login компоненте могло бы быть установлено иначе? - person epsilon; 07.12.2018
comment
о, мальчик, ты вообще видел комментарий КентДодда? Комментарий создателя библиотеки ... Я не знаю, зачем вам что-то понижать, даже если создатель библиотеки уже сказал, что это ошибка. Проблема в jsdom, он не всплывает, как предполагалось. Поскольку внутри Button есть Span ... - person leogoesger; 08.12.2018
comment
Я так понимаю, что это ошибка jsdom. Я специально нацелился на вашу тестовую установку. Вы заметили обратный вызов, который использовал значения из состояния компонента Login, но эти значения в любом случае никогда не использовались в вашем компоненте. Для записи в исходном ответе не было обработчиков изменений в TextField. Они были добавлены после моего ответа. - person epsilon; 08.12.2018

Я размышлял над этим некоторое время и, наконец, получил кое-что, чтобы поместить data-testid на ввод, а не div вокруг него, который составлял TextField в material-ui.

Вы должны установить data-testid в inputProps следующим образом:

<TextField
     type="password"
     variant="outlined"
     required
     value={password}
     onChange={onChangePassword}
     inputProps={{
         'data-testid': 'testPassword'
     }}
 />

А затем вы можете получить к нему доступ в своем тесте таким образом и смоделировать новый ввод пользователя:

const passwordInput = getByTestId('testPassword');
fireEvent.change(passwordInput, { target: { value: VALID_PASSWORD } });
person Benjamin Yvernault    schedule 15.02.2021

Я видел много людей, у которых были проблемы с material-ui. Я предполагаю, что Button пытается сделать что-то необычное и нарушает нормальный поток событий HTML.

Я советую использовать jest.mock () и имитировать Button, чтобы вместо этого отобразить button.

person Giorgio Polvara - Gpx    schedule 06.12.2018