ウェブアプリのテスト
サーバー オーケストレーション ヘルパーとトラブルシューティングのヒントを含む、Playwright ベースの Web アプリ テストのためのエージェント スキル ハンドブック。
出典: anthropics/skills (MIT) を基にしたコンテンツ。
ローカル Web アプリケーションをテストするには、ネイティブ Python Playwright スクリプトを作成します。
利用可能なヘルパー スクリプト:
scripts/with_server.py- サーバーのライフサイクルを管理します (複数のサーバーをサポート)
使用状況を確認するには、必ず最初に--helpを使用してスクリプトを実行してください。最初にスクリプトを実行してみて、カスタマイズされたソリューションが絶対に必要であることがわかるまで、ソースを読まないでください。これらのスクリプトは非常に大きくなる可能性があるため、コンテキスト ウィンドウが汚くなります。これらは、コンテキスト ウィンドウに取り込まれるのではなく、ブラックボックス スクリプトとして直接呼び出すために存在します。
デシジョン ツリー: アプローチの選択
User task -> Is it static HTML?
Yes -> Read HTML file directly to identify selectors
Success -> Write Playwright script using selectors
Fails/Incomplete -> Treat as dynamic (below)
No (dynamic webapp) -> Is the server already running?
No -> Run: python scripts/with_server.py --help
Then use the helper + write simplified Playwright script
Yes -> Reconnaissance-then-action:
1. Navigate and wait for networkidle
2. Take screenshot or inspect DOM
3. Identify selectors from rendered state
4. Execute actions with discovered selectors例: with_server.py の使用
サーバーを起動するには、まず--helpを実行してから、ヘルパーを使用します。
単一サーバー:
python scripts/with_server.py --server "npm run dev" --port 5173 -- python your_automation.py複数のサーバー (例: バックエンド + フロントエンド):
python scripts/with_server.py \
--server "cd backend && python server.py" --port 3000 \
--server "cd frontend && npm run dev" --port 5173 \
-- python your_automation.py自動化スクリプトを作成するには、Playwright ロジックのみを含めます (サーバーは自動的に管理されます)。
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True) # Always launch chromium in headless mode
page = browser.new_page()
page.goto('http://localhost:5173') # Server already running and ready
page.wait_for_load_state('networkidle') # CRITICAL: Wait for JS to execute
# ... your automation logic
browser.close()偵察→行動パターン
-
レンダリングされた DOM を検査します:
page.screenshot(path='/tmp/inspect.png', full_page=True) content = page.content() page.locator('button').all() -
検査結果からセレクターを特定
-
検出されたセレクターを使用してアクションを実行
よくある落とし穴
動的アプリでnetworkidleを待つ前に DOM を検査しないでください
必ず 検査の前にpage.wait_for_load_state('networkidle')まで待ってください
ベストプラクティス
- バンドルされたスクリプトをブラック ボックスとして使用する - タスクを達成するには、
scripts/で利用可能なスクリプトのいずれかが役立つかどうかを検討してください。これらのスクリプトは、コンテキスト ウィンドウを煩雑にすることなく、一般的で複雑なワークフローを確実に処理します。--helpを使用して使用法を確認し、直接呼び出します。 - 同期スクリプトには
sync_playwright()を使用します - 完了したら必ずブラウザを閉じてください
- 説明的なセレクターを使用します:
text=、role=、CSS セレクター、または ID - 適切な待機を追加します:
page.wait_for_selector()またはpage.wait_for_timeout()
参照ファイル
- examples/ - 一般的なパターンを示す例:
element_discovery.py- ページ上のボタン、リンク、入力の検出static_html_automation.py- ローカル HTML に file:// URL を使用するconsole_logging.py- 自動化中のコンソール ログのキャプチャ
リソースファイル
ライセンス.txt
バイナリリソース
例/console_logging.py
examples/console_logging.py をダウンロード
from playwright.sync_api import sync_playwright
# Example: Capturing console logs during browser automation
url = 'http://localhost:5173' # Replace with your URL
console_logs = []
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={'width': 1920, 'height': 1080})
# Set up console log capture
def handle_console_message(msg):
console_logs.append(f"[{msg.type}] {msg.text}")
print(f"Console: [{msg.type}] {msg.text}")
page.on("console", handle_console_message)
# Navigate to page
page.goto(url)
page.wait_for_load_state('networkidle')
# Interact with the page (triggers console logs)
page.click('text=Dashboard')
page.wait_for_timeout(1000)
browser.close()
# Save console logs to file
with open('/mnt/user-data/outputs/console.log', 'w') as f:
f.write('\n'.join(console_logs))
print(f"\nCaptured {len(console_logs)} console messages")
print(f"Logs saved to: /mnt/user-data/outputs/console.log")例/element_discovery.py
examples/element_discovery.py をダウンロード
from playwright.sync_api import sync_playwright
# Example: Discovering buttons and other elements on a page
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Navigate to page and wait for it to fully load
page.goto('http://localhost:5173')
page.wait_for_load_state('networkidle')
# Discover all buttons on the page
buttons = page.locator('button').all()
print(f"Found {len(buttons)} buttons:")
for i, button in enumerate(buttons):
text = button.inner_text() if button.is_visible() else "[hidden]"
print(f" [{i}] {text}")
# Discover links
links = page.locator('a[href]').all()
print(f"\nFound {len(links)} links:")
for link in links[:5]: # Show first 5
text = link.inner_text().strip()
href = link.get_attribute('href')
print(f" - {text} -> {href}")
# Discover input fields
inputs = page.locator('input, textarea, select').all()
print(f"\nFound {len(inputs)} input fields:")
for input_elem in inputs:
name = input_elem.get_attribute('name') or input_elem.get_attribute('id') or "[unnamed]"
input_type = input_elem.get_attribute('type') or 'text'
print(f" - {name} ({input_type})")
# Take screenshot for visual reference
page.screenshot(path='/tmp/page_discovery.png', full_page=True)
print("\nScreenshot saved to /tmp/page_discovery.png")
browser.close()例/static_html_automation.py
examples/static_html_automation.py をダウンロード
from playwright.sync_api import sync_playwright
import os
# Example: Automating interaction with static HTML files using file:// URLs
html_file_path = os.path.abspath('path/to/your/file.html')
file_url = f'file://{html_file_path}'
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={'width': 1920, 'height': 1080})
# Navigate to local HTML file
page.goto(file_url)
# Take screenshot
page.screenshot(path='/mnt/user-data/outputs/static_page.png', full_page=True)
# Interact with elements
page.click('text=Click Me')
page.fill('#name', 'John Doe')
page.fill('#email', '[email protected]')
# Submit form
page.click('button[type="submit"]')
page.wait_for_timeout(500)
# Take final screenshot
page.screenshot(path='/mnt/user-data/outputs/after_submit.png', full_page=True)
browser.close()
print("Static HTML automation completed!")スクリプト/with_server.py
#!/usr/bin/env python3
"""
Start one or more servers, wait for them to be ready, run a command, then clean up.
Usage:
# Single server
python scripts/with_server.py --server "npm run dev" --port 5173 -- python automation.py
python scripts/with_server.py --server "npm start" --port 3000 -- python test.py
# Multiple servers
python scripts/with_server.py \
--server "cd backend && python server.py" --port 3000 \
--server "cd frontend && npm run dev" --port 5173 \
-- python test.py
"""
import subprocess
import socket
import time
import sys
import argparse
def is_server_ready(port, timeout=30):
"""Wait for server to be ready by polling the port."""
start_time = time.time()
while time.time() - start_time < timeout:
try:
with socket.create_connection(('localhost', port), timeout=1):
return True
except (socket.error, ConnectionRefusedError):
time.sleep(0.5)
return False
def main():
parser = argparse.ArgumentParser(description='Run command with one or more servers')
parser.add_argument('--server', action='append', dest='servers', required=True, help='Server command (can be repeated)')
parser.add_argument('--port', action='append', dest='ports', type=int, required=True, help='Port for each server (must match --server count)')
parser.add_argument('--timeout', type=int, default=30, help='Timeout in seconds per server (default: 30)')
parser.add_argument('command', nargs=argparse.REMAINDER, help='Command to run after server(s) ready')
args = parser.parse_args()
# Remove the '--' separator if present
if args.command and args.command[0] == '--':
args.command = args.command[1:]
if not args.command:
print("Error: No command specified to run")
sys.exit(1)
# Parse server configurations
if len(args.servers) != len(args.ports):
print("Error: Number of --server and --port arguments must match")
sys.exit(1)
servers = []
for cmd, port in zip(args.servers, args.ports):
servers.append({'cmd': cmd, 'port': port})
server_processes = []
try:
# Start all servers
for i, server in enumerate(servers):
print(f"Starting server {i+1}/{len(servers)}: {server['cmd']}")
# Use shell=True to support commands with cd and &&
process = subprocess.Popen(
server['cmd'],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
server_processes.append(process)
# Wait for this server to be ready
print(f"Waiting for server on port {server['port']}...")
if not is_server_ready(server['port'], timeout=args.timeout):
raise RuntimeError(f"Server failed to start on port {server['port']} within {args.timeout}s")
print(f"Server ready on port {server['port']}")
print(f"\nAll {len(servers)} server(s) ready")
# Run the command
print(f"Running: {' '.join(args.command)}\n")
result = subprocess.run(args.command)
sys.exit(result.returncode)
finally:
# Clean up all servers
print(f"\nStopping {len(server_processes)} server(s)...")
for i, process in enumerate(server_processes):
try:
process.terminate()
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
process.wait()
print(f"Server {i+1} stopped")
print("All servers stopped")
if __name__ == '__main__':
main()GitHub で見る
MCPビルダー
モデル コンテキスト プロトコル サーバーの構築、ツールの定義、およびクロード スキルが信頼できる評価スイートの作成に関するエージェント スキル マニュアル。
ドキュメントの共同編集
ドキュメントを共同編集するための構造化されたワークフローを通じてユーザーをガイドします。ユーザーがドキュメント、提案書、技術仕様、意思決定文書、または同様の構造化コンテンツを作成したい場合に使用します。このワークフローは、ユーザーが効率的にコンテキストを転送し、繰り返しを通じてコンテンツを改良し、ドキュメントが読者にとって機能することを確認するのに役立ちます。ユーザーがドキュメントの作成、提案書の作成、仕様書の草案、または同様のドキュメント化タスクについて言及したときにトリガーされます。
クロードスキルのドキュメント