Test EventOS web application end-to-end using Playwright browser automation via MCP...
Automates browser testing for EventOS using Playwright MCP (Model Context Protocol). Tests critical user journeys, validates Supabase RLS policies, captures screenshots of errors, and monitors network requests—all without pixel-based selectors.
Key Capabilities:
Use PROACTIVELY when:
Don't use for:
# Invoke the skill via Skill tool
command: "playwright-e2e-skill"
# Or load specific playbook
command: "playwright-e2e-skill/playbooks/SMOKE.txt"
Scenario: Verify dashboard loads without authentication errors
// 1. Navigate to dashboard
await browser_navigate({ url: "https://event-studio-rho.vercel.app/dashboard" })
// 2. Capture snapshot
await browser_snapshot()
// 3. Check network for RLS errors
const requests = await browser_network_requests()
// Look for 401/403 responses
// 4. Verify stats displayed
await wait_for({ text: "Total Events" })
// 5. Screenshot for docs
await browser_take_screenshot({ filename: "dashboard-loaded.png" })
Expected Result: Dashboard loads, stats show counts, no RLS errors
Scenario: Create new event from wizard form
// 1. Navigate to event creation
await browser_navigate({ url: "/events/new" })
// 2. Fill event details
await browser_fill_form({
fields: [
{ name: "Event Name", ref: "input-name", value: "Tech Conference 2025" },
{ name: "Date", ref: "input-date", value: "2025-12-01" },
{ name: "Venue", ref: "select-venue", value: "Convention Center" }
]
})
// 3. Submit form
await browser_click({ element: "Create Event button", ref: "btn-submit" })
// 4. Wait for success
await wait_for({ text: "Event created successfully" })
// 5. Monitor console for errors
const logs = await browser_console_messages({ onlyErrors: true })
Expected Result: Event created, success message shown, no console errors
Scenario: Complete ticket booking as anonymous user
// 1. Navigate to event page
await browser_navigate({ url: "/events/123/book" })
// 2. Monitor network (check RLS)
const startRequests = await browser_network_requests()
// 3. Fill booking form
await browser_fill_form({
fields: [
{ name: "Tickets", ref: "input-quantity", value: "2" },
{ name: "Email", ref: "input-email", value: "test@example.com" }
]
})
// 4. Click checkout
await browser_click({ element: "Checkout button", ref: "btn-checkout" })
// 5. Wait for confirmation
await wait_for({ text: "Booking confirmed" })
// 6. Verify no auth errors
const allRequests = await browser_network_requests()
const authErrors = allRequests.filter(r => r.status === 401 || r.status === 403)
Expected Result: Booking completes, no 401/403 errors, confirmation shown
# Run smoke tests
Skill("playwright-e2e-skill/playbooks/SMOKE.txt")
# Run auth tests
Skill("playwright-e2e-skill/playbooks/AUTH.txt")
# Run wizard tests
Skill("playwright-e2e-skill/playbooks/PITCH_DECK_WIZARD.txt")
| Tool | Purpose | Example |
|---|---|---|
browser_navigate |
Load URL | Navigate to /dashboard |
browser_snapshot |
Get page structure | Capture accessibility tree |
browser_click |
Click element | Click "Create Event" |
browser_fill |
Fill single input | Enter event name |
browser_fill_form |
Fill multiple inputs | Complete entire form |
wait_for |
Wait for text | Wait for "Success" |
browser_network_requests |
Get API calls | Check Supabase queries |
browser_console_messages |
Get logs | Find React errors |
browser_take_screenshot |
Capture visual | Screenshot error state |
Full feature list: See resources/FEATURES.md
┌─────────────────────────────────────────┐
│ 1. NAVIGATE to page │
│ browser_navigate({ url }) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 2. SNAPSHOT to understand structure │
│ browser_snapshot() │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 3. INTERACT with elements │
│ browser_fill_form(), browser_click() │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 4. VALIDATE result │
│ wait_for(), browser_snapshot() │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 5. CAPTURE evidence │
│ browser_take_screenshot() │
│ browser_network_requests() │
│ browser_console_messages() │
└─────────────────────────────────────────┘
Problem: Dashboard shows blank because RLS blocks anon users Solution: Monitor network requests for 401/403 errors
await browser_navigate({ url: "/dashboard" })
const requests = await browser_network_requests()
const blockedRequests = requests.filter(r =>
r.status === 401 || r.status === 403
)
// If blockedRequests.length > 0, RLS policies need updating
Problem: VITE_DISABLE_AUTH=true not working
Solution: Check if protected routes redirect to /auth
await browser_navigate({ url: "/dashboard" })
await wait_for({ time: 2 }) // Allow redirect
const snapshot = await browser_snapshot()
// If snapshot shows /auth login form, auth bypass failed
Problem: Forms submit but errors not displayed Solution: Capture console errors and screenshot
await browser_fill_form({ fields: [...] })
await browser_click({ element: "Submit", ref: "btn-submit" })
const errors = await browser_console_messages({ onlyErrors: true })
await browser_take_screenshot({ filename: "form-error.png" })
See resources/agent.config.json for full agent setup.
Key settings:
true for CI, false for debugginghttps://event-studio-rho.vercel.appRequired in .env:
PLAYWRIGHT_BROWSER=chromium
PLAYWRIGHT_HEADLESS=true
PLAYWRIGHT_BASE_URL=https://event-studio-rho.vercel.app
Solution: Take snapshot first to see available elements
await browser_snapshot() // Shows all interactive elements with refs
Solution: Navigate THEN check requests (cleared on navigation)
await browser_navigate({ url: "/dashboard" })
const requests = await browser_network_requests() // Requests since navigation
Solution: Check console for errors that prevented render
await browser_console_messages() // May show React error that blocked render
Solution: Use headed mode locally for debugging
PLAYWRIGHT_HEADLESS=false npm test
resources/FEATURES.mdresources/agent.config.jsonresources/examples/inputs.jsonscripts/RUNBOOK.md# Run all smoke tests
npm run test:smoke
# Run auth tests
npm run test:auth
# Run wizard tests
npm run test:wizard
# Run headless (CI mode)
PLAYWRIGHT_HEADLESS=true npm test
# Run headed (debug mode)
PLAYWRIGHT_HEADLESS=false npm test
# Run specific browser
PLAYWRIGHT_BROWSER=firefox npm test
- name: Run Playwright Smoke Tests
run: npm run test:smoke
env:
PLAYWRIGHT_HEADLESS: true
PLAYWRIGHT_BASE_URL: ${{ secrets.VERCEL_URL }}
# After successful Vercel deploy
vercel deploy --prod
npm run test:smoke -- --base-url=$VERCEL_URL
Browser not installed?
npx @playwright/mcp install
MCP server not responding?
# Check .mcp.json has playwright configured
cat .mcp.json | grep playwright
Tests passing locally but failing in CI?
PLAYWRIGHT_BASE_URL environment variableQuestions? See scripts/RUNBOOK.md for detailed local setup and CI integration instructions.