0
# Image Snapshot Testing
1
2
Automated visual regression testing through screenshot capture and comparison using jest-image-snapshot integration.
3
4
## Capabilities
5
6
### Image Snapshot Function
7
8
Creates a test function that captures screenshots of Storybook stories and compares them against stored baseline images.
9
10
```typescript { .api }
11
/**
12
* Creates a test function for automated visual regression testing
13
* @param customConfig - Optional configuration to override defaults
14
* @returns Test function that captures and compares screenshots
15
*/
16
function imageSnapshot(customConfig?: Partial<ImageSnapshotConfig>): TestFunction;
17
18
interface ImageSnapshotConfig extends CommonConfig {
19
/** Function to provide jest-image-snapshot matching options */
20
getMatchOptions: (options: Options) => MatchImageSnapshotOptions | undefined;
21
/** Function to provide Puppeteer screenshot options */
22
getScreenshotOptions: (options: Options) => Base64ScreenShotOptions;
23
/** Hook executed before screenshot capture */
24
beforeScreenshot: (page: Page, options: Options) => Promise<void | ElementHandle>;
25
/** Hook executed after screenshot capture */
26
afterScreenshot: (options: { image: string | void | Buffer; context: Context }) => Promise<void>;
27
}
28
29
interface Base64ScreenShotOptions extends ScreenshotOptions {
30
encoding: 'base64';
31
}
32
```
33
34
**Usage Examples:**
35
36
```typescript
37
import initStoryshots from '@storybook/addon-storyshots';
38
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
39
40
// Basic image snapshot testing
41
initStoryshots({
42
suite: 'Image storyshots',
43
test: imageSnapshot()
44
});
45
46
// With custom match options for jest-image-snapshot
47
initStoryshots({
48
suite: 'Visual regression',
49
test: imageSnapshot({
50
storybookUrl: 'http://localhost:6006',
51
getMatchOptions: ({ context: { kind, story } }) => ({
52
failureThreshold: 0.2,
53
failureThresholdType: 'percent',
54
customSnapshotIdentifier: `${kind}-${story}`
55
})
56
})
57
});
58
```
59
60
### Screenshot Configuration
61
62
Configure Puppeteer screenshot options for precise image capture control.
63
64
```typescript { .api }
65
/**
66
* Function type for providing screenshot options
67
* @param options - Test context and URL information
68
* @returns Screenshot options with base64 encoding
69
*/
70
type GetScreenshotOptions = (options: Options) => Base64ScreenShotOptions;
71
72
interface Base64ScreenShotOptions extends ScreenshotOptions {
73
encoding: 'base64';
74
fullPage?: boolean;
75
clip?: {
76
x: number;
77
y: number;
78
width: number;
79
height: number;
80
};
81
omitBackground?: boolean;
82
quality?: number;
83
type?: 'jpeg' | 'png' | 'webp';
84
}
85
```
86
87
**Usage Examples:**
88
89
```typescript
90
// Custom screenshot options
91
const getScreenshotOptions = ({ context, url }) => ({
92
encoding: 'base64',
93
fullPage: false, // Viewport only
94
type: 'png',
95
omitBackground: true
96
});
97
98
initStoryshots({
99
suite: 'Viewport screenshots',
100
test: imageSnapshot({
101
storybookUrl: 'http://localhost:6006',
102
getScreenshotOptions
103
})
104
});
105
106
// Element-specific screenshots
107
const getScreenshotOptions = ({ context }) => ({
108
encoding: 'base64',
109
fullPage: false,
110
clip: { x: 0, y: 0, width: 800, height: 600 }
111
});
112
```
113
114
### Jest Image Snapshot Integration
115
116
Configure jest-image-snapshot matching behavior for visual comparison.
117
118
```typescript { .api }
119
/**
120
* Function type for providing jest-image-snapshot options
121
* @param options - Test context and URL information
122
* @returns Configuration for image comparison
123
*/
124
type GetMatchOptions = (options: Options) => MatchImageSnapshotOptions | undefined;
125
126
interface MatchImageSnapshotOptions {
127
/** Threshold for pixel differences (0-1) */
128
failureThreshold?: number;
129
/** Type of threshold: 'pixel' | 'percent' */
130
failureThresholdType?: 'pixel' | 'percent';
131
/** Custom snapshot identifier */
132
customSnapshotIdentifier?: string;
133
/** Custom snapshots directory */
134
customSnapshotsDir?: string;
135
/** Custom diff directory */
136
customDiffDir?: string;
137
/** Update snapshots mode */
138
updateSnapshot?: boolean;
139
/** Allow size mismatch */
140
allowSizeMismatch?: boolean;
141
}
142
```
143
144
**Usage Examples:**
145
146
```typescript
147
// Strict visual comparison
148
const getMatchOptions = ({ context: { kind, story } }) => ({
149
failureThreshold: 0.01,
150
failureThresholdType: 'percent',
151
customSnapshotIdentifier: `${kind.replace(/\s+/g, '-')}-${story}`
152
});
153
154
// Relaxed comparison for dynamic content
155
const getMatchOptions = ({ context }) => ({
156
failureThreshold: 0.5,
157
failureThresholdType: 'percent',
158
allowSizeMismatch: true
159
});
160
```
161
162
### Screenshot Lifecycle Hooks
163
164
Control screenshot timing and processing with before and after hooks.
165
166
```typescript { .api }
167
/**
168
* Hook executed before screenshot capture
169
* @param page - Puppeteer page instance
170
* @param options - Test context and URL information
171
* @returns Promise resolving to void or ElementHandle for element screenshots
172
*/
173
type BeforeScreenshot = (page: Page, options: Options) => Promise<void | ElementHandle>;
174
175
/**
176
* Hook executed after screenshot capture
177
* @param params - Screenshot result and context
178
* @returns Promise for post-processing
179
*/
180
type AfterScreenshot = (params: {
181
image: string | void | Buffer;
182
context: Context;
183
}) => Promise<void>;
184
```
185
186
**Usage Examples:**
187
188
```typescript
189
// Wait for animations before screenshot
190
const beforeScreenshot = async (page, { context }) => {
191
// Wait for loading spinners to disappear
192
await page.waitForSelector('.loading', { hidden: true });
193
194
// Trigger hover state for interactive elements
195
if (context.story.includes('hover')) {
196
await page.hover('[data-testid="button"]');
197
}
198
199
// Additional delay for animations
200
await page.waitForTimeout(500);
201
};
202
203
// Screenshot specific element only
204
const beforeScreenshot = async (page) => {
205
return await page.$('#storybook-root > *');
206
};
207
208
// Save screenshot to custom location
209
const afterScreenshot = async ({ image, context }) => {
210
if (image) {
211
const filename = `custom-${context.kind}-${context.story}.png`;
212
require('fs').writeFileSync(`./screenshots/${filename}`, image, 'base64');
213
}
214
};
215
216
initStoryshots({
217
suite: 'Custom lifecycle',
218
test: imageSnapshot({
219
beforeScreenshot,
220
afterScreenshot
221
})
222
});
223
```
224
225
### Default Configuration
226
227
Default values provide full-page screenshots with base64 encoding.
228
229
```typescript { .api }
230
const defaultImageSnapshotConfig: ImageSnapshotConfig = {
231
...defaultCommonConfig,
232
getMatchOptions: () => undefined,
233
getScreenshotOptions: () => ({ fullPage: true, encoding: 'base64' }),
234
beforeScreenshot: () => Promise.resolve(),
235
afterScreenshot: () => Promise.resolve(),
236
};
237
```
238
239
### Jest Integration
240
241
The function automatically extends Jest's expect with toMatchImageSnapshot matcher.
242
243
```typescript { .api }
244
// Automatic Jest extension
245
expect.extend({ toMatchImageSnapshot });
246
247
// Usage in test execution
248
expect(image).toMatchImageSnapshot(matchOptions);
249
```