CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-storybook--addon-storyshots-puppeteer

Image snapshots addition to StoryShots based on puppeteer

Pending
Overview
Eval results
Files

image-snapshot.mddocs/

Image Snapshot Testing

Automated visual regression testing through screenshot capture and comparison using jest-image-snapshot integration.

Capabilities

Image Snapshot Function

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}`
    })
  })
});

Screenshot Configuration

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 }
});

Jest Image Snapshot Integration

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
});

Screenshot Lifecycle Hooks

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 Configuration

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(),
};

Jest Integration

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

docs

axe-test.md

configuration.md

image-snapshot.md

index.md

puppeteer-test.md

tile.json