Custom jest matchers to test the state of the DOM
[![Build Status][build-badge]][build] [![Code Coverage][coverage-badge]][coverage] [![version][version-badge]][package] [![downloads][downloads-badge]][npmtrends] [![MIT License][license-badge]][license]
[![PRs Welcome][prs-badge]][prs] [![Code of Conduct][coc-badge]][coc]
[![Discord][discord-badge]][discord]
[![Watch on GitHub][github-watch-badge]][github-watch] [![Star on GitHub][github-star-badge]][github-star] [![Tweet][twitter-badge]][twitter]
You want to use [jest][] to write tests that assert various things about the state of a DOM. As part of that goal, you want to avoid all the repetitive patterns that arise in doing so. Checking for an element's attributes, its text content, its css classes, you name it.
The @testing-library/jest-dom library provides a set of custom jest matchers
that you can use to extend jest. These will make your tests more declarative,
clear to read and to maintain.
@jest/globalsexpecttoBeDisabledtoBeEnabledtoBeEmptyDOMElementtoBeInTheDocumenttoBeInvalidtoBeRequiredtoBeValidtoBeVisibletoContainElementtoContainHTMLtoHaveAccessibleDescriptiontoHaveAccessibleErrorMessagetoHaveAccessibleNametoHaveAttributetoHaveClasstoHaveFocustoHaveFormValuestoHaveStyletoHaveTextContenttoHaveValuetoHaveDisplayValuetoBeCheckedtoBePartiallyCheckedtoHaveRoletoHaveErrorMessagetoBePressedtoBePartiallyPressedtoAppearBeforetoAppearAftertoBeEmptytoBeInTheDOMtoHaveDescriptiontoHaveSelectionThis module is distributed via [npm][npm] which is bundled with [node][node] and
should be installed as one of your project's devDependencies:
npm install --save-dev @testing-library/jest-dom
or
for installation with yarn package manager.
yarn add --dev @testing-library/jest-dom
Note: We also recommend installing the jest-dom eslint plugin which provides auto-fixable lint rules that prevent false positive tests and improve test readability by ensuring you are using the right matchers in your tests. More details can be found at eslint-plugin-jest-dom.
Import @testing-library/jest-dom once (for instance in your tests setup
file) and you're good to go:
// In your own jest-setup.js (or any other name)
import '@testing-library/jest-dom'
// In jest.config.js add (if you haven't already)
setupFilesAfterEnv: ['<rootDir>/jest-setup.js']
@jest/globalsIf you are using @jest/globals with
injectGlobals: false, you will need to use a different
import in your tests setup file:
// In your own jest-setup.js (or any other name)
import '@testing-library/jest-dom/jest-globals'
If you are using vitest, this module will work as-is, but you will need to
use a different import in your tests setup file. This file should be added to
the setupFiles property in your vitest config:
// In your own vitest-setup.js (or any other name)
import '@testing-library/jest-dom/vitest'
// In vitest.config.js add (if you haven't already)
setupFiles: ['./vitest-setup.js']
Also, depending on your local setup, you may need to update your
tsconfig.json:
// In tsconfig.json
"compilerOptions": {
...
"types": ["vitest/globals", "@testing-library/jest-dom"]
},
"include": [
...
"./vitest-setup.ts"
],
If you're using TypeScript, make sure your setup file is a .ts and not a .js
to include the necessary types.
You will also need to include your setup file in your tsconfig.json if you
haven't already:
// In tsconfig.json
"include": [
...
"./jest-setup.ts"
],
expectIf you are using a different test runner that is compatible with Jest's expect
interface, it might be possible to use it with this library:
import * as matchers from '@testing-library/jest-dom/matchers'
import {expect} from 'my-test-runner/expect'
expect.extend(matchers)
@testing-library/jest-dom can work with any library or framework that returns
DOM elements from queries. The custom matcher examples below are written using
matchers from @testing-library's suite of libraries (e.g. getByTestId,
queryByTestId, getByText, etc.)
toBeDisabledtoBeDisabled()
This allows you to check whether an element is disabled from the user's
perspective. According to the specification, the following elements can be
disabled:
button, input, select, textarea, optgroup, option, fieldset, and
custom elements.
This custom matcher considers an element as disabled if the element is among the
types of elements that can be disabled (listed above), and the disabled
attribute is present. It will also consider the element as disabled if it's
inside a parent form element that supports being disabled and has the disabled
attribute present.
<button data-testid="button" type="submit" disabled>submit</button>
<fieldset disabled><input type="text" data-testid="input" /></fieldset>
<a href="https://github.com/testing-library/jest-dom/raw/v6.9.1/" disabled>link</a>
expect(getByTestId('button')).toBeDisabled()
expect(getByTestId('input')).toBeDisabled()
expect(getByText('link')).not.toBeDisabled()
This custom matcher does not take into account the presence or absence of the
aria-disabledattribute. For more on why this is the case, check #144.
toBeEnabledtoBeEnabled()
This allows you to check whether an element is not disabled from the user's perspective.
It works like not.toBeDisabled(). Use this matcher to avoid double negation in
your tests.
This custom matcher does not take into account the presence or absence of the
aria-disabledattribute. For more on why this is the case, check #144.
toBeEmptyDOMElementtoBeEmptyDOMElement()
This allows you to assert whether an element has no visible content for the user. It ignores comments but will fail if the element contains white-space.
<span data-testid="not-empty"><span data-testid="empty"></span></span>
<span data-testid="with-whitespace"> </span>
<span data-testid="with-comment"></span>
expect(getByTestId('empty')).toBeEmptyDOMElement()
expect(getByTestId('not-empty')).not.toBeEmptyDOMElement()
expect(getByTestId('with-whitespace')).not.toBeEmptyDOMElement()
toBeInTheDocumenttoBeInTheDocument()
This allows you to assert whether an element is present in the document or not.
<span data-testid="html-element"><span>Html Element</span></span>
<svg data-testid="svg-element"></svg>
expect(
getByTestId(document.documentElement, 'html-element'),
).toBeInTheDocument()
expect(getByTestId(document.documentElement, 'svg-element')).toBeInTheDocument()
expect(
queryByTestId(document.documentElement, 'does-not-exist'),
).not.toBeInTheDocument()
Note: This matcher does not find detached elements. The element must be added to the document to be found by toBeInTheDocument. If you desire to search in a detached element please use:
toContainElement
toBeInvalidtoBeInvalid()
This allows you to check if an element, is currently invalid.
An element is invalid if it has an
aria-invalid attribute
with no value or a value of "true", or if the result of
checkValidity()
is false.
<input data-testid="no-aria-invalid" />
<input data-testid="aria-invalid" aria-invalid />
<input data-testid="aria-invalid-value" aria-invalid="true" />
<input data-testid="aria-invalid-false" aria-invalid="false" />
<form data-testid="valid-form">
<input />
</form>
<form data-testid="invalid-form">
<input required />
</form>
expect(getByTestId('no-aria-invalid')).not.toBeInvalid()
expect(getByTestId('aria-invalid')).toBeInvalid()
expect(getByTestId('aria-invalid-value')).toBeInvalid()
expect(getByTestId('aria-invalid-false')).not.toBeInvalid()
expect(getByTestId('valid-form')).not.toBeInvalid()
expect(getByTestId('invalid-form')).toBeInvalid()
toBeRequiredtoBeRequired()
This allows you to check if a form element is currently required.
An element is required if it is having a required or aria-required="true"
attribute.
<input data-testid="required-input" required />
<input data-testid="aria-required-input" aria-required="true" />
<input data-testid="conflicted-input" required aria-required="false" />
<input data-testid="aria-not-required-input" aria-required="false" />
<input data-testid="optional-input" />
<input data-testid="unsupported-type" type="image" required />
<select data-testid="select" required></select>
<textarea data-testid="textarea" required></textarea>
expect(getByTestId('required-input')).toBeRequired()
expect(getByTestId('aria-required-input')).toBeRequired()
expect(getByTestId('conflicted-input')).toBeRequired()
expect(getByTestId('aria-not-required-input')).not.toBeRequired()
expect(getByTestId('optional-input')).not.toBeRequired()
expect(getByTestId('unsupported-type')).not.toBeRequired()
expect(getByTestId('select')).toBeRequired()
expect(getByTestId('textarea')).toBeRequired()
expect(getByTestId('supported-role')).not.toBeRequired()
expect(getByTestId('supported-role-aria')).toBeRequired()
toBeValidtoBeValid()
This allows you to check if the value of an element, is currently valid.
An element is valid if it has no
aria-invalid attributes
or an attribute value of "false". The result of
checkValidity()
must also be true if it's a form element.
<input data-testid="no-aria-invalid" />
<input data-testid="aria-invalid" aria-invalid />
<input data-testid="aria-invalid-value" aria-invalid="true" />
<input data-testid="aria-invalid-false" aria-invalid="false" />
<form data-testid="valid-form">
<input />
</form>
<form data-testid="invalid-form">
<input required />
</form>
expect(getByTestId('no-aria-invalid')).toBeValid()
expect(getByTestId('aria-invalid')).not.toBeValid()
expect(getByTestId('aria-invalid-value')).not.toBeValid()
expect(getByTestId('aria-invalid-false')).toBeValid()
expect(getByTestId('valid-form')).toBeValid()
expect(getByTestId('invalid-form')).not.toBeValid()
toBeVisibletoBeVisible()
This allows you to check if an
$ claude mcp add jest-dom \
-- python -m otcore.mcp_server <graph>