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 specify clickCount, button ('left', 'right', 'middle'), delay between 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. x and y are 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.

Want structured learning?

Take the full Cypress course →