Automate Visual Regression Testing with BackstopJS
Skill for BackstopJS visual regression testing - backstop.json config, engine scripts, CI/CD, and troubleshooting.
Maintainer of this project? Claim this page to edit the listing.
1.0.0Add to Favorites
Why it matters
Ensure the visual integrity of your web applications by automating visual regression testing. This skill leverages BackstopJS to detect and report unintended visual changes across different viewports and scenarios.
Outcomes
What it gets done
Configure BackstopJS for various viewports and scenarios.
Implement advanced selector strategies and dynamic content handling.
Integrate visual regression tests into CI/CD pipelines (e.g., GitHub Actions).
Troubleshoot common issues like font rendering and set up Docker environments.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/vb-visual-regression-backstop | bash Overview
Visual Regression Testing with BackstopJS
A skill for BackstopJS visual regression testing - scenario and viewport configuration, Puppeteer engine scripts for auth and dynamic content, CI/CD integration, and rendering-consistency troubleshooting. Use it for BackstopJS screenshot-comparison testing specifically, not general end-to-end or functional test frameworks.
What it does
This skill sets up and troubleshoots visual regression testing with BackstopJS, detecting unintended visual changes in web applications through automated screenshot comparison. The backstop.json configuration defines viewports (phone/tablet/desktop dimensions), scenarios (a URL, CSS selectors, a readyEvent, delay, and misMatchThreshold), reference/test/report paths, the puppeteer engine, and engine options like --no-sandbox/--disable-setuid-sandbox. Advanced selector strategies use removeSelectors to strip elements like ads or timestamps, hideSelectors for loading spinners or avatars, a readySelector to wait for content, and postInteractionWait; dynamic-content scenarios add onBeforeScript/onReadyScript hooks and requireSameDimensions.
Engine scripts handle complex interactions: an onBefore.js script sets authentication cookies and mocks API responses via Puppeteer's request interception (returning a JSON body for /api/dynamic-data while letting other requests continue), and an onReady.js script waits for document.getAnimations() to finish, closes modals, and scrolls to trigger lazy-loaded content on scenarios labeled "Infinite Scroll." Advanced configuration covers multi-environment testing (comparing a staging URL against a referenceUrl on production with a tighter misMatchThreshold) and a responsive-testing viewport set spanning phone portrait/landscape through desktop. CI/CD integration is shown via a 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
Performance tuning covers asyncCaptureLimit/asyncCompareLimit and additional Chromium flags (--disable-dev-shm-usage, --disable-gpu, --no-first-run) for parallel testing. Troubleshooting covers font-rendering consistency flags (--font-render-hinting=none, --disable-font-subpixel-positioning, --disable-lcd-text) and a Docker environment installing Chromium via Alpine packages with PUPPETEER_SKIP_CHROMIUM_DOWNLOAD and PUPPETEER_EXECUTABLE_PATH set for a consistent containerized rendering environment. Overall guidance favors specific selectors over full-document captures, proper wait strategies for dynamic content, and separate reference images per environment.
When to use - and when NOT to
Use it when setting up or debugging BackstopJS visual regression tests - configuring scenarios and viewports, writing engine scripts for auth or dynamic content, wiring CI/CD, or fixing font-rendering or containerization issues, keeping the CI Node.js version (18) and the Docker base image aligned for consistent rendering. It is not a general end-to-end or functional-testing framework guide - it is scoped specifically to BackstopJS screenshot-comparison testing.
Inputs and outputs
Given a web application and its key pages, it produces a backstop.json scenario and viewport configuration, onBefore/onReady Puppeteer engine scripts, a CI/CD workflow, and Docker and Chromium-flag configuration for consistent rendering.
Integrations
Built on BackstopJS with a Puppeteer engine and GitHub Actions for CI/CD; the containerized setup uses a node:18-alpine base image with Chromium installed via Alpine's apk alongside nss, freetype, harfbuzz, and ttf-freefont packages, matching the CI runner's Node 18 version.
Who it's for
Frontend and QA engineers setting up or maintaining automated visual regression testing.
Source README
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.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.