Cypress test events from DOM elements

Cypress test events from DOM elementsIn JavaScript many components communicate to each other via Events. To write good integration tests in Cypress it is also crucial to assert all events fired from DOM elements. I wrote this guide since I struggled myself on listening for events with cypress a canvas fired. So let's dive into it!
Categories
Date
2022-02-28
Hire me on Upwork!

Why should I assert Events in my Cypress tests?

When performing a Cypress test you are using an application as a real user would use it. From this point of view, we could assume that events are not important for cypress tests since a user also will not see them.

Maybe testing events is more a thing for Unit or Component tests?

I think they are not! I think it is important to also assert on events. Events are a way to transfer data between components. If the transfers data is wrong the behavior of the component would be wrong too, even if the UI may still look correct. So in my opinion it is crucial to test events within cypress tests.

I initially thought this is a trivial task, but I got stuck at it for nearly a day.

I tried to access the event I want to listen to and work my way further with invokes and spies. I tried to add the spy in the before each. I tried to listen to the events using Cypress jquery methods. But I had every time the problem that my listeners inside cypress seemed to be ignored. Or the test seemed to assert or end before the listener was listening. So how did I solve this?

How to assert events in Cypress

Unfortunately, Cypress and events are not a good team. It took me days to figure out how to test custom events emitted from components in my Cypress tests. But I finally got it after working through every resource I found on google and doing hours of trial and error (many errors) testing I made my way through a guide originally made for React and redux store event testing in cypress.

To assert events in Cypress you first need to set up the kuker event library. Then you can simply access the Kuker instance from cypress and access all the custom events from your components inside your cypress tests.

Let's prepare an example and go through this step by step.

step by step guide to assert events in cypress

To try this out I have prepared a simple example inside my Testing Playground.

It seems like kuker is not working in my production environment. So feel free to copy the below code and play around with cypress and kuker.

Before we start, the test components:

playground.vue

    <template>
      <Layout>
        <div class="container">
          <extroverted-div data-cy="component" @customEvent="handleCustomEvent"/> {{ divResult}}
        </div>
      </Layout>
    </template>
    
    <script>
    import ExtrovertedDiv from "../components/codingExamples/extrovertedDiv";
    
    export default {
      components: {
        ExtrovertedDiv,
      },
      data () {
        return{
          divResult : ""
        }
      },
      methods : {
        handleCustomEvent(payload){
          console.log("handleCustomEvent")
          this.divResult = payload;
        }
      },
    }
    </script>

ExtrovertedDiv.vue

    <template>
        <div @click="emitEvent" id="why" data-cy="whyCypress">
          Why should i use Cypress?
        </div>
    </template>
    
    <script>
    import { VueEmitter } from 'kuker-emitters';
    
    VueEmitter();
    
    export default {
      name: "extrovertedDiv",
      methods: {
        emitEvent(){
          this.$emit("customEvent", "Because it is amazing!");
        }
      }
    }
    </script>
    
    <style scoped>
    
    </style>
  1. Install Kuker

    What is kuker?

    Kuker is a package and browser extension to access and visualize all events fired from your application. Kuker was developed for react, but works with all major frameworks like angular and vue.

    Take a look at Kuker

    Kuker events example screenshot

    to install kuker in your application run

    npm install kuker-emitters

    to install the kuker extension and have everything visible visit the chrome store.

  2. Activate Kuker inside the app

    To activate kuker and make use of its emitters paste the following snippet on top of your vue components script section. As I did in exportedDiv.vue

import { VueEmitter } from 'kuker-emitters';
       
VueEmitter();
  1. Save Kuker events inside Cypress

    To save kuker events inside cypress we need to access the kuker instance before each test. After accessing the instance I will save the result in two arrays. One Contains all events fired and one contains also the interesting events with a payload.

           let kukerEvents
           let vueEvents
           beforeEach(() => {
               kukerEvents = []
               vueEvents = []
               cy.visit('https://the-koi.com/playground', {
                   onBeforeLoad (win) {
                       const postMessage = win.postMessage.bind(win)
                       win.postMessage = (what, target) => {
                       // check if the event is a kuker event
                           if (isKuker(what)) {
                               let raw = Cypress._.pick(what,  'state');
                               let obj = null
                               try{
                               	// save events with a payload here
                                   obj = JSON.parse(raw.state);
                                   vueEvents.push(obj)
                               }catch(e){
                                   console.log(e);
                               }
                               // save all the events here
                               // you would not need them, 
                               // this is just interesting to get a deeper insight
                               // into vue
                               kukerEvents.push(Cypress._.pick(what, 'label', 'state'))
                           }
                           return postMessage(what, target)
                       }
                       // save for sync
                       cy.spy(win, 'postMessage').as('postMessage')
                   }
                   })
           });
  1. assert with kuker events

    inside the test we then can now access all the events and assert on them.

    I do this after performing the click three times, but I suggest you play also around with the alias and see how kuker behaves for different actions. This is then a great example to understand how kuker works and how to use kuker to access custom events inside cypress

it.only("asserts event with kuker", () =>{
                   cy.get('[data-cy="component"]').click().click().click().then(()=>{
                       console.log("kuker store: ", kukerEvents)
                       console.log("vue event store: ", vueEvents)
                       // we expect to get 3 messages from Vue
                       expect(vueEvents.length).to.be.greaterThan(3);
                       // the event should contain the sent message
                       expect(vueEvents.find(e => 
                       e.eventName === "customEvent").
                           payload).to.includes("Because it is amazing!");
                       })
                   // directly listen to post message
                   cy.get("@postMessage").then(msg => {
                       console.log("post msg: ", msg)
                       console.log("kuker store: ", kukerEvents)
                       console.log("vue event store: ", vueEvents)
                   })
               })
  1. feel great

    You now can write stronger tests by also asserting on custom events from your components! Congratulations!

    Here is the complete test file for better understanding:

/// <reference types="cypress" />
const isKuker = what =>
    Cypress._.isPlainObject(what) && what.kuker && what.type !== 'NEW_EMITTER'

const kukerMessage = ke => (ke.label ? `${ke.type}: ${ke.label}` : ke.type)

context('customEvent Assertions', () => {
    let kukerEvents
    let vueEvents
    beforeEach(() => {
        kukerEvents = []
        vueEvents = []
        cy.visit('https://the-koi/playground', {
            onBeforeLoad (win) {
                const postMessage = win.postMessage.bind(win)
                win.postMessage = (what, target) => {
                    if (isKuker(what)) {
                        let raw = Cypress._.pick(what,  'state');
                        let obj = null
                        try{
                            obj = JSON.parse(raw.state);
                            vueEvents.push(obj)
                        }catch(e){
                            console.log(e);
                        }
                        kukerEvents.push(Cypress._.pick(what, 'label', 'state'))
                    }
                    return postMessage(what, target)
                }
                cy.spy(win, 'postMessage').as('postMessage')
            }
            })
    });

    describe('find and assert on custom events', () => {
        it.only("asserts event with kuker", () =>{
            cy.get('[data-cy="component"]').click().click().click().then(()=>{
                console.log("kuker store: ", kukerEvents)
                console.log("vue event store: ", vueEvents)
                // we expect to get 3 messages from Vue
                expect(vueEvents.length).to.be.greaterThan(3);
                // the event should contain the sent message
                expect(vueEvents.find(e => e.eventName === "customEvent").
                    payload).to.includes("Because it is amazing!");
            })
        })
    });
});

Conclusion

Accessing custom events within cypress tests could be hard, but with the right tools in place it is easy. You will end up with stronger cypress tests, a nice additional debug tool, and with a better understanding of your application's event flow.

I hope I could provide you with some value with this article. If you want to support me and the work I´m doing you can buy me a coffee. I will donate half of it to Ukraine.

Happy, strong asserting,

Alex

Recommended Products for you
recommended for you