Image snapshots addition to StoryShots based on puppeteer
—
Automated visual regression testing through screenshot capture and comparison using jest-image-snapshot integration.
Creates a test function that captures screenshots of Storybook stories and compares them against stored baseline images.
/**
* Creates a test function for automated visual regression testing
* @param customConfig - Optional configuration to override defaults
* @returns Test function that captures and compares screenshots
*/
function imageSnapshot(customConfig?: Partial<ImageSnapshotConfig>): TestFunction;
interface ImageSnapshotConfig extends CommonConfig {
/** Function to provide jest-image-snapshot matching options */
getMatchOptions: (options: Options) => MatchImageSnapshotOptions | undefined;
/** Function to provide Puppeteer screenshot options */
getScreenshotOptions: (options: Options) => Base64ScreenShotOptions;
/** Hook executed before screenshot capture */
beforeScreenshot: (page: Page, options: Options) => Promise<void | ElementHandle>;
/** Hook executed after screenshot capture */
afterScreenshot: (options: { image: string | void | Buffer; context: Context }) => Promise<void>;
}
interface Base64ScreenShotOptions extends ScreenshotOptions {
encoding: 'base64';
}Usage Examples:
import initStoryshots from '@storybook/addon-storyshots';
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
// Basic image snapshot testing
initStoryshots({
suite: 'Image storyshots',
test: imageSnapshot()
});
// With custom match options for jest-image-snapshot
initStoryshots({
suite: 'Visual regression',
test: imageSnapshot({
storybookUrl: 'http://localhost:6006',
getMatchOptions: ({ context: { kind, story } }) => ({
failureThreshold: 0.2,
failureThresholdType: 'percent',
customSnapshotIdentifier: `${kind}-${story}`
})
})
});Configure Puppeteer screenshot options for precise image capture control.
/**
* Function type for providing screenshot options
* @param options - Test context and URL information
* @returns Screenshot options with base64 encoding
*/
type GetScreenshotOptions = (options: Options) => Base64ScreenShotOptions;
interface Base64ScreenShotOptions extends ScreenshotOptions {
encoding: 'base64';
fullPage?: boolean;
clip?: {
x: number;
y: number;
width: number;
height: number;
};
omitBackground?: boolean;
quality?: number;
type?: 'jpeg' | 'png' | 'webp';
}Usage Examples:
// Custom screenshot options
const getScreenshotOptions = ({ context, url }) => ({
encoding: 'base64',
fullPage: false, // Viewport only
type: 'png',
omitBackground: true
});
initStoryshots({
suite: 'Viewport screenshots',
test: imageSnapshot({
storybookUrl: 'http://localhost:6006',
getScreenshotOptions
})
});
// Element-specific screenshots
const getScreenshotOptions = ({ context }) => ({
encoding: 'base64',
fullPage: false,
clip: { x: 0, y: 0, width: 800, height: 600 }
});Configure jest-image-snapshot matching behavior for visual comparison.
/**
* Function type for providing jest-image-snapshot options
* @param options - Test context and URL information
* @returns Configuration for image comparison
*/
type GetMatchOptions = (options: Options) => MatchImageSnapshotOptions | undefined;
interface MatchImageSnapshotOptions {
/** Threshold for pixel differences (0-1) */
failureThreshold?: number;
/** Type of threshold: 'pixel' | 'percent' */
failureThresholdType?: 'pixel' | 'percent';
/** Custom snapshot identifier */
customSnapshotIdentifier?: string;
/** Custom snapshots directory */
customSnapshotsDir?: string;
/** Custom diff directory */
customDiffDir?: string;
/** Update snapshots mode */
updateSnapshot?: boolean;
/** Allow size mismatch */
allowSizeMismatch?: boolean;
}Usage Examples:
// Strict visual comparison
const getMatchOptions = ({ context: { kind, story } }) => ({
failureThreshold: 0.01,
failureThresholdType: 'percent',
customSnapshotIdentifier: `${kind.replace(/\s+/g, '-')}-${story}`
});
// Relaxed comparison for dynamic content
const getMatchOptions = ({ context }) => ({
failureThreshold: 0.5,
failureThresholdType: 'percent',
allowSizeMismatch: true
});Control screenshot timing and processing with before and after hooks.
/**
* Hook executed before screenshot capture
* @param page - Puppeteer page instance
* @param options - Test context and URL information
* @returns Promise resolving to void or ElementHandle for element screenshots
*/
type BeforeScreenshot = (page: Page, options: Options) => Promise<void | ElementHandle>;
/**
* Hook executed after screenshot capture
* @param params - Screenshot result and context
* @returns Promise for post-processing
*/
type AfterScreenshot = (params: {
image: string | void | Buffer;
context: Context;
}) => Promise<void>;Usage Examples:
// Wait for animations before screenshot
const beforeScreenshot = async (page, { context }) => {
// Wait for loading spinners to disappear
await page.waitForSelector('.loading', { hidden: true });
// Trigger hover state for interactive elements
if (context.story.includes('hover')) {
await page.hover('[data-testid="button"]');
}
// Additional delay for animations
await page.waitForTimeout(500);
};
// Screenshot specific element only
const beforeScreenshot = async (page) => {
return await page.$('#storybook-root > *');
};
// Save screenshot to custom location
const afterScreenshot = async ({ image, context }) => {
if (image) {
const filename = `custom-${context.kind}-${context.story}.png`;
require('fs').writeFileSync(`./screenshots/${filename}`, image, 'base64');
}
};
initStoryshots({
suite: 'Custom lifecycle',
test: imageSnapshot({
beforeScreenshot,
afterScreenshot
})
});Default values provide full-page screenshots with base64 encoding.
const defaultImageSnapshotConfig: ImageSnapshotConfig = {
...defaultCommonConfig,
getMatchOptions: () => undefined,
getScreenshotOptions: () => ({ fullPage: true, encoding: 'base64' }),
beforeScreenshot: () => Promise.resolve(),
afterScreenshot: () => Promise.resolve(),
};The function automatically extends Jest's expect with toMatchImageSnapshot matcher.
// Automatic Jest extension
expect.extend({ toMatchImageSnapshot });
// Usage in test execution
expect(image).toMatchImageSnapshot(matchOptions);Install with Tessl CLI
npx tessl i tessl/npm-storybook--addon-storyshots-puppeteer