Extend Vue Test Utils - findByText
Tags: vue, testing, javascript, webdev
Published: 2023-06-10 11:33:55

When it comes to testing Vue components, [Vue Test Utils](https://test-utils.vuejs.org/) is the official testing library that provides a rich set of utilities for interacting with and asserting on components in a Vue application. However, just like any library, there are always opportunities for enhancements and additions to further improve the testing experience.

If you've used other testing libraries like [testing-library](https://testing-library.com), [Cypress](https://www.cypress.io/) etc, one pattern that you might have found useful is to select elements based on their text content (e.g `getByText`, `cy.contains` etc).
Although Vue Test Utils supports finding elements using CSS selectors via the `find` and `findAll` methods, it doesn't provide a method for getting elements by text.

Thankfully, Vue Test Utils includes a `Plugin` API that lets us extend its capabilities with custom logic, methods, or functionality, so we can create our own `findByText` method :tada:.

**Let's get right to it then!**

## Create and register the extends file

Create a file for your Vue Test Utils extends, e.g. `vue-test-utils.extends.js` and save it to your preffered location.

You'll need to register that file in your test runner's configuration so that it runs before each test.

**For Jest**, that would be inside `jest.config.js` `setupFilesAfterEnv` array

`jest.config.js`
```js
module.exports = {
  ...
  setupFilesAfterEnv: ['./tests/jest.extends.js'],
}
```

**For Vitest** that would be inside `vite.config.js` `test` `setupFiles`

`vite.config.js`
```js
/// <reference types="vitest" />
import { defineConfig } from 'vite'

export default defineConfig({
  ...
  test: {
    ...
    setupFiles: './tests/jest.extends.js',
  },
});
```

With that out of the way it's time to add our `findByText` function.

## `findByText` function

`jest.extends.js`

```js
import { config, createWrapperError } from '@vue/test-utils';

const Plugin = (wrapper) => {
  function findByText(str, selector = '*') {
    const items = wrapper.findAll(selector);

    for (let i = 0; i < items.length; i++) {
      const item = items[i];

      if (item.text().trim() === str.trim()) {
        return item;
      }
    }

    return createWrapperError('DOMWrapper');
  }

  return {
    findByText,
  };
};

config.plugins.VueWrapper.install(Plugin);
```

## Details

The `findByText` function uses the `findAll` method of Vue Test Utils under the hood in order to find elements and then loops over them to find the text that matches the `string` passed as an argument. Optionally, we can also pass a `selector` as a second argument if we want to narrow down the results to a specific container.

**Things to note:** 
The function performs a case-sensitive text match so ensure that the text parameter exactly matches the casing of the desired text content. It also trims both the text and the actual text content of the elements being searched to remove leading and trailing whitespace, allowing for more flexible matching.

Feel free to change the code that does the matching part to your own liking.

### Parameters

`text` (string): The text content to search for within the component's wrapper.
`selector` (string): (Optional) CSS selector used to narrow down the search scope. Defaults to '*', which matches all elements within the wrapper.

### Return Value

`element` (DOMWrapper): A `DOMWrapper` object representing the first element that matches the given text within the wrapper. If no matching element is found, an error is thrown.

## Example

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

const Component = {
  template: `
    <div>
      <span class="test1">Foo</span>
      <span class="test2">Bar</span>
    </div>
  `,
};

const wrapper = mount(Component);

const span = wrapper.findByText('Foo');

expect(span.classes()).toContain('test1');
```

## Final notes

We saw how you can use Vue Test Utils `Plugin` API to extend its functionality. Using the same format we can create other query functions to use in our tests without the need to install another 3rd party testing library.

**Happy component testing!**