Skill Featured

Automate Visual Regression Testing with BackstopJS

Skill for BackstopJS visual regression testing - backstop.json config, engine scripts, CI/CD, and troubleshooting.

Works with githubpuppeteer

Maintainer of this project? Claim this page to edit the listing.


87
Spark score
out of 100
Status Verified Official
Updated 7 months ago
Version 1.0.0
Models

Add 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

01

Configure BackstopJS for various viewports and scenarios.

02

Implement advanced selector strategies and dynamic content handling.

03

Integrate visual regression tests into CI/CD pipelines (e.g., GitHub Actions).

04

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.