kufu / smarthr-ui

React components for creating SmartHR applications.
https://story.smarthr-ui.dev
MIT License
879 stars 141 forks source link

test: RSC対応状況のテストを仕組みを見直し、クライアントコンポーネントに対応する #5087

Closed s-sasaki-0529 closed 1 week ago

s-sasaki-0529 commented 2 weeks ago

概要

各コンポーネントの RSC の対応状況を担保する自動テストの構成を見直し、"use client" を付与したクライアントコンポーネントの対応状況までカバーできるように仕組みを見直します。

asIs

各コンポーネントに対して、以下のどちらかに分類し、それを満たしているかをテストしています。

A. サーバーコンポーネントとして描画できること (RSC上で使えること) B. サーバーコンポーネントで描画するとエラーが発生すること (RSC上で使えないこと)

しかし、この区分けの場合、コンポーネントに "use client" を付与してクライアントコンポーネントとした場合、それはサーバーコンポーネントには成れないのに、Aのテストにパスしてしまい、不正確な判定となってしまいます。

toBe

各コンポーネントに対して、以下の3種類のどれかに分類し、それを満たしてるかをテストします。

A. サーバーコンポーネント内で、サーバーコンポーネントとして描画できること B. サーバーコンポーネント内で、クライアントコンポーネントとして描画できること C. サーバーコンポーネントで描画するとエラーが発生すること (RSC上で使えないこと)

変更内容

asis の A,B の識別は、シンプルにサーバーコンポーネント上にコンポーネントを配置し、エラーなく描画できるかをチェックするだけでした。

toBe の A,B の識別では、ランタイムで対象コンポーネントがサーバーコンポーネントなのか、クライアントコンポーネントなのかを識別する必要があります。

そのために以下のような検証コンポーネントを挟むようにしました。

const CLIENT_COMPONENT_SYMBOL = Symbol.for('react.client.reference')

const isClientComponent = (actualComponent: any) =>
  actualComponent?.$$typeof === CLIENT_COMPONENT_SYMBOL

export const RSCChecker: FC<Props> = ({ actualComponent }) => {
  const message = isClientComponent(actualComponent)
    ? 'This is client component'
    : 'This is server component'
  return <p>{message}</p>
}

これはドキュメントがあるとかベストプラクティスってことは全然なくて、実際に各パターンのコンポーネントを調べた結果、どうやらクライアントコンポーネントの場合は Proxy でラップされ、$$typeof フィールドにクライアントコンポーネントを示すシンボルが挿入されていることがわかりました。

よって、これを用いて、各コンポーネントのテスト用ページで This is client component / This is server component のどちらかを描画させ、期待している方の文字列が描画されているかをチェックすることで、A,Bの分類をできるようにします。

確認方法

本PRの時点では、テストコード(sandbox) 以外に手を入れていないため、すべて A と C に分類され、テストが通ることを確認しています。

そのうえで、検証用の以下PRをマージした状態で確認したところ、Stack / Cluster など各コンポーネントが B に分類され、それらに依存していたせいで C になっていたコンポーネントが A になれることを確認済みです。

マージ後の計画

pkg-pr-new[bot] commented 2 weeks ago

Open in Stackblitz

pnpm add https://pkg.pr.new/kufu/smarthr-ui@5087

commit: fe60c0b