Awesome-omni-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
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/frontend/browser-test" ~/.claude/skills/diegosouzapw-awesome-omni-skill-browser-test && rm -rf "$T"
manifest: skills/frontend/browser-test/SKILL.md
source content

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:

  1. Take screenshot to see current page state
  2. Try alternative selectors ("button with text logout", "sign out button")
  3. Check if page loaded correctly (read_page)
  4. 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:

  1. Wait for page to settle (animations, loading)
  2. Scroll element into view
  3. Try alternative interaction (keyboard instead of mouse)
  4. 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:

  1. Verify failure is real (not test issue):

    • Re-run test to confirm not flaky
    • Check environment is correct
    • Verify test steps match spec
  2. Document failure clearly:

    • Screenshot showing actual vs expected
    • Steps to reproduce
    • Severity (blocking or minor)
  3. Route to fix:

    • Use
      /implement
      to fix implementation
    • Update spec if expectation was wrong
    • Re-run browser tests after fix

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: "test@example.com", 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
    /docs
    for documentation, then commit
  • If PASS (no public API) → Ready for commit
  • If FAIL → Use
    /implement
    to fix, then re-test

Before browser testing:

  • Run
    /unify
    for spec-impl-test convergence
  • Run
    /security
    for 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)