Cypress: How to test custom events?

Created on 10 Jun 2018  路  7Comments  路  Source: cypress-io/cypress

Hi!

I want to test application that fire custom events.
How can I catch them (".on" ?) in Cypress?

Thanks

question

Most helpful comment

I finally got it working properly. It wasn't fun, I'm all ears if there's a better way to validate a custom event.

// set up (and clean up) a mock event listener
const container = {};
cy.window().then(win => {
  const listener = e => {
    win.removeEventListener("MyNotification", listener);
    container.e = e;
  };
  win.addEventListener("MyNotification", listener);
});

// application (asynchronously) fires the event on click (or whatever)
cy.get("#notificationButton").click();

// this should retries until container has an event in it.
// there's quite a lot going on here -- the JavaScript event loop is firing the event
// and the Cypress event loop is polling the container until it has a value
cy.wrap(container).should(container => expect(container.e).not.to.be.undefined);

// Add one more statement to the Cypress event loop to validate the event from the JavaScript event loop
cy.wrap(container).then(container => validateEvent(container.e));

https://stackoverflow.com/questions/55582982/how-to-listen-global-events-with-cypress

return new Cypress.Promise(resolve => { // Cypress will wait for this Promise to resolve

https://medium.com/@hayavuk/perform-async-operations-sequentially-using-recursive-promises-in-javascript-c4e5ab921c37

https://docs.cypress.io/api/utilities/promise.html#Waiting-for-Promises

return new Cypress.Promise((resolve, reject) => {
    setTimeout(() => {

https://medium.com/@NicholasBoll/cypress-io-using-async-and-await-4034e9bab207

This library allows a Cypress chain to be converted into a real promise which is required to use async/await.

https://github.com/cypress-io/cypress/issues/1417

(long discussion about awaiting Cypress chains)

I also tried using Cypress.Promise but the .click never got called so the event never occurred so the promise never resolved.

cy.window().then(win => {
  return new Cypress.Promise(resolve => {
    const listener = e => {
      win.removeEventListener("MyNotification", listener);
      validateEvent(e);
      resolve();
    };
    win.addEventListener("MyNotification", listener);
    cy.get("#notificationButton").click();
  });
});

I also tried stubbing out or spying on window.dispatchEvent but that was really noisy.
I also tried without the container (just a raw variable, let e;) but that bound too early.
I also tried using setTimeout to poll but that was even more horrible than the above (recursive).

All 7 comments

Cypress has native access to your application, the same way your application has access to the window.

If you rephrase the question: "How can I test that custom events are firing on my DOM elements in javascript" the answer would be the same.

cy.window().then((win) => {
  // listen for events bubbling up to the window
  win.addEventListener(...) 
  Cypress.$(win).on(...)
})

or directly on your DOM elements...

cy.get('button').then(($button) => {
  $button.get(0).addEventListener(...) 
  $button.on(...)
})

thank you very much!

hey @brian-mann, where is Cypress.$(foo).on(...) documented?
I'm searching for the guide/docs section to find out how I can "export" variables from it for the rest of my test (like a JS object from a library I accessed via the window)

I can't use wrap inside the .on callback, for example

@codeofsumit You can proxy jQuery methods using Cypress.$ as documented here: https://on.cypress.io/$

@brian-mann This approach creates an event handler that will validate that the event is correct.

I can't figure out how to check if the custom event completely fails to be triggered at all.

I think I am getting confused or stuck between Cypress' custom event queue and the native JavaScript event queue.

I finally got it working properly. It wasn't fun, I'm all ears if there's a better way to validate a custom event.

// set up (and clean up) a mock event listener
const container = {};
cy.window().then(win => {
  const listener = e => {
    win.removeEventListener("MyNotification", listener);
    container.e = e;
  };
  win.addEventListener("MyNotification", listener);
});

// application (asynchronously) fires the event on click (or whatever)
cy.get("#notificationButton").click();

// this should retries until container has an event in it.
// there's quite a lot going on here -- the JavaScript event loop is firing the event
// and the Cypress event loop is polling the container until it has a value
cy.wrap(container).should(container => expect(container.e).not.to.be.undefined);

// Add one more statement to the Cypress event loop to validate the event from the JavaScript event loop
cy.wrap(container).then(container => validateEvent(container.e));

https://stackoverflow.com/questions/55582982/how-to-listen-global-events-with-cypress

return new Cypress.Promise(resolve => { // Cypress will wait for this Promise to resolve

https://medium.com/@hayavuk/perform-async-operations-sequentially-using-recursive-promises-in-javascript-c4e5ab921c37

https://docs.cypress.io/api/utilities/promise.html#Waiting-for-Promises

return new Cypress.Promise((resolve, reject) => {
    setTimeout(() => {

https://medium.com/@NicholasBoll/cypress-io-using-async-and-await-4034e9bab207

This library allows a Cypress chain to be converted into a real promise which is required to use async/await.

https://github.com/cypress-io/cypress/issues/1417

(long discussion about awaiting Cypress chains)

I also tried using Cypress.Promise but the .click never got called so the event never occurred so the promise never resolved.

cy.window().then(win => {
  return new Cypress.Promise(resolve => {
    const listener = e => {
      win.removeEventListener("MyNotification", listener);
      validateEvent(e);
      resolve();
    };
    win.addEventListener("MyNotification", listener);
    cy.get("#notificationButton").click();
  });
});

I also tried stubbing out or spying on window.dispatchEvent but that was really noisy.
I also tried without the container (just a raw variable, let e;) but that bound too early.
I also tried using setTimeout to poll but that was even more horrible than the above (recursive).

Is it possible to use shiny events as described here, e.g. for shiny:value event?

// use event.target to obtain the output element
$(document).on('shiny:value', function(event) {
  // cancel the output of the element with id 'foo'
  if (event.target.id === 'foo') {
    event.preventDefault();
  }
});
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jennifer-shehane picture jennifer-shehane  路  3Comments

SecondFlight picture SecondFlight  路  3Comments

brian-mann picture brian-mann  路  3Comments

carloscheddar picture carloscheddar  路  3Comments

zbigniewkalinowski picture zbigniewkalinowski  路  3Comments