Search code examples
unit-testingvue.jsvuejs2vue-test-utils

How to test events on the root element of component in vue 2?


I'm writing unit tests for the following component:

<template>
  <sub-component
     @foo="bar"
  />
</template>
<script>
import SubComponent from './SubComponent';

export default {
  name: 'MyComponent',
  components: { SubComponent },
  methods: {
    bar(payload) {
       this.$emit('baz', ...payload);
    }
  }
}
</script>

And the test would be:

import { shallowMount } from '@vue/test-utils';
import _ from 'lodash';
import MyComponent from '../../components/MyComponent';

describe('MyComponent.vue', () => {
  let wrapper;

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

  it('should emit baz on subcomponent foo', () => {
    const subComp = wrapper.find('sub-component-stub');
    expect(subComp.exists()).toBe(true);          // passes

    subComp.vm.$emit('foo');

    return wrapper.vm.$nextTick().then(() => {
      expect(wrapper.emitted().baz).toBeTruthy(); // does not pass;

      // upon logging: 
      console.log(_.isEqual(wrapper, subComp));   // => true 
    })
  })
})

The example is oversimplified, but the principle here is I want a reusable <sub-component> (a modal) and various functional wrappers around it (related to one particular task the modal type performs) which map additional functionality. I don't want the functionality in the parent components, as it would violate DRY - i'd have to place it in each component containing a particular type of modal.

This would work fine if <sub-component> was not the direct child of <template>. Somehow, it appears wrapper and subComp are hosted on the same element.

How should this be tested properly?


Solution

  • Another possibility it's to find your element in the dom and check the emitted value of your root component.

    import { shallowMount } from '@vue/test-utils'
    import MyComponent from './MyComponent.vue'
    import SubComponent from './SubComponent.vue'
    
    describe('MyComponent', () => {    
    
      it('should emit baz on subcomponent foo', () => {
        const wrapper = shallowMount(MyComponent)
        const subComponent = wrapper.find(SubComponent)
    
        expect(subComponent.exists()).toBe(true)
        expect(wrapper.emitted('baz')).toBeUndefined()
    
        subComponent.vm.$emit('foo', ['hello'])
        expect(wrapper.emitted('baz')[0]).toEqual(['hello'])
        // or expect(wrapper).toEmit('baz', 'hello') cf. below for toEmit
      })
    
    })
    

    If you want a custom matcher for Jest:

    toEmit(received, eventName, data) {
      if (data) {
        expect(received.emitted()[eventName][0]).toEqual([data])
      } else {
        expect(received.emitted()[eventName][0]).toEqual([])
      }
      return { pass: true }
    }