UI検証 (Verify UI)
画面を構築するのは第一歩です。UIの変更は、実際のブラウザで動作を確認し、そのスクリーンショットがPRに含まれて初めて完了します。型チェック、リンター、単体テストが通っただけでは、コードがコンパイルされロジックが正しいことは保証されても、パネルがレンダリングされることや、フッターがデザイン通りの位置にあることは保証されません。ブランチを実行したことのないレビュアーは、スクリーンショットで作業を判断します。スクリーンショットがなければ判断できません。
1. 実際のブラウザで、実際のデータを使って駆動する
モックではなく、実際の(シードされた)データベースを使用した実行中のアプリを使います。ハッピーパスのスクリーンショットだけでは不十分です。デザインで指定されたすべての状態に加え、指定されていない状態も網羅します。
- 両方のテーマ。 手書きの色はダークモードのバリエーションが忘れられる場所です。
- モバイルとデスクトップ。 レイアウトはブレークポイントで並び替えられ、フッターやツールバーはコンテンツの半分を孤立させることがあります。
- 空、ローディング、上限、エラーの状態。 データがある状態だけでなく。
- すべてのハンドオフ。 あるダイアログやビューが閉じて、別のものが開く瞬間。
- 追加したすべてのメニュー、ポップオーバー、ドロップダウンを開く。 開いたことのないメニューは、開いた瞬間にページがクラッシュするメニューと見分けがつきません。
- その間、ブラウザのコンソールとネットワークパネルにエラーがないか確認します。
2. レビュースイープを実行する
リポジトリが使用するデザインおよびフレームワークガイドライン(デザインシステム、アクセシビリティ、フレームワークのベストプラクティス)に照らして変更をレビューします。エージェントがサブエージェントを生成できる場合は、より安価なモデルでガイドラインごとに1つのレビュアーを並列実行します。これはパターンマッチング作業であり、判断を伴いません。
- レビュアーにリポジトリ独自のルールを与える。 ルールを読んでいないレビュアーは、文書化された決定事項を違反として報告します。
- レビュアーには報告のみを指示する。 その後、実際の確率に基づいて各指摘を自分で判断します。別のプロダクトに当てはまる指摘(例:最大十数行のリストに対する仮想化のアドバイス)は却下します。
3. PR用のスクリーンショットを撮影する
画面の説明ではなく、画面そのものです。スクリーンショットを撮影することは、実際には見ていない状態を発見する最も安価な方法でもあります。
- 実際のアプリを駆動する。 各状態に到達する使い捨てのブラウザスクリプトで構いません。E2Eハーネスのシードヘルパーを使用して、UIフローでは生成されない状態に到達し、画像が存在するようになったらスクリプトを削除します。アサーションは行わず、カバレッジの対象でもありません。
- 撮影前にリビルドする。 そうしないと、修正したばかりのバグのスクリーンショットを撮ることになります。
- シャッターの前にアニメーションが完了するのを待つ。 「Visible」は要素がレイアウトされたときに解決され、エントランス遷移が終了したときではないため、単純なキャプチャでは背景のない半透明のダイアログが表示されます。有限のアニメーション(スケルトンパルスなどの無限アニメーションは除外)が完了するのをタイムアウト付きで待ちます。
- 可能な限り、ユーザーがたどる方法で各状態に到達する。 フラグを強制すると、バグを露呈する可能性のある競合をスキップしてしまいます。
- コミットする。
docs/screenshots/<topic>/に、実装するデザインがあればその隣に配置します。 - ブランチではなくコミットSHAで参照する。 例:
https://github.com/<org>/<repo>/blob/<sha>/<path>?raw=true。ブランチURLはマージ時にブランチが削除されると無効になり、PRがリンク切れの画像で埋め尽くされます。
ブラウザが利用できない場合
環境がどうしてもUIを実行できない場合(ブラウザツールがない、認証情報がない、制御できないインフラストラクチャ)、その旨をPRのTestingセクションに明記します。何が妨げになったか、代わりに何を検証したかを記載します。沈黙は「検証済み」と読まれます。
レビュー修正はステージとして上に重ねる
このパスで見つかった修正は、機能の上に新しいスタックPRとして配置し、ビルドとレビューパスが別々にレビュー可能な状態を維持します(stacked-prs を参照)。
このパスで捕捉できるもの
これらは型チェッカー、リンター、単体テストのいずれからも発見されませんでした。
- ページと一致しないローディングスケルトン。 ページにないツールバーをスケルトン表示したため、ナビゲーションのたびに2つのレイアウト間でジャンプしていました。実際のレイアウトを1行1行反映させるか、スケルトンを出荷しないでください。
- ハンドオフ時に2つのダイアログが同時に表示される。 すべてのダイアログがマウントされ、
openフラグのみが切り替えられていたためです。非表示はアンマウントとは異なります。 - 誤ったアイデンティティのフラッシュ。 初期レンダリング後にクライアントがセッションを読み取ったため、自分の行が1フレームだけ自分を削除するように表示されました。サーバーがすでに知っているアイデンティティは、ページと共に送信されるべきです。
- 誤った空の状態。 クエリが解決される前のフレームで「結果が見つかりません」がレンダリングされました。これは、0行が「ローディング」と「空」の両方を意味していたためです。
- 類似のデザイントークンとすり替えられたもの。 一方のテーマで視覚的に異なって見えます。
- ハードコードされたラベル。 同じラベルを共有カタログから読み取っている他の4つの呼び出し箇所の隣にあります。
- 開くとページがクラッシュするメニュー。 サブコンポーネントが必要とする親コンポーネントがなかったためです。手動でのクリックスルーでは2回見逃されました。誰もメニューを開かなかったからです。
- 仕様項目が静かにスキップされ、別の項目が正当な理由で削除されたが文書化されていない。 デザインからの逸脱は、理由を記載する必要があります。
- 自己矛盾するダイアログ。 スクリーンショットを見て発見されました。「すべての座席が使用中」という通知がある一方で、隣のフッターには「残り2席」と表示されていました。