vitest-plugin-vis 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/client-api.d.ts +1 -0
- package/esm/client-api.d.ts.map +1 -1
- package/esm/client-api.js +1 -0
- package/esm/config/types.d.ts +4 -2
- package/esm/config/types.d.ts.map +1 -1
- package/esm/config/vis.js +1 -1
- package/esm/server/snapshot_path.d.ts +9 -0
- package/esm/server/snapshot_path.d.ts.map +1 -1
- package/esm/server/snapshot_path.js +9 -1
- package/esm/server-utils.d.ts +1 -0
- package/esm/server-utils.d.ts.map +1 -1
- package/esm/server-utils.js +1 -0
- package/esm/setup.d.ts.map +1 -1
- package/esm/setup.js +1 -2
- package/package.json +16 -6
- package/src/changelog.mdx +6 -0
- package/src/client/auto_snapshot_options.mdx +255 -0
- package/src/client/expect/to_match_image_snapshot.mdx +298 -0
- package/src/client/page/has_image_snapshot.mdx +328 -0
- package/src/client/page/to_match_image_snapshot.mdx +471 -0
- package/src/client-api.ts +1 -0
- package/src/config/types.ts +4 -2
- package/src/config/vis.ts +1 -1
- package/src/config.vis.mdx +76 -0
- package/src/overview.mdx +92 -0
- package/src/server/snapshot_path.ts +9 -1
- package/src/server-utils.ts +1 -0
- package/src/setup.create_vis.mdx +12 -0
- package/src/setup.ts +1 -2
- package/src/setup.vis.mdx +122 -0
- package/src/shared/trim_common_folder.mdx +153 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="expect/toMatchImageSnapshot" />
|
|
4
|
+
|
|
5
|
+
# toMatchImageSnapshot
|
|
6
|
+
|
|
7
|
+
`expect().toMatchImageSnapshot()` is a custom matcher that compares the current DOM to a snapshot image.
|
|
8
|
+
|
|
9
|
+
It can be used whether you have enabled the `auto` preset or not.
|
|
10
|
+
|
|
11
|
+
But typically, you would disable the auto snapshot when doing manual snapshot testing.
|
|
12
|
+
|
|
13
|
+
You can disable the auto snapshot at various levels:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// vitest.setup.ts
|
|
17
|
+
import { vis } from 'vitest-plugin-vis/setup'
|
|
18
|
+
|
|
19
|
+
// Setup without auto snapshots
|
|
20
|
+
vis.setup()
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// some.test.ts
|
|
25
|
+
import { setAutoSnapshotOptions } from 'vitest-plugin-vis'
|
|
26
|
+
|
|
27
|
+
beforeAll(() => {
|
|
28
|
+
setAutoSnapshotOptions(false)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
setAutoSnapshotOptions(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('...', () => {
|
|
36
|
+
setAutoSnapshotOptions(false)
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Basic Usage
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// some.test.ts
|
|
44
|
+
import { setAutoSnapshotOptions } from 'vitest-plugin-vis'
|
|
45
|
+
|
|
46
|
+
it('should take snapshot manually', async ({ expect }) => {
|
|
47
|
+
// Your test code here
|
|
48
|
+
render(<MyComponent />)
|
|
49
|
+
|
|
50
|
+
// Manual snapshot
|
|
51
|
+
await expect(document.body).toMatchImageSnapshot()
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Options
|
|
56
|
+
|
|
57
|
+
The `toMatchImageSnapshot()` matcher accepts an optional options object with the following properties:
|
|
58
|
+
|
|
59
|
+
### Snapshot Identification
|
|
60
|
+
|
|
61
|
+
#### `snapshotKey`
|
|
62
|
+
|
|
63
|
+
Customize the snapshot key/name for the snapshot file.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
it('should use custom snapshot key', async ({ expect }) => {
|
|
67
|
+
render(<MyComponent />)
|
|
68
|
+
|
|
69
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
70
|
+
snapshotKey: 'my_custom_snapshot'
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Failure Threshold
|
|
76
|
+
|
|
77
|
+
#### `failureThreshold` and `failureThresholdType`
|
|
78
|
+
|
|
79
|
+
Control how much difference is allowed before the test fails.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// Allow up to 50 pixels to be different
|
|
83
|
+
it('should allow pixel differences', async ({ expect }) => {
|
|
84
|
+
render(<MyComponent />)
|
|
85
|
+
|
|
86
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
87
|
+
failureThreshold: 50,
|
|
88
|
+
failureThresholdType: 'pixel' // default
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Allow up to 1% difference
|
|
93
|
+
it('should allow percentage differences', async ({ expect }) => {
|
|
94
|
+
render(<MyComponent />)
|
|
95
|
+
|
|
96
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
97
|
+
failureThreshold: 0.01,
|
|
98
|
+
failureThresholdType: 'percent'
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Comparison Methods
|
|
104
|
+
|
|
105
|
+
#### `comparisonMethod`
|
|
106
|
+
|
|
107
|
+
Choose between pixel-by-pixel comparison or structural similarity comparison.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// Default pixel comparison
|
|
111
|
+
it('should use pixel comparison', async ({ expect }) => {
|
|
112
|
+
render(<MyComponent />)
|
|
113
|
+
|
|
114
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
115
|
+
comparisonMethod: 'pixel' // default
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// SSIM (Structural Similarity) comparison
|
|
120
|
+
it('should use SSIM comparison', async ({ expect }) => {
|
|
121
|
+
render(<MyComponent />)
|
|
122
|
+
|
|
123
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
124
|
+
comparisonMethod: 'ssim'
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Diff Options
|
|
130
|
+
|
|
131
|
+
#### `diffOptions` for Pixel Comparison
|
|
132
|
+
|
|
133
|
+
When using `comparisonMethod: 'pixel'` (default), you can customize pixelmatch options:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
it('should use custom pixel diff options', async ({ expect }) => {
|
|
137
|
+
render(<MyComponent />)
|
|
138
|
+
|
|
139
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
140
|
+
comparisonMethod: 'pixel',
|
|
141
|
+
diffOptions: {
|
|
142
|
+
threshold: 0.1, // Matching threshold (0-1, default: 0.1)
|
|
143
|
+
includeAA: false, // Include anti-aliased pixels (default: false)
|
|
144
|
+
alpha: 0.1, // Blending factor for unchanged pixels (default: 0.1)
|
|
145
|
+
aaColor: [255, 255, 0], // Color for anti-aliased pixels [R, G, B]
|
|
146
|
+
diffColor: [255, 0, 0], // Color for different pixels [R, G, B]
|
|
147
|
+
diffColorAlt: null, // Alternative color for dark-on-light differences
|
|
148
|
+
diffMask: false // Draw diff over transparent background (default: false)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### `diffOptions` for SSIM Comparison
|
|
155
|
+
|
|
156
|
+
When using `comparisonMethod: 'ssim'`, you can customize SSIM options:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
it('should use custom SSIM diff options', async ({ expect }) => {
|
|
160
|
+
render(<MyComponent />)
|
|
161
|
+
|
|
162
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
163
|
+
comparisonMethod: 'ssim',
|
|
164
|
+
diffOptions: {
|
|
165
|
+
ssim: 'bezkrovny', // 'bezkrovny' (fast, default) | 'fast' (accurate) | 'weber' | 'original'
|
|
166
|
+
windowSize: 11, // Window size for SSIM calculation (default: 11)
|
|
167
|
+
k1: 0.01, // First stability constant (default: 0.01)
|
|
168
|
+
k2: 0.03, // Second stability constant (default: 0.03)
|
|
169
|
+
bitDepth: 8, // Bits per channel (default: 8)
|
|
170
|
+
downsample: 'original' // 'original' | 'fast' | false
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Timeout
|
|
177
|
+
|
|
178
|
+
#### `timeout`
|
|
179
|
+
|
|
180
|
+
Set a timeout for taking the snapshot.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
it('should use custom timeout', async ({ expect }) => {
|
|
184
|
+
render(<MyComponent />)
|
|
185
|
+
|
|
186
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
187
|
+
timeout: 60000 // 60 seconds (default: 30000ms)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Testing Utilities
|
|
193
|
+
|
|
194
|
+
#### `expectToFail`
|
|
195
|
+
|
|
196
|
+
Expect the matcher to fail - useful for testing that changes are detected.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
it('should detect differences', async ({ expect }) => {
|
|
200
|
+
render(<MyComponent />)
|
|
201
|
+
|
|
202
|
+
// First, create the baseline
|
|
203
|
+
if (!(await page.hasImageSnapshot({ snapshotKey: 'test_changes' }))) {
|
|
204
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
205
|
+
snapshotKey: 'test_changes'
|
|
206
|
+
})
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Modify the component
|
|
211
|
+
document.body.style.backgroundColor = 'red'
|
|
212
|
+
|
|
213
|
+
// Expect the test to fail due to changes
|
|
214
|
+
await expect(document.body)
|
|
215
|
+
.toMatchImageSnapshot({
|
|
216
|
+
snapshotKey: 'test_changes',
|
|
217
|
+
expectToFail: true
|
|
218
|
+
})
|
|
219
|
+
.then(
|
|
220
|
+
() => {
|
|
221
|
+
throw new Error('Should not reach - expected failure')
|
|
222
|
+
},
|
|
223
|
+
(error) => {
|
|
224
|
+
expect(error.message).toMatch(/Expected image to match but was differ by \d+ pixels/)
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Complete Example
|
|
231
|
+
|
|
232
|
+
Here's a comprehensive example showing multiple options:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
it('should demonstrate all options', async ({ expect }) => {
|
|
236
|
+
render(<MyComponent />)
|
|
237
|
+
|
|
238
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
239
|
+
// Snapshot identification
|
|
240
|
+
snapshotKey: 'comprehensive_test',
|
|
241
|
+
|
|
242
|
+
// Failure threshold
|
|
243
|
+
failureThreshold: 0.01,
|
|
244
|
+
failureThresholdType: 'percent',
|
|
245
|
+
|
|
246
|
+
// Comparison method
|
|
247
|
+
comparisonMethod: 'ssim',
|
|
248
|
+
|
|
249
|
+
// SSIM-specific options
|
|
250
|
+
diffOptions: {
|
|
251
|
+
ssim: 'bezkrovny',
|
|
252
|
+
windowSize: 11,
|
|
253
|
+
k1: 0.01,
|
|
254
|
+
k2: 0.03
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// Timeout
|
|
258
|
+
timeout: 45000,
|
|
259
|
+
|
|
260
|
+
// Testing utility
|
|
261
|
+
expectToFail: false
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Comparison Method Recommendations
|
|
267
|
+
|
|
268
|
+
### When to use Pixel Comparison (`comparisonMethod: 'pixel'`)
|
|
269
|
+
|
|
270
|
+
- **Default choice** for most visual regression tests
|
|
271
|
+
- **Precise pixel-by-pixel** comparison
|
|
272
|
+
- **Good for detecting** small UI changes, text changes, layout shifts
|
|
273
|
+
- **Fast execution** for most image sizes
|
|
274
|
+
- **Customizable colors** for diff visualization
|
|
275
|
+
|
|
276
|
+
### When to use SSIM Comparison (`comparisonMethod: 'ssim'`)
|
|
277
|
+
|
|
278
|
+
- **Structural similarity** comparison that correlates better with human perception
|
|
279
|
+
- **Reduced false positives** - less likely to fail on minor rendering differences
|
|
280
|
+
- **Better for images** with gradients, shadows, or anti-aliasing
|
|
281
|
+
- **Recommended threshold**: 1% (`failureThreshold: 0.01, failureThresholdType: 'percent'`)
|
|
282
|
+
- **Performance considerations**: Use `ssim: 'bezkrovny'` for speed, `ssim: 'fast'` for accuracy
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
// Recommended SSIM configuration
|
|
286
|
+
it('should use recommended SSIM settings', async ({ expect }) => {
|
|
287
|
+
render(<MyComponent />)
|
|
288
|
+
|
|
289
|
+
await expect(document.body).toMatchImageSnapshot({
|
|
290
|
+
comparisonMethod: 'ssim',
|
|
291
|
+
failureThreshold: 0.01,
|
|
292
|
+
failureThresholdType: 'percent',
|
|
293
|
+
diffOptions: {
|
|
294
|
+
ssim: 'bezkrovny' // Fast and accurate for most cases
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
```
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { Meta } from "@storybook/addon-docs/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="page/hasImageSnapshot" />
|
|
4
|
+
|
|
5
|
+
# page.hasImageSnapshot
|
|
6
|
+
|
|
7
|
+
`page.hasImageSnapshot()` is a utility method that checks if an image snapshot exists for the current test.
|
|
8
|
+
|
|
9
|
+
This method allows you to conditionally execute code based on whether a snapshot has been previously created for the current test. It's particularly useful for negative testing scenarios, conditional snapshot creation, and handling different behavior for first-time runs vs. subsequent runs.
|
|
10
|
+
|
|
11
|
+
## Basic Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { page } from '@vitest/browser/context'
|
|
15
|
+
|
|
16
|
+
it('should check if snapshot exists', async ({ expect }) => {
|
|
17
|
+
const hasSnapshot = await page.hasImageSnapshot()
|
|
18
|
+
|
|
19
|
+
if (!hasSnapshot) {
|
|
20
|
+
// First run - create baseline snapshot
|
|
21
|
+
await page.toMatchImageSnapshot()
|
|
22
|
+
} else {
|
|
23
|
+
// Subsequent runs - perform comparison
|
|
24
|
+
await expect(page.getByTestId('component')).toMatchImageSnapshot()
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Options
|
|
30
|
+
|
|
31
|
+
The `page.hasImageSnapshot()` method accepts an optional options object:
|
|
32
|
+
|
|
33
|
+
### `snapshotKey`
|
|
34
|
+
|
|
35
|
+
Custom key to identify the specific snapshot. If not provided, uses the default snapshot key for the test.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
it('should check for specific snapshot', async ({ expect }) => {
|
|
39
|
+
// Check for default snapshot
|
|
40
|
+
const hasDefaultSnapshot = await page.hasImageSnapshot()
|
|
41
|
+
|
|
42
|
+
// Check for custom snapshot
|
|
43
|
+
const hasCustomSnapshot = await page.hasImageSnapshot({
|
|
44
|
+
snapshotKey: 'dark_mode'
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
expect(hasDefaultSnapshot).toBe(false)
|
|
48
|
+
expect(hasCustomSnapshot).toBe(false)
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Important**: The `snapshotKey` cannot contain dash characters (-) as they are reserved for internal path separation.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// ❌ This will throw an error
|
|
56
|
+
await page.hasImageSnapshot({ snapshotKey: 'invalid-key' })
|
|
57
|
+
// Error: Snapshot key cannot contain dash
|
|
58
|
+
|
|
59
|
+
// ✅ Use underscores or camelCase instead
|
|
60
|
+
await page.hasImageSnapshot({ snapshotKey: 'valid_key' })
|
|
61
|
+
await page.hasImageSnapshot({ snapshotKey: 'validKey' })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Use Cases
|
|
65
|
+
|
|
66
|
+
### Negative Testing
|
|
67
|
+
|
|
68
|
+
Test behavior when snapshots don't exist:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
it('should handle missing snapshots gracefully', async ({ expect }) => {
|
|
72
|
+
const hasSnapshot = await page.hasImageSnapshot({ snapshotKey: 'nonexistent' })
|
|
73
|
+
expect(hasSnapshot).toBe(false)
|
|
74
|
+
|
|
75
|
+
// Now create the snapshot
|
|
76
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'nonexistent' })
|
|
77
|
+
|
|
78
|
+
// Verify it now exists
|
|
79
|
+
const hasSnapshotAfter = await page.hasImageSnapshot({ snapshotKey: 'nonexistent' })
|
|
80
|
+
expect(hasSnapshotAfter).toBe(true)
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Conditional Snapshot Creation
|
|
85
|
+
|
|
86
|
+
Handle different behavior for first-time runs vs. subsequent runs:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
it('should create or compare snapshots conditionally', async ({ expect }) => {
|
|
90
|
+
const hasSnapshot = await page.hasImageSnapshot()
|
|
91
|
+
|
|
92
|
+
if (!hasSnapshot) {
|
|
93
|
+
// First run - setup and create baseline
|
|
94
|
+
render(<MyComponent theme="light" />)
|
|
95
|
+
await page.toMatchImageSnapshot()
|
|
96
|
+
} else {
|
|
97
|
+
// Subsequent runs - test different scenarios
|
|
98
|
+
render(<MyComponent theme="dark" />)
|
|
99
|
+
await expect(page.getByTestId('component')).toMatchImageSnapshot({
|
|
100
|
+
expectToFail: true,
|
|
101
|
+
snapshotKey: 'dark_theme'
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Theme or State-Specific Snapshots
|
|
108
|
+
|
|
109
|
+
Work with different themes or application states:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
it('should support theme-specific snapshots', async ({ expect }) => {
|
|
113
|
+
// Check for dark mode snapshot
|
|
114
|
+
const hasDarkSnapshot = await page.hasImageSnapshot({ snapshotKey: 'dark_mode' })
|
|
115
|
+
|
|
116
|
+
if (!hasDarkSnapshot) {
|
|
117
|
+
// Setup dark mode and create snapshot
|
|
118
|
+
document.body.classList.add('dark')
|
|
119
|
+
render(<MyApp />)
|
|
120
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'dark_mode' })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check for light mode snapshot
|
|
124
|
+
const hasLightSnapshot = await page.hasImageSnapshot({ snapshotKey: 'light_mode' })
|
|
125
|
+
|
|
126
|
+
if (!hasLightSnapshot) {
|
|
127
|
+
// Setup light mode and create snapshot
|
|
128
|
+
document.body.classList.remove('dark')
|
|
129
|
+
render(<MyApp />)
|
|
130
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'light_mode' })
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Progressive Snapshot Building
|
|
136
|
+
|
|
137
|
+
Build up complex test scenarios progressively:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
it('should build snapshots progressively', async ({ expect }) => {
|
|
141
|
+
// Check if we have the base component snapshot
|
|
142
|
+
const hasBaseSnapshot = await page.hasImageSnapshot({ snapshotKey: 'base' })
|
|
143
|
+
|
|
144
|
+
if (!hasBaseSnapshot) {
|
|
145
|
+
render(<BaseComponent />)
|
|
146
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'base' })
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check if we have the enhanced component snapshot
|
|
150
|
+
const hasEnhancedSnapshot = await page.hasImageSnapshot({ snapshotKey: 'enhanced' })
|
|
151
|
+
|
|
152
|
+
if (!hasEnhancedSnapshot) {
|
|
153
|
+
render(<EnhancedComponent />)
|
|
154
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'enhanced' })
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Now we can safely compare both exist
|
|
158
|
+
expect(await page.hasImageSnapshot({ snapshotKey: 'base' })).toBe(true)
|
|
159
|
+
expect(await page.hasImageSnapshot({ snapshotKey: 'enhanced' })).toBe(true)
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Behavior and Limitations
|
|
164
|
+
|
|
165
|
+
### Test Context Required
|
|
166
|
+
|
|
167
|
+
The method must be called within a test context:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
// ❌ This will throw an error
|
|
171
|
+
const hasSnapshot = await page.hasImageSnapshot()
|
|
172
|
+
// Error: `hasImageSnapshot()` must be called in a test.
|
|
173
|
+
|
|
174
|
+
// ✅ Call within a test
|
|
175
|
+
it('should work in test context', async () => {
|
|
176
|
+
const hasSnapshot = await page.hasImageSnapshot()
|
|
177
|
+
// This works correctly
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Concurrent Tests Not Supported
|
|
182
|
+
|
|
183
|
+
Cannot be used in concurrent tests because they share the same iframe environment:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
// ❌ This will throw an error
|
|
187
|
+
it.concurrent('should not work in concurrent test', async () => {
|
|
188
|
+
const hasSnapshot = await page.hasImageSnapshot()
|
|
189
|
+
// Error: `hasImageSnapshot()` cannot be called in a concurrent test because
|
|
190
|
+
// concurrent tests run at the same time in the same iframe and affect each other's environment.
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// ✅ Use regular tests
|
|
194
|
+
it('should work in regular test', async () => {
|
|
195
|
+
const hasSnapshot = await page.hasImageSnapshot()
|
|
196
|
+
// This works correctly
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Async Operation
|
|
201
|
+
|
|
202
|
+
Always returns a Promise, so you must use `await` or `.then()`:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// ✅ Using await
|
|
206
|
+
const hasSnapshot = await page.hasImageSnapshot()
|
|
207
|
+
|
|
208
|
+
// ✅ Using .then()
|
|
209
|
+
page.hasImageSnapshot().then(hasSnapshot => {
|
|
210
|
+
if (hasSnapshot) {
|
|
211
|
+
// Handle existing snapshot
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
// ❌ Don't forget await
|
|
216
|
+
const hasSnapshot = page.hasImageSnapshot() // This is a Promise, not a boolean!
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Error Handling
|
|
220
|
+
|
|
221
|
+
The method will throw errors in specific cases:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
// Test context errors
|
|
225
|
+
try {
|
|
226
|
+
await page.hasImageSnapshot()
|
|
227
|
+
} catch (error) {
|
|
228
|
+
// Error: `hasImageSnapshot()` must be called in a test.
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Concurrent test errors
|
|
232
|
+
it.concurrent('concurrent test', async () => {
|
|
233
|
+
try {
|
|
234
|
+
await page.hasImageSnapshot()
|
|
235
|
+
} catch (error) {
|
|
236
|
+
// Error: `hasImageSnapshot()` cannot be called in a concurrent test because
|
|
237
|
+
// concurrent tests run at the same time in the same iframe and affect each other's environment.
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// Snapshot key validation errors
|
|
242
|
+
try {
|
|
243
|
+
await page.hasImageSnapshot({ snapshotKey: 'invalid-key' })
|
|
244
|
+
} catch (error) {
|
|
245
|
+
// Error: Snapshot key cannot contain dash
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Integration with Other Methods
|
|
250
|
+
|
|
251
|
+
`page.hasImageSnapshot()` works seamlessly with other visual testing methods:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
it('should integrate with other methods', async ({ expect }) => {
|
|
255
|
+
// Check if snapshot exists
|
|
256
|
+
const hasSnapshot = await page.hasImageSnapshot({ snapshotKey: 'integration' })
|
|
257
|
+
|
|
258
|
+
if (!hasSnapshot) {
|
|
259
|
+
// Create page snapshot
|
|
260
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'integration' })
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Now test element snapshots
|
|
264
|
+
const element = page.getByTestId('component')
|
|
265
|
+
await expect(element).toMatchImageSnapshot({ snapshotKey: 'element' })
|
|
266
|
+
|
|
267
|
+
// Verify both snapshots exist
|
|
268
|
+
expect(await page.hasImageSnapshot({ snapshotKey: 'integration' })).toBe(true)
|
|
269
|
+
expect(await page.hasImageSnapshot({ snapshotKey: 'element' })).toBe(true)
|
|
270
|
+
})
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Best Practices
|
|
274
|
+
|
|
275
|
+
### Use Descriptive Snapshot Keys
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
// ✅ Good - descriptive and clear
|
|
279
|
+
await page.hasImageSnapshot({ snapshotKey: 'homepage_header' })
|
|
280
|
+
await page.hasImageSnapshot({ snapshotKey: 'modal_open_state' })
|
|
281
|
+
await page.hasImageSnapshot({ snapshotKey: 'dark_theme_sidebar' })
|
|
282
|
+
|
|
283
|
+
// ❌ Avoid - unclear purpose
|
|
284
|
+
await page.hasImageSnapshot({ snapshotKey: 'test1' })
|
|
285
|
+
await page.hasImageSnapshot({ snapshotKey: 'snapshot' })
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Handle Both Cases
|
|
289
|
+
|
|
290
|
+
Always handle both the existence and non-existence cases:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
it('should handle both snapshot states', async () => {
|
|
294
|
+
const hasSnapshot = await page.hasImageSnapshot({ snapshotKey: 'feature' })
|
|
295
|
+
|
|
296
|
+
if (hasSnapshot) {
|
|
297
|
+
// Test against existing snapshot
|
|
298
|
+
render(<FeatureComponent />)
|
|
299
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'feature' })
|
|
300
|
+
} else {
|
|
301
|
+
// Create initial snapshot
|
|
302
|
+
render(<FeatureComponent />)
|
|
303
|
+
await page.toMatchImageSnapshot({ snapshotKey: 'feature' })
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Combine with Conditional Logic
|
|
309
|
+
|
|
310
|
+
Use with environment or configuration checks:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
it('should adapt to environment', async () => {
|
|
314
|
+
const isDarkMode = process.env.THEME === 'dark'
|
|
315
|
+
const snapshotKey = isDarkMode ? 'dark_mode' : 'light_mode'
|
|
316
|
+
|
|
317
|
+
const hasSnapshot = await page.hasImageSnapshot({ snapshotKey })
|
|
318
|
+
|
|
319
|
+
if (!hasSnapshot) {
|
|
320
|
+
// Setup appropriate theme
|
|
321
|
+
if (isDarkMode) {
|
|
322
|
+
document.body.classList.add('dark')
|
|
323
|
+
}
|
|
324
|
+
render(<App />)
|
|
325
|
+
await page.toMatchImageSnapshot({ snapshotKey })
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
```
|