Categories
Vue 3 Testing

Testing Vue 3 Apps — Slots and Async Behavior

Spread the love

With apps getting more complex than ever, it’s important to test them automatically. We can do this with unit tests, and then we don’t have to test everything by hand.

In this article, we’ll look at how to test Vue 3 apps by writing a simple app and testing it.

Testing Slots with Render Functions

We can test slots with render functions and single-file components.

For example, we can write:

Header.vue

<template>
  <div>Header</div>
</template>

example.spec.js

import { mount } from 'vue-test-utils'
import { h } from 'vue'
import Header from './Header.vue'

const Layout = {
  template: `
    <div>
      <header>
        <slot name="header" />
      </header>
      <main>
        <slot name="main" />
      </main>
      <footer>
        <slot name="footer" />
      </footer>
    </div>
  `
}

test('layout full page layout', () => {
  const wrapper = mount(Layout, {
    slots: {
      header: Header,
      main: h('div', 'Main Content'),
      footer: '<div>Footer</div>'
    }
  })

  expect(wrapper.html()).toContain('<div>Header</div>')
  expect(wrapper.html()).toContain('<div>Main Content</div>')
  expect(wrapper.html()).toContain('<div>Footer</div>')
})

We have the Layout component with several slots.

And we add a test to test it by population slots with a single file component for the header.

The main slot is populated with a render function.

h is a function to render a component. The first arg is the tag name and the 2nd arg is the content of the div.

The footer has an HTML string as its value.

Then we check its content with the expect calls.

Scoped Slots

We can test scoped slots with Vue Test Utils.

For example, we can write:

import { mount } from '@vue/test-utils'

const ComponentWithSlots = {
  template: `
    <div class="scoped">
      <slot name="scoped" v-bind="{ msg }" />
    </div>
  `,
  data() {
    return {
      msg: 'world'
    }
  }
}

test('scoped slots', () => {
  const wrapper = mount(ComponentWithSlots, {
    slots: {
      scoped: `<template #scoped="params">
        Hello {{ params.msg }}
        </template>
      `
    }
  })
  expect(wrapper.html()).toContain('Hello world')
})

Our ComponentWithSlots component has a slot name scoped .

It exposes the msg property to the parent.

In the test, we render it in the template tag.

And we check the rendered content in the last line of the test.

Asynchronous Behavior

We can test async behavior in our tests.

For example, we can write:

import { mount } from '@vue/test-utils'

const Counter = {
  template: `
    <div>
      <p>Count: {{ count }}</p>
      <button @click="handleClick">Increment</button>
    </div>
  `,
  data() {
    return {
      count: 0
    }
  },
  methods: {
    handleClick() {
      this.count += 1
    }
  }
}

test('increments by 1', async () => {
  const wrapper = mount(Counter)
  await wrapper.find('button').trigger('click')
  expect(wrapper.find('p').text()).toMatch('Count: 1')
})

We mount the Counter component.

Then we get the button and trigger the click event on it.

Then we check the text of the p element to see if it’s what we expect.

Equivalently, we can write:

import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'

const Counter = {
  template: `
    <div>
      <p>Count: {{ count }}</p>
      <button @click="handleClick">Increment</button>
    </div>
  `,
  data() {
    return {
      count: 0
    }
  },
  methods: {
    handleClick() {
      this.count += 1
    }
  }
}

test('increments by 1', async () => {
  const wrapper = mount(Counter)
  wrapper.find('button').trigger('click')
  await nextTick()
  expect(wrapper.find('p').text()).toMatch('Count: 1')
})

We trigger the click event on the button the same way.

But we call nextTick to wait for the latest count to be rendered.

Then we can do the check the same way.

Conclusion

We can test named and scoped slots in Vue 3 components.

Also, we can test async behavior like clicks triggered in our components.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *