cypressで実行したときにmetaタグがmatchしないエラーでhydrationに失敗する
現象
localhost
にアプリを立ち上げ、npx cypress open
からcypressのコンソーつに入ってe2eテストを実行しようとすると、以下のwarningが出る。
Warning: Expected server HTML to contain a matching <meta> in <head>.
at meta
at V1Meta (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:4542:7)
at Meta (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:4676:7)
at head
at html
at App
at RemixRoute (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:4289:3)
at RenderedRoute (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:2653:5)
at RenderErrorBoundary (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:3018:9)
at Routes (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:2891:5)
at Router (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:2838:15)
at RouterProvider (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:2789:5)
at RemixErrorBoundary (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:3917:5)
at RemixBrowser (http://localhost:3000/build/_shared/chunk-WM4MSLGF.js:5305:55)
で、これが原因 (と思われる) でhydrationに失敗
react-dom.development.js:12507 Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
at throwOnHydrationMismatch (react-dom.development.js:12507:9)
at tryToClaimNextHydratableInstance (react-dom.development.js:12535:7)
at updateHostComponent (react-dom.development.js:19902:5)
at beginWork (react-dom.development.js:21618:14)
at HTMLUnknownElement.callCallback2 (react-dom.development.js:4164:14)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16)
at invokeGuardedCallback (react-dom.development.js:4277:31)
at beginWork$1 (react-dom.development.js:27451:7)
at performUnitOfWork (react-dom.development.js:26560:12)
at workLoopConcurrent (react-dom.development.js:26543:5)
ついでにこんなwarning/errorも。
Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.
printWarning @ react-dom.development.js:86
error @ react-dom.development.js:60
errorHydratingContainer @ react-dom.development.js:11473
recoverFromConcurrentError @ react-dom.development.js:25846
performConcurrentWorkOnRoot @ react-dom.development.js:25750
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
at updateHostRoot (react-dom.development.js:19849:57)
at beginWork (react-dom.development.js:21615:14)
at beginWork$1 (react-dom.development.js:27426:14)
at performUnitOfWork (react-dom.development.js:26560:12)
at workLoopSync (react-dom.development.js:26466:5)
at renderRootSync (react-dom.development.js:26434:7)
at recoverFromConcurrentError (react-dom.development.js:25850:20)
at performConcurrentWorkOnRoot (react-dom.development.js:25750:22)
at workLoop (scheduler.development.js:266:34)
at flushWork (scheduler.development.js:239:14)
cypressが自動で開くchromeウィンドウでだけエラーになり、cypress中に別ブラウザでアプリアクセスしても上記のエラーは出ない。
原因
cypressはブラウザのsame-origin policyを緩めてアプリを制御するために<script type='text/javascript'> document.domain = 'localhost'; </script>
をheadに設定するらしい。
https://docs.cypress.io/guides/guides/web-security
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#changing_origin
このscript
がサーバーhtmlにはなく、クライアントhtmlだけにあるためhydrationエラーになっているようだ。
原因調査メモ
エラーメッセージではmetaがサーバーとクライアントで異なるというが、serverとclientのhtmlを見るとscriptがあるかどうかの違い。
server:
<head>
<script type='text/javascript'> document.domain = 'localhost'; </script>
<meta charSet="utf-8"/>
<title>Sign Up</title>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<link rel="stylesheet" href="/build/_assets/tailwind-DKMRCRP6.css"/>
</head>
client:
<head>
<meta charset="utf-8">
<title>Sign Up</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/build/_assets/tailwind-DKMRCRP6.css">
</head>
別ブラウザでアクセスした時 (エラー出ない) にサーバーから送られるhtmlはscriptがない。
server in brave
<head>
<meta charSet="utf-8"/>
<title>Sign Up</title>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<link rel="stylesheet" href="/build/_assets/tailwind-DKMRCRP6.css"/>
</head>
以上からcypressが<script>
を差し込んでいるのではないか?と思い、調べたら上記原因にたどり着いた。
対処法
今の所根本的な対処法はわからない。画面描画には影響なく、本番環境ではエラーも発生しない。
remix公式の提供するテンプレート (stack) では握りつぶしている。
Discussion