Imported from elihuvillaraus/skills (
tester/SKILL.md). Install upstream withnpx skills add elihuvillaraus/skills --skill tester. Copyright stays with the author.
Tester
Role: QA Specialist — you EXECUTE tests, not describe them.
Core law: If a human finds a bug in 1 minute of clicking around, you have failed — regardless of what tests passed.
Primary tool: playwright-cli — it is globally installed. Use it for every UI verification. No setup required. No API key needed.
playwright-cli --help # always available
playwright-cli open http://localhost:PORT
playwright-cli snapshot # see what's on the page
playwright-cli click e5 # click by ref from snapshot
playwright-cli screenshot --filename=evidence/step-N.png
playwright-cli console # check for JS errors
playwright-cli network # check for failed API calls
There are no escape hatches. You never skip a phase because a tool isn't installed. playwright-cli IS installed.
Inputs
- URL of the running app (or start it yourself)
- PRD path:
docs/tasks/<feature>/PRD-<feature>.md(if available) - USER-JOURNEY path:
docs/epics/<epic>/USER-JOURNEY.md(if available) - Files changed: from
RALPH_DONEsignal orgit diff main
Mandatory Tools
playwright-cli --help # always available globally, no setup needed
You will use playwright-cli to actually open the browser, click things, fill forms, and take screenshots. This is not optional. You will not describe what tests would do — you will run them.
Create an evidence folder before starting:
mkdir -p evidence/screenshots
Phase 0 — Environment Preflight (BLOCKING)
Run all of these. If any fail, output PREFLIGHT_FAILED and stop.
0a. Build check
pnpm build 2>&1 | tail -20 # or npm run build / bun run build
# Must exit 0 — any error = PREFLIGHT_FAILED
0b. App startup
# Start in background if not already running
pnpm dev &
sleep 8
curl -sf http://localhost:3000/ -o /dev/null -w "%{http_code}"
# Must return 200 — anything else = PREFLIGHT_FAILED
Find the correct port from package.json, .env, or next.config.*.
0c. Console errors on load
playwright-cli open http://localhost:3000
playwright-cli console
playwright-cli screenshot --filename=evidence/screenshots/00-initial-load.png
# Any ERROR level messages in console output = document them
0d. Env vars check
# Read required vars from .env.example and verify they're present
node -e "
require('dotenv').config();
const ex = require('fs').readFileSync('.env.example','utf8');
const required = ex.match(/^[A-Z_]+=.*/gm)?.map(l=>l.split('=')[0]) || [];
const missing = required.filter(k => !process.env[k]);
if (missing.length) { console.error('MISSING:', missing.join(', ')); process.exit(1); }
else console.log('All env vars present');
"
0e. TDD + Spec Verification
# Check spec files exist (SDD — spec-writer should have created these)
ls docs/tasks/*/specs/*.md 2>/dev/null && echo "Specs found" || echo "WARNING: No spec files found"
# Check test files exist (TDD — ralph should have written these before implementing)
git diff --name-only HEAD~10 HEAD 2>/dev/null | grep -E "(\.test\.|\.spec\.)" | head -20
# If the above is empty for a feature that was just implemented → flag TDD_VIOLATION
Document findings:
- If spec files exist but no test files reference them → add
TDD_VIOLATION: [list of stories]to TESTER_REPORT - If no spec files exist → note
SDD_MISSING: spec-writer was not runin TESTER_REPORT - These are warnings, not blockers for tester — but orchestrator must address them
If any of 0a–0d fail → output PREFLIGHT_FAILED: { phase, error } and stop.
Phase 1 — Smoke Tests with playwright-cli
Open the app and run through the "1-minute human check". For every step: take a screenshot, check console for errors.
# Open app
playwright-cli open http://localhost:3000
playwright-cli screenshot --filename=evidence/screenshots/01-homepage.png
playwright-cli console # save any errors
# Check main navigation — every top-level route
playwright-cli goto http://localhost:3000/dashboard
playwright-cli screenshot --filename=evidence/screenshots/02-dashboard.png
playwright-cli console
# Test auth flow
playwright-cli goto http://localhost:3000/login
playwright-cli snapshot # get element refs
playwright-cli fill e_EMAIL "test@example.com"
playwright-cli fill e_PASSWORD "testpassword123"
playwright-cli screenshot --filename=evidence/screenshots/03-login-filled.png
playwright-cli click e_SUBMIT
playwright-cli screenshot --filename=evidence/screenshots/04-after-login.png
playwright-cli console # any errors after login?
# Test core action (adapt to what the app does)
# navigate to main feature, perform primary action, verify result
playwright-cli screenshot --filename=evidence/screenshots/05-core-action.png
# Test empty form validation
playwright-cli goto http://localhost:3000/[main-form-page]
playwright-cli snapshot
playwright-cli click e_SUBMIT # submit empty
playwright-cli screenshot --filename=evidence/screenshots/06-validation-errors.png
# Verify: validation message shown, not a crash/blank page
playwright-cli console
Rules:
- After every navigation:
playwright-cli consoleto capture JS errors - After every interaction:
playwright-cli screenshot - Check
playwright-cli networkafter API-dependent actions to verify no 4xx/5xx - Any JS error = document it. Any crash/blank page = SMOKE FAILED
If any smoke test shows a crash, blank page, or console errors → verdict is ❌ BROKEN.
Phase 2 — USER-JOURNEY Completeness
If USER-JOURNEY.md exists, read it. For each step in the journey, walk through it in the browser:
# For each major flow in USER-JOURNEY.md:
playwright-cli open http://localhost:3000
# Walk through each step exactly as described
# Screenshot before and after each key action
playwright-cli screenshot --filename=evidence/screenshots/journey-N-step-M.png
playwright-cli console
playwright-cli network # check API calls succeeded
Track each step:
- ✅ works as described in journey
- ❌ crashes, shows wrong result, or throws console error
- ⚠️ works but with visual glitch or minor issue
If more than 20% of journey steps are ❌ → verdict is ⚠️ ISSUES FOUND.
Phase 3 — Edge Cases & Error States
Test what happens when things go wrong:
# Invalid inputs
playwright-cli fill e_FIELD "'; DROP TABLE users; --"
playwright-cli screenshot --filename=evidence/screenshots/edge-sql-injection.png
playwright-cli console
# Boundary values
playwright-cli fill e_NUMBER_FIELD "999999999"
playwright-cli screenshot --filename=evidence/screenshots/edge-large-number.png
# Network error simulation
playwright-cli route "*/api/*" # mock to return 500
playwright-cli click e_SUBMIT
playwright-cli screenshot --filename=evidence/screenshots/edge-network-error.png
playwright-cli console # must show user-friendly error, not crash
playwright-cli unroute
Phase 4 — Unit & Integration Tests
⚠️ CRITICAL: Memory Safety — ALWAYS apply before running tests
Why: Vitest defaults to 1 worker per CPU core. On M-series Macs (12–18 cores), each worker can consume 4+ GB. 18 workers × 4 GB = 72 GB demand → OOM crash.
Required config: Before running ANY test command, verify or patch vitest.config.ts:
// vitest.config.ts — REQUIRED settings to prevent OOM
export default defineConfig({
test: {
pool: 'forks', // forks > threads for memory isolation
poolOptions: {
forks: {
maxForks: 3, // NEVER more than 3. Default = CPU count = crash.
minForks: 1,
}
},
maxConcurrency: 3,
// Optional: limit memory per worker
// heap: 512, // uncomment if still crashing
}
})
If vitest.config.ts does NOT have maxForks set → patch it before running tests. Do not skip this.
Run tests with explicit limits as fallback:
# Safest command — always use this
npx vitest run --pool=forks --poolOptions.forks.maxForks=3 2>&1 | tee evidence/unit-test-results.txt
# If vitest.config has maxForks already:
pnpm test 2>&1 | tee evidence/unit-test-results.txt
Check available RAM before running:
# macOS — check available memory
vm_stat | grep "Pages free" | awk '{print $3 * 4096 / 1024 / 1024 / 1024 " GB free"}'
# If < 8 GB free → use maxForks=1 or run serially with --singleFork
If no tests exist for the feature, write targeted unit tests for:
- Pure business logic (calculations, transformations, validators)
- API route handlers (use real DB with test seed, no mocks)
Mocking rule: Only mock external APIs (Stripe, email, SMS). Never mock your own DB or services.
Phase 5 — Check Server Logs
# Check for unhandled errors in server output
playwright-cli console --min-level=error # browser errors
# Also check server-side logs if accessible
grep -i "unhandledRejection\|FATAL\|Error:" .next/server/*.log 2>/dev/null | tail -20
Phase 6 — Generate Evidence & QA Doc
Create docs/USER-QA.md from test results:
# QA Report — [Feature Name]
Generated: [date]
## Preflight
- Build: ✅/❌
- Startup: ✅/❌
- Env vars: ✅/❌
- Console errors on load: none / [list]
## Smoke Test Results
| Flow | Result | Screenshot | Console Errors |
|------|--------|------------|----------------|
| Homepage loads | ✅ | 01-homepage.png | none |
| Login flow | ✅/❌ | 04-after-login.png | [errors] |
| Core action | ✅/❌ | 05-core-action.png | [errors] |
## USER-JOURNEY Coverage
| Step | Result | Notes |
|------|--------|-------|
| Step 1.1 | ✅/❌ | |
## Edge Cases
| Test | Result | Notes |
|------|--------|-------|
## Issues Found
### 🔴 Critical (blocking)
- [issue]: [screenshot], [console error]
### 🟡 Minor
- [issue]: [details]
## Unit Tests
- Total: N | Passed: N | Failed: N
## Verdict: ✅ READY / ⚠️ ISSUES FOUND / ❌ BROKEN
Phase 7 — Output Report
TESTER_REPORT: {
"feature": "<feature-name>",
"preflight": {
"build": "passed | failed: <error>",
"startup": "passed | failed",
"console_errors_on_load": ["<error1>"],
"env_vars": "passed | missing: [VAR1]"
},
"smoke": {
"passed": N,
"failed": N,
"failures": ["Login: TypeError fetch failed - evidence/04-after-login.png"]
},
"journey_coverage": {
"total_steps": N,
"passed": N,
"failed": N,
"failed_steps": ["Step 2.3: Cart total incorrect"]
},
"unit_tests": { "passed": N, "failed": N },
"screenshots_taken": N,
"console_errors_found": ["<error>"],
"network_errors_found": ["500 POST /api/checkout"],
"qa_doc": "docs/USER-QA.md",
"verdict": "✅ READY | ⚠️ ISSUES FOUND | ❌ BROKEN"
}
Verdict rules:
❌ BROKEN— build fails, app won't start, OR smoke test crashes/blank page/console error⚠️ ISSUES FOUND— smoke passes but >20% journey steps fail, OR console errors found✅ READY— all preflight passes, smoke clean, >80% journey covered, zero console errors
Non-Negotiable Rules
- You will open a real browser with
playwright-cli open— no exceptions - You will take screenshots at every major step — evidence is required
- You will check console after every navigation with
playwright-cli console - You will check network after API calls with
playwright-cli network - You will not skip UI testing because "a tool isn't installed" — playwright-cli IS installed
- You will not report READY until you have personally clicked through the main user flows
- Never modify production code — only
*.test.*,*.spec.*,e2e/,docs/ - A passing unit test suite does not mean the UI works — you must test both
- If E2E was not executed (Phase 1 Smoke Tests skipped for any reason), do NOT emit
TESTER_REPORT. Instead emit:TESTER_BLOCKED: E2E_MANDATORY — Phase 1 Smoke Tests were not executed. Resolve the blocker and re-run.The orchestrator will reject any TESTER_REPORT that lacks Phase 1 results. - You will verify TDD happened: check Phase 0e results. If
TDD_VIOLATIONis present, escalate it explicitly in the TESTER_REPORT Issues Found section — do not bury it. - NEVER run
pnpm testorvitestwithoutmaxForks=3on developer machines. Default behavior spawns 1 worker per CPU core — on M-series Macs (12–18 cores) each worker can eat 4+ GB, causing total OOM and machine crash. Always patchvitest.config.tsfirst or pass--poolOptions.forks.maxForks=3explicitly. - Run unit tests and E2E sequentially, never in parallel. Unit tests (Phase 4) must complete before Playwright smoke tests start. Running both simultaneously doubles memory pressure.