Testing Library学習中ということで、いろんなテストケースを記事にまとめています。
今回はマウスオーバーでポップアップが表示されるケースについてまとめました。
テスト実装中に遭遇したWarningの解消にかなり手こずったので、同様の問題にぶつかっている方がいたら記載した解消法が参考になるかと思います。
実装
Terms and Conditionの部分をマウスオーバーしたときに、No ice cream will actually be delivered
というポップアップが表示されるケースを考えます。
テストするファイルのコンポーネントSummaryForm
の中身は以下のようになっています。
スタイルにはreact-bootstrap
を使用しています。
export default function SummaryForm() {
const [tcChecked, setTcChecked] = useState(false);
const popover = (
<Popover id="termsandconditions-popover">
<Popover.Content>No ice cream will actually be delivered</Popover.Content>
</Popover>
);
const checkboxLabel = (
<span>
I agree to
<OverlayTrigger placement="right" overlay={popover}>
<span style={{ color: 'blue' }}> Terms and Conditions</span>
</OverlayTrigger>
</span>
);
return (
<Form>
<Form.Group controlId="terms-and-conditions">
<Form.Check
type="checkbox"
checked={tcChecked}
onChange={(e) => setTcChecked(e.target.checked)}
label={checkboxLabel}
/>
</Form.Group>
<Button variant="primary" type="submit" disabled={!tcChecked}>
Confirm order
</Button>
</Form>
);
}
同じ階層にtestフォルダを作成し、その中にSummaryForm.test.jsx
を作成します。
test('popover responds to hover', async () => {
// SummaryFormの仮想DOMにアクセス
render(<SummaryForm />);
// ポップアップが初期状態で隠れていれば成功
const nullPopover = screen.queryByText(
/no ice cream will actually be delivered/i
);
expect(nullPopover).not.toBeInTheDocument();
// マウスオーバーを模擬
const termsAndConditions = screen.getByText(/terms and conditions/i);
userEvent.hover(termsAndConditions);
// ポップアップが表示されていれば成功
const popover = screen.getByText(/no ice cream will actually be delivered/i);
expect(popover).toBeInTheDocument();
// マウスオーバーの解除を模擬
userEvent.unhover(termsAndConditions);
await waitForElementToBeRemoved(() =>
screen.queryByText(/no ice cream will actually be delivered/i)
);
});
要素へのマウスオーバーのイベント模擬には、userEventを使います。
userEventを使うことで細かいブラウザ操作の模擬を行うことができるため、fireEventよりもテストでの使用に適しています。
また、テストファイル内ではqueryByText
getByText
というクエリメソッドを使い分けています。
query
get
にあたる部分をコマンド、Text
にあたる部分をクエリタイプと呼ぶのですが、それぞれの部分で以下のようなことを指定しています。
コマンド
- get: DOMに要素があることを期待しており、要素がなければエラーを返す
- query: DOMに要素がないことを期待しており、要素がなければnullを返す
- find: 要素が非同期で現れることを期待しており、Promiseを返す
クエリタイプ
- Role: すべての要素
- AltText: imgやinputなどのText以外の要素
- Text: divやspanなどのForm外の要素
クエリタイプの使い分けについては、公式Docsに記載されている優先度に従います。
最後に、await waitForElementToBeRemoved
の記述についての補足です。
アサーションについてはexpect(popover).toBeInTheDocument()
で終わっているのですが、userEvent.unhover(termsAndConditions)
以降の記述がないと以下のWarningに遭遇します。
このWarningはテスト内でDOMの更新を行った場合に発生します。
ただ、Warningに従ってact()でのラッピングを行う必要はなく(Testing Libraryですでに行っているため)、代わりにテスト終了後の状態を決めるような操作を行ってあげる必要があります。
userEvent.unhover(termsAndConditions)
でマウスオーバーを解除したときのイベントを発生させ、await waitForElementToBeRemoved(...)
で要素が消えた後のコールバックを非同期で実行しています。
参考資料