diff --git a/README.md b/README.md index dac65a0f..59364ffb 100644 --- a/README.md +++ b/README.md @@ -45,17 +45,17 @@ to maintain. - [Installation](#installation) - [Usage](#usage) - [Custom matchers](#custom-matchers) + - [`toBeDisabled`](#tobedisabled) - [`toBeEmpty`](#tobeempty) - [`toBeInTheDocument`](#tobeinthedocument) + - [`toBeVisible`](#tobevisible) - [`toContainElement`](#tocontainelement) - [`toContainHTML`](#tocontainhtml) - - [`toHaveTextContent`](#tohavetextcontent) - [`toHaveAttribute`](#tohaveattribute) - [`toHaveClass`](#tohaveclass) - - [`toHaveStyle`](#tohavestyle) - [`toHaveFocus`](#tohavefocus) - - [`toBeVisible`](#tobevisible) - - [`toBeDisabled`](#tobedisabled) + - [`toHaveStyle`](#tohavestyle) + - [`toHaveTextContent`](#tohavetextcontent) - [Deprecated matchers](#deprecated-matchers) - [`toBeInTheDOM`](#tobeinthedom) - [Inspiration](#inspiration) @@ -100,6 +100,49 @@ expect.extend({toBeInTheDocument, toHaveClass}) ## Custom matchers +`jest-dom` can work with any library or framework that returns DOM elements from queries. The custom matcher examples below demonstrate using `document.querySelector` and [dom-testing-library](https://github.com/kentcdodds/dom-testing-library) for querying DOM elements. + +### `toBeDisabled` + +```typescript +toBeDisabled() +``` + +This allows you to check whether an element is disabled from the user's perspective. + +It matches if the element is a form control and the `disabled` attribute is +specified on this element or the element is a descendant of a form element +with a `disabled` attribute. + +According to the specification, the following elements can be [actually disabled](https://html.spec.whatwg.org/multipage/semantics-other.html#disabled-elements): +`button`, `input`, `select`, `textarea`, `optgroup`, `option`, `fieldset`. + +#### Examples + +```html + +
+link +``` + +##### Using document.querySelector + +```javascript +expect(document.querySelector('[data-testid="button"]')).toBeDisabled() +expect(document.querySelector('[data-testid="input"]')).toBeDisabled() +expect(document.querySelector('a')).not.toBeDisabled() +``` + +##### Using dom-testing-library + +```javascript +expect(getByTestId(container, 'button')).toBeDisabled() +expect(getByTestId(container, 'input')).toBeDisabled() +expect(getByText(container, 'link')).not.toBeDisabled() +``` + +
+ ### `toBeEmpty` ```typescript @@ -108,17 +151,28 @@ toBeEmpty() This allows you to assert whether an element has content or not. +#### Examples + +```html + +``` + +##### Using document.querySelector + ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +expect(document.querySelector('[data-testid="empty"]').toBeEmpty() +expect(document.querySelector('[data-testid="not-empty"]').not.toBeEmpty() +``` + +##### Using dom-testing-library -// ... -// +```javascript expect(queryByTestId(container, 'empty')).toBeEmpty() expect(queryByTestId(container, 'not-empty')).not.toBeEmpty() -// ... ``` +
+ ### `toBeInTheDocument` ```typescript @@ -127,26 +181,92 @@ toBeInTheDocument() This allows you to assert whether an element is present in the document or not. -```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +#### Examples + +```html +Html Element + +``` -// ... -// document.body.innerHTML = `Html Element` +##### Using document.querySelector -// const htmlElement = document.querySelector('[data-testid="html-element"]') -// const svgElement = document.querySelector('[data-testid="svg-element"]') -// const nonExistantElement = document.querySelector('does-not-exist') -// const detachedElement = document.createElement('div') +```javascript +const htmlElement = document.querySelector('[data-testid="html-element"]') +const svgElement = document.querySelector('[data-testid="svg-element"]') +const nonExistantElement = document.querySelector('does-not-exist') +const detachedElement = document.createElement('div') expect(htmlElement).toBeInTheDocument() expect(svgElement).toBeInTheDocument() expect(detacthedElement).not.toBeInTheDocument() expect(nonExistantElement).not.toBeInTheDocument() -// ... ``` -> Note: This will not find detached elements. The element must be added to the document to be found. If you desire to search in a detached element please use: [`toContainElement`](#tocontainelement) +##### Using dom-testing-library + +```javascript +expect( + queryByTestId(document.documentElement, 'html-element'), +).toBeInTheDocument() +expect( + queryByTestId(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`](#tocontainelement) + +
+ +### `toBeVisible` + +```typescript +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 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) + +#### Examples + +```html +
Zero Opacity Example
+
Visibility Hidden Example
+
Display None Example
+
Hidden Parent Example
+
Visible Example
+``` + +##### Using document.querySelector + +```javascript +expect(document.querySelector('[data-testid="zero-opacity"]'])).not.toBeVisible() +expect(document.querySelector('[data-testid="visibility-hidden"]'])).not.toBeVisible() +expect(document.querySelector('[data-testid="display-none"]'])).not.toBeVisible() +expect(document.querySelector('[data-testid="hidden-parent"]'])).not.toBeVisible() +expect(document.querySelector('[data-testid="visible"]'])).toBeVisible() +``` + +##### Using dom-testing-library + +```javascript +expect(getByText(container, 'Zero Opacity Example')).not.toBeVisible() +expect(getByText(container, 'Visibility Hidden Example')).not.toBeVisible() +expect(getByText(container, 'Display None Example')).not.toBeVisible() +expect(getByText(container, 'Hidden Parent Example')).not.toBeVisible() +expect(getByText(container, 'Visible Example')).toBeVisible() +``` + +
### `toContainElement` @@ -156,12 +276,31 @@ toContainElement(element: HTMLElement | SVGElement | null) This allows you to assert whether an element contains another element as a descendant or not. +#### Examples + +```html + +``` + +##### Using document.querySelector + ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +const ancestor = document.querySelector('[data-testid="ancestor"]') +const descendant = document.querySelector('[data-testid="descendant"]') +const nonExistantElement = document.querySelector( + '[data-testid="does-not-exist"]', +) + +expect(ancestor).toContainElement(descendant) +expect(descendant).not.toContainElement(ancestor) +expect(ancestor).not.toContainElement(nonExistantElement) +``` + +##### Using dom-testing-library + +```javascript +const {queryByTestId} = render(/* Rendered HTML */) -// ... -// const ancestor = queryByTestId(container, 'ancestor') const descendant = queryByTestId(container, 'descendant') const nonExistantElement = queryByTestId(container, 'does-not-exist') @@ -169,9 +308,10 @@ const nonExistantElement = queryByTestId(container, 'does-not-exist') expect(ancestor).toContainElement(descendant) expect(descendant).not.toContainElement(ancestor) expect(ancestor).not.toContainElement(nonExistantElement) -// ... ``` +
+ ### `toContainHTML` ```typescript @@ -180,15 +320,26 @@ toContainHTML(htmlText: string) Assert whether a string representing a HTML element is contained in another element: +#### Examples + +```html + +``` + +##### Using document.querySelector + ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +expect(document.querySelector('[data-testid="parent"]')).toContainHTML( + '', +) +``` + +##### Using dom-testing-library -// ... -// -const parent = queryByTestId('parent') -expect(parentElement).toContainHTML('') -// ... +```javascript +expect(getByTestId(container, 'parent')).toContainHTML( + '', +) ``` > 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. @@ -197,51 +348,45 @@ expect(parentElement).toContainHTML('') > > It should not be used to check DOM structure that you control. Please use [`toContainElement`](#tocontainelement) instead. -### `toHaveTextContent` +
+ +### `toHaveAttribute` ```typescript -toHaveTextContent(text: string | RegExp) +toHaveAttribute(attr: string, value?: string) ``` -This API allows you to check whether the given element has a text content or not. +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. -```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +#### Examples -// ... -// 2 -expect(getByTestId(container, 'count-value')).toHaveTextContent('2') -expect(getByTestId(container, 'count-value')).not.toHaveTextContent('21') -// ... +```html + ``` -### `toHaveAttribute` +##### Using document.querySelector -```typescript -toHaveAttribute(attr: string, value?: string) +```javascript +const button = document.querySelector('[data-testid="ok-button"]') + +expect(button).toHaveAttribute('disabled') +expect(button).toHaveAttribute('type', 'submit') +expect(button).not.toHaveAttribute('type', 'button') ``` -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. +##### Using dom-testing-library ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +const button = getByTestId(container, 'ok-button') -// ... -// -expect(getByTestId(container, 'ok-button')).toHaveAttribute('disabled') -expect(getByTestId(container, 'ok-button')).toHaveAttribute('type', 'submit') -expect(getByTestId(container, 'ok-button')).not.toHaveAttribute( - 'type', - 'button', -) -// ... +expect(button).toHaveAttribute('disabled') +expect(button).toHaveAttribute('type', 'submit') +expect(button).not.toHaveAttribute('type', 'button') ``` +
+ ### `toHaveClass` ```typescript @@ -251,65 +396,45 @@ toHaveClass(...classNames: string[]) This allows you to check whether the given element has certain classes within its `class` attribute. -```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +You must provide at least one class, unless you are asserting that an element +does not have any classes. -// ... -// -expect(getByTestId(container, 'delete-button')).toHaveClass('extra') -expect(getByTestId(container, 'delete-button')).toHaveClass('btn-danger btn') -expect(getByTestId(container, 'delete-button')).toHaveClass('btn-danger', 'btn') -expect(getByTestId(container, 'delete-button')).not.toHaveClass('btn-link') -// ... +#### Examples + +```html + + ``` -You must provide at least one class, unless you are asserting that an element -does not have any classes. +##### Using document.querySelector ```javascript -// ... -// -expect(getByTestId(container, 'no-classes')).not.toHaveClass() -``` +const deleteButton = document.querySelector('[data-testid="delete-button"]') +const noClasses = document.querySelector('[data-testid="no-classes"]') -### `toHaveStyle` +expect(deleteButton).toHaveClass('extra') +expect(deleteButton).toHaveClass('btn-danger btn') +expect(deleteButton).toHaveClass('btn-danger', 'btn') +expect(deleteButton).not.toHaveClass('btn-link') -```typescript -toHaveStyle(css: string) +expect(noClasses).not.toHaveClass() ``` -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. +##### Using dom-testing-library ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +const deleteButton = getByTestId(container, 'delete-button') +const noClasses = getByTestId(container, 'no-classes') -// ... -// -expect(getByTestId(container, 'delete-button')).toHaveStyle('display: none') -expect(getByTestId(container, 'delete-button')).toHaveStyle(` - color: red; - display: none; -`) -expect(getByTestId(container, 'delete-button')).not.toHaveStyle(` - display: none; - color: blue; -`) -// ... +expect(deleteButton).toHaveClass('extra') +expect(deleteButton).toHaveClass('btn-danger btn') +expect(deleteButton).toHaveClass('btn-danger', 'btn') +expect(deleteButton).not.toHaveClass('btn-link') + +expect(noClasses).not.toHaveClass() ``` -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. +
### `toHaveFocus` @@ -319,93 +444,130 @@ toHaveFocus() This allows you to assert whether an element has focus or not. +#### Examples + +```html +
+``` + +##### Using document.querySelector + ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +const input = document.querySelector(['data-testid="element-to-focus"') + +input.focus() +expect(input).toHaveFocus() + +input.blur() +expect(input).not.toHaveFocus() +``` -// ... -//
-// const { container } = render(...) -// const input = container.querySelector('#focused'); +##### Using dom-testing-library -// input.focus() -expect(queryByTestId(container, 'focused')).toHaveFocus() +```javascript +const input = queryByTestId(container, 'element-to-focus') -// input.blur() -expect(queryByTestId(container, 'focused')).not.toHaveFocus() +fireEvent.focus(input) +expect(input).toHaveFocus() -// ... +fireEvent.blur(input) +expect(input).not.toHaveFocus() ``` -### `toBeVisible` +
+ +### `toHaveStyle` ```typescript -toBeVisible() +toHaveStyle(css: string) ``` -This allows you to check if an element is currently visible to the user. +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. -An element is visible if **all** the following conditions are met: +#### Examples -- 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) +```html + +``` + +##### Using document.querySelector ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +const input = document.querySelector(['data-testid="delete-button"') -// ... -//
-//

Page title

-//
-//
-//

Hello World -//

-expect(container.querySelector('header')).toBeVisible() -expect(container.querySelector('h1')).not.toBeVisible() -expect(container.querySelector('strong')).not.toBeVisible() -// ... +expect(button).toHaveStyle('display: none') +expect(button).toHaveStyle(` + color: red; + display: none; +`) +expect(button).not.toHaveStyle(` + display: none; + color: blue; +`) ``` -### `toBeDisabled` +##### Using dom-testing-library + +```javascript +const button = getByTestId(container, 'delete-button') + +expect(button).toHaveStyle('display: none') +expect(button).toHaveStyle(` + color: red; + display: none; +`) +expect(button).not.toHaveStyle(` + display: none; + color: blue; +`) +``` + +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` ```typescript -toBeDisabled() +toHaveTextContent(text: string | RegExp) ``` -This allows you to check whether an element is disabled from the user's perspective. +This API allows you to check whether the given element has a text content or not. -It matches if the element is a form control and the `disabled` attribute is -specified on this element or the element is a descendant of a form element -with a `disabled` attribute. +#### Examples -According to the specification, the following elements can be [actually disabled](https://html.spec.whatwg.org/multipage/semantics-other.html#disabled-elements): -`button`, `input`, `select`, `textarea`, `optgroup`, `option`, `fieldset`. +```html +2 +``` + +##### Using document.querySelector ```javascript -// add the custom expect matchers once -import 'jest-dom/extend-expect' +const button = document.querySelector('[data-testid="count-value"]') -// ... -// -//
-// -//
-expect(getByTestId(container, 'button')).toBeDisabled() -expect(getByTestId(container, 'text')).toBeDisabled() -// ... +expect(content).toHaveTextContent('2') +expect(content).toHaveTextContent(/^2$/) +expect(content).not.toHaveTextContent('21') +``` -// ... -// LINK -expect(getByText(container, 'LINK')).not.toBeDisabled() -// ... +##### Using dom-testing-library + +```javascript +const content = getByTestId(container, 'count-value') + +expect(content).toHaveTextContent('2') +expect(content).toHaveTextContent(/^2$/) +expect(content).not.toHaveTextContent('21') ``` +
+ ## Deprecated matchers ### `toBeInTheDOM`