Testing Vue 3 Apps — Custom Inputs and Slots
Tags: vue, javascript, webdev, programming
Published: 2021-01-19 21:31:19

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**


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.

### Test Multiple Modifiers

We can test multiple event modifiers applied to events.

For example, we can write:

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

const FormComponent = {
  template: `
    <div>
      <input @keydown.meta.c.exact.prevent="captureCopy" v-model="input" />
    </div>
  `,
  data() {
    return {
      input: ''
    }
  },
  methods: {
    captureCopy() {
      this.$emit('submit', this.input)
    }
  }
}

test('handles complex events', async () => {
  const wrapper = mount(FormComponent)
  await wrapper.find('input').trigger('keydown.meta.c.exact.prevent')
  expect(wrapper.emitted()).toHaveProperty('submit')
})
```

We have the `FormComponent` with an input that has the `keydown` event listener attached to it.

It has various modifiers applied to it.

Then in our test, we call `trigger` the `keydown` event with all the modifiers:

```
await wrapper.find('input').trigger('keydown.meta.c.exact.prevent')
```

Then we check if the `submit` event is emitted in the last line.

Vue Test Utils reads the event and applies the appropriate properties to the event object.

### Interacting with Vue Component Inputs

We can interact with component inputs.

For example, we can write:

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

const CustomInput = {
  template: `
    <div>
      <label>
        {{ label }}
        <input
          type="text"
          :value="modelValue"
          [@input](https://medium.com/r/?url=http%3A%2F%2Ftwitter.com%2Finput "Twitter profile for @input")="$emit('update:modelValue', $event.target.value)"
        >
      </label>
    </div>
  `,
  name: 'CustomInput',
  props: ['modelValue', 'label']
}

const Form = {
  template: `
    <div>
      <custom-input v-model="input" label="Text Input" class="text-input"/>
    </div>
  `,
  components: {
    CustomInput
  }
}

test('fills in the form', async () => {
  const wrapper = mount(Form)
  await wrapper.find('.text-input input').setValue('text')
  expect(wrapper.find('.text-input input').element.value).toBe('text')
})
```

We mount the `Form` component with the `mount` method.

Then we get the input from the `custom-input` and call `setValue` to set its value.

Then we check the value of the input in the last line.

### Slots

We can populate slots of a given component and test it.

For example, we can write:

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

const Layout = {
  template: `
    <div>
      <h1>Welcome!</h1>
      <main>
        <slot />
      </main>
      <footer>
        Thanks for visiting.
      </footer>
    </div>
  `
}

test('layout default slot', () => {
  const wrapper = mount(Layout, {
    slots: {
      default: 'Main Content'
    }
  })
  expect(wrapper.html()).toContain('Main Content')
})
```

We have the `slots` property in the object we pass into `mount` .

The `default` property populates the default slot.

Then we check that the rendered HTML has the `'Main Content'` text.

### Testing Named Slots

We can populate named slots and test its rendered content.

For example, we can write:

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

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: '<div>Header</div>',
      main: '<div>Main Content</div>',
      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 multiple slots.

Then in the test, we mount the components with all the slots filled.

The keys have the slot names, the values are the HTML we want to put inside it.

Then we can check the HTML that’s rendered in the last 3 lines.

### Conclusion

We can test Vue 3 custom input components and slots with Vue Test Utils.