Closed7

React-hook-formでsubmit中はボタンがdisabledになることをテストしたい

masashimasashi

React-hook-formにはformStateとしてsubmit中を表すisSubmittingが存在する
Submit時に非同期処理を走らせることが多く、その非同期処理中にボタンを押させたくないため次のようにしたい

<button disabled={isSubmitting}>submit</button>
masashimasashi

イメージとして次のようなテストを書いてみるが想定通りの動作をしない

import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('submit処理中は、submitボタンはdisabledである', async () => {
  const user = userEvent.setup();
  const mockOnSubmit = jest.fn();

  render(
    <Form
      title="フォーム"
      onSubmit={mockOnSubmit}
      defaultValues={defaultValuesPassingValidations}
    />
  );

  await act(async () => {
    await user.click(screen.getByRole('button', { name: /submit/ })).then(() => {
      expect(screen.getByRole('button', { name: /submit/ })).toBeDisabled();
    });
  });
});
masashimasashi

Received element is not disabledとのことなのでボタンがdisabledではない

  • submit処理中の状態を補足できていないのか?
  • 単にdisabledが効いていないのか
masashimasashi

Kent C. Dobbs 曰くThe more your tests resemble the way your software is used, the more confidence they can give you.  とのことなので、ダブルクリックを検証すればいいと思い、次のコードを書いた

test.skip('submitボタンをダブルクリックしても、onValidの処理は1回のみ実行される', async () => {
  const mockOnValid = jest.fn();

  const { user } = renderWithUser(
    <Form
      onValid={mockOnValid}
    />
  );

  await act(async () => {
    await user.dblClick(screen.getByRole('button', { name: /submit/ }));
  });
  expect(mockOnValid).toHaveBeenCalledTimes(1);
});

しかし残念ながらこれは2回呼び出されててエラーが起きる!
一方でonValidをPromiseを返せばうまく動く・・・なぜ・・・?

masashimasashi

以下のコードで解決
actが終わった後にstateを処理する仕様であったので、解決しないPromiseを利用すればよかった!

import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('submit処理中は、submitボタンはdisabledであり、処理後はdisabledではない', async () => {
  const user = userEvent.setup();
  let resolve: (v: unknown) => void;

  const mockOnSubmit = jest.fn().mockReturnValue(
      new Promise((_resolve) => {
        resolve = _resolve;
      })
    );

  render(
    <Form
      title="フォーム"
      onSubmit={mockOnSubmit}
      defaultValues={defaultValuesPassingValidations}
    />
  );

  await act(async () => {
    await user.click(screen.getByRole('button', { name: /submit/ }))   
  });

  expect(screen.getByRole('button', { name: /submit/ })).toBeDisabled();

  await act(async () => {
    resolve(jest.fn)
  });

  expect(screen.getByRole('button', { name: /submit/ })).not.toBeDisabled();
  });
});
このスクラップは2022/08/28にクローズされました