diff --git a/.all-contributorsrc b/.all-contributorsrc
index 1dc8d820..91f0d2cd 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -77,6 +77,17 @@
"platform"
]
},
+ {
+ "login": "antoaravinth",
+ "name": "Anto Aravinth",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/1241511?s=460&v=4",
+ "profile": "https://github.com/antoaravinth",
+ "contributions": [
+ "code",
+ "test",
+ "doc"
+ ]
+ },
{
"login": "JonahMoses",
"name": "Jonah Moses",
diff --git a/README.md b/README.md
index cfe56d0f..3d767216 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
[![downloads][downloads-badge]][npmtrends]
[![MIT License][license-badge]][license]
-[](#contributors)
+[](#contributors)
[![PRs Welcome][prs-badge]][prs]
[![Code of Conduct][coc-badge]][coc]
@@ -48,6 +48,7 @@ components. It provides light utility functions on top of `react-dom` and
* [`Simulate`](#simulate)
* [`flushPromises`](#flushpromises)
* [`render`](#render)
+* [Custom Jest Matchers](#custom-jest-matchers)
* [`TextMatch`](#textmatch)
* [`query` APIs](#query-apis)
* [Examples](#examples)
@@ -219,6 +220,44 @@ const usernameInputElement = getByTestId('username-input')
> Learn more about `data-testid`s from the blog post
> ["Making your UI tests resilient to change"][data-testid-blog-post]
+## Custom Jest Matchers
+
+There are two simple API which extend the `expect` API of jest for making assertions easier.
+
+### `toBeInTheDOM`
+
+This allows you to assert whether an element present in the DOM or not.
+
+```javascript
+// add the custom expect matchers
+import 'react-testing-library/extend-expect'
+
+// ...
+const {queryByTestId} = render(2)
+expect(queryByTestId('count-value')).toBeInTheDOM()
+expect(queryByTestId('count-value1')).not.toBeInTheDOM()
+// ...
+```
+
+> Note: when using `toBeInTheDOM`, make sure you use a query function
+> (like `queryByTestId`) rather than a get function (like `getByTestId`).
+> Otherwise the `get*` function could throw an error before your assertion.
+
+### `toHaveTextContent`
+
+This API allows you to check whether the given element has a text content or not.
+
+```javascript
+// add the custom expect matchers
+import 'react-testing-library/extend-expect'
+
+// ...
+const {getByTestId} = render(2)
+expect(getByTestId('count-value')).toHaveTextContent('2')
+expect(getByTestId('count-value')).not.toHaveTextContent('21')
+// ...
+```
+
## `TextMatch`
Several APIs accept a `TextMatch` which can be a `string`, `regex` or a
@@ -564,7 +603,7 @@ Thanks goes to these people ([emoji key][emojis]):
| [
Kent C. Dodds](https://kentcdodds.com)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=kentcdodds "Code") [๐](https://github.com/kentcdodds/react-testing-library/commits?author=kentcdodds "Documentation") [๐](#infra-kentcdodds "Infrastructure (Hosting, Build-Tools, etc)") [โ ๏ธ](https://github.com/kentcdodds/react-testing-library/commits?author=kentcdodds "Tests") | [
Ryan Castner](http://audiolion.github.io)
[๐](https://github.com/kentcdodds/react-testing-library/commits?author=audiolion "Documentation") | [
Daniel Sandiego](https://www.dnlsandiego.com)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=dnlsandiego "Code") | [
Paweล Mikoลajczyk](https://github.com/Miklet)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=Miklet "Code") | [
Alejandro รรกรฑez Ortiz](http://co.linkedin.com/in/alejandronanez/)
[๐](https://github.com/kentcdodds/react-testing-library/commits?author=alejandronanez "Documentation") | [
Matt Parrish](https://github.com/pbomb)
[๐](https://github.com/kentcdodds/react-testing-library/issues?q=author%3Apbomb "Bug reports") [๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=pbomb "Code") [๐](https://github.com/kentcdodds/react-testing-library/commits?author=pbomb "Documentation") [โ ๏ธ](https://github.com/kentcdodds/react-testing-library/commits?author=pbomb "Tests") | [
Justin Hall](https://github.com/wKovacs64)
[๐ฆ](#platform-wKovacs64 "Packaging/porting to new platform") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
-| [
Jonah Moses](https://github.com/JonahMoses)
[๐](https://github.com/kentcdodds/react-testing-library/commits?author=JonahMoses "Documentation") |
+| [
Anto Aravinth](https://github.com/antoaravinth)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Code") [โ ๏ธ](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Tests") [๐](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Documentation") | [
Jonah Moses](https://github.com/JonahMoses)
[๐](https://github.com/kentcdodds/react-testing-library/commits?author=JonahMoses "Documentation") |
diff --git a/extend-expect.js b/extend-expect.js
new file mode 100644
index 00000000..3cee4049
--- /dev/null
+++ b/extend-expect.js
@@ -0,0 +1 @@
+require('./dist/extend-expect')
diff --git a/package.json b/package.json
index 06c161a7..d709aae9 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,8 @@
"eslintConfig": {
"extends": "./node_modules/kcd-scripts/eslint.js",
"rules": {
- "react/prop-types": "off"
+ "react/prop-types": "off",
+ "import/no-unassigned-import": "off"
}
},
"eslintIgnore": [
@@ -61,4 +62,4 @@
"url": "https://github.com/kentcdodds/react-testing-library/issues"
},
"homepage": "https://github.com/kentcdodds/react-testing-library#readme"
-}
+}
\ No newline at end of file
diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js
index cec3f1ab..a5912c8d 100644
--- a/src/__tests__/element-queries.js
+++ b/src/__tests__/element-queries.js
@@ -1,5 +1,6 @@
import React from 'react'
import {render} from '../'
+import '../extend-expect'
test('query can return null', () => {
const {
@@ -66,4 +67,17 @@ test('totally empty label', () => {
expect(() => getByLabelText('')).toThrowErrorMatchingSnapshot()
})
+test('using jest helpers to assert element states', () => {
+ const {queryByTestId} = render(2)
+
+ // other ways to assert your test cases, but you don't need all of them.
+ expect(queryByTestId('count-value')).toBeInTheDOM()
+ expect(queryByTestId('count-value1')).not.toBeInTheDOM()
+ expect(queryByTestId('count-value')).toHaveTextContent('2')
+ expect(queryByTestId('count-value')).not.toHaveTextContent('21')
+ expect(() =>
+ expect(queryByTestId('count-value2')).toHaveTextContent('2'),
+ ).toThrowError()
+})
+
/* eslint jsx-a11y/label-has-for:0 */
diff --git a/src/__tests__/fetch.js b/src/__tests__/fetch.js
index c20c7fbc..bddb1489 100644
--- a/src/__tests__/fetch.js
+++ b/src/__tests__/fetch.js
@@ -35,7 +35,7 @@ test('Fetch makes an API call and displays the greeting when load-greeting is cl
}),
)
const url = '/greeting'
- const {getByText, container} = render()
+ const {container, getByText} = render()
// Act
Simulate.click(getByText('Fetch'))
diff --git a/src/extend-expect.js b/src/extend-expect.js
new file mode 100644
index 00000000..58359822
--- /dev/null
+++ b/src/extend-expect.js
@@ -0,0 +1,7 @@
+import expect from 'expect' //eslint-disable-line import/no-extraneous-dependencies
+import extensions from './jest-extensions'
+
+const {toBeInTheDOM, toHaveTextContent} = extensions
+expect.extend({toBeInTheDOM, toHaveTextContent})
+
+export default expect
diff --git a/src/jest-extensions.js b/src/jest-extensions.js
new file mode 100644
index 00000000..69e1e92c
--- /dev/null
+++ b/src/jest-extensions.js
@@ -0,0 +1,78 @@
+import {matcherHint, printReceived, printExpected} from 'jest-matcher-utils' //eslint-disable-line import/no-extraneous-dependencies
+import {matches} from './utils'
+
+function getDisplayName(subject) {
+ if (subject && subject.constructor) {
+ return subject.constructor.name
+ } else {
+ return typeof subject
+ }
+}
+
+const assertMessage = (assertionName, message, received, expected) =>
+ `${matcherHint(`${assertionName}`, 'received', '')} \n${message}: ` +
+ `${printExpected(expected)} \nReceived: ${printReceived(received)}`
+
+const extensions = {
+ toBeInTheDOM(received) {
+ getDisplayName(received)
+ if (received) {
+ return {
+ message:
+ `${matcherHint(
+ '.not.toBeInTheDOM',
+ 'received',
+ '',
+ )} Expected the element not to be present` +
+ `\nReceived : ${printReceived(received)}`,
+ pass: true,
+ }
+ } else {
+ return {
+ message:
+ `${matcherHint(
+ '.toBeInTheDOM',
+ 'received',
+ '',
+ )} Expected the element to be present` +
+ `\nReceived : ${printReceived(received)}`,
+ pass: false,
+ }
+ }
+ },
+
+ toHaveTextContent(htmlElement, checkWith) {
+ if (!(htmlElement instanceof HTMLElement))
+ throw new Error(
+ `The given subject is a ${getDisplayName(
+ htmlElement,
+ )}, not an HTMLElement`,
+ )
+
+ const textContent = htmlElement.textContent
+ const pass = matches(textContent, htmlElement, checkWith)
+ if (pass) {
+ return {
+ message: assertMessage(
+ '.not.toHaveTextContent',
+ 'Expected value not equals to',
+ htmlElement,
+ checkWith,
+ ),
+ pass: true,
+ }
+ } else {
+ return {
+ message: assertMessage(
+ '.toHaveTextContent',
+ 'Expected value equals to',
+ htmlElement,
+ checkWith,
+ ),
+ pass: false,
+ }
+ }
+ },
+}
+
+export default extensions
diff --git a/src/queries.js b/src/queries.js
index 94382dab..485e2826 100644
--- a/src/queries.js
+++ b/src/queries.js
@@ -1,3 +1,5 @@
+import {matches} from './utils'
+
// Here are the queries for the library.
// The queries here should only be things that are accessible to both users who are using a screen reader
// and those who are not using a screen reader (with the exception of the data-testid attribute query).
@@ -69,16 +71,6 @@ function getText(node) {
.join(' ')
}
-function matches(textToMatch, node, matcher) {
- if (typeof matcher === 'string') {
- return textToMatch.toLowerCase().includes(matcher.toLowerCase())
- } else if (typeof matcher === 'function') {
- return matcher(textToMatch, node)
- } else {
- return matcher.test(textToMatch)
- }
-}
-
// getters
// the reason we're not dynamically generating these functions that look so similar:
// 1. The error messages are specific to each one and depend on arguments
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 00000000..87ecef8b
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,10 @@
+//eslint-disable-next-line import/prefer-default-export
+export function matches(textToMatch, node, matcher) {
+ if (typeof matcher === 'string') {
+ return textToMatch.toLowerCase().includes(matcher.toLowerCase())
+ } else if (typeof matcher === 'function') {
+ return matcher(textToMatch, node)
+ } else {
+ return matcher.test(textToMatch)
+ }
+}