Interactive Testing
Test hover states, form interactions, and dynamic UI with user interactions
Interactions let you test dynamic UI elements by performing actions before capturing screenshots. This is essential for testing hover states, form submissions, modal openings, and loading states that only appear after user interaction.
How It Works
- Load the target page/test case
- Execute configured interactions (clicks, fills, hovers, etc.)
- Wait for UI to stabilize
- Capture the screenshot
Configuration
Interactions are defined at the test-case level:
Storybook Adapter
Define interactions at the story level:
export const FilledForm: Story = {
parameters: {
visualTesting: {
interactions: [
{ type: 'fill', selector: 'input[name="email"]', text: 'test@example.com' },
{ type: 'press', selector: 'input[name="email"]', key: 'Enter' },
{ type: 'wait', selector: '.loaded' },
{ type: 'waitForTimeout', duration: 500 },
]
}
}
};URL Adapter
Define interactions in your visnap.config.ts:
{
name: "@visnap/url-adapter",
options: {
urls: [
{
id: "homepage",
url: "https://www.example.com/",
interactions: [
{ type: "fill", selector: 'input[name="email"]', text: "test@example.com" },
{ type: "press", selector: 'input[name="email"]', key: "Enter" },
],
},
],
},
}Available Interactions
Click Actions
{ type: 'click', selector: 'button.primary' } // Basic click
{ type: 'click', selector: 'button', button: 'right' } // Right-click
{ type: 'dblclick', selector: 'button' } // Double-clickInput Actions
{ type: 'fill', selector: 'input[name="email"]', text: 'test@example.com' } // Fill input
{ type: 'type', selector: 'input[name="search"]', text: 'search query' } // Type slowly
{ type: 'clear', selector: 'input[name="email"]' } // Clear inputMouse Actions
{ type: 'hover', selector: '.tooltip-trigger' } // Hover
{ type: 'focus', selector: 'input[name="email"]' } // Focus
{ type: 'blur', selector: 'input[name="email"]' } // Blur
{ type: 'dragAndDrop', sourceSelector: '.draggable', targetSelector: '.drop-zone' } // Drag & dropKeyboard Actions
{ type: 'press', selector: 'body', key: 'Enter' } // Press Enter
{ type: 'press', selector: 'body', key: 'Tab' } // Press Tab
{ type: 'press', selector: 'body', key: 'Escape' } // Press EscapeSelection Actions
{ type: 'select', selector: 'select[name="country"]', value: 'US' } // Select option
{ type: 'selectOption', selector: 'select[name="hobbies"]', values: ['reading', 'gaming'] } // Multiple select
{ type: 'check', selector: 'input[name="terms"]' } // Check checkbox
{ type: 'uncheck', selector: 'input[name="newsletter"]' } // Uncheck checkbox
{ type: 'setChecked', selector: 'input[name="notifications"]', checked: true } // Set checkbox stateFile Actions
{ type: 'setInputFiles', selector: 'input[type="file"]', files: ['path/to/file.jpg'] } // Upload filesWait Actions
{ type: 'waitForTimeout', duration: 1000 } // Wait for duration
{ type: 'wait', selector: '.loaded' } // Wait for element
{ type: 'waitForLoadState', state: 'networkidle' } // Wait for load state
{ type: 'scrollIntoView', selector: '.scrollable' } // Scroll to elementBest Practices
Wait for State Changes
Always wait for UI updates after interactions:
// Wait for the result after clicking
{ type: 'click', selector: 'button.submit' },
{ type: 'wait', selector: '.result' }
// Without waiting, the screenshot might capture before the result appears
{ type: 'click', selector: 'button.submit' }Note: Try to keep wait times as short as possible—long delays will slow down the snapshot capture.
Use Specific Selectors
Specific selectors help avoid targeting the wrong element:
// Specific selector targets the exact element
{ type: 'click', selector: 'button[type="submit"]' }
// Generic selector might target the wrong button
{ type: 'click', selector: 'button' }Add Reasonable Delays
Timeouts help handle animations and transitions:
{ type: 'click', selector: 'button.animate' },
{ type: 'waitForTimeout', duration: 500 } // Wait for animationTest Edge Cases
Test different interaction scenarios:
// Test empty form submission
{ type: 'click', selector: 'button[type="submit"]' }
// Test form with invalid data
{ type: 'fill', selector: 'input[name="email"]', text: 'invalid-email' },
{ type: 'click', selector: 'button[type="submit"]' }Troubleshooting
Interactions Not Working
Verify that selectors exist on the page:
// Check that the selector is correct
{ type: 'click', selector: 'button.primary' } // not 'button.primery'Timing Issues
Add waits if elements aren't ready when interactions run:
{ type: 'click', selector: 'button' },
{ type: 'waitForTimeout', duration: 1000 } // Wait for state changeElement Not Found
For dynamic content, wait for elements to appear:
{ type: 'wait', selector: '.dynamic-content' },
{ type: 'click', selector: '.dynamic-content button' }Next Steps
- Screenshot Stabilization - Make tests more stable
- Storybook Setup - Test your components
- URL Testing - Test any website
- Configuration Reference - All configuration options