Git Product home page Git Product logo

jest-dom's Introduction

jest-dom

owl

Custom jest matchers to test the state of the DOM


Build Status Code Coverage version downloads MIT License

All Contributors PRs Welcome Code of Conduct Discord

Watch on GitHub Star on GitHub Tweet

The problem

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.

This solution

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.

Table of Contents

Installation

This module is distributed via npm which is bundled with 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.

Usage

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']

With @jest/globals

If 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'

With Vitest

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']

With TypeScript

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"
  ],

With another Jest-compatible expect

If 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)

Custom 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.)

toBeDisabled

toBeDisabled()

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.

Examples

<button data-testid="button" type="submit" disabled>submit</button>
<fieldset disabled><input type="text" data-testid="input" /></fieldset>
<a href="..." 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-disabled attribute. For more on why this is the case, check #144.


toBeEnabled

toBeEnabled()

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-disabled attribute. For more on why this is the case, check #144.


toBeEmptyDOMElement

toBeEmptyDOMElement()

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.

Examples

<span data-testid="not-empty"><span data-testid="empty"></span></span>
<span data-testid="with-whitespace"> </span>
<span data-testid="with-comment"><!-- comment --></span>
expect(getByTestId('empty')).toBeEmptyDOMElement()
expect(getByTestId('not-empty')).not.toBeEmptyDOMElement()
expect(getByTestId('with-whitespace')).not.toBeEmptyDOMElement()

toBeInTheDocument

toBeInTheDocument()

This allows you to assert whether an element is present in the document or not.

Examples

<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


toBeInvalid

toBeInvalid()

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.

Examples

<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()

toBeRequired

toBeRequired()

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.

Examples

<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>
<div data-testid="supported-role" role="tree" required></div>
<div data-testid="supported-role-aria" role="tree" aria-required="true"></div>
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()

toBeValid

toBeValid()

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.

Examples

<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()

toBeVisible

toBeVisible()

This allows you to check if an element is currently visible to the user.

An element is visible if all the following conditions are met:

  • it is present in the document
  • it does not have its css property display set to none
  • it does not have its css property visibility set to either hidden or collapse
  • it does not have its css property opacity set to 0
  • its parent element is also visible (and so on up to the top of the DOM tree)
  • it does not have the hidden attribute
  • if <details /> it has the open attribute

Examples

<div data-testid="zero-opacity" style="opacity: 0">Zero Opacity Example</div>
<div data-testid="visibility-hidden" style="visibility: hidden">
  Visibility Hidden Example
</div>
<div data-testid="display-none" style="display: none">Display None Example</div>
<div style="opacity: 0">
  <span data-testid="hidden-parent">Hidden Parent Example</span>
</div>
<div data-testid="visible">Visible Example</div>
<div data-testid="hidden-attribute" hidden>Hidden Attribute Example</div>
<details>
  <summary>Title of hidden text</summary>
  Hidden Details Example
</details>
<details open>
  <summary>Title of visible text</summary>
  <div>Visible Details Example</div>
</details>
expect(getByText('Zero Opacity Example')).not.toBeVisible()
expect(getByText('Visibility Hidden Example')).not.toBeVisible()
expect(getByText('Display None Example')).not.toBeVisible()
expect(getByText('Hidden Parent Example')).not.toBeVisible()
expect(getByText('Visible Example')).toBeVisible()
expect(getByText('Hidden Attribute Example')).not.toBeVisible()
expect(getByText('Hidden Details Example')).not.toBeVisible()
expect(getByText('Visible Details Example')).toBeVisible()

toContainElement

toContainElement(element: HTMLElement | SVGElement | null)

This allows you to assert whether an element contains another element as a descendant or not.

Examples

<span data-testid="ancestor"><span data-testid="descendant"></span></span>
const ancestor = getByTestId('ancestor')
const descendant = getByTestId('descendant')
const nonExistantElement = getByTestId('does-not-exist')

expect(ancestor).toContainElement(descendant)
expect(descendant).not.toContainElement(ancestor)
expect(ancestor).not.toContainElement(nonExistantElement)

toContainHTML

toContainHTML(htmlText: string)

Assert whether a string representing a HTML element is contained in another element. The string should contain valid html, and not any incomplete html.

Examples

<span data-testid="parent"><span data-testid="child"></span></span>
// These are valid uses
expect(getByTestId('parent')).toContainHTML('<span data-testid="child"></span>')
expect(getByTestId('parent')).toContainHTML('<span data-testid="child" />')
expect(getByTestId('parent')).not.toContainHTML('<br />')

// These won't work
expect(getByTestId('parent')).toContainHTML('data-testid="child"')
expect(getByTestId('parent')).toContainHTML('data-testid')
expect(getByTestId('parent')).toContainHTML('</span>')

Chances are you probably do not need to use this matcher. We encourage testing from the perspective of how the user perceives the app in a browser. That's why testing against a specific DOM structure is not advised.

It could be useful in situations where the code being tested renders html that was obtained from an external source, and you want to validate that that html code was used as intended.

It should not be used to check DOM structure that you control. Please use toContainElement instead.


toHaveAccessibleDescription

toHaveAccessibleDescription(expectedAccessibleDescription?: string | RegExp)

This allows you to assert that an element has the expected accessible description.

You can pass the exact string of the expected accessible description, or you can make a partial match passing a regular expression, or by using expect.stringContaining/expect.stringMatching.

Examples

<a
  data-testid="link"
  href="/"
  aria-label="Home page"
  title="A link to start over"
  >Start</a
>
<a data-testid="extra-link" href="/about" aria-label="About page">About</a>
<img src="avatar.jpg" data-testid="avatar" alt="User profile pic" />
<img
  src="logo.jpg"
  data-testid="logo"
  alt="Company logo"
  aria-describedby="t1"
/>
<span id="t1" role="presentation">The logo of Our Company</span>
<img
  src="logo.jpg"
  data-testid="logo2"
  alt="Company logo"
  aria-description="The logo of Our Company"
/>
expect(getByTestId('link')).toHaveAccessibleDescription()
expect(getByTestId('link')).toHaveAccessibleDescription('A link to start over')
expect(getByTestId('link')).not.toHaveAccessibleDescription('Home page')
expect(getByTestId('extra-link')).not.toHaveAccessibleDescription()
expect(getByTestId('avatar')).not.toHaveAccessibleDescription()
expect(getByTestId('logo')).not.toHaveAccessibleDescription('Company logo')
expect(getByTestId('logo')).toHaveAccessibleDescription(
  'The logo of Our Company',
)
expect(getByTestId('logo2')).toHaveAccessibleDescription(
  'The logo of Our Company',
)

toHaveAccessibleErrorMessage

toHaveAccessibleErrorMessage(expectedAccessibleErrorMessage?: string | RegExp)

This allows you to assert that an element has the expected accessible error message.

You can pass the exact string of the expected accessible error message. Alternatively, you can perform a partial match by passing a regular expression or by using expect.stringContaining/expect.stringMatching.

Examples

<input
  aria-label="Has Error"
  aria-invalid="true"
  aria-errormessage="error-message"
/>
<div id="error-message" role="alert">This field is invalid</div>

<input aria-label="No Error Attributes" />
<input
  aria-label="Not Invalid"
  aria-invalid="false"
  aria-errormessage="error-message"
/>
// Inputs with Valid Error Messages
expect(getByRole('textbox', {name: 'Has Error'})).toHaveAccessibleErrorMessage()
expect(getByRole('textbox', {name: 'Has Error'})).toHaveAccessibleErrorMessage(
  'This field is invalid',
)
expect(getByRole('textbox', {name: 'Has Error'})).toHaveAccessibleErrorMessage(
  /invalid/i,
)
expect(
  getByRole('textbox', {name: 'Has Error'}),
).not.toHaveAccessibleErrorMessage('This field is absolutely correct!')

// Inputs without Valid Error Messages
expect(
  getByRole('textbox', {name: 'No Error Attributes'}),
).not.toHaveAccessibleErrorMessage()

expect(
  getByRole('textbox', {name: 'Not Invalid'}),
).not.toHaveAccessibleErrorMessage()

toHaveAccessibleName

toHaveAccessibleName(expectedAccessibleName?: string | RegExp)

This allows you to assert that an element has the expected accessible name. It is useful, for instance, to assert that form elements and buttons are properly labelled.

You can pass the exact string of the expected accessible name, or you can make a partial match passing a regular expression, or by using expect.stringContaining/expect.stringMatching.

Examples

<img data-testid="img-alt" src="" alt="Test alt" />
<img data-testid="img-empty-alt" src="" alt="" />
<svg data-testid="svg-title"><title>Test title</title></svg>
<button data-testid="button-img-alt"><img src="" alt="Test" /></button>
<p><img data-testid="img-paragraph" src="" alt="" /> Test content</p>
<button data-testid="svg-button"><svg><title>Test</title></svg></p>
<div><svg data-testid="svg-without-title"></svg></div>
<input data-testid="input-title" title="test" />
expect(getByTestId('img-alt')).toHaveAccessibleName('Test alt')
expect(getByTestId('img-empty-alt')).not.toHaveAccessibleName()
expect(getByTestId('svg-title')).toHaveAccessibleName('Test title')
expect(getByTestId('button-img-alt')).toHaveAccessibleName()
expect(getByTestId('img-paragraph')).not.toHaveAccessibleName()
expect(getByTestId('svg-button')).toHaveAccessibleName()
expect(getByTestId('svg-without-title')).not.toHaveAccessibleName()
expect(getByTestId('input-title')).toHaveAccessibleName()

toHaveAttribute

toHaveAttribute(attr: string, value?: any)

This allows you to check whether the given element has an attribute or not. You can also optionally check that the attribute has a specific expected value or partial match using expect.stringContaining/expect.stringMatching

Examples

<button data-testid="ok-button" type="submit" disabled>ok</button>
const button = getByTestId('ok-button')

expect(button).toHaveAttribute('disabled')
expect(button).toHaveAttribute('type', 'submit')
expect(button).not.toHaveAttribute('type', 'button')

expect(button).toHaveAttribute('type', expect.stringContaining('sub'))
expect(button).toHaveAttribute('type', expect.not.stringContaining('but'))

toHaveClass

toHaveClass(...classNames: string[], options?: {exact: boolean})

This allows you to check whether the given element has certain classes within its class attribute. You must provide at least one class, unless you are asserting that an element does not have any classes.

The list of class names may include strings and regular expressions. Regular expressions are matched against each individual class in the target element, and it is NOT matched against its full class attribute value as whole.

Examples

<button data-testid="delete-button" class="btn extra btn-danger">
  Delete item
</button>
<button data-testid="no-classes">No Classes</button>
const deleteButton = getByTestId('delete-button')
const noClasses = getByTestId('no-classes')

expect(deleteButton).toHaveClass('extra')
expect(deleteButton).toHaveClass('btn-danger btn')
expect(deleteButton).toHaveClass(/danger/, 'btn')
expect(deleteButton).toHaveClass('btn-danger', 'btn')
expect(deleteButton).not.toHaveClass('btn-link')
expect(deleteButton).not.toHaveClass(/link/)
expect(deleteButton).not.toHaveClass(/btn extra/) // It does not match

expect(deleteButton).toHaveClass('btn-danger extra btn', {exact: true}) // to check if the element has EXACTLY a set of classes
expect(deleteButton).not.toHaveClass('btn-danger extra', {exact: true}) // if it has more than expected it is going to fail

expect(noClasses).not.toHaveClass()

toHaveFocus

toHaveFocus()

This allows you to assert whether an element has focus or not.

Examples

<div><input type="text" data-testid="element-to-focus" /></div>
const input = getByTestId('element-to-focus')

input.focus()
expect(input).toHaveFocus()

input.blur()
expect(input).not.toHaveFocus()

toHaveFormValues

toHaveFormValues(expectedValues: {
  [name: string]: any
})

This allows you to check if a form or fieldset contains form controls for each given name, and having the specified value.

It is important to stress that this matcher can only be invoked on a form or a fieldset element.

This allows it to take advantage of the .elements property in form and fieldset to reliably fetch all form controls within them.

This also avoids the possibility that users provide a container that contains more than one form, thereby intermixing form controls that are not related, and could even conflict with one another.

This matcher abstracts away the particularities with which a form control value is obtained depending on the type of form control. For instance, <input> elements have a value attribute, but <select> elements do not. Here's a list of all cases covered:

  • <input type="number"> elements return the value as a number, instead of a string.
  • <input type="checkbox"> elements:
    • if there's a single one with the given name attribute, it is treated as a boolean, returning true if the checkbox is checked, false if unchecked.
    • if there's more than one checkbox with the same name attribute, they are all treated collectively as a single form control, which returns the value as an array containing all the values of the selected checkboxes in the collection.
  • <input type="radio"> elements are all grouped by the name attribute, and such a group treated as a single form control. This form control returns the value as a string corresponding to the value attribute of the selected radio button within the group.
  • <input type="text"> elements return the value as a string. This also applies to <input> elements having any other possible type attribute that's not explicitly covered in different rules above (e.g. search, email, date, password, hidden, etc.)
  • <select> elements without the multiple attribute return the value as a string corresponding to the value attribute of the selected option, or undefined if there's no selected option.
  • <select multiple> elements return the value as an array containing all the values of the selected options.
  • <textarea> elements return their value as a string. The value corresponds to their node content.

The above rules make it easy, for instance, to switch from using a single select control to using a group of radio buttons. Or to switch from a multi select control, to using a group of checkboxes. The resulting set of form values used by this matcher to compare against would be the same.

Examples

<form data-testid="login-form">
  <input type="text" name="username" value="jane.doe" />
  <input type="password" name="password" value="12345678" />
  <input type="checkbox" name="rememberMe" checked />
  <button type="submit">Sign in</button>
</form>
expect(getByTestId('login-form')).toHaveFormValues({
  username: 'jane.doe',
  rememberMe: true,
})

toHaveStyle

toHaveStyle(css: string | object)

This allows you to check if a certain element has some specific css properties with specific values applied. It matches only if the element has all the expected properties applied, not just some of them.

Examples

<button
  data-testid="delete-button"
  style="display: none; background-color: red"
>
  Delete item
</button>
const button = getByTestId('delete-button')

expect(button).toHaveStyle('display: none')
expect(button).toHaveStyle({display: 'none'})
expect(button).toHaveStyle(`
  background-color: red;
  display: none;
`)
expect(button).toHaveStyle({
  backgroundColor: 'red',
  display: 'none',
})
expect(button).not.toHaveStyle(`
  background-color: blue;
  display: none;
`)
expect(button).not.toHaveStyle({
  backgroundColor: 'blue',
  display: 'none',
})

This also works with rules that are applied to the element via a class name for which some rules are defined in a stylesheet currently active in the document. The usual rules of css precedence apply.


toHaveTextContent

toHaveTextContent(text: string | RegExp, options?: {normalizeWhitespace: boolean})

This allows you to check whether the given node has a text content or not. This supports elements, but also text nodes and fragments.

When a string argument is passed through, it will perform a partial case-sensitive match to the node content.

To perform a case-insensitive match, you can use a RegExp with the /i modifier.

If you want to match the whole content, you can use a RegExp to do it.

Examples

<span data-testid="text-content">Text Content</span>
const element = getByTestId('text-content')

expect(element).toHaveTextContent('Content')
expect(element).toHaveTextContent(/^Text Content$/) // to match the whole content
expect(element).toHaveTextContent(/content$/i) // to use case-insensitive match
expect(element).not.toHaveTextContent('content')

toHaveValue

toHaveValue(value: string | string[] | number)

This allows you to check whether the given form element has the specified value. It accepts <input>, <select> and <textarea> elements with the exception of <input type="checkbox"> and <input type="radio">, which can be meaningfully matched only using toBeChecked or toHaveFormValues.

For all other form elements, the value is matched using the same algorithm as in toHaveFormValues does.

Examples

<input type="text" value="text" data-testid="input-text" />
<input type="number" value="5" data-testid="input-number" />
<input type="text" data-testid="input-empty" />
<select multiple data-testid="select-number">
  <option value="first">First Value</option>
  <option value="second" selected>Second Value</option>
  <option value="third" selected>Third Value</option>
</select>
Using DOM Testing Library
const textInput = getByTestId('input-text')
const numberInput = getByTestId('input-number')
const emptyInput = getByTestId('input-empty')
const selectInput = getByTestId('select-number')

expect(textInput).toHaveValue('text')
expect(numberInput).toHaveValue(5)
expect(emptyInput).not.toHaveValue()
expect(selectInput).toHaveValue(['second', 'third'])

toHaveDisplayValue

toHaveDisplayValue(value: string | RegExp | (string|RegExp)[])

This allows you to check whether the given form element has the specified displayed value (the one the end user will see). It accepts <input>, <select> and <textarea> elements with the exception of <input type="checkbox"> and <input type="radio">, which can be meaningfully matched only using toBeChecked or toHaveFormValues.

Examples

<label for="input-example">First name</label>
<input type="text" id="input-example" value="Luca" />

<label for="textarea-example">Description</label>
<textarea id="textarea-example">An example description here.</textarea>

<label for="single-select-example">Fruit</label>
<select id="single-select-example">
  <option value="">Select a fruit...</option>
  <option value="banana">Banana</option>
  <option value="ananas">Ananas</option>
  <option value="avocado">Avocado</option>
</select>

<label for="multiple-select-example">Fruits</label>
<select id="multiple-select-example" multiple>
  <option value="">Select a fruit...</option>
  <option value="banana" selected>Banana</option>
  <option value="ananas">Ananas</option>
  <option value="avocado" selected>Avocado</option>
</select>
Using DOM Testing Library
const input = screen.getByLabelText('First name')
const textarea = screen.getByLabelText('Description')
const selectSingle = screen.getByLabelText('Fruit')
const selectMultiple = screen.getByLabelText('Fruits')

expect(input).toHaveDisplayValue('Luca')
expect(input).toHaveDisplayValue(/Luc/)
expect(textarea).toHaveDisplayValue('An example description here.')
expect(textarea).toHaveDisplayValue(/example/)
expect(selectSingle).toHaveDisplayValue('Select a fruit...')
expect(selectSingle).toHaveDisplayValue(/Select/)
expect(selectMultiple).toHaveDisplayValue([/Avocado/, 'Banana'])

toBeChecked

toBeChecked()

This allows you to check whether the given element is checked. It accepts an input of type checkbox or radio and elements with a role of checkbox, radio or switch with a valid aria-checked attribute of "true" or "false".

Examples

<input type="checkbox" checked data-testid="input-checkbox-checked" />
<input type="checkbox" data-testid="input-checkbox-unchecked" />
<div role="checkbox" aria-checked="true" data-testid="aria-checkbox-checked" />
<div
  role="checkbox"
  aria-checked="false"
  data-testid="aria-checkbox-unchecked"
/>

<input type="radio" checked value="foo" data-testid="input-radio-checked" />
<input type="radio" value="foo" data-testid="input-radio-unchecked" />
<div role="radio" aria-checked="true" data-testid="aria-radio-checked" />
<div role="radio" aria-checked="false" data-testid="aria-radio-unchecked" />
<div role="switch" aria-checked="true" data-testid="aria-switch-checked" />
<div role="switch" aria-checked="false" data-testid="aria-switch-unchecked" />
const inputCheckboxChecked = getByTestId('input-checkbox-checked')
const inputCheckboxUnchecked = getByTestId('input-checkbox-unchecked')
const ariaCheckboxChecked = getByTestId('aria-checkbox-checked')
const ariaCheckboxUnchecked = getByTestId('aria-checkbox-unchecked')
expect(inputCheckboxChecked).toBeChecked()
expect(inputCheckboxUnchecked).not.toBeChecked()
expect(ariaCheckboxChecked).toBeChecked()
expect(ariaCheckboxUnchecked).not.toBeChecked()

const inputRadioChecked = getByTestId('input-radio-checked')
const inputRadioUnchecked = getByTestId('input-radio-unchecked')
const ariaRadioChecked = getByTestId('aria-radio-checked')
const ariaRadioUnchecked = getByTestId('aria-radio-unchecked')
expect(inputRadioChecked).toBeChecked()
expect(inputRadioUnchecked).not.toBeChecked()
expect(ariaRadioChecked).toBeChecked()
expect(ariaRadioUnchecked).not.toBeChecked()

const ariaSwitchChecked = getByTestId('aria-switch-checked')
const ariaSwitchUnchecked = getByTestId('aria-switch-unchecked')
expect(ariaSwitchChecked).toBeChecked()
expect(ariaSwitchUnchecked).not.toBeChecked()

toBePartiallyChecked

toBePartiallyChecked()

This allows you to check whether the given element is partially checked. It accepts an input of type checkbox and elements with a role of checkbox with a aria-checked="mixed", or input of type checkbox with indeterminate set to true

Examples

<input type="checkbox" aria-checked="mixed" data-testid="aria-checkbox-mixed" />
<input type="checkbox" checked data-testid="input-checkbox-checked" />
<input type="checkbox" data-testid="input-checkbox-unchecked" />
<div role="checkbox" aria-checked="true" data-testid="aria-checkbox-checked" />
<div
  role="checkbox"
  aria-checked="false"
  data-testid="aria-checkbox-unchecked"
/>
<input type="checkbox" data-testid="input-checkbox-indeterminate" />
const ariaCheckboxMixed = getByTestId('aria-checkbox-mixed')
const inputCheckboxChecked = getByTestId('input-checkbox-checked')
const inputCheckboxUnchecked = getByTestId('input-checkbox-unchecked')
const ariaCheckboxChecked = getByTestId('aria-checkbox-checked')
const ariaCheckboxUnchecked = getByTestId('aria-checkbox-unchecked')
const inputCheckboxIndeterminate = getByTestId('input-checkbox-indeterminate')

expect(ariaCheckboxMixed).toBePartiallyChecked()
expect(inputCheckboxChecked).not.toBePartiallyChecked()
expect(inputCheckboxUnchecked).not.toBePartiallyChecked()
expect(ariaCheckboxChecked).not.toBePartiallyChecked()
expect(ariaCheckboxUnchecked).not.toBePartiallyChecked()

inputCheckboxIndeterminate.indeterminate = true
expect(inputCheckboxIndeterminate).toBePartiallyChecked()

toHaveRole

This allows you to assert that an element has the expected role.

This is useful in cases where you already have access to an element via some query other than the role itself, and want to make additional assertions regarding its accessibility.

The role can match either an explicit role (via the role attribute), or an implicit one via the implicit ARIA semantics.

Note: roles are matched literally by string equality, without inheriting from the ARIA role hierarchy. As a result, querying a superclass role like 'checkbox' will not include elements with a subclass role like 'switch'.

toHaveRole(expectedRole: string)
<button data-testid="button">Continue</button>
<div role="button" data-testid="button-explicit">Continue</button>
<button role="switch button" data-testid="button-explicit-multiple">Continue</button>
<a href="/about" data-testid="link">About</a>
<a data-testid="link-invalid">Invalid link<a/>
expect(getByTestId('button')).toHaveRole('button')
expect(getByTestId('button-explicit')).toHaveRole('button')
expect(getByTestId('button-explicit-multiple')).toHaveRole('button')
expect(getByTestId('button-explicit-multiple')).toHaveRole('switch')
expect(getByTestId('link')).toHaveRole('link')
expect(getByTestId('link-invalid')).not.toHaveRole('link')
expect(getByTestId('link-invalid')).toHaveRole('generic')

toHaveErrorMessage

This custom matcher is deprecated. Prefer toHaveAccessibleErrorMessage instead, which is more comprehensive in implementing the official spec.

toHaveErrorMessage(text: string | RegExp)

This allows you to check whether the given element has an ARIA error message or not.

Use the aria-errormessage attribute to reference another element that contains custom error message text. Multiple ids is NOT allowed. Authors MUST use aria-invalid in conjunction with aria-errormessage. Learn more from aria-errormessage spec.

Whitespace is normalized.

When a string argument is passed through, it will perform a whole case-sensitive match to the error message text.

To perform a case-insensitive match, you can use a RegExp with the /i modifier.

To perform a partial match, you can pass a RegExp or use expect.stringContaining("partial string").

Examples

<label for="startTime"> Please enter a start time for the meeting: </label>
<input
  id="startTime"
  type="text"
  aria-errormessage="msgID"
  aria-invalid="true"
  value="11:30 PM"
/>
<span id="msgID" aria-live="assertive" style="visibility:visible">
  Invalid time: the time must be between 9:00 AM and 5:00 PM
</span>
const timeInput = getByLabel('startTime')

expect(timeInput).toHaveErrorMessage(
  'Invalid time: the time must be between 9:00 AM and 5:00 PM',
)
expect(timeInput).toHaveErrorMessage(/invalid time/i) // to partially match
expect(timeInput).toHaveErrorMessage(expect.stringContaining('Invalid time')) // to partially match
expect(timeInput).not.toHaveErrorMessage('Pikachu!')

Deprecated matchers

toBeEmpty

Note: This matcher is being deprecated due to a name clash with jest-extended. See more info in #216. In the future, please use only toBeEmptyDOMElement

toBeEmpty()

This allows you to assert whether an element has content or not.

Examples

<span data-testid="not-empty"><span data-testid="empty"></span></span>
expect(getByTestId('empty')).toBeEmpty()
expect(getByTestId('not-empty')).not.toBeEmpty()

toBeInTheDOM

This custom matcher is deprecated. Prefer toBeInTheDocument instead.

toBeInTheDOM()

This allows you to check whether a value is a DOM element, or not.

Contrary to what its name implies, this matcher only checks that you passed to it a valid DOM element. It does not have a clear definition of what "the DOM" is. Therefore, it does not check whether that element is contained anywhere.

This is the main reason why this matcher is deprecated, and will be removed in the next major release. You can follow the discussion around this decision in more detail here.

As an alternative, you can use toBeInTheDocument or toContainElement. Or if you just want to check if a value is indeed an HTMLElement you can always use some of jest's built-in matchers:

expect(document.querySelector('.ok-button')).toBeInstanceOf(HTMLElement)
expect(document.querySelector('.cancel-button')).toBeTruthy()

Note: The differences between toBeInTheDOM and toBeInTheDocument are significant. Replacing all uses of toBeInTheDOM with toBeInTheDocument will likely cause unintended consequences in your tests. Please make sure when replacing toBeInTheDOM to read through the documentation of the proposed alternatives to see which use case works better for your needs.


toHaveDescription

This custom matcher is deprecated. Prefer toHaveAccessibleDescription instead, which is more comprehensive in implementing the official spec.

toHaveDescription(text: string | RegExp)

This allows you to check whether the given element has a description or not.

An element gets its description via the aria-describedby attribute. Set this to the id of one or more other elements. These elements may be nested inside, be outside, or a sibling of the passed in element.

Whitespace is normalized. Using multiple ids will join the referenced elements’ text content separated by a space.

When a string argument is passed through, it will perform a whole case-sensitive match to the description text.

To perform a case-insensitive match, you can use a RegExp with the /i modifier.

To perform a partial match, you can pass a RegExp or use expect.stringContaining("partial string").

Examples

<button aria-label="Close" aria-describedby="description-close">X</button>
<div id="description-close">Closing will discard any changes</div>

<button>Delete</button>
const closeButton = getByRole('button', {name: 'Close'})

expect(closeButton).toHaveDescription('Closing will discard any changes')
expect(closeButton).toHaveDescription(/will discard/) // to partially match
expect(closeButton).toHaveDescription(expect.stringContaining('will discard')) // to partially match
expect(closeButton).toHaveDescription(/^closing/i) // to use case-insensitive match
expect(closeButton).not.toHaveDescription('Other description')

const deleteButton = getByRole('button', {name: 'Delete'})
expect(deleteButton).not.toHaveDescription()
expect(deleteButton).toHaveDescription('') // Missing or empty description always becomes a blank string

Inspiration

This whole library was extracted out of Kent C. Dodds' DOM Testing Library, which was in turn extracted out of React Testing Library.

The intention is to make this available to be used independently of these other libraries, and also to make it more clear that these other libraries are independent from jest, and can be used with other tests runners as well.

Other Solutions

I'm not aware of any, if you are please make a pull request and add it here!

If you would like to further test the accessibility and validity of the DOM consider jest-axe. It doesn't overlap with jest-dom but can complement it for more in-depth accessibility checking (eg: validating aria attributes or ensuring unique id attributes).

Guiding Principles

The more your tests resemble the way your software is used, the more confidence they can give you.

This library follows the same guiding principles as its mother library DOM Testing Library. Go check them out for more details.

Additionally, with respect to custom DOM matchers, this library aims to maintain a minimal but useful set of them, while avoiding bloating itself with merely convenient ones that can be easily achieved with other APIs. In general, the overall criteria for what is considered a useful custom matcher to add to this library, is that doing the equivalent assertion on our own makes the test code more verbose, less clear in its intent, and/or harder to read.

Contributors

Thanks goes to these people (emoji key):

Kent C. Dodds
Kent C. Dodds

πŸ’» πŸ“– πŸš‡ ⚠️
Ryan Castner
Ryan Castner

πŸ“–
Daniel Sandiego
Daniel Sandiego

πŸ’»
PaweΕ‚ MikoΕ‚ajczyk
PaweΕ‚ MikoΕ‚ajczyk

πŸ’»
Alejandro ÑÑñez Ortiz
Alejandro ÑÑñez Ortiz

πŸ“–
Matt Parrish
Matt Parrish

πŸ› πŸ’» πŸ“– ⚠️
Justin Hall
Justin Hall

πŸ“¦
Anto Aravinth
Anto Aravinth

πŸ’» ⚠️ πŸ“–
Jonah Moses
Jonah Moses

πŸ“–
Łukasz Gandecki
Łukasz Gandecki

πŸ’» ⚠️ πŸ“–
Ivan Babak
Ivan Babak

πŸ› πŸ€”
Jesse Day
Jesse Day

πŸ’»
Ernesto GarcΓ­a
Ernesto GarcΓ­a

πŸ’» πŸ“– ⚠️
Mark Volkmann
Mark Volkmann

πŸ› πŸ’»
smacpherson64
smacpherson64

πŸ’» πŸ“– ⚠️
John Gozde
John Gozde

πŸ› πŸ’»
Iwona
Iwona

πŸ’» πŸ“– ⚠️
Lewis
Lewis

πŸ’»
Leandro Lourenci
Leandro Lourenci

πŸ› πŸ“– πŸ’» ⚠️
Shukhrat Mukimov
Shukhrat Mukimov

πŸ›
Roman Usherenko
Roman Usherenko

πŸ’» ⚠️
Joe Hsu
Joe Hsu

πŸ“–
Haz
Haz

πŸ› πŸ’»
Revath S Kumar
Revath S Kumar

πŸ’»
hiwelo.
hiwelo.

πŸ’» πŸ€” ⚠️
Łukasz Fiszer
Łukasz Fiszer

πŸ’»
Jean Chung
Jean Chung

πŸ’» ⚠️
CarlaTeo
CarlaTeo

πŸ’» ⚠️
Yarden Shoham
Yarden Shoham

πŸ“–
Jaga Santagostino
Jaga Santagostino

πŸ› ⚠️ πŸ“–
Connor Meredith
Connor Meredith

πŸ’» ⚠️ πŸ“–
Pawel Wolak
Pawel Wolak

⚠️
MichaΓ«l De Boey
MichaΓ«l De Boey

πŸš‡
Jānis Zaržeckis
Jānis Zaržeckis

πŸ“–
koala-lava
koala-lava

πŸ“–
Juan Pablo Blanco
Juan Pablo Blanco

πŸ“–
Ben Monro
Ben Monro

πŸ“–
Jeff Bernstein
Jeff Bernstein

πŸ“–
Sergi
Sergi

πŸ’» ⚠️
Spencer Miskoviak
Spencer Miskoviak

πŸ“–
Jon Rimmer
Jon Rimmer

πŸ’» ⚠️
Luca Barone
Luca Barone

πŸ’» ⚠️ πŸ€”
Malte Felmy
Malte Felmy

πŸ’» ⚠️
Championrunner
Championrunner

πŸ“–
Patrick Smith
Patrick Smith

πŸ’» ⚠️ πŸ“–
RubΓ©n Moya
RubΓ©n Moya

πŸ’» ⚠️ πŸ“–
Daniela Valero
Daniela Valero

πŸ’» ⚠️ πŸ“–
Vladislav Katsura
Vladislav Katsura

πŸ’» ⚠️
Tim Fischbach
Tim Fischbach

πŸ’» ⚠️ πŸ€”
Katie Boedges
Katie Boedges

πŸš‡
Brian Alexis
Brian Alexis

⚠️
Boris Serdiuk
Boris Serdiuk

πŸ› πŸ’» ⚠️
Dana Woodman
Dana Woodman

πŸ“–
Mo Sattler
Mo Sattler

πŸ“–
Geoff Rich
Geoff Rich

πŸ’» ⚠️ πŸ€” πŸ›
Syneva
Syneva

πŸ’»
Nick McCurdy
Nick McCurdy

πŸ“– πŸ› πŸ’»
Obed Marquez Parlapiano
Obed Marquez Parlapiano

πŸ“–
Caleb Eby
Caleb Eby

πŸ“– πŸ’» ⚠️
Marcel Barner
Marcel Barner

πŸ’» ⚠️
Doma
Doma

πŸ’» ⚠️
Julien Wajsberg
Julien Wajsberg

πŸ’» ⚠️
steven nguyen
steven nguyen

πŸ“–
tu4mo
tu4mo

πŸ“–
Matan Borenkraout
Matan Borenkraout

πŸ“¦
Yann Braga
Yann Braga

πŸ’»
Ian VanSchooten
Ian VanSchooten

πŸ’»
Chantal Broeren
Chantal Broeren

πŸ“–
JΓ©rΓ©mie Astori
JΓ©rΓ©mie Astori

πŸ’» πŸ€”
Ashley Ryan
Ashley Ryan

πŸ’» πŸ€”
Fotis Papadogeorgopoulos
Fotis Papadogeorgopoulos

πŸ’» πŸ“– ⚠️
Jake Boone
Jake Boone

πŸ’» ⚠️
Stephan KΓΆninger
Stephan KΓΆninger

πŸ› πŸ’»
Michael Manzinger
Michael Manzinger

πŸ› πŸ’» ⚠️
Dennis Chen
Dennis Chen

πŸ’»
Tony Hallett
Tony Hallett

πŸ›
David DOLCIMASCOLO
David DOLCIMASCOLO

🚧
Aleksandr Elkin
Aleksandr Elkin

🚧
Mordechai Dror
Mordechai Dror

πŸ’»

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

MIT

jest-dom's People

Contributors

alexkrolick avatar allcontributors[bot] avatar brrianalexis avatar calebeby avatar connormeredith avatar dreyks avatar eps1lon avatar fpapado avatar gnapse avatar hiwelo avatar ianvs avatar jakeboone02 avatar jgoz avatar just-boris avatar kandros avatar lourenci avatar lwojcik avatar matanbobi avatar michaeldeboey avatar mufasa71 avatar nickserv avatar r-walsh avatar re-taro avatar renatoagds avatar revathskumar avatar rubenmoya avatar sergicl avatar sheerun avatar simenb avatar smacpherson64 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jest-dom's Issues

Scoped package?

No pressure. I'm just wondering whether you've thought about moving to the scoped package format like the rest of the testing-library family of tools.

Deprecate toBeInTheDOM in favor of toContainElement

Describe the feature you'd like:

It's been previously noted that toBeInTheDOM does not really perform the check that its name suggests (testing-library/dom-testing-library#9, #3). This was partially addressed in #25, where also toContainElement was introduced.

However, toBeInTheDOM continues to feel not right, at least for me. It now has two different behaviors: if it receives an element as argument, it'll check that the expect argument is contained within that extra element. If it does not receive an extra element as argument, it behaves as a glorified .toBeInstanceOf(HTMLElement), merely checking that the received expect argument is a DOM element, regardless of whether is contained anywhere.

Suggested implementation:

I propose that we remove toBeInTheDOM, maybe with a deprecation period.

Describe alternatives you've considered:

At the very least, we should update the documentation in the README about it. This current intro to this matcher is not accurate about what it does:

This allows you to assert whether an element present in the DOM container or not. If no DOM container is specified it will use the default DOM context.

There's no default DOM context (whatever that means). This matcher's only task when no DOM container is specified, is to assert that the given element is indeed an HTML (or SVG since recently) element.

Teachability, Documentation, Adoption, Migration Strategy:

We would need to update the documentation and provide users with alternatives moving forward:

One recommendation is to replace current uses with toContainElement.

Also, if users just want to check if an element exist, they can assert that what they have is not null or undefined. For instance, using dom-testing-library queries and regular jest matchers:

expect(queryByTestId('ok-button')).toBeTruthy();
expect(queryByTestId('ok-button')).toBeInstanceOf(HTMLElement);

Also, this would be a breaking change, so a major version bump is involved and users would have to explicitly update to get the change and be really forced to replace its uses.

toBeEmpty yields false positives with self closing tags

  • jest-dom version: 3.1.2
  • node version: 10.15.0
  • npm (or yarn) version: 6.4.1 and 1.13.0

Relevant code or config

expect(<div />).toBeEmpty(); // Fails

What you did:

Tried asserting that a React component would be empty based on certain props.

What happened:

Test fails with the following message:

expect(received).toBeEmpty()

Expected value to be empty received:
    <div />

Reproduction repository:

Problem description:

Suggested solution:

Not really sure but I’m open to looking into it.

New custom matchers proposal

After a quick scan of the custom matchers provided in chai-dom I think there are many in there that I consider to be just for convenience and I do not favor adding them for the moment (for instance "to have id" and "to have value" are just a minor convenience over simply using .toHaveAttribute).

However, there are a few that I think could be good additions to this library. I think the overall criteria for what is considered a good addition is that doing the equivalent assertion on our own makes the test code more verbose and/or harder to read.

Here are some of the ones I see as convenient to add (all up for discussion):

  • expect(element).toBeEmpty(): the element exists but it contains no child elements. Not sure if that name is too generic outside of the scope of dom matchers. Also not sure if it's really just a convenience over a simple alternative with vanilla DOM APIs.
  • expect(element).toBeDisplayed(): asserts that display is not equal to "none".
  • expect(element).toBeVisible(): asserts that visibility is not "hidden" or "collapsed".

Additionally I wonder if instead of the last two, we could get away with a single .toHaveStyle that would work similar to .toHaveAttribute (e.g. .toHaveStyle('display', 'none'), .not.toHaveStyle('visibility', 'hidden'))

toHaveStyle inconsistency with colors

  • jest-dom version: 1.11.0
  • node version: 8.11.3
  • react-testing-library version: 4.1.4

Relevant code or config:

const { getByText } = render(
  <span style={{ backgroundColor: '#123456' }}>Hello World</span>
);
expect(getByText('Hello World')).toHaveStyle('background-color: #123456');
// The above assertion failed

What you did:

I rendered an element with a specific hex background color in its styles.

What happened:

I can't assert back that the background-color style is indeed there. This is because the actual styles I get back from the element give me the color in the rgb(r, g, b) format:

    expect(element).toHaveStyle()

    - Expected

    - background-color: #123456;
    + background-color: rgb(18, 52, 86);

Problem description:

The problem is that users are forced to use the rgb() format.

Suggested solution:

It'd be nice if toHaveStyle would convert hex colors to the rgb(r, g, b) format so this is transparent for users. Otherwise document the caveat and maybe provide the user with some alternative or direct them to a library they can use to make this conversion, and that they make sure they always use rgb(r, g, b) either directly or by converting their hex value dynamically themselves.

toContainHTML failure output is not useful

  • jest-dom version: 3.1.3
  • node version: v10.11.0
  • npm (or yarn) version: 6.9.0

Relevant code or config:

What you did:

const container = render(<SubmitButton>)
expect(container).toContainHtml('color: blue')

What happened:

 2019-04-25 at 3 27 53 PM

Reproduction:

Problem description:

The output failure message does not contain any useful information for tracking down the reason why it failed.

I have to convert it to something like expect(container.innerHTML).toContain('color: blue') to be able to debug it, making the custom matcher less efficient than just using the simpler builtin option.

Note, I saw theres a style matcher that could be used in this specific test, however the problem applies to any call to this matcher.

Suggested solution:

Pretty sure it just needs to change to cloneNode(true) at https://github.com/testing-library/jest-dom/blob/master/src/to-contain-html.js#L14 for it to print out all the nodes.

That fix would make it debuggable. To make it better than the output of toContain() it could be pretty-printed as indented and syntax highlighted html πŸ˜ƒ - any chance that has been considered or used by other matchers?

Here is the toContain() output for reference. You can imagine its much harder to debug a larger dom node

 2019-04-25 at 3 40 59 PM

.toBeVisible should consider "hidden" attribute

Describe the feature you'd like:

expect(element).toBeVisible() should return false if element.hasAttribute('hidden') unless it's been explicitly shown with styles (display && display !== 'none' for example).

Suggested implementation:

Add && !element.hasAttribute('hidden') here, plus a check on display:
https://github.com/gnapse/jest-dom/blob/238cc91bfaffa9dd5baed1b5a02d060909f7c30c/src/to-be-visible.js#L17-L22

Describe alternatives you've considered:

I'm using .toHaveAttribute("hidden"), but I feel like I'm testing an implementation detail.

Descriptively assert that a form input/select/textarea has a given value

The problem

<select> elements, unlike input elements, do not have a value attribute. So there's easy no natural way to assert that a select element has a certain option selected. One would have to traverse through the options to see which one is selected.

To make it even worse, React makes it look like so, to the point that I got confused myself recently, when I spent more minutes that I would've liked trying to figure out why the following test (using react-testing-library) did not pass:

const { getByTitle } = render(
  <select title="Client status" value="archived">
    <option value="active">Active</option>
    <option value="archived">Archived</option>
    <option value="pending">Pending</option>
  </select>
);

expect(getByTitle('Client status')).toHaveAttribute('value', 'archived')

And to continue to make it even worse, the overall task may become more complex if using options grouped in <optgroup>s, or with multi-select elements.

Describe the feature you'd like

I started this issue thinking of suggesting a specific matcher to check a selected option in select elements. But while describing all of the above, I realize that it may be better to provide some generic way to check the value of form elements, regardless of them being inputs, textareas or selects (are there any other ones?).

Suggested implementation:

It could be providing assertions to work at the actual form input element level:

expect(userInputElement).toHaveValue('janedoe');
expect(passwordInputElement).toHaveValue('12345678');

or even at the form element level (identifying an input/select/textarea element by its name attribute):

expect(formElement).toHaveValue('username', 'janedoe');
expect(formElement).toHaveValue('password', '12345678');

It could be both use cases with the same matcher! And it could be created so that it would understand inputs, selects and textures (which are also different, their value is their child text content).

It could even be so that <select multiple> elements' values are arrays, and users could check at once the entire multiple selection (and maybe even part of it too??)

Describe alternatives you've considered:

select elements probably have some property at the dom level to check their value, instead of it being an attribute. I did not google for it, and do not have it at the top of my head, but if so, then expect(selectElement.value).toEqual(...) could help, but still it's not a generic solution for all kinds of form elements (or is it?)

dom-testing-library recently added the ability to getBySelectText, that allows users to select a select (pun not intended) by the text content of the option element within that select that is currently selected. This certainly helps, but it still leaves a weird not-so-expressive way to assert that a given select has a certain value selected:

expect(getByLabel('Client status')).toBe(getBySelectText('Archived'))

And even then, what happens if there are two selects that happen to have an option selected, both with the same text "Archived"?

And still this only addresses the problem with select elements.

Thoughts?

New toBeEnabled() matcher

Describe the feature you'd like:

I'd like a matcher to check if a specific input element is enabled.

Describe alternatives you've considered:

Today, I check for the input being enable using the not.toBeDisabled() matcher. I think It would be better for the read/clarity of the test, I be able to write .toBeEnabled().

Discussion - extending matchers to handle NodeList

Describe the feature:

Would it make sense to extend the current matchers to handle NodeLists? Is there a need for this? I was thinking about queryAllBy* and querySelectorAll and wondering if it would be helpful to pass them directly to the matchers.

For example:

expect(queryAllByText('text')).toBeVisible();
expect(document.querySelectorAll('button')).toBeDisabled();

If all elements match the matcher, then it passes. If any of the elements do not match then it fails.

toHaveFormValues is not working on fieldsets

  • jest-dom version: 3.1.2
  • node version: v8.11.4
  • npm version: 5.6.0

Relevant code or config:

test('toHaveFormValues', () => {
  const { getByTestId } = render(
    <form data-testid="login-form">
      <fieldset data-testid="login-fieldset">
        <input type="text" name="username" value="jane.doe" />
        <input type="password" name="password" value="12345678" />
        <input type="checkbox" name="rememberMe" checked />
        <button type="submit">Sign in</button>
      </fieldset>
    </form>
  );

  const form = getByTestId('login-form') as HTMLFormElement;
  const fieldset = getByTestId('login-fieldset') as HTMLFieldSetElement;

  // passes
  expect(form).toHaveFormValues({
    username: 'jane.doe'
  });

  // fails with "toHaveFormValues must be called on a form or a fieldset"
  expect(fieldset).toHaveFormValues({
    username: 'jane.doe'
  });
});

What you did:

I tried to check form values with toHaveFormValues on a fieldset.

What happened:

The test fails "toHaveFormValues must be called on a form or a fieldset". It works well when running the assertions on the form element, though.

Reproduction:

Run the test.

Add .toBeInvalid and .toBeRequired

Describe the feature you'd like:

I was wondering if we could add toBeRequired and toBeInvalid as tests based on the same logic as toBeDisabled.

The toBeRequired test could be based on either having:

  • a required attribute;
  • an aria-required attribute.

The toBeInvalid test could be based on having:

  • an aria-invalid attribute.

For accessibility reasons, I am writing a lot of tests using these requirements and it would be way cleaner if I could use the same kind of syntax as toBeDisabled.
What are your thoughts about that?

Describe alternatives you've considered:

At the moment, I am using .toHaveAttribute('aria-invalid') or similar.

HtmlElementTypeError for custom jsdom instance

  • jest-dom version: 1.11.0
  • node version: 10.4.1
  • npm (or yarn) version: yarn 1.7.0

Relevant code or config:

describe('Index', () => {
  it('Suggests cities to search by', async () => {
    const { window } = await next.render('/')
    expect(window.document).toHaveTextContent('Warszawa')
  })
})

next.render is a helper I've created that returns custom jsdom instance

What you did:

Tried to use toHaveTextContent matcher with custom jsdom instance

What happened:

Error is thrown:

    received value must be an HTMLElement or an SVGElement.
    Received:
      object:

Reproduction:

As above

Problem description:

Problem description:

It seems you throw HtmlElementTypeError with htmlElement instanceof HTMLElement but the element that I pass has separate of HTMLElement created along separate window object.

See:

const { window } = new JSDOM(...)
console.log(window.document instanceof Document) # => false
console.log(window.document instanceof window.Document) # => true

Suggested solution:

  • Search for Document class in scope of passed container?
  • Use constructor.name, __proto__.constructor.name, __proto__.__proto__.constructor.name etc. instead of instanceof?

Accessibility-related matchers

Describe the feature you'd like:

In the light of projects like Reach UI and react-beautiful-dnd and others like them, that have a focus on accessibility, I was thinking that it could be useful to test that stuff in the Dom have some features that help with accessibility.

I'm not sure though, if it'd be enough to test for aria- attributes with toHaveAttribute, or if there's something else we could do with custom matchers that would help even more. It does not help that I my self am fairly new to being aware and worried about a11y in my apps (shame on me!) but I'm catching up, and wanted to raise the issue here in hope for others to chime in and determine if there could be something jest-dom could do about it.

Suggested implementation:

Not sure yet. Not even sure if it's needed and kind of matchers we would need.

Describe alternatives you've considered:

Not sure of any. If you know of any, let me know in the comments.

Teachability, Documentation, Adoption, Migration Strategy:

TODO

toBeInTheDocument and toContainElement throws error if element is null

  • jest-dom version: 1.10.0
  • node version: 10.5.0
  • npm (or yarn) version: 6.2.0

Relevant code or config:

// downshift/src/__tests__/portal-support.js
const {getByTestId, queryByTestId} = render(
  <MyPortalAutocomplete />,
)
expect(queryByTestId('menu')).not.toBeInTheDocument()
getByTestId('button').click()
expect(getByTestId('menu')).toBeInTheDocument()

What you did:

Tried replacing toBeInDOM with any of the new suggested ways (toBeInTheDocument/ toContainElement )

What happened:

Both throws an error then the element is null

Reproduction:

↑ See relevant code or the Downshift repo https://github.com/paypal/downshift/blob/master/src/__tests__/portal-support.js#L57-L60

Problem description:

As far as I can tell there is no way to replace the current usage of toBeInTheDOM with the other functions. But I might be missing something?

Suggested solution:

I think they should accept falsy (or at least null) values and just return false if that's the case

toHaveTextContent should ignore whitespace

  • jest-dom version: 1.0.0

What you did:

//  <span>
//    Step
//      1
//    of 4
//  </span>

expect(container.querySelector('span')).toHaveTextContent('Step 1 of 4')

What happened:

    expect(element).toHaveTextContent()

    Expected element to have text content:
      Step 1 of 4
    Received:

          Step
            1
          of 4

Problem description:

This was initially reported in testing-library/react-testing-library#53, and it was really about a bug in testing-library/dom-testing-library#19. However, this also applies to a piece of code in jest-dom. The custom matchet .toHaveTextContent should also ignore a node's whitespace in its text content before determining if the text matches the expectation.

Suggested solution:

Normalize the text of a node before using to match it with the expected text. Similar to what was done in testing-library/dom-testing-library#19

toBeInTheDOM name is misleading about what it really does

As reported by @sompylasar in testing-library/dom-testing-library#9 .toBeInTheDOM is not really checking for what it name implies (because it does not have the concept of what "the DOM" is to being with). Right now is just a glorified .toBeInstanceOfClass(HTMLElement).

Refer to the original discussion for all suggested solutions. I'll mention here a few from there plus a new one:

// I kinda favor something like this (exact name is open for discussion)
expect(container).toContainElement(element)

// This makes me wonder if it only checks for direct parent-child relationship
// We could add it too, but not as a solution to this issue
expect(element).toBeAChildOf(parent)

// Keep .toBeInTheDOM allowing it to receive an optional element
// This still does not solve the problem when the container is not given, but I wouldn't rule it out
expect(element).toBeInTheDOM()
expect(element).toBeInTheDOM(container)

Helper to attach a stylesheet to the document

Describe the feature you'd like:

This is prompted by this question on Twitter:

does toBeVisible in jest-dom take styles from CSS files into account? I'm checking a component that has a class that adds display: none; but expect(component).not.toBeVisible() is failing because it thinks the component is visible.

The problem, as I pointed out responding that tweet, is that when testing isolated components, css stylesheets that would normally be attached to the document via a <link /> element in the head area, are not attached to the virtual jsdom document in which the tests are being run.

Suggested implementation:

I'm not sure how this would be. Especially with css-in-js and css-modules in the picture, I'm not even sure if there's still a framework-agnostic way to automatically handle what these better css systems do with the css to dynamically generate a stylesheet.

Keeping it concrete, only considering stylesheets in general, I'm not even sure if we can make a helper load a css file. All I know is that we can attach css to the document having the actual css code as a string, as we already do here.

We can simplify the above providing some kind of helper to attach styles and run assertions with those styles on. It has to be some kind of wrapper that gives us the opportunity to clean up, because as you see in that test I linked to, the style element has to be detached and cleaned up after the test (something we're not even doing consistently in all those test cases 😱).

So at the very least I can come up with something like this:

const stylesheet = `
  .hidden { display: none }
`

// ...

test('...', () => {
  withStyles(stylesheet, () => {
    // ...
    expect(element).not.toBeVisible();
  });
});

Describe alternatives you've considered:

We can keep things as they are, but at the very least we need to document this better in the README.

If we keep things as they are, we can tell people to assert against the element having the css class name or not, but that kinda goes against the guiding principle. Because end users of web apps do not really care about the element having the class or not, but wether it is visible or not. Furthermore, with the popularity of css-in-js or even css-modules, people cannot rely on static css class names anymore.

Teachability, Documentation, Adoption, Migration Strategy:

TODO

toContainElement should accept null or undefined

I got to this idea when I realized that is not always possible to negate .toContainElement:

expect(container).not.toContainElement(document.querySelector('.button'));

If indeed there's no element with class="button", the above will still fail. But not because the matcher finds the assertion to not be true. It fails because toContainElement does not allow null or undefined as argument.

I wanted to propose changing this matcher so that it will accept null and undefined.

Congrats!

Hey! Congrats on the 1.0.0 release! πŸŽ‰

New toBeVisible custom matcher

As discussed in #4, this library needs a .toBeVisible custom matcher. It operates on an element, receives no extra arguments, and passes if the element is visible to the user.

expect(element).toBeVisible()

How?

This should be implemented by checking the element's computed style, and make sure that display is not none, and also visibility is not hidden or collapsed.

Why?

Doing the equivalent assertion using vanilla DOM APIs is cumbersome:

// Two assertions needed, both dealing with verbose APIs
expect(getComputedStyle(element).getPropertyValue('display')).not.toEqual('none')
expect(getComputedStyle(element).getPropertyValue('visibility')).not.toMatch(/(hidden|collapsed)/)

New toHaveStyle custom matcher

As discussed in #4, this library needs a .toHaveStyle custom matcher. It should receive an object of css properties and their values, and will pass if the element has all the css properties in that object with the expected values.

expect(element).toHaveStyle({
  backgroundColor: '#fff',
  height: '100%',
})

How?

This should be implemented to take advantage of window.getComputedStyle to obtain all the styles applied to an element. This includes styles acquired from css classes applied to it, as well as styles applied directly via the style="..." attribute.

Also, this probably would have to deal with supporting css properties specified in camelCase, and covert them to the "kebab-case" before comparing them to the actual styles. It'd be nice to do that without adding too many external dependencies if possible, or better yet, none at all.

Why?

Doing the equivalent check with vanilla DOM APIs would be a bit verbose. It's not just about checking the style=".." attribute, and decode information out of it (which by itself would be even more verbose), but it involves using the DOM api mentioned above. Also the fact that it'd enable users to check for multiple style properties at the same time makes it even more useful, and the alternative even more verbose.

For instance, the above example would be something like this:

expect(getComputedStyle(element).getPropertyValue('background-color')).toEqual('#fff')
expect(getComputedStyle(element).getPropertyValue('height')).toEqual('100%')

Is there a migration guide for old jest-dom ?

Sorry but yet, if i just switch from jest-dom to @testing-library/jest-dom nothing behave as expected.

My guess is that Jest may not manage it yet, but i did not find any migration helper. Is there one ?

Type definitions not properly loaded when importing from jest's setupTests

  • jest-dom version: 1.10.0
  • node version: 8.11.3
  • react-testing-library version: 4.1.4

Relevant code or config:

// package.json
  "jest": {
    "setupTestFrameworkScriptFile": "<rootDir>/src/setupTests.ts",
  },

// src/setupTests.ts
import 'jest-dom/extend-expect';

// src/components/SomeComponent/SomeComponent.test.ts
expect(element).toHaveTextContent('Hello World!');
// TypeScript error in the line above:
// Property 'toHaveTextContent' does not exist on type 'Matchers<void>'

Problem description:

When importing jest-dom/extend-expect, as instructed in the README, within jest's setupTestFrameworkScriptFile file, and using TypeScript at the same time, I get TypeScript errors in my test files saying that this library's custom matchers are not found:

Property 'toHaveTextContent' does not exist on type 'Matchers<void>'

However, when I import jest-dom/extend-expect from within the very text files that need those matchers it all works. Moreover, it even works if I import it in just one of those files, which suddenly removes the TS warning from a second test file, without having to import it again from that second test file.

Suggested solution:

This StackOverflow answer may be part of the solution, but I wanted to bring this up first to see if someone more knowledgeable with TypeScript can help. @jgoz maybe?

add .toHaveValue matcher

The problem

#65 implemented a high-level API for asserting values of an entire form, which is great. However, this API it does not allow to assert the value of a single form element, which is more desired is some scenarios

Describe the feature you'd like:

Add toHaveValue matcher (as described in the initial comment in #60) which can be used to assert value of a single form element - an input, select or textarea

expect(getByLabelText('Username')).toHaveValue('janedoe');

Suggested implementation:

Create toHaveValue(formElement, expectedValues) that calls existing getSingleElementValue (https://github.com/gnapse/jest-dom/blob/master/src/to-have-form-values.js#L38)

I will be glad to help with implementing this feature

toHaveStyle not properly matching compound statement

  • jest-dom version: 2.1.0
  • node version: 8.9.4
  • npm version: 6.4.18
  • dom-testing-library version: 3.11.1
  • react-testing-library version: 5.2.0

Relevant code or config:

Copied & modified from your base CodeSandbox:

import React from "react";

const styles = {
  fontFamily: "monospace",
  textAlign: "center",
  transition: "opacity 0.2s ease-out, top 0.3s cubic-bezier(1.175, 0.885, 0.32, 1.275)"
};

export default ({ name }) => <h1 style={styles}>Hello {name}!</h1>;

Test:

test("react-testing-library works!", () => {
  const { container } = render(<Hello name="Jill" />);
  expect(container.firstChild).toHaveStyle(`font-family: monospace`);
  expect(container.firstChild).toHaveStyle(`text-align: center`);
  expect(container.firstChild).toHaveStyle(
    `transition: all 0.7s ease, width 1.0s cubic-bezier(3, 4, 5, 6);`
  );

What you did:

I was trying to test my transition style that's actually a compound statement.

What happened:

It seems toHaveStyle() will return true even if my assertion differs greatly. It does seem if I remove the compound statement in the test, it will properly fail:

  expect(container.firstChild).toHaveStyle(
    `transition: all 0.7s ease;`
  );

Reproduction:

https://codesandbox.io/s/14mlwzjjvj

Problem description:

I have two code paths (via props), one with transition, one without. I want to ensure the transition CSS is what it should be given the props input.

Suggested solution:

My one workaround for now is to put the transition in a separate class and include it based on my props, thus I can use toHaveClass().

Add toBeChecked for <input type="radio" /> and <input type="checkbox" />

Describe the feature you'd like:

I want this to work:

const input = document.createElement('input')
input.setAttribute('type', 'checkbox')
input.setAttribute('checked', true)
expect(input).toBeChecked()
input.setAttribute('checked', false)
expect(input).not.toBeChecked()

Describe alternatives you've considered:

expect(input.checked).toBeTrue()

Teachability, Documentation, Adoption, Migration Strategy:

This would need to be added to the docs like the rest of the assertions.

Codesandbox expect is not defined

  • jest-dom version: 3.1.3
  • react-testing-library version: 6.0.0

Relevant code or config:

https://codesandbox.io/s/ymjjk77x0z

What you did:

There are 2 files in the tests folder, one imports jest-dom and another does not.

ErrorFormatDateView.test.js Does import jest-dom and breaks by not finding the expect export.

FormatDateView.test.js Does not import, and using other matches can pass the test case.

Suggested solution:

jest-dom/extend-expect should export a default expect from jest.

strange toHaveFocus() behaviour

Sorry for posting the question here, but I have tried spectrum.chat and SO without much success so far...

I'm having trouble understanding how toHaveFocus() works exactly. Here's my setup:

MyComponent.js

import React from 'react'
import styled from 'styled-components'

import TextArea from './TextArea'


const Container = styled.div`
  flex: 1;
  height: 100%;
  padding: ${props => props.theme.size};
`

const Title = styled(TextArea)`
  font-weight: bold;
  font-size: ${props => props.theme.sizeLarger};
  margin-left: ${props => props.theme.sizeSmall};
`

class MyComponent extends React.Component {

  handleTitleChange = e => {
    this.props.onTitleChange(e.target.value)
  }

  handleTitleLostFocus = () => {
    this.props.onChangeComplete()
  }

  render () {
    return (
      <Container>
        <Title 
          value={this.props.item.title || ''}
          onChange={this.handleTitleChange}
          onBlur={this.handleTitleLostFocus}
        />
      </Container>
    )
  }
}

export default MyComponent

MyComponent.test.js

import React from 'react'
import {render, fireEvent, prettyDOM} from 'react-testing-library'

import MyComponent from '../MyComponent'


describe('MyComponent', () => {
  it('handles title changes', () => {
    const title = 'title'
    const handleTitleChangeMock = jest.fn()
    const {getByText} = render(
      <MyComponent
        item={{
          title: title
        }}
        onTitleChange={handleTitleChangeMock}
        onChangeComplete={() => {}}
      />
    )
    const titleInput = getByText(title)
    console.log(prettyDOM(titleInput))
    fireEvent.click(getByText(title))
    expect(getByText(title)).toHaveFocus()
    fireEvent.change(getByText(title), {target: {value: title.slice(0, -1)}})
    expect(handleTitleChangeMock).toHaveBeenCalledTimes(1)
    expect(handleTitleChangeMock).toHaveBeenCalledWith(title.slice(0, -1))
  })
})

When I do this:

const titleInput = getByText(title)
console.log(prettyDOM(titleInput))

The console logs the following:

<textarea
    class="sc-htoDjs dLjZCT sc-bxivhb jdLTBU"
    style="height: 0px;"
  >
    title
  </textarea>

That textarea element is the one I am targeting. But then, when I do this:

fireEvent.click(titleInput)
expect(titleInput).toHaveFocus()

I get this error:

Received:
  <body><textarea style="min-height: 0 !important; max-height: none !important; height: 0px !important; visibility: hidden !important; overflow: hidden !important; position: absolute !important; z-index: -1000 !important; top: 0px !important; right: 0px;" /><div><div class="sc-bZQynM ePHCfO"><textarea class="sc-htoDjs dLjZCT sc-bxivhb jdLTBU" style="height: 0px;">title</textarea></div></div></body>

  at Object.it (src/__tests__/MyComponent.test.js:84:30)
      at new Promise (<anonymous>)
  at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
      at <anonymous>
  at process._tickCallback (internal/process/next_tick.js:188:7)

I don't quite understand why when I'm trying to assert a textarea element toHaveFocus(), I'm receiving an error that references the entire DOM tree under body...

<select> element value change is not recognized properly by toHaveFormValues

  • jest-dom version: 3.5.0

Relevant code or config:

const { container } = render(
  <form>
    <select name="priority">
      <option value="low">Low</option>
      <option value="medium">Medium</option>
      <option value="high">High</option>
    </select>
  </form>
)
const form = container.querySelector('form')
const select = container.querySelector('select')
fireEvent.change(select, { target: { value: 'high' } })
expect(form).toHaveFormValues({ priority: 'high' }) // <- fails here

The above test should pass, but it does not. It fails to recognize the change in the selected value of the select.

Problem description:

After some digging I'm pretty sure this has something to do with the cause of this issue in dom-testing-library. This issue was fixed in this PR, and this is mention in that PR description:

Since selectedOptions is not a reactive property, JSDOM was not updating to match the selected option in a select Element. The selected attribute is reactive.

We're using selectedOptions ourselves to check the selected options in a select. See the relevant code.

Suggested solution:

Maybe following the same approach as testing-library/dom-testing-library#124 and switch to use the select attribute instead.

SVG elements fail on most matchers

  • jest-dom version: 1.5.1

  • node version: 8.11.3

  • npm (or yarn) version: yarn 1.7.0

  • dom-testing-library version: 2.7.0

  • react-testing-library version: 4.1.2

Relevant code or config:

expect(someSvgElement).toHaveClass("any-class");

What you did:

  • Make expect calls on SVG elements

What happened:

Exception thrown because they are not HTML elements.

Problem description:

Most assertions defined in this library would also be applicable to SVG elements, but they are being blocked unnecessarily.

Suggested solution:

Modify checkHtmlElement to allow elements that satisfy instanceof SVGElement.

I'm happy to open a PR if no one opposes.

Support async matchers?

Just a thought... Perhaps we could take advantage of the Jest 23 release and support custom async matchers. Something like:

expect(node).toEventuallyHaveTextContent('foo')

That would eliminate the need for using wait in dom-testing-library in many cases... πŸ€”

Better document toHaveTextContent's case sensitiveness

After having merged #72 I've realized the README is not clear about this, not even the examples hint at this, and they were even inadequate for how it behaved before this change.

toHaveTextContent documentation should be explicit about this. Maybe even mentioning that if someone wants it to behave as case-insensitive, they should use a regex with the i modifier (/some text/i) instead of a string.

@lourenci can you take this? I could but maybe you can tackle it before I can.

New toBeEmpty custom matcher

As discussed in #4, this library needs some sort of .toBeEmpty custom matcher. This will pass if the given element does not contain any child elements.

expect(element).toBeEmpty()

A few aspects of this are open to discussion. We'd like to come up with a different name that would be clearer and avoid name clashes with other matchers out there that might be checking emptiness of other things (e.g. array or collections of some sort).

How?

// TODO

Why?

Presumably doing the equivalent checks on one's own is not straightforward and concise enough.

Media queries

First thank you for this awesome library!

I would like to use toHaveStyle to test a media query. I am migrating xstyled tests from jest-styled-components to jest-dom since the approach is better for me.

My question is simple. How to do the equivalent with toHaveStyle?

expect(container.firstChild).toHaveStyleRule('color', 'red', {
   media: '(min-width: 768px)',
})

Make it so the TypeScript definitions work automatically without config

If we follow the path we took in React Testing Library then that'll mean that people wont have to do anything fancy to make the typings/autocomplete show up which would be rad.

Specifically:

  1. Add testing-library__jest-dom to DefinitelyTyped
  2. Add @types/testing-library__jest-dom to @testing-library/jest-dom dependencies

I just tested it locally and that appears to work. If I have it in my node_modules then it just worksℒ️

Anyone up for this?

Invitation to move to testing-library org

It's up to you, but if you'd like, you're welcome to transfer this repository to the testing-library organization on GitHub. This will give your project more credibility as part of the testing-library family of projects with a unified mission of tools which help people avoid testing implementation details.

Again, totally up to you. I just want you to know we appreciate all you've done and you're welcome to join us if you like.

toHaveStyle ignores hover styles

Describe the feature you'd like:

I have a button that displays different styles when mouse moves over it:

background-color: green;
&:hover {
  background-color: red;
}

Here is my test:

fireEvent.mouseOver(button);
expect(button).toHaveStyle(`
  background-color: red;
`);

However, the test complained that the background color is still green instead of red.
I tried fireEvent.mouseEnter before calling mouseOver. Didn't make any difference.

I don't think it is fireEvent.mouseOver's problem. A onMouseOver event listener actually gets called when a mouseOver event is fired by fireEvent. Could it be a bug in jest-dom?

Describe alternatives you've considered:

So far, I commented out all test that verify hover styles. And I probably should leave this style test to visual/browser test?

Create new matcher `toBeLabelled`

Describe the feature you'd like:

The idea is to have a custom matcher on the model of toBeRequired or toBeDisabled helping folks to know if they labelled correctly the markup they are testing.

This helper would be interesting for me because there is a variety of ways to label correctly an element, such as:

  • aria-label
  • aria-labelledby
  • alt on images
  • title on images and SVGs
  • having a text content (by using .toHaveTextContent)

The main limitation is that I am not sure we can easily know when a DOM node is labelled thanks to a nearby visually-hidden or displayed text which is not part of the same parent (but in this case, it would also be complex to find for screen readers/assistive technologies).

Suggested implementation:

Just doing all the tests decribed few lines before.

Describe alternatives you've considered:

Doing each test one after the other -or- using jest-axe but in this case lacking flexibility on doing more granular tests.

Teachability, Documentation, Adoption, Migration Strategy:

I could help with the implementation if you're up to adding this to this library πŸ‘

Reformat README to use generic DOM selectors

Problem description:

The problem discussed in #47 was that the documentation is tightly coupled with dom-testing-library. End users may get confused, thinking jest-dom requires dom-testing-library selectors to work.

Suggested solution:

Update the README to use generic DOM selectors: document.querySelector in all examples.

Option 1:

Specific section for dom-testing-library:
Create a section discussing how to use it with dom-testing-library with examples and a link.

Option 2:

Each section could have examples for both:

Option 2: using <details> example

Example using document.querySelector
// 
test
const element = document.querySelector('.some-item'); expect(element)... // ...
Example using dom-testing-library
// 
test
expect(queryByText('test'))... // ...

Option 2: using direct example

Example using document.querySelector

// <div class="some-item">test</div>
const element = document.querySelector('.some-item');
expect(element)...

Example using dom-testing-library

// <div class="some-item">test</div>
expect(queryByText('test'))...

toBeDisabled matcher proposal

Describe the feature you'd like:

I would like to have a matcher that would check if the form element is disabled from user's perspective.
In this case, simply checking the 'disabled' attribute is not enough.

Describe alternatives you've considered:

In many cases .toHaveAttribute('disabled') is sufficient alternative.
But I think that the dedicated matcher would be less dependent on the implementation details

Teachability, Documentation, Adoption, Migration Strategy:

Examples and comparison with toHaveAttribute matcher.

// in simple cases toHaveAttribute and toBeDisabled behave the same

// ...
// <button disabled={true}>CLICK</button>
expect(getByText(container, 'CLICK')).toHaveAttribute('disabled')
expect(getByText(container, 'CLICK')).toBeDisabled()
// ...
// toBeDisabled can detect when a disabled state is inherited

// ...
// <fieldset disabled={true}><button>CLICK</button></fieldset>
expect(getByText(container, 'CLICK')).not.toHaveAttribute('disabled')
expect(getByText(container, 'CLICK')).toBeDisabled()
// ...
// 'a' element is not disabled, even if it has the 'disabled' attribute

// ...
// <a href="http://..." disabled={true}>CLICK</a>
expect(getByText(container, 'CLICK')).toHaveAttribute('disabled')
expect(getByText(container, 'CLICK')).not.toBeDisabled()
// ...

add toContainQuerySelector matcher for better errors

Describe the feature you'd like:

Currently the recommendation for checking if an element exists is:

const htmlElement = document.querySelector('[data-testid="html-element"]')
const nonExistantElement = document.querySelector('does-not-exist')

expect(htmlElement).toBeInTheDocument()
expect(nonExistantElement).not.toBeInTheDocument()

(abridged from the docs)

But this returns pretty useless errors if the condition is not met. It would be much more useful to combine the querySelector with the existence check, such as:

expect(document).toContainQuerySelector('[data-testid="html-element"]')
expect(document).not.toContainQuerySelector('does-not-exist')

(this is also much shorter syntax)

This is comparable to toContainMatchingElement from the enzyme-matchers project.

Suggested implementation:

I've tried to match the code style of this project, but might have missed something:

import {matcherHint, printReceived, printExpected} from 'jest-matcher-utils'
import {checkHtmlElement} from './utils'

export function toContainQuerySelector(element, selector) {
  checkHtmlElement(element, toContainQuerySelector, this)
  return {
    pass: Boolean(element.querySelector(selector)),
    message: () => [
      matcherHint(`${this.isNot ? '.not' : ''}.toContainQuerySelector`, 'element', 'selector'),
      '',
      `Expected ${this.isNot ? 'no elements' : 'an element'} matching`,
      `  ${printExpected(selector)}`,
      'Received',
      `  ${printReceived(element)}`,
    ].join('\n'),
  }
}

Describe alternatives you've considered:

  • .not.toBe(null) or a hypothetical .toExist(), etc. have similar problems to .toBeInTheDocument: the returned error is not helpful for diagnosis because it does not include the content which was actually observed when an element is missing.

  • It might be valuable to be able to count the number of occurrences (e.g. toContainQuerySelector('foo', 4) to check for exactly 4 occurrences, but I can't think of a self-descriptive method name for this, so as-proposed this checks for 1-or-more, or exactly zero.

Teachability, Documentation, Adoption, Migration Strategy:

Recommendation can be updated to:

expect(document).toContainQuerySelector('[data-testid="html-element"]')
expect(document).not.toContainQuerySelector('does-not-exist')

No migration necessary; existing matchers do not need to change.

Should .toHaveTextContent be case-sensitive?

I don't know if this is a bug or it is a feature, but I expected that .toHaveTextContent was case-sensitive. I could address that if it is a bug.

  • jest-dom version: 2.1.1

What you did:

<div className="App">
  <h1>Hello CodeSandbox</h1>
  <h2>Start editing to see some magic happen!</h2>
</div>

I expect the expect below would fail, but it passes.

expect(subject.container.querySelector("h1")).toHaveTextContent("hello codesandbox");

support for asymmetricMatch as the value for toHaveAttribute matcher

Describe the feature you'd like:

Adding support for asymmetricMatchers like expec.stringContaining as the value to toHaveAttribute matcher
Eg:

      expect(getByLabelText(/Download Template/)).toHaveAttribute(
        "href",
        expect.stringContaining("?type=instruments")
      );

Suggested implementation:

Describe alternatives you've considered:

As of now this is achived by

      expect(getByLabelText(/Download Template/).getAttribute("href")).toEqual(
        expect.stringContaining("type=instruments")
      );

Teachability, Documentation, Adoption, Migration Strategy:

passing a number to toHaveTextContent result in wrong error message

  • jest-dom version: 1.3.1
  • node version: 10.4.1
  • npm version: 6.1.0
  • dom-testing-library version: 2.6.4
  • react-testing-library version: 4.1.0

Relevant code or config:

expect(countSpan).toHaveTextContent(0);

What you did:

I should have passed a string to toHaveTextContent, but I passed a number.

What happened:

TypeError: matcher.test is not a function

Reproduction:

https://codesandbox.io/s/vjmp702z63
See tests/hello.js

Problem description:

The error message is misleading.

Suggested solution:

"TypeError" is fine, but the rest of the message should say something different.

"expect is not defined" when using with jest

  • @testing-library/jest-dom version: 4.0.0
  • node version: 12.8.0
  • npm (or yarn) version: 6.10.2

Relevant code or config:

Jest config

module.exports = {
  snapshotSerializers: ['jest-emotion'],
  setupFilesAfterEnv: ['./config/jest.js']
};

jest.js file

require('@testing-library/jest-dom/extend-expect');

What you did:

run the npm test

What happened:

Getting error expect is not defined ...

Determining test suites to run...

  ● Test suite failed to run

    ReferenceError: expect is not defined

      at Object.<anonymous> (node_modules/@testing-library/jest-dom/dist/extend-expect.js:7:1)
      at Object.newLoader [as .js] (node_modules/pirates/lib/index.js:104:7)
      at Object.<anonymous> (node_modules/@testing-library/jest-dom/extend-expect.js:2:1)

Reproduction:

https://github.com/tomitrescak/react-boilerplate/tree/Demo

Problem description:

I cannot run my tests

Suggested solution:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.