Agent skill
browser-test
Execute browser-based UI testing using Chrome MCP tools. Tests user interactions, visual outcomes, captures evidence. Use for UI features after implementation and security review.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/browser-test
SKILL.md
Browser Testing Skill
Purpose
Execute browser-based UI testing for web applications using Claude in Chrome MCP tools. Verify UI acceptance criteria, capture evidence, and validate user flows.
When to Use
Mandatory for:
- UI components or page changes
- User interaction flows (clicks, forms, navigation)
- Visual features (modals, toasts, animations)
- Responsive design changes
Optional for:
- Backend-only changes
- API-only features
- Non-visual refactoring
Browser Testing Process
Step 1: Load Spec and Identify UI Criteria
# Load spec
cat .claude/specs/active/<slug>.md
Extract UI-specific acceptance criteria:
- User interactions (button clicks, form submissions)
- Visual feedback (toasts, modals, error messages)
- Navigation (redirects, route changes)
- State changes (UI updates, data display)
Example:
## UI Acceptance Criteria (from spec)
- AC1.3: Confirmation toast displayed after logout
- AC2.3: Retry button appears on network error
- AC1.2: User redirected to /login page
Step 2: Get Browser Context
// Get existing tabs
tabs_context_mcp({ createIfEmpty: true });
// Create new tab for testing
tabs_create_mcp();
Best practice: Use a fresh tab for each test session to avoid state pollution.
Step 3: Navigate to Test Environment
navigate({
url: "http://localhost:3000/dashboard", // or staging URL
tabId: <tabId>
});
Environment options:
- Local dev:
http://localhost:3000 - Staging:
https://staging.example.com - Production: Only for smoke tests, never for destructive tests
Step 4: Execute UI Test Cases
For each UI acceptance criterion, execute a test case.
Test Case Structure
**Test Case**: AC1.3 - Confirmation toast after logout
**Steps**:
1. Navigate to /dashboard
2. Find logout button
3. Click logout button
4. Wait for toast to appear
5. Verify toast message
**Expected**: Toast with message "You have been logged out"
**Evidence**: Screenshot of toast
Executing Test Steps
// Step 1: Navigate
navigate({ url: "http://localhost:3000/dashboard", tabId });
// Step 2: Find element
find({ query: "logout button", tabId });
// Returns: ref_1 (logout button reference)
// Step 3: Interact
computer({
action: "left_click",
ref: "ref_1",
tabId
});
// Step 4: Wait and verify
await computer({ action: "wait", duration: 1, tabId });
// Step 5: Take evidence screenshot
computer({ action: "screenshot", tabId });
// Screenshot captured with ID for later reference
Step 5: Verify Outcomes
Use multiple verification methods:
Visual Verification
// Take screenshot
const screenshot = computer({ action: "screenshot", tabId });
// Use find to locate expected element
find({ query: "confirmation toast", tabId });
// Returns: ref_2 if found, error if not
DOM Verification
// Read page to check element exists
read_page({ tabId, filter: "all" });
// Returns accessibility tree with elements
// Or use JavaScript to verify
javascript_tool({
tabId,
action: "javascript_exec",
text: `
const toast = document.querySelector('[role="status"]');
toast?.textContent.includes("logged out");
`
});
// Returns: true/false
Navigation Verification
// Check current URL
javascript_tool({
tabId,
action: "javascript_exec",
text: "window.location.pathname"
});
// Returns: "/login" (verify redirect)
Step 6: Handle Failures and Errors
Element Not Found
find({ query: "logout button", tabId });
// Error: "No elements found matching 'logout button'"
Actions:
- Take screenshot to see current page state
- Try alternative selectors ("button with text logout", "sign out button")
- Check if page loaded correctly (read_page)
- If element genuinely missing → Report as test failure
Interaction Failed
computer({ action: "left_click", ref: "ref_1", tabId });
// Element not clickable, or click has no effect
Actions:
- Wait for page to settle (animations, loading)
- Scroll element into view
- Try alternative interaction (keyboard instead of mouse)
- Report as test failure if truly broken
Unexpected Behavior
// Expected redirect to /login, but stayed on /dashboard
javascript_tool({ text: "window.location.pathname", tabId });
// Returns: "/dashboard" (unexpected)
Action: Document failure with evidence (screenshot) and report.
Step 7: Capture Evidence
For each test case, capture evidence:
// Screenshot of key states
computer({ action: "screenshot", tabId });
// Zoom in on specific element
computer({
action: "zoom",
region: [x0, y0, x1, y1], // Element bounds
tabId
});
Evidence includes:
- Initial state before interaction
- Interaction point (e.g., button being clicked)
- Final state after interaction
- Error states (if testing error paths)
Step 8: Document Test Results
Create test results document:
# Browser Test Results: <Task Name>
**Date**: 2026-01-02 17:30
**Environment**: http://localhost:3000
**Browser**: Chrome
---
## Test Cases
### TC1: Logout Button Click (AC1.1, AC1.2)
**Status**: ✅ PASS
**Steps**:
1. ✅ Navigated to http://localhost:3000/dashboard
2. ✅ Found logout button (ref_1)
3. ✅ Clicked logout button
4. ✅ Verified redirect to /login
**Evidence**: screenshot-001.png, screenshot-002.png
**Result**: User successfully logged out and redirected
---
### TC2: Confirmation Toast (AC1.3)
**Status**: ✅ PASS
**Steps**:
1. ✅ Clicked logout button
2. ✅ Toast appeared with message "You have been logged out"
3. ✅ Toast auto-dismissed after 3 seconds
**Evidence**: screenshot-003.png
**Result**: Confirmation toast displayed correctly
---
### TC3: Retry Button on Error (AC2.3)
**Status**: ❌ FAIL
**Steps**:
1. ✅ Simulated network error (DevTools network throttling)
2. ✅ Clicked logout button
3. ❌ Expected retry button, but only error message shown
**Evidence**: screenshot-004.png
**Result**: FAILURE - Retry button not rendered
**Issue**: Implementation missing retry button component
---
## Summary
**Passed**: 2/3 (67%)
**Failed**: 1/3 (33%)
**Blocker**: TC3 failure blocks merge - retry button required per spec AC2.3
**Action**: Fix retry button implementation, re-run browser tests
Step 9: Update Spec with Browser Test Evidence
Add results to spec:
## Browser Test Results
**Date**: 2026-01-02 17:30
**Environment**: localhost:3000
| AC | Test Case | Status | Evidence |
|----|-----------|--------|----------|
| AC1.1 | Logout clears auth | ✅ Pass | screenshot-001.png |
| AC1.2 | Redirect to /login | ✅ Pass | screenshot-002.png |
| AC1.3 | Confirmation toast | ✅ Pass | screenshot-003.png |
| AC2.3 | Retry button | ❌ Fail | screenshot-004.png |
**Overall**: 3/4 pass (75%) - 1 blocking failure
Step 10: Handle Test Failures
If tests fail:
-
Verify failure is real (not test issue):
- Re-run test to confirm not flaky
- Check environment is correct
- Verify test steps match spec
-
Document failure clearly:
- Screenshot showing actual vs expected
- Steps to reproduce
- Severity (blocking or minor)
-
Route to fix:
- Use
/implementto fix implementation - Update spec if expectation was wrong
- Re-run browser tests after fix
- Use
Testing Patterns
Pattern 1: Form Submission
// Find form fields
find({ query: "email input", tabId });
// Returns: ref_1
find({ query: "password input", tabId });
// Returns: ref_2
// Fill form
form_input({ ref: "ref_1", value: "[email protected]", tabId });
form_input({ ref: "ref_2", value: "password123", tabId });
// Find and click submit
find({ query: "submit button", tabId });
// Returns: ref_3
computer({ action: "left_click", ref: "ref_3", tabId });
// Verify outcome
await computer({ action: "wait", duration: 1, tabId });
computer({ action: "screenshot", tabId });
Pattern 2: Modal Interaction
// Open modal
find({ query: "delete button", tabId });
computer({ action: "left_click", ref: "ref_1", tabId });
// Verify modal appears
await computer({ action: "wait", duration: 0.5, tabId });
find({ query: "confirmation dialog", tabId });
// Returns: ref_2
// Interact with modal
find({ query: "confirm delete button", tabId });
computer({ action: "left_click", ref: "ref_3", tabId });
// Verify modal dismissed
await computer({ action: "wait", duration: 0.5, tabId });
computer({ action: "screenshot", tabId });
Pattern 3: Navigation Flow
// Start at page A
navigate({ url: "http://localhost:3000/page-a", tabId });
// Click link to page B
find({ query: "go to page B link", tabId });
computer({ action: "left_click", ref: "ref_1", tabId });
// Verify navigation
await computer({ action: "wait", duration: 1, tabId });
const currentPath = javascript_tool({
tabId,
action: "javascript_exec",
text: "window.location.pathname"
});
// Assert
if (currentPath === "/page-b") {
// Success
} else {
// Failure
}
Pattern 4: Error State Testing
// Simulate error condition
javascript_tool({
tabId,
action: "javascript_exec",
text: `
// Mock API to fail
window.fetch = async () => {
throw new Error("Network error");
};
`
});
// Trigger action that calls API
find({ query: "save button", tabId });
computer({ action: "left_click", ref: "ref_1", tabId });
// Verify error UI
await computer({ action: "wait", duration: 1, tabId });
find({ query: "error message", tabId });
computer({ action: "screenshot", tabId });
Best Practices
Use Semantic Selectors
// Good - Semantic and resilient
find({ query: "logout button", tabId });
find({ query: "button with text logout", tabId });
find({ query: "button with aria-label logout", tabId });
// Avoid - Brittle selectors
// (find tool doesn't use CSS selectors, but JavaScript tool could)
Wait for Interactions to Complete
// After click, wait for action to complete
computer({ action: "left_click", ref: "ref_1", tabId });
await computer({ action: "wait", duration: 1, tabId }); // Wait 1 second
// Or check for expected element
find({ query: "success message", tabId });
Capture Evidence Liberally
// Before interaction
computer({ action: "screenshot", tabId });
// After interaction
computer({ action: "left_click", ref: "ref_1", tabId });
await computer({ action: "wait", duration: 1, tabId });
computer({ action: "screenshot", tabId });
// Evidence trail for debugging
Clean Up Test State
// After tests, reset state
javascript_tool({
tabId,
action: "javascript_exec",
text: "localStorage.clear(); sessionStorage.clear();"
});
// Or create fresh tab for next test
tabs_create_mcp();
Integration with Other Skills
After browser testing:
- If PASS with public API → Trigger
/docsfor documentation, then commit - If PASS (no public API) → Ready for commit
- If FAIL → Use
/implementto fix, then re-test
Before browser testing:
- Run
/unifyfor spec-impl-test convergence - Run
/securityfor security review - Browser testing validates UI before final gates
Documentation trigger: If the implementation adds or modifies public APIs, user-facing features, or configuration options, dispatch the documenter subagent after browser tests pass (before commit).
Example Test Suite
Example: Logout Feature Browser Tests
Spec ACs:
- AC1.1: Logout clears token
- AC1.2: Redirect to /login
- AC1.3: Confirmation toast
- AC2.3: Retry button on error
Test Suite:
// Test 1: Happy path logout
navigate({ url: "http://localhost:3000/dashboard", tabId });
find({ query: "logout button", tabId }); // ref_1
computer({ action: "screenshot", tabId }); // Before
computer({ action: "left_click", ref: "ref_1", tabId });
await computer({ action: "wait", duration: 1, tabId });
computer({ action: "screenshot", tabId }); // After
// Verify redirect (AC1.2)
const path = javascript_tool({
tabId,
action: "javascript_exec",
text: "window.location.pathname"
});
// Result: "/login" ✅
// Verify toast (AC1.3)
find({ query: "confirmation toast", tabId }); // Found ✅
// Test 2: Error path with retry
navigate({ url: "http://localhost:3000/dashboard", tabId });
// Simulate network error
javascript_tool({
tabId,
action: "javascript_exec",
text: "window.fetch = () => Promise.reject(new Error('Network error'));"
});
find({ query: "logout button", tabId }); // ref_1
computer({ action: "left_click", ref: "ref_1", tabId });
await computer({ action: "wait", duration: 1, tabId });
// Verify error message
find({ query: "error message", tabId }); // Found ✅
// Verify retry button (AC2.3)
find({ query: "retry button", tabId }); // Not found ❌
computer({ action: "screenshot", tabId }); // Evidence
// Result: FAIL - Retry button missing
Output: 3/4 ACs pass, 1 blocking failure (retry button missing)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?