UNPKG

vitest-plugin-vis

Version:
329 lines (249 loc) 9.08 kB
import { Meta } from "@storybook/addon-docs/blocks"; <Meta title="page/hasImageSnapshot" /> # page.hasImageSnapshot `page.hasImageSnapshot()` is a utility method that checks if an image snapshot exists for the current test. 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. ## Basic Usage ```ts import { page } from 'vitest/browser' it('should check if snapshot exists', async ({ expect }) => { const hasSnapshot = await page.hasImageSnapshot() if (!hasSnapshot) { // First run - create baseline snapshot await page.toMatchImageSnapshot() } else { // Subsequent runs - perform comparison await expect(page.getByTestId('component')).toMatchImageSnapshot() } }) ``` ## Options The `page.hasImageSnapshot()` method accepts an optional options object: ### `snapshotKey` Custom key to identify the specific snapshot. If not provided, uses the default snapshot key for the test. ```ts it('should check for specific snapshot', async ({ expect }) => { // Check for default snapshot const hasDefaultSnapshot = await page.hasImageSnapshot() // Check for custom snapshot const hasCustomSnapshot = await page.hasImageSnapshot({ snapshotKey: 'dark_mode' }) expect(hasDefaultSnapshot).toBe(false) expect(hasCustomSnapshot).toBe(false) }) ``` **Important**: The `snapshotKey` cannot contain dash characters (-) as they are reserved for internal path separation. ```ts // ❌ This will throw an error await page.hasImageSnapshot({ snapshotKey: 'invalid-key' }) // Error: Snapshot key cannot contain dash // ✅ Use underscores or camelCase instead await page.hasImageSnapshot({ snapshotKey: 'valid_key' }) await page.hasImageSnapshot({ snapshotKey: 'validKey' }) ``` ## Use Cases ### Negative Testing Test behavior when snapshots don't exist: ```ts it('should handle missing snapshots gracefully', async ({ expect }) => { const hasSnapshot = await page.hasImageSnapshot({ snapshotKey: 'nonexistent' }) expect(hasSnapshot).toBe(false) // Now create the snapshot await page.toMatchImageSnapshot({ snapshotKey: 'nonexistent' }) // Verify it now exists const hasSnapshotAfter = await page.hasImageSnapshot({ snapshotKey: 'nonexistent' }) expect(hasSnapshotAfter).toBe(true) }) ``` ### Conditional Snapshot Creation Handle different behavior for first-time runs vs. subsequent runs: ```ts it('should create or compare snapshots conditionally', async ({ expect }) => { const hasSnapshot = await page.hasImageSnapshot() if (!hasSnapshot) { // First run - setup and create baseline render(<MyComponent theme="light" />) await page.toMatchImageSnapshot() } else { // Subsequent runs - test different scenarios render(<MyComponent theme="dark" />) await expect(page.getByTestId('component')).toMatchImageSnapshot({ expectToFail: true, snapshotKey: 'dark_theme' }) } }) ``` ### Theme or State-Specific Snapshots Work with different themes or application states: ```ts it('should support theme-specific snapshots', async ({ expect }) => { // Check for dark mode snapshot const hasDarkSnapshot = await page.hasImageSnapshot({ snapshotKey: 'dark_mode' }) if (!hasDarkSnapshot) { // Setup dark mode and create snapshot document.body.classList.add('dark') render(<MyApp />) await page.toMatchImageSnapshot({ snapshotKey: 'dark_mode' }) } // Check for light mode snapshot const hasLightSnapshot = await page.hasImageSnapshot({ snapshotKey: 'light_mode' }) if (!hasLightSnapshot) { // Setup light mode and create snapshot document.body.classList.remove('dark') render(<MyApp />) await page.toMatchImageSnapshot({ snapshotKey: 'light_mode' }) } }) ``` ### Progressive Snapshot Building Build up complex test scenarios progressively: ```ts it('should build snapshots progressively', async ({ expect }) => { // Check if we have the base component snapshot const hasBaseSnapshot = await page.hasImageSnapshot({ snapshotKey: 'base' }) if (!hasBaseSnapshot) { render(<BaseComponent />) await page.toMatchImageSnapshot({ snapshotKey: 'base' }) } // Check if we have the enhanced component snapshot const hasEnhancedSnapshot = await page.hasImageSnapshot({ snapshotKey: 'enhanced' }) if (!hasEnhancedSnapshot) { render(<EnhancedComponent />) await page.toMatchImageSnapshot({ snapshotKey: 'enhanced' }) } // Now we can safely compare both exist expect(await page.hasImageSnapshot({ snapshotKey: 'base' })).toBe(true) expect(await page.hasImageSnapshot({ snapshotKey: 'enhanced' })).toBe(true) }) ``` ## Behavior and Limitations ### Test Context Required The method must be called within a test context: ```ts // ❌ This will throw an error const hasSnapshot = await page.hasImageSnapshot() // Error: `hasImageSnapshot()` must be called in a test. // ✅ Call within a test it('should work in test context', async () => { const hasSnapshot = await page.hasImageSnapshot() // This works correctly }) ``` ### Concurrent Tests Not Supported Cannot be used in concurrent tests because they share the same iframe environment: ```ts // ❌ This will throw an error it.concurrent('should not work in concurrent test', async () => { const hasSnapshot = await page.hasImageSnapshot() // Error: `hasImageSnapshot()` cannot be called in a concurrent test because // concurrent tests run at the same time in the same iframe and affect each other's environment. }) // ✅ Use regular tests it('should work in regular test', async () => { const hasSnapshot = await page.hasImageSnapshot() // This works correctly }) ``` ### Async Operation Always returns a Promise, so you must use `await` or `.then()`: ```ts // ✅ Using await const hasSnapshot = await page.hasImageSnapshot() // ✅ Using .then() page.hasImageSnapshot().then(hasSnapshot => { if (hasSnapshot) { // Handle existing snapshot } }) // ❌ Don't forget await const hasSnapshot = page.hasImageSnapshot() // This is a Promise, not a boolean! ``` ## Error Handling The method will throw errors in specific cases: ```ts // Test context errors try { await page.hasImageSnapshot() } catch (error) { // Error: `hasImageSnapshot()` must be called in a test. } // Concurrent test errors it.concurrent('concurrent test', async () => { try { await page.hasImageSnapshot() } catch (error) { // Error: `hasImageSnapshot()` cannot be called in a concurrent test because // concurrent tests run at the same time in the same iframe and affect each other's environment. } }) // Snapshot key validation errors try { await page.hasImageSnapshot({ snapshotKey: 'invalid-key' }) } catch (error) { // Error: Snapshot key cannot contain dash } ``` ## Integration with Other Methods `page.hasImageSnapshot()` works seamlessly with other visual testing methods: ```ts it('should integrate with other methods', async ({ expect }) => { // Check if snapshot exists const hasSnapshot = await page.hasImageSnapshot({ snapshotKey: 'integration' }) if (!hasSnapshot) { // Create page snapshot await page.toMatchImageSnapshot({ snapshotKey: 'integration' }) } // Now test element snapshots const element = page.getByTestId('component') await expect(element).toMatchImageSnapshot({ snapshotKey: 'element' }) // Verify both snapshots exist expect(await page.hasImageSnapshot({ snapshotKey: 'integration' })).toBe(true) expect(await page.hasImageSnapshot({ snapshotKey: 'element' })).toBe(true) }) ``` ## Best Practices ### Use Descriptive Snapshot Keys ```ts // ✅ Good - descriptive and clear await page.hasImageSnapshot({ snapshotKey: 'homepage_header' }) await page.hasImageSnapshot({ snapshotKey: 'modal_open_state' }) await page.hasImageSnapshot({ snapshotKey: 'dark_theme_sidebar' }) // ❌ Avoid - unclear purpose await page.hasImageSnapshot({ snapshotKey: 'test1' }) await page.hasImageSnapshot({ snapshotKey: 'snapshot' }) ``` ### Handle Both Cases Always handle both the existence and non-existence cases: ```ts it('should handle both snapshot states', async () => { const hasSnapshot = await page.hasImageSnapshot({ snapshotKey: 'feature' }) if (hasSnapshot) { // Test against existing snapshot render(<FeatureComponent />) await page.toMatchImageSnapshot({ snapshotKey: 'feature' }) } else { // Create initial snapshot render(<FeatureComponent />) await page.toMatchImageSnapshot({ snapshotKey: 'feature' }) } }) ``` ### Combine with Conditional Logic Use with environment or configuration checks: ```ts it('should adapt to environment', async () => { const isDarkMode = process.env.THEME === 'dark' const snapshotKey = isDarkMode ? 'dark_mode' : 'light_mode' const hasSnapshot = await page.hasImageSnapshot({ snapshotKey }) if (!hasSnapshot) { // Setup appropriate theme if (isDarkMode) { document.body.classList.add('dark') } render(<App />) await page.toMatchImageSnapshot({ snapshotKey }) } }) ```