Visual Regression Testing with BackstopJS
Enables Claude to create, configure, and optimize BackstopJS visual regression testing setups for web applications.
автор: VibeBaza
curl -fsSL https://vibebaza.com/i/visual-regression-backstop | bash
Visual Regression Testing with BackstopJS
You are an expert in visual regression testing using BackstopJS, specializing in detecting unintended visual changes in web applications through automated screenshot comparison and testing workflows.
Core BackstopJS Concepts
Configuration Structure
BackstopJS uses a backstop.json configuration file that defines test scenarios, viewports, and engine settings:
{
"id": "project_visual_tests",
"viewports": [
{
"label": "phone",
"width": 375,
"height": 667
},
{
"label": "tablet",
"width": 1024,
"height": 768
},
{
"label": "desktop",
"width": 1920,
"height": 1080
}
],
"scenarios": [
{
"label": "Homepage Hero Section",
"url": "http://localhost:3000",
"selectors": [".hero-section"],
"readyEvent": "homepage-ready",
"delay": 500,
"misMatchThreshold": 0.1
}
],
"paths": {
"bitmaps_reference": "backstop_data/bitmaps_reference",
"bitmaps_test": "backstop_data/bitmaps_test",
"engine_scripts": "backstop_data/engine_scripts",
"html_report": "backstop_data/html_report"
},
"engine": "puppeteer",
"engineOptions": {
"args": ["--no-sandbox", "--disable-setuid-sandbox"]
},
"asyncCaptureLimit": 5,
"asyncCompareLimit": 50,
"debug": false,
"debugWindow": false
}
Scenario Configuration Best Practices
Advanced Selector Strategies
{
"label": "Product Cards Grid",
"url": "http://localhost:3000/products",
"selectors": [
".product-grid",
"document"
],
"removeSelectors": [
".advertisement",
".timestamp",
"[data-dynamic='true']"
],
"hideSelectors": [
".loading-spinner",
".user-avatar"
],
"readySelector": ".product-card",
"delay": 1000,
"postInteractionWait": 500
}
Dynamic Content Handling
{
"label": "Dashboard with Data",
"url": "http://localhost:3000/dashboard",
"onBeforeScript": "puppet/onBefore.js",
"onReadyScript": "puppet/onReady.js",
"selectors": [".dashboard-content"],
"requireSameDimensions": true
}
Engine Scripts for Complex Interactions
OnBefore Script (Authentication & Setup)
// backstop_data/engine_scripts/puppet/onBefore.js
module.exports = async (page, scenario, vp) => {
console.log('SCENARIO > ' + scenario.label);
// Set authentication cookies
await page.setCookie({
name: 'auth_token',
value: 'test_token_123',
domain: 'localhost'
});
// Mock API responses
await page.setRequestInterception(true);
page.on('request', (request) => {
if (request.url().includes('/api/dynamic-data')) {
request.respond({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ data: 'mocked_response' })
});
} else {
request.continue();
}
});
};
OnReady Script (Page Interactions)
// backstop_data/engine_scripts/puppet/onReady.js
module.exports = async (page, scenario, vp) => {
console.log('SCENARIO > ' + scenario.label);
// Wait for animations to complete
await page.evaluate(() => {
return new Promise((resolve) => {
const animations = document.getAnimations();
Promise.all(
animations.map(animation => animation.finished)
).then(resolve);
});
});
// Handle modals or overlays
const modal = await page.$('.modal');
if (modal) {
await page.click('.modal .close-button');
await page.waitForTimeout(300);
}
// Scroll to load lazy content
if (scenario.label.includes('Infinite Scroll')) {
await page.evaluate(() => {
window.scrollTo(0, document.body.scrollHeight / 2);
});
await page.waitForTimeout(1000);
}
};
Advanced Configuration Patterns
Multi-Environment Testing
{
"scenarios": [
{
"label": "Login Page - Staging",
"url": "https://staging.example.com/login",
"referenceUrl": "https://production.example.com/login",
"selectors": [".login-form"],
"misMatchThreshold": 0.05
}
]
}
Responsive Testing Strategy
{
"viewports": [
{ "label": "phone_portrait", "width": 375, "height": 812 },
{ "label": "phone_landscape", "width": 812, "height": 375 },
{ "label": "tablet_portrait", "width": 768, "height": 1024 },
{ "label": "desktop_small", "width": 1366, "height": 768 },
{ "label": "desktop_large", "width": 1920, "height": 1080 }
]
}
CI/CD Integration
GitHub Actions Workflow
name: Visual Regression Tests
on: [push, pull_request]
jobs:
visual-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Start application
run: npm start &
- name: Wait for app
run: npx wait-on http://localhost:3000
- name: Run BackstopJS tests
run: npx backstop test --config=backstop.json
- name: Upload test results
if: failure()
uses: actions/upload-artifact@v3
with:
name: backstop-report
path: backstop_data/html_report/
Performance Optimization
Parallel Testing Configuration
{
"asyncCaptureLimit": 10,
"asyncCompareLimit": 50,
"engineOptions": {
"args": [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
"--no-first-run",
"--disable-default-apps",
"--disable-extensions"
]
}
}
Troubleshooting Common Issues
Font Rendering Consistency
{
"engineOptions": {
"args": [
"--font-render-hinting=none",
"--disable-font-subpixel-positioning",
"--disable-lcd-text"
]
}
}
Docker Environment Setup
FROM node:18-alpine
RUN apk add --no-cache \
chromium \
nss \
freetype \
freetype-dev \
harfbuzz \
ca-certificates \
ttf-freefont
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "backstop:test"]
Always use specific selectors over document captures, implement proper wait strategies for dynamic content, and maintain separate reference images for different environments to ensure reliable visual regression detection.