0

I'm having trouble working out how to do a simple test of a Vue component (using the Cypress Component Test Runner) to see if a button click results in an event being emitted

// MyButton.vue component
    <template>
      <Button
        data-testid="button"
        label="Click Button"
        @click="clickButton()"
      />
    </template>
    <script setup lang="ts">
    import { defineEmits } from "vue";
    const emit = defineEmits(["clickButtonEvent"]);
    
    function clickButton() {
      emit("clickButtonEvent");
    }
    </script>
// MyButton.spec.ts
    it.only("should emit an even when clicked", () => {
      const clickButtonSpy = cy.spy().as("clickButtonEvent");
    
      mount(FulfilButton, {
        global: {
          components: {
            Button,
          },
        },
      })
      .get('[data-testid="button"]')
      .click();
    
      cy.get("@clickButtonEvent").should("have.been.called");
    });

This doesn't work - in the console I see

mount
get
-click

but then this:

expect clickButtonEvent to have been called at least once, but it was never called

So I guess I am not hooking up this cy.spy correctly - presumably because I am not doing it as part of the mount? What do I need to do? The button itself is a PrimeVue button component but I'm pretty sure that should not stop me doing this test?

Michal Levý
  • 33,064
  • 4
  • 68
  • 86
Richard Shergold
  • 572
  • 1
  • 8
  • 21

1 Answers1

4

Well, you are not hooking the spy at all.

The Cypress mount command has the same interface as vue-test-utils mount (it is using vue-test-utils under the hood)

In vue-test-utils v1 (for Vue 2) you can use listeners mount option to attach the spy as demonstrated in this answer

But since you are using Vue 3 and in turn vue-test-utils v2 where listeners mount option was removed, probably your best option to use recommended API of the wrapper - emitted

This example is taken from the recent talk of Jessica Sachs (Cypress team member) (repo) where she does something like this:

mount(FulfilButton, {
  global: {
    components: {
      Button,
    },
  },
})
.get('[data-testid="button"]')
.click()
.vue()
.then((wrapper) => {
  expect(wrapper.emitted('clickButtonEvent')).to.have.length(1)
})

Note that vue() is not a build-in Cypress command. In this demo/repo it was added by Jessica in the support file

// ./cypress/support/index.js

Cypress.Commands.add('vue', () => {
  return cy.wrap(Cypress.vueWrapper)
})

You can do something very similar without introducing any helper (example)

it('emits "increment" event on click', () => {
    cy.get('button')
    .click()
    .click()
    .then(() => {
      cy.wrap(Cypress.vueWrapper.emitted()).should('have.property', 'increment')
    })
  })
Michal Levý
  • 33,064
  • 4
  • 68
  • 86
  • Thanks - actually I watched the same talk and I reached out to Jessica about this vue() command yesterday because that does not work for me (I get "property vue does not exist on type Chainable>"- she said the Vue command was "not installed by default" and I'm not entirely sure what that means - do you? This is exactly the way I wanted to do this test but without the Vue command in there I can't – Richard Shergold Dec 03 '21 at 12:02
  • sorry - not "added by default" was what she said – Richard Shergold Dec 03 '21 at 12:08
  • Thank you! cy.wrap(Cypress.vueWrapper.emitted()).should('have.property' seems to work – Richard Shergold Dec 03 '21 at 13:17
  • I had the same issue with Chainable>. Types are not working but test pass. @RichardShergold Did you find out a working solution ? – Boogie Jun 29 '23 at 13:30