All skills
browserbase avatar

/ui-test

@b2ae728 official
by browserbasebrowserbase/skills3.7k stars
240

AI-powered adversarial UI testing via the browse CLI. Analyzes git diffs to test only what changed, or explores the full app to find bugs. Tests functional correctness, accessibility, responsive layout, and UX heuristics. Use when the user asks to test UI changes, QA a pull request, audit accessibility, or run exploratory testing. Supports local browser (localhost) and remote Browserbase (deployed sites).

Use this Skill: https://skilld.dev/gh/browserbase/skills/ui-test

This session only. Nothing lands on disk.

referencesbrowser-recipes.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Browser Recipes for UI Testing

Copy-paste recipes using the browse CLI for deterministic UI checks. Works with both local and remote browsers.

Important: browse eval does not support top-level await. Use .then() for async operations, or split into multiple eval calls.

Accessibility Audit (axe-core)

Two-step recipe (load script, then run):

browse open "TARGET_URL"
browse wait load

# Step 1: Load axe-core
browse eval "const s = document.createElement('script'); s.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js'; document.head.appendChild(s); 'loading'"

# Step 2: Wait for script to load
browse wait timeout 3000

# Step 3: Run audit
browse eval "axe.run().then(r => JSON.stringify({ violations: r.violations.map(v => ({ id: v.id, impact: v.impact, description: v.description, nodes: v.nodes.length, help: v.helpUrl })), passes: r.passes.length, incomplete: r.incomplete.length }))"

Interpret results:

  • impact: "critical" or "serious" = must fix
  • impact: "moderate" or "minor" = should fix
  • Check helpUrl for remediation guidance

Performance Metrics

browse open "TARGET_URL"
browse wait load
browse eval "
  const nav = performance.getEntriesByType('navigation')[0];
  const paint = performance.getEntriesByType('paint');
  JSON.stringify({
    domContentLoaded: Math.round(nav?.domContentLoadedEventEnd),
    loadComplete: Math.round(nav?.loadEventEnd),
    firstPaint: Math.round(paint.find(p => p.name === 'first-paint')?.startTime),
    firstContentfulPaint: Math.round(paint.find(p => p.name === 'first-contentful-paint')?.startTime),
    transferSize: nav?.transferSize,
    domInteractive: Math.round(nav?.domInteractive),
  });
"

Thresholds (Doherty Threshold + Web Vitals):

  • FCP < 1.8s = good, < 3s = needs improvement, > 3s = poor
  • Load complete < 3s = good for SaaS dashboards
  • DOM interactive < 400ms = feels instant (Doherty Threshold)

Broken Images

browse open "TARGET_URL"
browse wait load
browse eval "
  const imgs = Array.from(document.querySelectorAll('img'));
  const broken = imgs.filter(i => !i.complete || i.naturalWidth === 0);
  JSON.stringify(broken.map(i => ({ src: i.src, alt: i.alt })));
"

Console Errors

Important: Injecting on about:blank then navigating wipes the capture (different page context). Instead, use one of these approaches:

Method 1: Check failed resources on page load (deterministic)

browse open "TARGET_URL"
browse wait load
browse eval "JSON.stringify(performance.getEntries().filter(e => e.entryType === 'resource' && e.responseStatus >= 400).map(e => ({ url: e.name, status: e.responseStatus })))"

Method 2: Capture runtime errors during interaction

browse open "TARGET_URL"
browse wait load

# Inject capture on the page itself
browse eval "window.__logs = []; const orig = { error: console.error, warn: console.warn }; console.error = (...args) => { window.__logs.push({type:'error', text: args.join(' ')}); orig.error(...args); }; console.warn = (...args) => { window.__logs.push({type:'warn', text: args.join(' ')}); orig.warn(...args); }; window.addEventListener('error', e => window.__logs.push({type:'uncaught', text: e.message})); window.addEventListener('unhandledrejection', e => window.__logs.push({type:'rejection', text: String(e.reason)})); 'installed'"

# Interact with the page (clicks, form submits, etc.)
browse click @some-ref
browse eval "JSON.stringify(window.__logs)"

This captures errors that occur during interaction, not on initial load. For initial load errors, use Method 1.

Keyboard Navigation

Tab through all focusable elements and record the order:

# Start on the page
browse open "TARGET_URL"
browse wait load

# Tab through elements one at a time
browse press Tab
browse eval "JSON.stringify({tag: document.activeElement?.tagName, text: document.activeElement?.textContent?.trim().slice(0,40), role: document.activeElement?.getAttribute('role'), ariaLabel: document.activeElement?.getAttribute('aria-label'), hasFocus: (() => { const s = window.getComputedStyle(document.activeElement); return s.outlineStyle !== 'none' || s.boxShadow !== 'none'; })()})"

# Repeat browse press Tab + eval to build the full tab order
# Stop when activeElement returns BODY (looped back)

What to check in the results:

  • Every interactive element should appear in the tab order
  • Order should follow visual layout (top-to-bottom, left-to-right)
  • hasFocus should be true for every element (visible focus ring)
  • No elements should be skipped or appear out of order

Responsive Screenshot Sweep

browse open "TARGET_URL"
browse wait load

# Mobile (iPhone SE)
browse viewport 375 812
browse wait timeout 1000
browse screenshot --path /tmp/mobile.png --full-page

# Tablet (iPad)
browse viewport 768 1024
browse wait timeout 1000
browse screenshot --path /tmp/tablet.png --full-page

# Desktop
browse viewport 1440 900
browse wait timeout 1000
browse screenshot --path /tmp/desktop.png --full-page

After capturing, read each screenshot with the Read tool and evaluate:

  • Mobile: is there a hamburger menu? Are touch targets ≥44px? Does content overflow?
  • Tablet: does the layout adapt or just shrink? Is the sidebar behavior correct?
  • Desktop: is content width reasonable? Not stretched edge-to-edge?

Check All Links

browse open "TARGET_URL"
browse wait load
browse eval "
  const links = Array.from(document.querySelectorAll('a[href]'));
  JSON.stringify(links.map(a => ({
    href: a.href,
    text: a.textContent?.trim().slice(0, 50),
    isExternal: !a.href.startsWith(location.origin),
    opensNewTab: a.target === '_blank'
  })));
"

Check Form Structure

browse open "TARGET_URL"
browse wait load
browse eval "
  const forms = Array.from(document.querySelectorAll('form'));
  JSON.stringify(forms.map(f => ({
    action: f.action,
    method: f.method,
    inputs: Array.from(f.querySelectorAll('input,select,textarea')).map(i => ({
      name: i.name, type: i.type, required: i.required,
      hasLabel: !!(i.labels?.length || i.getAttribute('aria-label') || i.getAttribute('aria-labelledby')),
      placeholder: i.placeholder
    }))
  })));
"

Check Empty State

Navigate to a page/section with no data and screenshot it:

browse open "TARGET_URL"  # e.g., /sessions with no sessions
browse wait load
browse screenshot --path /tmp/empty-state.png --full-page
browse snapshot  # check if there's helpful text/CTA

Evaluate: does it have a message? An illustration? A CTA to create the first item? Or is it just blank?

Source: SKILL.md on GitHub

1 warning16d4 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides automated user interface testing scripts and recipes using the `browse` CLI tool. The dependencies and remote script sources specified are well-known and reputable technology entities, and the operations described strictly relate to its core purpose of visual and functional interface verification.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: MEDIUM · 2 issues

  • ZeroLeaks5mo

    2 findings · Score: 80/100

Signed by skilld at b2ae728. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Activeupdated 5 months ago
metadata
{
  "author": "browserbase",
  "version": "0.4.0"
}
All 1 allowed tools
Bash Read Glob Grep Agent
Other metadata
compatibility
Requires the browse CLI (`npm install -g browse`). For remote testing: BROWSERBASE_API_KEY and cookie-sync skill.

README badge

README badge for browserbase/skills/ui-test