diff --git a/.all-contributorsrc b/.all-contributorsrc
index 05f6e72f..b61e4c41 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -126,6 +126,17 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "gnapse",
+ "name": "Ernesto Garcรญa",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/15199?v=4",
+ "profile": "http://gnapse.github.io",
+ "contributions": [
+ "question",
+ "code",
+ "doc"
+ ]
}
]
}
diff --git a/README.md b/README.md
index 5d25e52b..9c22d936 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]
@@ -82,7 +82,8 @@ facilitate testing implementation details). Read more about this in
* [Custom Jest Matchers](#custom-jest-matchers)
* [`toBeInTheDOM`](#tobeinthedom)
* [`toHaveTextContent`](#tohavetextcontent)
- * [Custom Jest Matchers - Typescript](#custom-jest-matchers-typescript)
+ * [`toHaveAttribute`](#tohaveattribute)
+ * [Custom Jest Matchers - Typescript](#custom-jest-matchers---typescript)
* [`TextMatch`](#textmatch)
* [`query` APIs](#query-apis)
* [Examples](#examples)
@@ -138,6 +139,7 @@ test('Fetch makes an API call and displays the greeting when load-greeting is cl
expect(axiosMock.get).toHaveBeenCalledTimes(1)
expect(axiosMock.get).toHaveBeenCalledWith(url)
expect(getByTestId('greeting-text')).toHaveTextContent('hello there')
+ expect(getByTestId('ok-button')).toHaveAttribute('disabled')
// snapshots work great with regular DOM nodes!
expect(container.firstChild).toMatchSnapshot()
})
@@ -347,33 +349,55 @@ expect(getByTestId('count-value')).toHaveTextContent('2')
expect(getByTestId('count-value')).not.toHaveTextContent('21')
// ...
```
+
+### `toHaveAttribute`
+
+This allows you to check wether 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
+import 'react-testing-library/extend-expect'
+
+// ...
+const {getByTestId} = render(
+ ,
+)
+expect(getByTestId('ok-button')).toHaveAttribute('disabled')
+expect(getByTestId('ok-button')).toHaveAttribute('type', 'submit')
+expect(getByTestId('ok-button')).not.toHaveAttribute('type', 'button')
+// ...
+```
+
### Custom Jest Matchers - Typescript
-When you use custom Jest Matchers with Typescript, you will need to extend the type signature of `jest.Matchers`, then cast the result of `expect` accordingly. Here's a handy usage example:
+When you use custom Jest Matchers with Typescript, you will need to extend the type signature of `jest.Matchers`, then cast the result of `expect` accordingly. Here's a handy usage example:
```typescript
// this adds custom expect matchers
-import 'react-testing-library/extend-expect';
+import 'react-testing-library/extend-expect'
interface ExtendedMatchers extends jest.Matchers {
- toHaveTextContent: (htmlElement: string) => object;
- toBeInTheDOM: () => void;
+ toHaveTextContent: (htmlElement: string) => object
+ toBeInTheDOM: () => void
}
test('renders the tooltip as expected', async () => {
const {
// getByLabelText,
getByText,
// getByTestId,
- container
- } = render(Child);
+ container,
+ } = render(Child)
// tests rendering of the child
- getByText('Child');
+ getByText('Child')
// tests rendering of tooltip label
- (expect(getByText('hello world')) as ExtendedMatchers).toHaveTextContent(
- 'hello world'
- );
+ ;(expect(getByText('hello world')) as ExtendedMatchers).toHaveTextContent(
+ 'hello world',
+ )
// snapshots work great with regular DOM nodes!
- expect(container.firstChild).toMatchSnapshot();
-});
+ expect(container.firstChild).toMatchSnapshot()
+})
```
## `TextMatch`
@@ -715,10 +739,12 @@ light-weight, simple, and understandable.
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") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
-| [
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") | [
ลukasz Gandecki](http://team.thebrain.pro)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Code") [โ ๏ธ](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Tests") [๐](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Documentation") | [
Ivan Babak](https://sompylasar.github.io)
[๐](https://github.com/kentcdodds/react-testing-library/issues?q=author%3Asompylasar "Bug reports") [๐ค](#ideas-sompylasar "Ideas, Planning, & Feedback") | [
Jesse Day](https://github.com/jday3)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=jday3 "Code") |
+| [
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") | [
ลukasz Gandecki](http://team.thebrain.pro)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Code") [โ ๏ธ](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Tests") [๐](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Documentation") | [
Ivan Babak](https://sompylasar.github.io)
[๐](https://github.com/kentcdodds/react-testing-library/issues?q=author%3Asompylasar "Bug reports") [๐ค](#ideas-sompylasar "Ideas, Planning, & Feedback") | [
Jesse Day](https://github.com/jday3)
[๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=jday3 "Code") | [
Ernesto Garcรญa](http://gnapse.github.io)
[๐ฌ](#question-gnapse "Answering Questions") [๐ป](https://github.com/kentcdodds/react-testing-library/commits?author=gnapse "Code") [๐](https://github.com/kentcdodds/react-testing-library/commits?author=gnapse "Documentation") |
+
This project follows the [all-contributors][all-contributors] specification.
diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js
index f8fd4830..cf21ecd0 100644
--- a/src/__tests__/element-queries.js
+++ b/src/__tests__/element-queries.js
@@ -117,4 +117,34 @@ test('using jest helpers to assert element states', () => {
).toThrowError()
})
+test('using jest helpers to check element attributes', () => {
+ const {queryByTestId} = render(
+ ,
+ )
+
+ expect(queryByTestId('ok-button')).toHaveAttribute('disabled')
+ expect(queryByTestId('ok-button')).toHaveAttribute('type')
+ expect(queryByTestId('ok-button')).not.toHaveAttribute('class')
+ expect(queryByTestId('ok-button')).toHaveAttribute('type', 'submit')
+ expect(queryByTestId('ok-button')).not.toHaveAttribute('type', 'button')
+
+ expect(() =>
+ expect(queryByTestId('ok-button')).not.toHaveAttribute('disabled'),
+ ).toThrowError()
+ expect(() =>
+ expect(queryByTestId('ok-button')).not.toHaveAttribute('type'),
+ ).toThrowError()
+ expect(() =>
+ expect(queryByTestId('ok-button')).toHaveAttribute('class'),
+ ).toThrowError()
+ expect(() =>
+ expect(queryByTestId('ok-button')).not.toHaveAttribute('type', 'submit'),
+ ).toThrowError()
+ expect(() =>
+ expect(queryByTestId('ok-button')).toHaveAttribute('type', 'button'),
+ ).toThrowError()
+})
+
/* eslint jsx-a11y/label-has-for:0 */
diff --git a/src/extend-expect.js b/src/extend-expect.js
index b2b99844..a2ddc2b4 100644
--- a/src/extend-expect.js
+++ b/src/extend-expect.js
@@ -1,4 +1,4 @@
import extensions from './jest-extensions'
-const {toBeInTheDOM, toHaveTextContent} = extensions
-expect.extend({toBeInTheDOM, toHaveTextContent})
+const {toBeInTheDOM, toHaveTextContent, toHaveAttribute} = extensions
+expect.extend({toBeInTheDOM, toHaveTextContent, toHaveAttribute})
diff --git a/src/jest-extensions.js b/src/jest-extensions.js
index c94e29f6..d0b74b8f 100644
--- a/src/jest-extensions.js
+++ b/src/jest-extensions.js
@@ -1,4 +1,12 @@
-import {matcherHint, printReceived, printExpected} from 'jest-matcher-utils' //eslint-disable-line import/no-extraneous-dependencies
+//eslint-disable-next-line import/no-extraneous-dependencies
+import {
+ matcherHint,
+ printReceived,
+ printExpected,
+ stringify,
+ RECEIVED_COLOR as receivedColor,
+ EXPECTED_COLOR as expectedColor,
+} from 'jest-matcher-utils'
import {matches} from './utils'
function getDisplayName(subject) {
@@ -9,10 +17,30 @@ function getDisplayName(subject) {
}
}
+function checkHtmlElement(htmlElement) {
+ if (!(htmlElement instanceof HTMLElement)) {
+ throw new Error(
+ `The given subject is a ${getDisplayName(
+ htmlElement,
+ )}, not an HTMLElement`,
+ )
+ }
+}
+
const assertMessage = (assertionName, message, received, expected) =>
`${matcherHint(`${assertionName}`, 'received', '')} \n${message}: ` +
`${printExpected(expected)} \nReceived: ${printReceived(received)}`
+function printAttribute(name, value) {
+ return value === undefined ? name : `${name}=${stringify(value)}`
+}
+
+function getAttributeComment(name, value) {
+ return value === undefined
+ ? `element.hasAttribute(${stringify(name)})`
+ : `element.getAttribute(${stringify(name)}) === ${stringify(value)}`
+}
+
const extensions = {
toBeInTheDOM(received) {
getDisplayName(received)
@@ -42,13 +70,7 @@ const extensions = {
},
toHaveTextContent(htmlElement, checkWith) {
- if (!(htmlElement instanceof HTMLElement))
- throw new Error(
- `The given subject is a ${getDisplayName(
- htmlElement,
- )}, not an HTMLElement`,
- )
-
+ checkHtmlElement(htmlElement)
const textContent = htmlElement.textContent
const pass = matches(textContent, htmlElement, checkWith)
if (pass) {
@@ -75,6 +97,41 @@ const extensions = {
}
}
},
+
+ toHaveAttribute(htmlElement, name, expectedValue) {
+ checkHtmlElement(htmlElement)
+ const isExpectedValuePresent = expectedValue !== undefined
+ const hasAttribute = htmlElement.hasAttribute(name)
+ const receivedValue = htmlElement.getAttribute(name)
+ return {
+ pass: isExpectedValuePresent
+ ? hasAttribute && receivedValue === expectedValue
+ : hasAttribute,
+ message: () => {
+ const to = this.isNot ? 'not to' : 'to'
+ const receivedAttribute = receivedColor(
+ hasAttribute
+ ? printAttribute(name, receivedValue)
+ : 'attribute was not found',
+ )
+ const expectedMsg = `Expected the element ${to} have attribute:\n ${expectedColor(
+ printAttribute(name, expectedValue),
+ )}`
+ const matcher = matcherHint(
+ `${this.isNot ? '.not' : ''}.toHaveAttribute`,
+ 'element',
+ printExpected(name),
+ {
+ secondArgument: isExpectedValuePresent
+ ? printExpected(expectedValue)
+ : undefined,
+ comment: getAttributeComment(name, expectedValue),
+ },
+ )
+ return `${matcher}\n\n${expectedMsg}\nReceived:\n ${receivedAttribute}`
+ },
+ }
+ },
}
export default extensions