@blazediff/vitest
Vitest matcher for visual regression testing with blazediff. Provides native toMatchImageSnapshot() matcher with automatic snapshot state tracking and support for multiple comparison algorithms.
Installation
npm install --save-dev @blazediff/vitestPeer dependencies: Vitest >= 1.0.0
Features
- Native Vitest matcher:
toMatchImageSnapshot()extends Vitest’sexpectAPI - Snapshot state tracking: Accurate snapshot counts in Vitest’s test summary
- Multiple algorithms: Choose from 6 comparison methods
- Auto-setup: Imports and registers automatically
- Update mode: Works seamlessly with Vitest’s
-uflag - Full TypeScript support: Complete type definitions included
Quick Start
import { expect, it } from 'vitest';
import '@blazediff/vitest';
it('should match screenshot', async () => {
const screenshot = await page.screenshot();
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
});
});The matcher auto-registers when you import @blazediff/vitest. No additional setup required!
API Reference
toMatchImageSnapshot(options?)
Vitest matcher for image snapshot comparison.
await expect(imageInput).toMatchImageSnapshot(options?);Parameters
| Parameter | Type | Description |
|---|---|---|
imageInput | ImageInput | Image to compare (file path or buffer with dimensions) |
options | Partial<MatcherOptions> | Optional comparison options |
Options
| Option | Type | Default | Description |
|---|---|---|---|
method | ComparisonMethod | 'core' | Comparison algorithm |
failureThreshold | number | 0 | Allowed difference (pixels or percentage) |
failureThresholdType | 'pixel' | 'percent' | 'pixel' | Threshold interpretation |
snapshotsDir | string | '__snapshots__' | Snapshot directory (relative to test) |
snapshotIdentifier | string | auto-generated | Custom snapshot filename |
updateSnapshots | boolean | false | Force snapshot update |
threshold | number | 0.1 | Color threshold for core/bin (0-1) |
See @blazediff/matcher for all available options.
Comparison Methods
core - Pure JavaScript (Default)
await expect(screenshot).toMatchImageSnapshot({
method: 'core', // Fast, works with buffers and file paths
});bin - Rust Native (Fastest)
await expect('/path/to/image.png').toMatchImageSnapshot({
method: 'bin', // Requires file paths
});ssim - Perceptual Similarity
await expect(screenshot).toMatchImageSnapshot({
method: 'ssim', // Structural similarity
});gmsd - Gradient-based
await expect(screenshot).toMatchImageSnapshot({
method: 'gmsd', // Detects structural changes
});Usage Patterns
Basic Snapshot Test
import { expect, it } from 'vitest';
import '@blazediff/vitest';
it('renders homepage correctly', async () => {
const screenshot = await browser.screenshot();
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
});
});Custom Thresholds
Allow small differences while catching regressions:
// Allow up to 100 pixels difference
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
failureThreshold: 100,
failureThresholdType: 'pixel',
});
// Allow up to 0.5% difference
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
failureThreshold: 0.5,
failureThresholdType: 'percent',
});Use percentage-based thresholds for responsive images that may change size across different viewports.
Update Snapshots
# Update all failing snapshots
vitest -u
# Update snapshots for specific test file
vitest -u src/components/Button.test.ts
# Update snapshots matching pattern
vitest -u --testNamePattern="renders correctly"
# Using environment variable
VITEST_UPDATE_SNAPSHOTS=true vitestProgrammatically:
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
updateSnapshots: true,
});Custom Snapshot Directory
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
snapshotsDir: '__image_snapshots__', // Custom directory
});Custom Snapshot Identifier
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
snapshotIdentifier: 'homepage-desktop-1920x1080',
});Negation
Test that images are intentionally different:
const before = await page.screenshot();
await page.click('.toggle-theme');
const after = await page.screenshot();
// Assert that theme toggle changed the UI
await expect(after).not.toMatchImageSnapshot({
method: 'core',
snapshotIdentifier: 'before-theme-toggle',
});Sequential Tests
For tests that share state, use sequential execution:
import { describe, it } from 'vitest';
describe.sequential('Visual regression tests', () => {
it('test 1', async () => {
await expect(image1).toMatchImageSnapshot({ method: 'core' });
});
it('test 2', async () => {
await expect(image2).toMatchImageSnapshot({ method: 'core' });
});
});Integrations
Playwright
import { test, expect } from 'vitest';
import '@blazediff/vitest';
import { chromium } from 'playwright';
test('visual regression with Playwright', async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
const screenshot = await page.screenshot();
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
});
await browser.close();
});Puppeteer
import { test, expect } from 'vitest';
import '@blazediff/vitest';
import puppeteer from 'puppeteer';
test('captures homepage', async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
const screenshot = await page.screenshot({ fullPage: true });
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
});
await browser.close();
});React Testing Library
import { test, expect } from 'vitest';
import { render } from '@testing-library/react';
import '@blazediff/vitest';
import html2canvas from 'html2canvas';
test('renders button correctly', async () => {
const { container } = render(<Button>Click me</Button>);
const canvas = await html2canvas(container);
const imageData = canvas.toDataURL();
await expect(imageData).toMatchImageSnapshot({
method: 'core',
});
});Snapshot State Tracking
The matcher integrates with Vitest’s snapshot state system. Test summaries show accurate counts:
Snapshots 2 written | 1 updated | 5 passedTracked states:
- Written: New snapshots created
- Updated: Snapshots updated with
-uflag - Passed: Existing snapshots matched
- Failed: Comparisons failed (shown in test results)
TypeScript
Full TypeScript support is included:
import { expect, it } from 'vitest';
import '@blazediff/vitest';
// Types are automatically augmented
it('typed test', async () => {
const screenshot: Buffer = await takeScreenshot();
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
failureThreshold: 0.1,
failureThresholdType: 'percent',
});
});The type augmentation is automatic when you import the package.
Configuration
Global Setup
To avoid importing in every test file, configure Vitest:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
setupFiles: ['./vitest.setup.ts'],
},
});// vitest.setup.ts
import '@blazediff/vitest';Default Options
Create a wrapper to set default options:
// test-utils.ts
import { expect } from 'vitest';
import type { MatcherOptions } from '@blazediff/matcher';
export async function expectImageSnapshot(
image: any,
options?: Partial<MatcherOptions>
) {
return expect(image).toMatchImageSnapshot({
method: 'core',
failureThreshold: 0.1,
failureThresholdType: 'percent',
...options,
});
}// test.spec.ts
import { expectImageSnapshot } from './test-utils';
it('uses default options', async () => {
await expectImageSnapshot(screenshot);
});Watch Mode
Vitest’s watch mode works seamlessly with image snapshots:
# Start in watch mode
vitest --watch
# Update snapshots in watch mode
# Press 'u' in the terminal when promptedIn watch mode, Vitest will prompt you to update snapshots when they fail. Press u to update all failing snapshots.
Troubleshooting
Snapshots Not Updating
Ensure you’re using the -u flag:
vitest -uOr set updateSnapshots: true in options.
Type Errors
Make sure @blazediff/vitest is imported:
import '@blazediff/vitest';Snapshot Directory Not Found
The matcher creates directories automatically. If you see errors, check file permissions or specify an absolute path:
await expect(screenshot).toMatchImageSnapshot({
method: 'core',
snapshotsDir: '/absolute/path/to/snapshots',
});Worker Thread Issues
If you encounter issues with Vitest’s worker threads, ensure you’re using the latest version:
npm update vitestLinks
- GitHub Repository
- NPM Package
- @blazediff/matcher - Core matcher logic
- Vitest Documentation