verify-form-validation について
実行中のアプリでフォーム検証を行う Reticle スキルです。アプリ側のエラー、実際の無効状態、不正な送信によるリクエスト、値の修正後の回復を確認します。
主な機能
- 01アプリ側のエラーとブラウザー検証を区別
- 02送信コントロールの実際の無効状態を確認
- 03処理完了後にリクエスト数を確認
- 04有効な入力でエラーが消え送信可能になることを検証
- 05テストしたフィールドと値に基づいて結果を報告
活用シーン
- 01フィールドの不正な値とエラー表示をテスト
- 02CSSだけで無効に見せた送信コントロールを検出
- 03不正な送信で該当リクエストが送られないか確認
- 04値の修正後に送信可能になることを検証
MCP 対応
このスキルは Reticle のツールで実行中のアプリを操作し、動作を検証します。入力には MCP サーバーを提供するとの記載はありません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ reticlehq/ reticle --skill verify-form-validationインストール後、Claude Code で/verify-form-validationを入力して読み込まれたか確認します。
詳細
拒否されたフォームでも画面上は問題なく見える
バリデーションコードは一度書かれ、明らかに不正な値をブラウザーで一度確認されただけで、その後は二度と動作確認されません。リリースされるバグは、試した値ではありません。試さなかった値や状態です。たとえば、フィールドからフォーカスが外れるまで送信ボタンが無効にならない、CSS だけでボタンを無効にしているためクリックできてしまう、あるいはチェックが終わる前にハンドラーがリクエストを送ってしまう、といったものです。
Reticle なら、実際のフォームを操作して、3 つの失敗ポイントを一度に確認できます。まだセットアップしていない場合は、reticle スキルに従ってください。Reticle をインストールし、init によってアプリのファイルが変更される前にユーザーに確認を求め、1 つのフローを実証します。
始める前に読むこと:ブラウザー独自のバリデーションがアプリの動作を覆い隠す場合があります
required、pattern、type="email" などを指定すると、アプリの JS が実行される前に、ブラウザーが制約検証の吹き出しを表示して処理を止めます。そのネイティブのツールチップを検証しているなら、確認できたのはブラウザーの動作であり、テスト対象のコードではありません。判定を信頼する前に、確認しているエラーがアプリ独自の要素(testid、ロール、描画された文字列など)であることを確かめてください。:invalid 疑似状態を確認するのではありません。フォームがアプリ側のチェックを設けず、ネイティブバリデーションだけに頼っているなら、それが指摘すべき問題です。たまたま通ってしまう述語でごまかさず、そのように伝えてください。
拒否を発生させる
Reticle の操作では常にそうするように、操作する前にその結果を明確にしてください。
reticle_look({ action: "page", sessionId, mode: "interactive" }) // get refs for the field and submit control
reticle_act_and_wait({ sessionId, ref, action: "fill", args: { value: "<invalid>" }, until: { kind: "allOf", predicates: [
{ kind: "element", query: { testid: "field-error" } },
{ kind: "element", query: { role: "button", name: "Submit" }, state: "disabled" },
]}})
チェックがキー入力時ではなくフォーカスが外れたときに実行される場合は、アサートする前に、別のコントロールにフォーカスを移す手順を独立して行ってください。送信ボタンまたは次のフィールドに対して reticle_act({ sessionId, ref: otherRef, action: "focus" }) を実行します。別のコントロールにフォーカスすることで、必要な blur が発生するためです。デバウンスされる場合は、待機しないでください。test-error-states と同じように、reticle_run({ tool: "reticle_clock", args: { sessionId, advanceMs } }) を使ってデバウンス時間を正確に超えるように時間を進めてください。固定時間の待機は手元の環境では通っても、CI では不安定になります。
リクエストが送られていないことを証明する
ボタンが無効に見えることと、リクエストが送信されていないことは別の主張です。すでにアサートした disabled 状態を信用するだけではいけません。実際に送信を試してください。そうすれば、CSS だけで無効に見せているためクリックできてしまうコントロールを見逃さずに検出できます。
reticle_act({ sessionId, ref: submitRef, action: "click" })
次に、リクエストがないことをアサートします。クリック直後に count: 0 を確認するだけでは、何も証明できません。単にリクエストがまだ送信されていないだけかもしれません。不在を信頼する前に、アプリが実際に処理を試みるための時間を確保してください。まず処理が落ち着くのを待ってから、同じ呼び出しで件数を確認します。
reticle_assert({ sessionId, since, timeout_ms: 3000, predicate: { kind: "allOf", predicates: [
{ kind: "settled" },
{ kind: "net", method: "POST", urlContains: "/api/...", count: 0 },
]}})
timeout_ms を指定すると、スナップショットではなく待機になります。reticle_assert のデフォルトは timeout_ms: 0 で、呼び出した瞬間に一度だけ評価されます。したがって、これを指定しないと、クリック直後に settled が一度確認されるだけで、アプリが実際にアイドル状態だったかどうかは何も証明できません。timeout_ms: 3000 を指定すると、ページが実際に静かになるまで最大 3 秒間、呼び出しがポーリングを続けます。処理が落ち着いて初めて、count: 0 の結果に意味が生まれます。since には、先ほどの入力時ではなく、クリックの結果から取得した値を使ってください。こうすることで、送信を試みた後のリクエストだけが対象になります。
エラーを解消する
値を正しいものに修正し、拒否が永続的ではなく、条件付きだったことを確認してください。
reticle_act_and_wait({ sessionId, ref, action: "fill", args: { value: "<valid>" }, until: { kind: "allOf", predicates: [
{ kind: "element", query: { testid: "field-error" }, absent: true },
{ kind: "element", query: { role: "button", name: "Submit" }, state: "enabled" },
]}})
一度拒否した後にフォームが二度と有効にならないなら、それは最初のバグに見せかけた、もう 1 つのバグです。
アサートする内容
合格となるのは verified: "yes" のみです。"no" は実際の指摘事項を意味し、"unknown" は Reticle が判断できず、より適切なキャプチャが必要であることを意味します。また、"no-fault" は反証されたことも、何かが判定されたこともないという意味です。この3つのいずれも、フォームが不正な入力を拒否する証拠にはなりません。
- エラーはアプリ独自のものであり、ブラウザー標準の吹き出しではありません。
- 送信コントロールの
disabled状態は実際に設定されているのであり、無効に見えるだけのopacity/cursorスタイルではありません。これは、無効に見える状態と無効状態の違いをdesign-system-complianceが示しているのと同じ区別です。 - 一致するリクエストが1件も送信されていないことを、推測ではなくカウントで確認します。「エラーがスローされなかった」ことから推測してはいけません。
- 拒否は条件付きです。有効な値を入力するとエラーが消え、送信が再び有効になります。
誠実さについて
ここでの判定対象は、このフィールドとこの無効な値であり、フォーム全体ではありません。必須フィールドのチェックから、別のフィールドに対するパターンチェックについては何も分かりません。入力欄を1つしか操作していない場合は、それがどれかを明記し、1つの合格判定が「フォームは検証されている」と受け取られないよう、残りのフォームは未検証であると伝えてください。
機能のリファレンス:curl https://docs.reticle.sh/capabilities.md。その他すべて:curl https://docs.reticle.sh/llms.txt。