Search code examples
vuejs3vue-test-utils

Vuejs 3, How can I properly test this: Composite API, shallowMount and emit trigger?


I have this project structure:

/App.vue
|-components/CoponentA.vue
|-components/CoponentB.vue

In App.vue the ComponentB is not mounted until ComponentA emits an event:

<!-- App.vue -->
<template>
  <ComponentA @onMessage="onMessage"/>
  <ComponentB v-if="message"/>
</template>

<script setup lang="ts">
import { type Ref, ref } from 'vue';
import ComponentA from '@/components/ComponentA.vue';
import ComponentB from '@/components/ComponentB.vue';

const message: Ref<String | undefined> = ref();

const onMessage = (messageParam: String): void => {
  message.value = messageParam;
};
</script>

In my test I am trying to check this but I am not able to trigger a custom event on my test. The documentation says trigger is only for DOM events :/

My test code:

// App.spec.ts
import { describe, it, expect } from 'vitest'

import { shallowMount } from '@vue/test-utils'
import App from '@/App.vue'
import ComponentA from '@/components/ComponentA.vue'
import ComponentB from '@/components/ComponentB.vue'
import { nextTick } from 'vue'

describe('App', () => {
  it("renders the ComponentA component", (): void => {
    const wrapper = shallowMount<App>(App);
    expect(wrapper.findComponent(ComponentA).exists()).toBe(true);
  }); // -> works

  it("no renders the ComponentB component", (): void => {
    const wrapper = shallowMount<App>(App);
    expect(wrapper.findComponent(ComponentB).exists()).toBe(false);
  }); // -> works

  it('renders ComponentB when event emitted', async () => {
    const wrapper = shallowMount<App>(App);
    wrapper.findComponent(ComponentA).trigger('onMessage', 'MESSAGE');
    await nextTick();
    expect(wrapper.findComponent(ComponentB).exists()).toBe(true);
  }) // -> fails
})

Full components code:

<!-- Component A -->
<template>
  <h2>Component A</h2>
</template>

<script setup lang="ts">
import { onMounted } from 'vue';

const emit = defineEmits<{
  onMessage: [message: string]
}>();

onMounted(async () => {
  setTimeout(() => {
    emit("onMessage", "Loading finished!");
  }, 1000);
});
</script>

and:

<!-- Component B -->
<template>
  <h2>Component B</h2>
</template>

Solution

  • The only way to do this is using:

    wrapper.vm.$emit()
    

    In this case the working test will be like this:

    it('renders ComponentB when event emitted', async () => {
      const wrapper = shallowMount<App>(App);
      const component = wrapper.findComponent(ComponentA);
      component.vm.$emit('onMessage', 'MESSAGE');
      await nextTick();
      expect(wrapper.findComponent(ComponentB).exists()).toBe(true);
    });