Search code examples
vue.jsjestjsvue-test-utils

Vue2: When testing component that contains a functional child component, how to emit event from the child?


I try to unit test a component that contains b-button (bootstrap vue button).

I need to emit a 'click' event to simulate a click on b-button child component, as I use shallowMount so all child components are mocked and I cannot .trigger('click') on it. In addition, I cannot access to the vm of that child as b-button is a functional components that don't have instance, so childWrapper.vm.$emit('click') won't work either.

What is my best option to test my component with shallowMount?

What I tried:

  1. Used mount instead of shallowMount .trigger('click') would do the job
  2. Used shallowMount and pass {stubs: {BButton}} b-button would be mocked with the real implementation and will work as section 1
  3. Tried btnWrapper.trigger('click'), btnWrapper.vm.$emit('click'), btnWrapper.element.click(), none of them worked.
// TemplateComponent.vue
<template>
  <div class="row">
    <div class="col-12 p-2">
      <b-button @click="eatMe">Eat me</b-button>
    </div>
  </div>
</template>

<script>
  import bButton from 'bootstrap-vue/es/components/button/button';

  export default {
    name: "TemplateComponent",
    components: {
      bButton
    },
    methods: {
      eatMe() {
        this.$emit('click')
      }
    }
  }
</script>
// TemplateComponent.test.js
import {shallowMount} from '@vue/test-utils';

import TemplateComponent from './TemplateComponent.vue';

describe('TemplateComponent', () => {
  let wrapper, vm;

  beforeEach(() => {
    wrapper = shallowMount(TemplateComponent);
  });

  it('should trigger the eatMe twice', () => {
    wrapper.setMethods({
      eatMe: jest.fn()
    });
    const btn = wrapper.find('b-button-stub');
    btn.trigger('click'); // won't work
    btn.vm.$emit('click'); // won't work

    expect(vm.eatMe.mock.calls.length).toEqual(2);
  });
});

Solution

  • Stub it with a regular button to capture the emit event.

    describe('TemplateComponent', () => {
      let wrapper, vm;
    
      beforeEach(() => {
    shallowMount(TemplateComponent, {
      stubs: {
        'b-button': {
          template: `<button @click='$listeners.click'></button>`
        }
      }
    });
    
     it('should trigger the eatMe twice', () => {
        wrapper.setMethods({
          eatMe: jest.fn()
        });
        const btn = wrapper.find('whatever');
        btn.trigger('click'); // will now work. as it's swapped with a generic button, still processing the event.
    
    
        expect(vm.eatMe.mock.calls.length).toEqual(2);
      });