Testing Vue 3 Apps — Form Triggered Events and Set Values of Form Elements

John Au-Yeung - Jan 19 '21 - - Dev Community

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.

Testing Form Triggered Events

We can test the events that are triggered when we submit the form.

For example, we can write:

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

const EmailInput = {
  template: `
    <div>
      <input type="email" v-model="email">
      <button @click="submit">Submit</button>
    </div>
  `,
  data() {
    return {
      email: ''
    }
  },
  methods: {
    submit() {
      this.$emit('submit', this.email)
    }
  }
}

test('sets the value', async () => {
  const wrapper = mount(EmailInput)
  const input = wrapper.find('input')
  await input.setValue('abc@mail.com')
  expect(input.element.value).toBe('abc@mail.com')
})

test('submit event is triggered', async () => {
  const wrapper = mount(EmailInput)
  await wrapper.find('button').trigger('click')
  expect(wrapper.emitted()).toHaveProperty('submit')
})
Enter fullscreen mode Exit fullscreen mode

In the 2nd test, we get the button and trigger the click event.

Then we check that the submit event is emitted with the wrapper.emitted method.

We can check what the event triggered with the argument that it’s emitted with.

For example, we can write:

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

const EmailInput = {
  template: `
    <div>
      <input type="email" v-model="email">
      <button @click="submit">Submit</button>
    </div>
  `,
  data() {
    return {
      email: ''
    }
  },
  methods: {
    submit() {
      this.$emit('submit', this.email)
    }
  }
}

test('emits the input to its parent', async () => {
  const wrapper = mount(EmailInput)
  await wrapper.find('input').setValue('abc@mail.com')
  await wrapper.find('button').trigger('click')
  expect(wrapper.emitted('submit')[0][0]).toBe('abc@mail.com')
})
Enter fullscreen mode Exit fullscreen mode

We mount the EmailInput .

Then we call setValue on the input to set its value.

Then we trigger the click event on the button.

And we check that the submit event is emitted with the input value.

Working with Various Form Elements

We can manipulate other form elements in our tests.

For instance, we can write:

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

const FormComponent = {
  template: `
    <div>
      <form @submit.prevent="submit">
        <input type="email" v-model="form.email">
        <textarea v-model="form.description"/>

        <select v-model="form.city">
          <option value="new-york">New York</option>
          <option value="toronto">Toronto</option>
        </select>

        <input type="checkbox" v-model="form.subscribe"/>
        <input type="radio" value="weekly" v-model="form.interval"/>
        <input type="radio" value="monthly" v-model="form.interval"/>

         <button type="submit">Submit</button>
      </form>
    </div>
  `,
  data() {
    return {
      form: {
        email: '',
        description: '',
        city: '',
        subscribe: false,
        interval: ''
      }
    }
  },
  methods: {
    async submit() {
      this.$emit('submit', this.form)
    }
  }
}

test('submits a form', async () => {
  const wrapper = mount(FormComponent)
  await wrapper.find('input[type=email]').setValue('name@mail.com')
  await wrapper.find('textarea').setValue('Lorem ipsum')
  await wrapper.find('select').setValue('toronto')
  await wrapper.find('input[type=checkbox]').setValue()
  await wrapper.find('input[type=radio][value=monthly]').setValue()
})
Enter fullscreen mode Exit fullscreen mode

We can use the setValue method with textarea , selecrt , checkboxes and radio buttons.

Also, we can trigger complex events.

For instance, we can write:

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

const FormComponent = {
  template: `
    <div>
      <form @submit.prevent="submit">
        <input type="email" v-model="form.email">
        <textarea v-model="form.description"/>

        <select v-model="form.city">
          <option value="new-york">New York</option>
          <option value="toronto">Toronto</option>
        </select>

        <input type="checkbox" v-model="form.subscribe"/>
        <input type="radio" value="weekly" v-model="form.interval"/>
        <input type="radio" value="monthly" v-model="form.interval"/>

        <button type="submit">Submit</button>
      </form>
    </div>
  `,
  data() {
    return {
      form: {
        email: '',
        description: '',
        city: '',
        subscribe: false,
        interval: ''
      }
    }
  },
  methods: {
    async submit() {
      this.$emit('submit', this.form)
    }
  }
}

test('submits a form', async () => {
  const wrapper = mount(FormComponent)
  const email = 'name@mail.com'
  const description = 'Lorem ipsum'
  const city = 'toronto'
  await wrapper.find('input[type=email]').setValue(email)
  await wrapper.find('textarea').setValue(description)
  await wrapper.find('select').setValue(city)
  await wrapper.find('input[type=checkbox]').setValue()
  await wrapper.find('input[type=radio][value=monthly]').setValue()
  await wrapper.find('form').trigger('submit.prevent')
  expect(wrapper.emitted('submit')[0][0]).toMatchObject({
    email,
    description,
    city,
    subscribe: true,
    interval: 'monthly',
  })
})
Enter fullscreen mode Exit fullscreen mode

We have the same component.

In the test, we mount the component and call setValue to set the value of each field.

Then we check if the object we emitted with the submit event matches the expected object with the toMatchObject method.

We test the this.form object in the submit method matches what we have in the last line.

Conclusion

We can test form triggered events and set the values of various form elements in our Vue 3 component tests with Vue Test Utils.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .