Introducing Vue Test Utils Plugins

Posted on November 3, 2020 by Jessica Sachs

VTU for Vue 3 introduces plugins so developers can bring their own sugary syntax to their component tests. Here are two examples - wrapper.findByTestId and wrapper.click()

Motivations

Vue Test Utils is a library solely focused on helping users mount, trigger, and find their components and associated DOM nodes. We, the VTU team, received many issues asking us to decorate the Vue Wrapper with helper methods – similar to the Testing Library’s API.

Here are a few:

  • Asks for an opinionated, official solution to targeting elements, #1234
  • Asks for wrapper.xpath method, #1511
  • Asks for wrapper.click as an alias to .trigger('click'), #285
  • Asks for wrapper.classes, #139

We are committed to being an un-opinionated rendering library, and as such, we urge developers to build plugins like these on top of Vue Test Utils.

Building a findByTestId Plugin

A best practice when testing UI is to decouple the selectors used to target DOM elements from the selectors used to style them or add functionality. One of the ways (though not the “best” way) to accomplish this is to use a selector called data-testid or data-test-id.

Written in its long form, the API for finding a component or DOM node with this selector looks something like this:

wrapper.find('[data-testid=increment]')

This involves a lot of boilerplate. The desired API looks like the below:

wrapper.findByTestId('increment') // <- New API

The following plugin will add this function to the wrapper prototype:

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

// Invoked on each mount command, with the wrapper being mounted
const FindByPlugin = (wrapper) => {
  function findByTestId(selector) {
    const dataSelector = `[data-testid='${selector}']`;
    const foundWrapper = wrapper.find(dataSelector);
    if (foundWrapper) {
      return foundWrapper;
    }

    return null;
  }

  return {
    findByTestId
  };
};

config.plugins.VueWrapper.install(FindByPlugin)

Building a DOM Events plugin

Below, we demonstrate how to add syntax for wrapper.click() instead of the more verbose wrapper.trigger('click')

This method uses a factory function to create the DOMTriggerPlugin, so you can pass in any string and it will wind up on the wrapper’s prototype as a chainable method.

import { config } from '@vue/test-utils'
const makeDOMTriggerPlugin = (events) => {
  const methods = {};
  const DOMTriggerPlugin = (wrapper) => {
    events.forEach((event) => {
      methods[event] = async () => wrapper.trigger(event);
    });
    return methods;
  };
  return DOMTriggerPlugin;
};

const DOMTriggerPlugin = makeDOMTriggerPlugin(['click'])
config.plugins.VueWrapper.install(DOMTriggerPlugin);
config.plugins.DOMWrapper.install(DOMTriggerPlugin);

I hope folks take full advantage of the pluggable architecture we baked into VTU for Vue 3. Developers have strong opinions on syntactical sugar, and we figured we’d best serve the community as a humble rendering utility, rather than an opinionated testing library.

Find me on Twitter if you’d like to chat.

Happy Testing!

– Jess