Q. ローカル環境でのE2Eテストが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");});