Q. ローカル環境でのE2EテストがReactDevOverlayにより失敗する
Next.jsを利用したサービスでPlaywrightテストをローカル実行する際に、ReactDevOverlayを非表示する方法について
状況
Next.jsを利用したサービスで動作確認のためにPlaywrightテストをローカル環境で実行したところ、Hydration ErrorによりReactDevOverlayが表示され、テストが失敗するという報告があった。
ReactDevOverlayとは
Next.jsアプリをローカルで開発している際に、エラーが発生すると表示されるモーダルはReactDevOverlayと呼ばれている。
https://www.npmjs.com/package/@next/react-dev-overlay
これの存在意義については、CRAのissueで言及されている。
https://github.com/facebook/create-react-app/issues/6530#issuecomment-662914362
また、ReactDevOverlayを無効にしない理由として上記のissueがNext.jsのメンテナに引用されている。
https://github.com/vercel/next.js/discussions/23970#discussioncomment-599510
つまり、Next.jsはReactDevOverlayを無効にする機能を提供していない。
解決案
次にあげている手法を安直に適用すると、通常の開発時にも影響があるため注意。
ReactDevOverlayを非表示にする
Next.jsで表示されるReactDevOverlayは、nextjs-portalというweb componentが実体のため、これを非表示にすればReactDevOverlayを無効化することが出来る。
nextjs-portal {
display: none;
}
Next.jsは、next.config.jsなどでReactDevOverlayを無効にする機能を提供していない。
Hydration Errorを無効にする
Reactでは、suppressHydrationWarning
を指定することで、その配下で発生するHydration Errorを無効にすることができる。
<div suppressHydrationWarning={true}>
Current Date: {new Date().toLocaleDateString()}
</div>
雑に無効化したいのであれば、suppressHydrationWarning
をhtml要素やbody要素に指定してしまえばいい。
ただし、本来suppressHydrationWarning
はHydration Errorを避けることが困難な状況においてのみ限定的に利用するのが望ましい。
解決案の応用例
通常の開発時にもReactDevOverlayが表示されなくなることは好ましくないため、E2Eテストの実行時のみ適用されるのが望ましい。
E2Eテスト実行時のみReactDevOverlayを非表示にする
Next.jsのPage Routerであれば_app.js
、App Routerであればlayout.js
などで、環境変数を参照してReactDevOverlayを非表示にする。
{
process.env.E2E ? (
<style>
{`
nextjs-portal {
display: none;
}
`}
</style>
) : null;
}
E2Eテスト実行時のみHydration Errorを無効にする
環境変数を参照して、suppressHydrationWarning
を指定することでHydration Errorを無効にする。
return (
<html lang="ja">
<body suppressHydrationWarning={process.env.E2E ? true : false}>
{children}
</body>
</html>
);
PlaywrightでReactDevOverlayを非表示にする
ページ遷移後に、実行する必要がある点に注意。
await page.goto("http://localhost:3000");
await page.addStyleTag({
content: `
nextjs-portal {
display: none;
}`,
});
PlaywrightでHydration Errorを無効にする
ページ遷移後に、実行する必要がある点に注意。
await page.goto("http://localhost:3000");
await page.evaluate(() => {
const body = document.querySelector("body");
body.setAttribute("suppressHydrationWarning", "true");
});