ui-data-testid について
UI実装時に安定した `data-testid` を追加し、翻訳や見た目の変更に左右されにくいテストセレクターを整えるスキルです。命名、対象範囲、繰り返し行の特定、テストでの検索方法を示します。
主な機能
- 01ルートと操作要素へのID付与
- 02意味のあるkebab-case命名
- 03繰り返しリストへの行ID付与
- 04テストで安定したセレクターを優先
- 05既存IDの維持と非同期状態の網羅
活用シーン
- 01新しいフォームに安定したテストセレクターを追加
- 02既存IDを保ちながらメニューやダイアログを改修
- 03繰り返し行とその操作を特定してテスト
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dtyq/ magic --skill ui-data-testidインストール後、Claude Code で/ui-data-testidを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yaml
詳細
UI の Data-testid
概要
後からパッチとして追加するのではなく、実装時に UI コードへ予測可能な data-testid 属性を追加してください。
i18n のテキスト変更やビジュアルリファクタリング後もセレクターが安定するようにしてください。
.cursor/rules にあるプロジェクトのテストルールに従ってください。
- リファクタリングや移行時に既存の
data-testidを維持する - プロジェクトのテストでは
data-testid-firstのクエリ戦略を使用する
ワークフロー
- 機能やモジュールのコンテキストからスコープのプレフィックスを決定します(例:
user-menus、organization-switch、settings-profile)。 - コンポーネントのルートコンテナに
data-testidを追加します。 - 主要なインタラクティブ要素すべてに
data-testidを追加します。- ボタンやリンクのトリガー
- input/select/checkbox/radio コントロール
- タブ、メニュー項目、サブメニューのトリガー
- モーダルやドロワーの起動操作と確定操作
- 設定駆動型 UI(例:
menu.items)では、設定に"data-testid"を追加し、レンダラーやラッパーコンポーネントで実際にクリック可能な DOM ノードへ引き継ぎます。 - 繰り返し表示されるリストの行や項目では、まず行や項目のコンテナに安定した
data-testidを設定します。原則として、子要素ごとに個別の一意な ID を追加しないでください。 - リストの行や項目内では、テストで行のスコープを再利用します。共有される行 ID とテキストや業務データで行を特定し、その後
within(row)またはロールやラベルのセレクターで子コントロールを検索します。 - 行のスコープ内で子要素を確実に選択できない場合に限り、繰り返し表示される行に子要素レベルの
data-testidを追加します。必要な場合も、行 ID を付け足すのではなく、行をまたいで共有される子要素 ID を使用します。 - ユーザーから明示的に名前の変更を求められない限り、既存の ID は変更しないでください。移行タスクで既存の ID を削除してはいけません。
- インタラクションを変更する場合は、可能であれば同じ場所にある
__tests__に Vitest/RTL テストを追加または更新します。
命名規則
- 小文字の kebab-case のみを使用します。
- 意味のある形式を使用します:
<scope>-<entity>-<action>。 - ID はテキストに依存しないようにします(i18n ラベルに依存させないでください)。
- 動的またはランダムな値(
Date.now、UUID、翻訳されたテキスト)を避けます。 - シークレット、メールアドレス、電話番号、トークンを含めないでください。
- 該当する場合は、安定したサフィックスを使用します:
trigger、content、button、input、option、item、row、loading、empty、error。
最低限のカバレッジチェックリスト
新しく作成する UI コンポーネントには、少なくとも以下を含めてください。
- ルートコンテナのテスト ID を 1 つ
- 主要な CTA のテスト ID を 1 つ
- 各副次アクションボタンのテスト ID
- 各フォームフィールドグループやコントロールのテスト ID
- メニューがある場合は、各メニュー項目トリガーのテスト ID
- 繰り返し表示されるリストやテーブルでは、リストコンテナ ID を 1 つ、行や項目のコンテナ ID を 1 つ。行スコープのセレクターでは不十分な場合を除き、セルやアクションごとに動的な ID を付けるのは避けます
- 変更対象ファイルにおける過去からの
data-testidの維持 - 非同期 UI における loading/empty/error のテスト ID
クエリの優先順位
テストを作成または更新する場合:
- このプロジェクトでは、安定したセレクターとして
getByTestIdを優先します - 補完的なアサーションには
getByRole、getByLabelText、getByTextを使用します - ユーザー向け動作テストでは、
container.querySelector(...)セレクターを避けます
これにより、.cursor/rules/testing-guide.mdc との整合性を保てます。
シナリオ別プレイブック
要素を安定かつ正確に指定するには、次のパターンを適用してください。
- フォーム
- フォームコンテナー、入力欄、送信/キャンセルボタン、バリデーションエラーに ID を追加します
- リストとテーブル
- リストコンテナーと行コンテナーに ID を追加します
- 共通の行 ID を優先し、その後テキスト/業務データで対象の行を選択します
- 直接行を検索する具体的な必要がある場合に限り、行 ID の接尾辞に安定した業務キーを使用します
- 行内の各フィールド/アクションに個別の動的 ID を追加しないでください。子アクションは
within(row)スコープで検索します - 子アクションに
data-testidが必要な場合は、collaborator-remove-buttonなどの共通 ID を使用し、行スコープから検索します
- メニューとドロップダウン
- トリガー、ポップアップの内容、各操作可能なメニュー項目に ID を追加します
- メニューが設定駆動の場合は、項目レベルの
data-testidをレンダリングされるノードに渡します
- モーダルとドロワー
- 開くトリガー、モーダルの内容、主要アクション、閉じる/キャンセルアクションに ID を追加します
- 非同期状態
- ローディング、空、エラーの各状態に ID を追加します
安定性のルール
- 順序が変わる可能性がある場合、配列インデックスから ID を生成しないでください。
- ランダム値やタイムスタンプから ID を生成しないでください。
- ページ内で単一要素の ID が一意になるようにしてください。
- 繰り返し表示されるコンポーネントでは、子要素に共通の ID を付け、
within(...)でスコープを指定してください。 - 繰り返し表示される行では、子要素単位の一意性より行単位の一意性を優先してください。行コンテナーでスコープを指定できない場合を除き、子要素の ID に行 ID を含めないでください。
パターン
コンポーネントのマークアップ
<div data-testid="user-menus-organization-info">
<button type="button" data-testid="user-menus-upgrade-button" />
<button type="button" data-testid="user-menus-recharge-button" />
</div>
繰り返し表示されるリストの行
<div data-testid="collaborator-list-content">
{collaborators.map((collaborator) => (
<div
key={collaborator.id}
data-testid="collaborator-item"
data-collaborator-id={collaborator.id}
>
<span>{collaborator.name}</span>
<button type="button" data-testid="collaborator-remove-button" />
</div>
))}
</div>
const row = page
.getByTestId("collaborator-list-content")
.locator('[data-testid="collaborator-item"]')
.filter({ hasText: receiverName })
await row.getByTestId("collaborator-remove-button").click()
設定とレンダラーへの引き継ぎ
const items = [
{ key: "logout", label: t("logout"), "data-testid": "user-menus-logout" },
]
<ItemComponent data-testid={menuItem["data-testid"]}>{menuItem.label}</ItemComponent>
完了条件
変更対象の UI ファイルにある新しい単一の操作可能なノードすべてに安定した data-testid 値が設定され、繰り返し表示されるリスト/テーブルの行に、行スコープから子コントロールを検索できる行レベルのセレクターが設定されている場合に限り、完了としてください。
移行/リファクタリングの差分で既存の data-testid が意図せず削除されていないことを確認してください。
コメント
Markdown 対応