cypress-real-events lets you simulate actual browser events, bypassing Cypress’s default event simulation, which can sometimes be a bit too perfect.
Imagine you’re testing a drag-and-drop component. Cypress’s default trigger('dragstart'), trigger('dragover'), trigger('drop') might work for simple cases, but real browsers have nuances. Mouse coordinates, precise timing between events, and how the browser itself interprets these interactions can differ. cypress-real-events bridges this gap.
Here’s a basic drag-and-drop scenario using cypress-real-events:
// cypress/e2e/drag_drop.cy.js
import 'cypress-real-events';
describe('Drag and Drop Simulation', () => {
it('should successfully drag and drop an element', () => {
cy.visit('/drag_drop_page'); // Assume a page with draggable and droppable areas
const draggableSelector = '#draggable';
const droppableSelector = '#droppable';
// Use real events to simulate the drag and drop
cy.get(draggableSelector)
.realMouseDown() // Simulate pressing the mouse button down on the draggable element
.realMouseMove(100, 50) // Simulate moving the mouse relative to the current position
.realMouseUp(); // Simulate releasing the mouse button
// Now, ensure the drop happened correctly (e.g., the element is in the droppable area)
cy.get(droppableSelector).should('contain', 'Dropped Item'); // Adjust assertion based on your app
});
});
Let’s break down what’s happening and why it matters.
The Problem: Flaky UI Tests
Cypress’s built-in event triggers (.trigger()) are powerful but abstract. They tell the DOM that an event occurred, but they don’t necessarily mimic the intricate dance of user input. This can lead to tests passing in Cypress but failing in the real world, or vice-versa. For complex interactions like drag-and-drop, touch events, or precise mouse movements, the difference becomes critical. cypress-real-events acts as a proxy, translating Cypress commands into actual browser-level events that are much closer to what a user would experience.
How cypress-real-events Works: The Mechanical Difference
Instead of just dispatching a synthetic event, cypress-real-events interacts with the browser’s actual event system. When you call .realMouseDown(), it’s not just element.dispatchEvent(new MouseEvent('mousedown')). It’s more akin to the browser’s internal handling of a genuine mouse button press. This includes factors like:
- Coordinates: It calculates and dispatches events with precise screen and client coordinates, often derived from the element’s bounding box and relative offsets.
- Event Bubbling & Capturing: It respects the browser’s natural event propagation model.
- Browser Defaults: It allows default browser actions (like text selection on mouse down) to occur unless explicitly prevented, mirroring user behavior.
This means if your application relies on specific event.clientX, event.clientY, event.pageX, or event.pageY values, or on the precise sequence and timing of mousedown, mousemove, and mouseup events, cypress-real-events will provide a more reliable simulation.
Key Commands and Their Purpose
.realClick(options): Simulates a full click (mousedown, mouseup, click).options: You can specifyclickCount,button('left', 'right', 'middle'),delaybetween mousedown and mouseup.
.realMouseDown(options): Simulates pressing a mouse button down..realMouseUp(options): Simulates releasing a mouse button..realMouseMove(x, y, options): Simulates moving the mouse pointer.xandyare relative to the current mouse position..realHover(options): Simulates hovering over an element..realDoubleClick(options): Simulates a double-click..realTap(options): Simulates a touch tap event..realTouchStart(options): Simulates the start of a touch interaction..realTouchMove(options): Simulates moving a touch point..realTouchEnd(options): Simulates the end of a touch interaction.
Configuration and Setup
First, install the plugin:
npm install --save-dev cypress-real-events
# or
yarn add --dev cypress-real-events
Then, import it into your cypress/support/e2e.js or cypress/support/command.js file:
// cypress/support/e2e.js
import 'cypress-real-events';
A Deeper Dive: Touch Events
Touch events are a prime example of where cypress-real-events shines. Simulating a swipe gesture requires a sequence of touchstart, touchmove, and touchend events with specific coordinates and delays.
Consider a mobile-like carousel:
// cypress/e2e/carousel.cy.js
import 'cypress-real-events';
describe('Carousel Swipe', () => {
it('should swipe to the next item', () => {
cy.visit('/carousel_page');
const carouselSelector = '.carousel-container';
const initialPosition = { x: 200, y: 150 }; // Approximate center of the carousel
const swipeDistance = -200; // Swipe left by 200 pixels
cy.get(carouselSelector)
.realTouchStart(initialPosition) // Start touch at a specific point
.realTouchMove(swipeDistance, 0) // Move finger horizontally
.realTouchEnd(); // Release finger
// Assert that the carousel has moved to the next item
cy.get('.carousel-item.active').should('contain', 'Item 2'); // Adjust assertion
});
});
The realTouchStart, realTouchMove, and realTouchEnd commands orchestrate the touch sequence. Notice realTouchMove(swipeDistance, 0): the 0 indicates no vertical movement, and swipeDistance is a negative value to signify movement to the left. Without cypress-real-events, you’d be wrestling with trigger and trying to manually construct TouchEvent objects, which is far more complex and error-prone.
The One Thing Most People Don’t Know: Event Delays
While the commands themselves are straightforward, the subtle power lies in the delay option available on many of these commands (e.g., realMouseDown({ delay: 100 })). This delay is not just a pause; it dictates the time between the mousedown and mouseup events. In browsers, this interval is crucial for determining if an action is a click, a drag, or just a momentary press. If your UI has logic that differentiates between a quick tap and a long press, or if it starts a drag operation only after the mouse has been held down for a certain duration, controlling this delay becomes paramount. It allows you to precisely tune the simulated user interaction to match the thresholds your application expects, preventing flaky tests that might pass with a default short delay but fail with a slightly longer one.
The next challenge you’ll face is handling complex multi-touch gestures or simulating interactions within iframes.