🔍
Fluent UI のダイアログのスナップショット テストがうまくいかない場合の対応方法
はじめに
Fluent UI のダイアログを含むコンポーネントのスナップショット テストを実施しようとするとスナップショットを取れないことがあります。
問題点
まずはどのような動作をするか確認します。
コンポーネント
公式ドキュメントのサンプルそのままです。
import React from 'react';
import {
Button,
Dialog,
DialogActions,
DialogBody,
DialogContent,
DialogSurface,
DialogTitle,
DialogTrigger
} from '@fluentui/react-components';
function App() {
return (
<Dialog>
<DialogTrigger disableButtonEnhancement>
<Button>Open dialog</Button>
</DialogTrigger>
<DialogSurface>
<DialogBody>
<DialogTitle>Dialog title</DialogTitle>
<DialogContent>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
exercitationem cumque repellendus eaque est dolor eius expedita
nulla ullam? Tenetur reprehenderit aut voluptatum impedit voluptates
in natus iure cumque eaque?
</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement>
<Button appearance="secondary">Close</Button>
</DialogTrigger>
<Button appearance="primary">Do Something</Button>
</DialogActions>
</DialogBody>
</DialogSurface>
</Dialog>
);
}
テスト コード
ボタンをクリックしてダイアログを表示させてからスナップショットを取得します。
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './App';
it('should create a shapshot', async () => {
const user = userEvent.setup();
const { asFragment } = render(<App />);
await user.click(screen.getByRole('button', { name: /Open dialog/i }));
expect(asFragment()).toMatchSnapshot();
});
スナップショット
ダイアログの部分がレンダリングされていません。
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should create a shapshot 1`] = `
<DocumentFragment>
<button
class="fui-Button r1alrhcs"
data-tabster="{"restorer":{"type":1}}"
type="button"
>
Open dialog
</button>
</DocumentFragment>
`;
原因
ダイアログは記述した場所にレンダリングされるのではなく、特別な div
要素の中にレンダリングされます。Jest の場合はその要素がないため、ダイアログはレンダリングされません。
対応方法
ダイアログのコンポーネントの一部である DialogSurface
には mountNode
プロパティがあります。そこに要素を指定することで任意の場所にダイアログを表示できるようになります。
実際にコードを修正してみます。
コンポーネント
App
コンポーネントは mountNode
プロパティを受け取るようにします。
+ interface AppProps {
+ mountNode?: HTMLElement;
+ }
- function App() {
+ function App({ mountNode }: AppProps) {
return (
<Dialog>
<DialogTrigger disableButtonEnhancement>
<Button>Open dialog</Button>
</DialogTrigger>
- <DialogSurface>
+ <DialogSurface mountNode={mountNode}>
...
テスト コード
新しい div
要素を作成します。作成した要素を App
コンポーネントの mountNode
プロパティに渡します。合わせて render
メソッドのパラメーターにも渡すようにします。
it('should create a shapshot', async () => {
const user = userEvent.setup();
- const { asFragment } = render(<App />);
+ const container = document.body.appendChild(document.createElement('div'));
+ const { asFragment } = render(<App mountNode={container} />, { container });
await user.click(screen.getByRole('button', { name: /Open dialog/i }));
expect(asFragment()).toMatchSnapshot();
});
スナップショット
ダイアログがレンダリングされていることを確認できます。
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should create a shapshot 1`] = `
<DocumentFragment>
<button
class="fui-Button r1alrhcs"
data-tabster="{"restorer":{"type":1}}"
type="button"
>
Open dialog
</button>
<div
aria-hidden="true"
class="fui-DialogSurface__backdrop rsptlh5"
/>
<div
aria-labelledby="dialog-title-r1"
aria-modal="true"
class="fui-DialogSurface rsmdyd3"
data-tabster="{"restorer":{"type":0},"modalizer":{"id":"modal-r0","isOthersAccessible":false,"isTrapped":true}}"
role="dialog"
tabindex="-1"
>
<i
aria-hidden="true"
data-tabster-dummy=""
role="none"
style="position: fixed; height: 1px; width: 1px; opacity: 0.001; z-index: -1;"
tabindex="0"
/>
<div
class="fui-DialogBody r1h3qql9"
>
<h2
class="fui-DialogTitle rxjm636 ___mh25s00_kwrsjh0 fsyjsko"
id="dialog-title-r1"
>
Dialog title
</h2>
<div
class="fui-DialogContent r1v5zwsm"
>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam exercitationem cumque repellendus eaque est dolor eius expedita nulla ullam? Tenetur reprehenderit aut voluptatum impedit voluptates in natus iure cumque eaque?
</div>
<div
class="fui-DialogActions rhfpeu0 ___pj8zjl0_1y84z0u f1a7i8kp fd46tj4 fsyjsko f1f41i0t f1jaqex3 f2ao6jk"
>
<button
class="fui-Button r1alrhcs"
data-tabster="{"restorer":{"type":1}}"
type="button"
>
Close
</button>
<button
class="fui-Button r1alrhcs ___1akj6hk_ih97uj0 ffp7eso f1p3nwhy f11589ue f1q5o8ev f1pdflbu f1phragk f15wkkf3 f1s2uweq fr80ssc f1ukrpxl fecsdlb f1rq72xc fnp9lpt f1h0usnq fs4ktlq f16h9ulv fx2bmrt f1d6v5y2 f1rirnrt f1uu00uk fkvaka8 f1ux7til f9a0qzu f1lkg8j3 fkc42ay fq7113v ff1wgvm fiob0tu f1j6scgf f1x4h75k f4xjyn1 fbgcvur f1ks1yx8 f1o6qegi fcnxywj fmxjhhp f9ddjv3 f17t0x8g f194v5ow f1qgg65p fk7jm04 fhgccpy f32wu9k fu5nqqq f13prjl2 f1czftr5 f1nl83rv f12k37oa fr96u23"
type="button"
>
Do Something
</button>
</div>
</div>
<i
aria-hidden="true"
data-tabster-dummy=""
role="none"
style="position: fixed; height: 1px; width: 1px; opacity: 0.001; z-index: -1;"
tabindex="0"
/>
</div>
<span
hidden=""
/>
</DocumentFragment>
`;
おわりに
ダイアログについて説明しましたがドロワー (Drawer
コンポーネント) も同様です。
Discussion