Skip to content

Improving api #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 28, 2018
11 changes: 11 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[![downloads][downloads-badge]][npmtrends]
[![MIT License][license-badge]][license]

[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors)
[![PRs Welcome][prs-badge]][prs]
[![Code of Conduct][coc-badge]][coc]

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(<span data-testid="count-value">2</span>)
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(<span data-testid="count-value">2</span>)
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
Expand Down Expand Up @@ -564,7 +603,7 @@ Thanks goes to these people ([emoji key][emojis]):
<!-- prettier-ignore -->
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub><b>Kent C. Dodds</b></sub>](https://kentcdodds.com)<br />[💻](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") | [<img src="https://avatars1.githubusercontent.com/u/2430381?v=4" width="100px;"/><br /><sub><b>Ryan Castner</b></sub>](http://audiolion.github.io)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=audiolion "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/8008023?v=4" width="100px;"/><br /><sub><b>Daniel Sandiego</b></sub>](https://www.dnlsandiego.com)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=dnlsandiego "Code") | [<img src="https://avatars2.githubusercontent.com/u/12592677?v=4" width="100px;"/><br /><sub><b>Paweł Mikołajczyk</b></sub>](https://github.com/Miklet)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=Miklet "Code") | [<img src="https://avatars3.githubusercontent.com/u/464978?v=4" width="100px;"/><br /><sub><b>Alejandro Ñáñez Ortiz</b></sub>](http://co.linkedin.com/in/alejandronanez/)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=alejandronanez "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1402095?v=4" width="100px;"/><br /><sub><b>Matt Parrish</b></sub>](https://github.com/pbomb)<br />[🐛](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") | [<img src="https://avatars1.githubusercontent.com/u/1288694?v=4" width="100px;"/><br /><sub><b>Justin Hall</b></sub>](https://github.com/wKovacs64)<br />[📦](#platform-wKovacs64 "Packaging/porting to new platform") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars2.githubusercontent.com/u/3462296?v=4" width="100px;"/><br /><sub><b>Jonah Moses</b></sub>](https://github.com/JonahMoses)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=JonahMoses "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/1241511?s=460&v=4" width="100px;"/><br /><sub><b>Anto Aravinth</b></sub>](https://github.com/antoaravinth)<br />[💻](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") | [<img src="https://avatars2.githubusercontent.com/u/3462296?v=4" width="100px;"/><br /><sub><b>Jonah Moses</b></sub>](https://github.com/JonahMoses)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=JonahMoses "Documentation") |

<!-- ALL-CONTRIBUTORS-LIST:END -->

Expand Down
1 change: 1 addition & 0 deletions extend-expect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('./dist/extend-expect')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand All @@ -61,4 +62,4 @@
"url": "https://github.com/kentcdodds/react-testing-library/issues"
},
"homepage": "https://github.com/kentcdodds/react-testing-library#readme"
}
}
14 changes: 14 additions & 0 deletions src/__tests__/element-queries.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import {render} from '../'
import '../extend-expect'

test('query can return null', () => {
const {
Expand Down Expand Up @@ -66,4 +67,17 @@ test('totally empty label', () => {
expect(() => getByLabelText('')).toThrowErrorMatchingSnapshot()
})

test('using jest helpers to assert element states', () => {
const {queryByTestId} = render(<span data-testid="count-value">2</span>)

// 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 */
2 changes: 1 addition & 1 deletion src/__tests__/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(<Fetch url={url} />)
const {container, getByText} = render(<Fetch url={url} />)

// Act
Simulate.click(getByText('Fetch'))
Expand Down
7 changes: 7 additions & 0 deletions src/extend-expect.js
Original file line number Diff line number Diff line change
@@ -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
78 changes: 78 additions & 0 deletions src/jest-extensions.js
Original file line number Diff line number Diff line change
@@ -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
12 changes: 2 additions & 10 deletions src/queries.js
Original file line number Diff line number Diff line change
@@ -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).
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -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)
}
}