@yofix/browser
Intelligent browser automation for visual regression testing of route changes. Powered by Playwright and Claude AI.
Features
- 🚀 Framework-agnostic - Works with any frontend framework
- 🔐 Smart authentication - AI-powered login flow detection using Claude AI
- 📱 Multi-viewport - Capture screenshots at different screen sizes with custom device scale factors
- 💾 Flexible storage - Local file system or GitHub Actions artifacts
- ⚡ Cache-first - Fast subsequent runs with cached login configs (30-day TTL)
- 🎯 Type-safe - Full TypeScript support with comprehensive type definitions
- 🔧 Dual interface - CLI + programmatic API
- 🎨 Customizable - Configurable viewports, timeouts, wait conditions, and browser options
- 📝 Routes from file - Support for reading routes from files or CLI arguments
- 🔄 Smart login - Skip login if already authenticated, force refresh detection
Installation
npm install @yofix/browser
Or use directly with npx:
npx @yofix/browser capture --help
Quick Start
Basic Usage (No Authentication)
route-impact-browser capture \
--codebase ./my-app \
--base-url https://example.com \
--routes / /about /pricing \
--viewports "1920x1080:desktop,768x1024:tablet,375x667:mobile"
With Authentication
route-impact-browser capture \
--codebase ./my-app \
--base-url https://preview.example.com \
--routes /dashboard /settings \
--enable-auth \
--auth-email user@example.com \
--auth-password "password" \
--llm-api-key sk-ant-api03-... \
--viewports "1920x1080:desktop"
Programmatic API
import { captureRouteScreenshots } from '@yofix/browser'
const result = await captureRouteScreenshots({
codebase: { path: './my-app' },
routes: ['/dashboard', '/settings'],
baseUrl: 'https://preview.example.com',
loginUrl: '/login/password',
credentials: {
email: 'user@example.com',
password: 'password'
},
options: {
viewports: [
{ width: 1920, height: 1080, name: 'desktop' }
],
llm: {
provider: 'anthropic',
apiKey: process.env.CLAUDE_API_KEY,
model: 'claude-sonnet-4-5-20250929'
},
auth: {
enabled: true,
forceRefresh: false,
skipLoginIfAuthenticated: true,
cache: {
enabled: true,
ttl: 30 * 24 * 60 * 60 * 1000
}
},
browser: {
headless: true,
timeout: 30000,
waitUntil: 'networkidle'
},
storage: {
provider: 'local',
artifactName: 'screenshots'
},
verbose: false
}
})
console.log(`Captured ${result.metadata.totalScreenshots} screenshots`)
GitHub Actions Integration
name: Visual Regression Testing
on:
pull_request:
types: [opened, synchronize]
jobs:
screenshot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Capture route screenshots
uses: ./.github/actions/screenshot-routes
with:
codebase-path: '.'
base-url: ${{ needs.deploy-preview.outputs.url }}
routes: '/,/dashboard,/settings'
viewports: '1920x1080:desktop,768x1024:tablet'
enable-auth: 'true'
auth-email: ${{ secrets.TEST_EMAIL }}
auth-password: ${{ secrets.TEST_PASSWORD }}
claude-api-key: ${{ secrets.CLAUDE_API_KEY }}
artifact-name: 'screenshots-${{ github.event.pull_request.number }}'
CLI Commands
Capture Screenshots
route-impact-browser capture [options]
Required Options:
--codebase <path> - Path to codebase directory
--base-url <url> - Base URL of the running application
--routes <routes...> - Routes to capture (space-separated)
--routes-file <path> - File containing routes (one per line)
Authentication Options:
--enable-auth - Enable authentication
--auth-email <email> - Authentication email (or use AUTH_EMAIL env var)
--auth-password <password> - Authentication password (or use AUTH_PASSWORD env var)
--llm-api-key <key> - Claude API key for login detection (or use CLAUDE_API_KEY env var)
--llm-model <model> - Claude model to use (default: claude-sonnet-4-5-20250929)
--force-refresh-login - Force re-detection of login flow (ignore cache)
--skip-login-if-authenticated - Skip login if already authenticated
Browser Options:
--viewports <viewports> - Viewport dimensions (default: "1920x1080:desktop")
--headless / --no-headless - Run browser in headless mode
--timeout <ms> - Navigation timeout (default: 30000)
Storage Options:
--output-dir <path> - Output directory for screenshots
--storage <provider> - Storage provider: local | github-actions-artifact
--artifact-name <name> - GitHub Actions artifact name
Other Options:
--verbose - Verbose output
Cache Management
route-impact-browser clear-cache --codebase ./my-app
route-impact-browser cache-stats --codebase ./my-app
How It Works
1. Login Flow Detection (First Run)
On the first run with authentication enabled, the tool:
- Analyzes your codebase structure (package.json, file structure, dependencies)
- Uses Claude AI (Sonnet 4.5) to intelligently identify:
- Login page URL (or uses custom
loginUrl if provided)
- Email/password input selectors
- Submit button selector
- Success indicator (post-login element for authentication verification)
- Wait conditions (navigation, selectors, timeouts)
- Framework detection (NextAuth.js, Firebase Auth, Custom, etc.)
- Caches the configuration for 30 days (configurable TTL)
- Provides confidence levels (high, medium, low) with reasoning
Example detected config:
{
"loginUrl": "/login",
"selectors": {
"emailInput": "input[type='email']",
"passwordInput": "input[type='password']",
"submitButton": "button[type='submit']",
"successIndicator": "[data-testid='user-menu']"
},
"waitConditions": {
"waitForNavigation": true,
"waitForSelector": "[data-testid='user-menu']",
"timeout": 30000
},
"confidence": "high",
"framework": "NextAuth.js",
"reasoning": "Detected NextAuth.js patterns in codebase..."
}
2. Screenshot Capture
For each route:
- Navigates to the full URL (baseUrl + route)
- Waits for page load (configurable:
load, domcontentloaded, or networkidle)
- Captures screenshots at each configured viewport with proper device scale factors
- Saves with naming:
{output-dir}/{route-slug}/{viewport-name}.png
- Tracks timing metrics (navigation time, screenshot time, total time)
- Handles errors gracefully with detailed error reporting
3. Storage
Local: Screenshots saved to directory (default: screenshots-{datetime})
GitHub Actions: Uploaded as artifacts (available for 90 days)
Viewport Configuration
Format
WIDTHxHEIGHT[:NAME[:SCALE]]
Examples
--viewports "1920x1080"
--viewports "1920x1080:desktop"
--viewports "1920x1080:desktop,768x1024:tablet,375x667:mobile"
--viewports "1920x1080:desktop:2"
Programmatic API
viewports: [
{ width: 1920, height: 1080, name: 'desktop' },
{ width: 768, height: 1024, name: 'tablet', deviceScaleFactor: 2 },
{ width: 375, height: 667, name: 'mobile', deviceScaleFactor: 3 }
]
Environment Variables
The following environment variables can be used as alternatives to CLI options:
AUTH_EMAIL - Authentication email (alternative to --auth-email)
AUTH_PASSWORD - Authentication password (alternative to --auth-password)
CLAUDE_API_KEY or CLAUDE_API_KEY - Claude API key (alternative to --llm-api-key)
GITHUB_ACTIONS - Auto-detected when running in GitHub Actions (enables artifact upload)
Output Structure
screenshots-20240115_120530/
├── dashboard/
│ ├── desktop.png
│ ├── tablet.png
│ └── mobile.png
├── settings/
│ ├── desktop.png
│ ├── tablet.png
│ └── mobile.png
└── guard-trends/
└── desktop.png
Result Object
{
success: boolean
metadata: {
timestamp: number
totalRoutes: number
totalScreenshots: number
successfulRoutes: number
failedRoutes: number
outputDirectory: string
authUsed: boolean
loginFlowDetected: boolean
totalDuration: number
baseUrl: string
}
screenshots: [
{
route: string
fullUrl: string
screenshots: [
{
viewport: string
path: string
destination: string
width: number
height: number
size: number
duration: number
contentType: string
metadata: {
route: string
fullUrl: string
viewport: string
width: string
height: string
}
}
]
timing: {
navigationTime: number
screenshotTime: number
totalTime: number
}
success: boolean
error?: string
}
]
errors?: [
{
code: string
message: string
route?: string
phase?: 'login' | 'navigation' | 'screenshot' | 'storage'
details?: unknown
}
]
artifactInfo?: {
uploaded: boolean
artifactName: string
artifactId?: number
size: number
}
}
Troubleshooting
Login Detection Fails
- Ensure Claude API key is provided
- Check that your codebase has login-related files
- Use
--force-refresh-login to re-detect
- Manually verify the detected selectors work
Screenshots Fail
- Increase timeout:
--timeout 60000
- Run non-headless to debug:
--no-headless
- Check routes are valid and accessible
- Ensure authentication succeeded
Cache Issues
- Clear cache:
route-impact-browser clear-cache --codebase ./my-app
- Use
--force-refresh-login to regenerate
- Check cache stats:
route-impact-browser cache-stats --codebase ./my-app
License
MIT
GitHub Repository
Related Projects