Damian Bartosik
Damian Bartosik

Reputation: 498

Testing D3.js drag events with Cypress.js

I have an SVG object which uses d3-zoom for zoom and pan functionality. It works flawlessly but the problem showed up when I started to work on integration tests using Cypress.js.

I tried using standard mouse events on the svg element, to simulate drag behavior:

cy.get('svg')
  .trigger('mousedown', { which: 1, force: true })
  .trigger('mousemove', { position: 'left' })
  .trigger('mouseup', { position: 'left', force: true });

The example above is taken from the Cypress drag and drop recipe, and it produces the following error in the nodrag.js file:

cannot read property document of undefined

Below you can see where the error occurs (view is undefined):

__webpack_exports__["default"] = (function(view) {
  var root = view.document.documentElement,
  ...

I spent a lot of hours trying to trigger the event in another way, but without a success - like trying the snippet above with the svg container.

Please keep in mind that I cannot access any d3.js package from the Cypress test because it's imported as an NPM package in a React application.

Thank you in advance for you help!

Upvotes: 14

Views: 2588

Answers (4)

Will T
Will T

Reputation: 45

FWIW I had this same issue and using this library allowed me to interact with the D3 elements: https://github.com/dmtrKovalenko/cypress-real-events

Upvotes: 0

Chris
Chris

Reputation: 2290

Try this:

cy.window().then(win => {
  cy.get('svg')
    .trigger('mousedown', {
      which: 1,
      force: true,
      view: win,
    })
    .trigger('mousemove', {
      clientX: 300,
      clientY: 500,
      force: true,
    })
    .trigger('mouseup', {
      force: true,
      view: win,
    });
});

Referencing Jennifer Shehane's answer in this GitHub issue, the answer to the cannot read property document of undefined part is to plug the window object into view in the trigger options. The issue mentioned in jacefarm's answer, where no movement occurred, seems to be resolved by specifying clientX/clientY rather than using positions relative to the selected element.

Upvotes: 6

jacefarm
jacefarm

Reputation: 7451

I could only arrive at a partial answer before I had to move on, but perhaps this can help you, or someone else, find the ultimate solution.

To remedy the error, a view property must be provided for mousedown. Providing window, like this, allowed the D3 methods to fire properly:

cy.get('svg')
  .trigger('mousedown', { which: 1, force: true, view: window }) // <-- here
  .trigger('mousemove', { position: 'left', view: window })      // <-- here
  .trigger('mouseup', { position: 'left', force: true });

However, no dragging or movement occurred during the test run, and other questions emerged from there. Starting with... Is this the right context to send along with the event? It seemed so, since window seems to be the only context that has the property chain that D3 anticipates:

view.document.documentElement

Or is that an anti-pattern... a code smell?

Running down those subsequent questions led to a few observations that seemed to have significance.

The first concerns how D3 handles mouse and drag events. D3 has numerous event listeners and callbacks that override standard events and their respective handlers.

The second, is that iframes are in play with the Cypress test runner.

Could it be that Cypress's programmatically triggered events are firing properly in the Cypress iframe, but due to D3's aggressive event handling, the translation of those events into the application iframe are getting lost? Especially considering that manually dragging a circle in the testing viewport worked fine.

Which, again, leads back to:

  • Are the programmatically triggered events not being called in the correct context?
  • Are those events somehow being swallowed by or colliding with D3's event handlers?

I selected the Zoomable Force Directed Graph as my D3 subject, inside of a simple Ember application, for researching this question. It perfectly reproduced the error mentioned, so it definitely seems to be a D3 + Cypress challenge, and unrelated to the front-end framework.

I hope this effort is helpful.


Continued...

After some further reading – Cypress's Trade-offs, and particularly, their open pull request Support for Native Browser Events – it seems likely that the event handling overrides within D3 are not yet fully reconcilable within Cypress. Simpler implementations, like those detailed in the drag and drop example, do not present the event handling challenges introduced by a 3rd party library like D3. However, this support does appear to be under development within the Cypress team.

Upvotes: 5

duc mai
duc mai

Reputation: 1422

I used a little bit different from your code so you can try

cy.get('svg')
.trigger('mousedown', { which: 1 })
.trigger('dragstart', {})
.trigger('drag', {});

Upvotes: 0

Related Questions