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

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

イメージとして次のようなテストを書いてみるが想定通りの動作をしない
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();
});
});
});

Received element is not disabled
とのことなのでボタンがdisabledではない
- submit処理中の状態を補足できていないのか?
- 単にdisabledが効いていないのか

React-hook-formのDiscussionに質問を投げてみた
解決してくれると嬉しいが

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を返せばうまく動く・・・なぜ・・・?

以下のコードで解決
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にクローズされました