Files
agent-skills/dogfood/references/playwright-qa.md
Hermes Agent ccc63d1e70 first commit
2026-05-10 13:52:46 +08:00

157 lines
4.6 KiB
Markdown

# Playwright Python for QA Testing
## Environment Setup
Playwright Python is available on this system:
- **Package**: `/home/ubuntu/.hermes/hermes-agent/venv/lib/python3.11/site-packages/playwright/`
- **Chromium**: `~/.cache/ms-playwright/chromium-1217/`
- **Import**: `from playwright.sync_api import sync_playwright`
- **Run**: `python3 script.py` (not `node` — Playwright Node module may not be installed)
## Basic Pattern
```python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()
# Login
page.goto('https://auth.example.com/login')
page.wait_for_load_state('networkidle')
page.fill('#username', 'admin_user')
page.fill('#password', 'password')
page.click('button[type="submit"]')
page.wait_for_url('**/login-success**', timeout=10000)
# Navigate to target
page.goto('https://www.example.com/admin')
page.wait_for_load_state('networkidle')
# Interact and test...
browser.close()
```
## Event Monitoring (Critical for QA)
### Console Messages and JS Errors
```python
console_msgs = []
page_errors = []
page.on("console", lambda m: console_msgs.append(f"[{m.type}] {m.text}"))
page.on("pageerror", lambda e: page_errors.append(str(e)))
# After interactions:
for m in console_msgs:
print(f" {m}")
for e in page_errors:
print(f" ERROR: {e}")
```
### Network Requests and Responses
```python
api_responses = []
def on_response(r):
if '/api/' in r.url or '/admin/' in r.url:
api_responses.append({"url": r.url, "status": r.status})
page.on("response", on_response)
# After interactions:
for r in api_responses:
print(f" {r['url']} -> {r['status']}")
```
### Failed Resource Loads
```python
failed_resources = []
page.on("requestfailed", lambda r: failed_resources.append({"url": r.url, "error": r.failure}))
```
## Element Query Patterns
```python
# By text content
btn = page.query_selector('button:has-text("Save")')
links = page.query_selector_all('a:has-text("Login")')
# By CSS selector with attribute
input_el = page.query_selector('input[name="csrf_token"]')
form = page.query_selector('#contentForm')
# By role
submit = page.query_selector('button[type="submit"]')
# Get all buttons (for debugging)
all_btns = page.query_selector_all('button')
btn_texts = [b.inner_text().strip() for b in all_btns]
```
## JavaScript Evaluation
```python
# Check if function is defined
is_defined = page.evaluate("typeof saveDraft === 'function'")
# Get element properties
val = page.evaluate("document.getElementById('contentJson').value")
# Get page content
page_html = page.content()
body_text = page.inner_text('body')
# Execute arbitrary JS
result = page.evaluate("() => { return document.title; }")
```
## Screenshots
```python
# Full page
page.screenshot(path='/tmp/screenshot.png', full_page=True)
# Then analyze with vision tool
```
## Cookie Inspection
```python
cookies = context.cookies()
for c in cookies:
print(f"{c['name']}: domain={c['domain']}, httpOnly={c['httpOnly']}, "
f"secure={c['secure']}, sameSite={c.get('sameSite','N/A')}")
```
## Testing with Custom Headers (e.g., Bearer Token)
```python
# Create separate context with extra headers
context2 = browser.new_context(extra_http_headers={"Authorization": "Bearer fake_token"})
page2 = context2.new_page()
page2.goto('https://www.example.com/admin')
# Check if redirected to login
print(f"URL: {page2.url}")
page2.close()
context2.close()
```
## CSP Bug Detection Pattern
When buttons with `onclick="fnName()"` do nothing:
1. Check console for CSP violation: `"Executing inline script violates the following Content Security Policy directive 'script-src-elem'"`
2. Verify function availability: `page.evaluate("typeof fnName")` returns `"undefined"`
3. Confirm script tag exists but is blocked by CSP
4. Check CSP header: `curl -sI URL | grep content-security-policy`
5. Look for `script-src-elem` directive that overrides `script-src`
## Pitfalls
- **Use Python, not Node.js**: The `playwright` npm module may not be installed. Python works.
- **`expect_response` timeout**: Don't use broad URL patterns. Use specific path matches or handle timeout gracefully.
- **`expect_navigation` for SPA**: Single-page apps may not trigger navigation events. Use `wait_for_timeout` or check state changes instead.
- **Rate limit testing**: Don't try to trigger rate limits via Playwright — too slow. Use curl for rate limit tests.
- **`page.on("console")` misses CSP errors**: CSP violations appear as `pageerror` events, not console messages. Listen to both.