Skip to content

Commit 8268764

Browse files
smacpherson64Kent C. Dodds
authored and
Kent C. Dodds
committedJul 5, 2018
feat: update toBeInTheDOM (#25)
* #3: Moved tests to their own files for easier viewing. * #3: Added to-contain-element * #3: Updated to-be-in-the-dom to have container element check * #3: Updated types to match functions * #3: Updated documentation for toContainElement and toBeInTheDOM * #3: Added user to contributors * #3: Updated to-contain-element for better code coverage * #3: Reverted tests back to one file * Set container to be optional in types (toBeInTheDOM)
1 parent dd4c8a3 commit 8268764

File tree

6 files changed

+164
-16
lines changed

6 files changed

+164
-16
lines changed
 

‎README.md

+40-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ to maintain.
4646
- [Usage](#usage)
4747
- [Custom matchers](#custom-matchers)
4848
- [`toBeInTheDOM`](#tobeinthedom)
49+
- [`toContainElement`](#tocontainelement)
4950
- [`toHaveTextContent`](#tohavetextcontent)
5051
- [`toHaveAttribute`](#tohaveattribute)
5152
- [`toHaveClass`](#tohaveclass)
@@ -95,7 +96,9 @@ expect.extend({toBeInTheDOM, toHaveClass})
9596

9697
### `toBeInTheDOM`
9798

98-
This allows you to assert whether an element present in the DOM or not.
99+
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.
100+
101+
#### Using the default DOM container
99102

100103
```javascript
101104
// add the custom expect matchers once
@@ -108,6 +111,23 @@ expect(queryByTestId(container, 'count-value1')).not.toBeInTheDOM()
108111
// ...
109112
```
110113

114+
#### Using a specified DOM container
115+
116+
```javascript
117+
// add the custom expect matchers once
118+
import 'jest-dom/extend-expect'
119+
120+
// ...
121+
// <span data-testid="ancestor"><span data-testid="descendant"></span></span>
122+
expect(queryByTestId(container, 'descendant')).toBeInTheDOM(
123+
queryByTestId(container, 'ancestor'),
124+
)
125+
expect(queryByTestId(container, 'ancestor')).not.toBeInTheDOM(
126+
queryByTestId(container, 'descendant'),
127+
)
128+
// ...
129+
```
130+
111131
> Note: when using `toBeInTheDOM`, make sure you use a query function
112132
> (like `queryByTestId`) rather than a get function (like `getByTestId`).
113133
> Otherwise the `get*` function could throw an error before your assertion.
@@ -127,6 +147,24 @@ expect(queryByTestId(container, 'not-empty')).not.toBeEmpty()
127147
// ...
128148
```
129149

150+
### `toContainElement`
151+
152+
This allows you to assert whether an element contains another element as a descendant or not.
153+
154+
```javascript
155+
// add the custom expect matchers once
156+
import 'jest-dom/extend-expect'
157+
158+
// ...
159+
// <span data-testid="ancestor"><span data-testid="descendant"></span></span>
160+
const ancestor = queryByTestId(container, 'ancestor')
161+
const descendant = queryByTestId(container, 'descendant')
162+
163+
expect(ancestor).toContainElement(descendant)
164+
expect(descendant).not.toContainElement(ancestor)
165+
// ...
166+
```
167+
130168
### `toHaveTextContent`
131169

132170
This API allows you to check whether the given element has a text content or not.
@@ -281,6 +319,7 @@ Thanks goes to these people ([emoji key][emojis]):
281319
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
282320
| [<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/gnapse/jest-dom/commits?author=antoaravinth "Code") [⚠️](https://github.com/gnapse/jest-dom/commits?author=antoaravinth "Tests") [📖](https://github.com/gnapse/jest-dom/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/gnapse/jest-dom/commits?author=JonahMoses "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/4002543?v=4" width="100px;"/><br /><sub><b>Łukasz Gandecki</b></sub>](http://team.thebrain.pro)<br />[💻](https://github.com/gnapse/jest-dom/commits?author=lgandecki "Code") [⚠️](https://github.com/gnapse/jest-dom/commits?author=lgandecki "Tests") [📖](https://github.com/gnapse/jest-dom/commits?author=lgandecki "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/498274?v=4" width="100px;"/><br /><sub><b>Ivan Babak</b></sub>](https://sompylasar.github.io)<br />[🐛](https://github.com/gnapse/jest-dom/issues?q=author%3Asompylasar "Bug reports") [🤔](#ideas-sompylasar "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/4439618?v=4" width="100px;"/><br /><sub><b>Jesse Day</b></sub>](https://github.com/jday3)<br />[💻](https://github.com/gnapse/jest-dom/commits?author=jday3 "Code") | [<img src="https://avatars0.githubusercontent.com/u/15199?v=4" width="100px;"/><br /><sub><b>Ernesto García</b></sub>](http://gnapse.github.io)<br />[💻](https://github.com/gnapse/jest-dom/commits?author=gnapse "Code") [📖](https://github.com/gnapse/jest-dom/commits?author=gnapse "Documentation") [⚠️](https://github.com/gnapse/jest-dom/commits?author=gnapse "Tests") | [<img src="https://avatars0.githubusercontent.com/u/79312?v=4" width="100px;"/><br /><sub><b>Mark Volkmann</b></sub>](http://ociweb.com/mark/)<br />[🐛](https://github.com/gnapse/jest-dom/issues?q=author%3Amvolkmann "Bug reports") [💻](https://github.com/gnapse/jest-dom/commits?author=mvolkmann "Code") |
283321
| [<img src="https://avatars1.githubusercontent.com/u/1659099?v=4" width="100px;"/><br /><sub><b>smacpherson64</b></sub>](https://github.com/smacpherson64)<br />[💻](https://github.com/gnapse/jest-dom/commits?author=smacpherson64 "Code") [📖](https://github.com/gnapse/jest-dom/commits?author=smacpherson64 "Documentation") [⚠️](https://github.com/gnapse/jest-dom/commits?author=smacpherson64 "Tests") |
322+
284323
<!-- ALL-CONTRIBUTORS-LIST:END -->
285324

286325
This project follows the [all-contributors][all-contributors] specification.

‎extend-expect.d.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
import {toContainElement} from './src'
2+
13
declare namespace jest {
24
interface Matchers<R> {
3-
toHaveAttribute: (attr: string, value?: string) => R
4-
toHaveTextContent: (text: string) => R
5-
toHaveClass: (className: string) => R
6-
toBeInTheDOM: () => R
5+
toBeInTheDOM: (container?: HTMLElement) => R
76
toBeVisible: () => R
87
toBeEmpty: () => R
8+
toContainElement: (element: HTMLElement) => R
9+
toHaveAttribute: (attr: string, value?: string) => R
10+
toHaveClass: (className: string) => R
911
toHaveStyle: (css: string) => R
12+
toHaveTextContent: (text: string) => R
1013
}
1114
}

‎src/__tests__/index.js

+82-6
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,97 @@ import {render} from './helpers/test-utils'
55
expect.addSnapshotSerializer(plugins.ConvertAnsi)
66

77
test('.toBeInTheDOM', () => {
8-
const {queryByTestId} = render(`<span data-testid="count-value">2</span>`)
8+
const {queryByTestId} = render(`
9+
<span data-testid="count-container">
10+
<span data-testid="count-value"></span>
11+
</span>`)
12+
13+
const containerElement = queryByTestId('count-container')
14+
const valueElement = queryByTestId('count-value')
15+
const nonExistantElement = queryByTestId('not-exists')
16+
const fakeElement = {thisIsNot: 'an html element'}
17+
18+
// Testing toBeInTheDOM without container
19+
expect(valueElement).toBeInTheDOM()
20+
expect(nonExistantElement).not.toBeInTheDOM()
21+
22+
// negative test cases wrapped in throwError assertions for coverage.
23+
expect(() => expect(valueElement).not.toBeInTheDOM()).toThrowError()
24+
25+
expect(() => expect(nonExistantElement).toBeInTheDOM()).toThrowError()
26+
27+
expect(() => expect(fakeElement).toBeInTheDOM()).toThrowError()
28+
29+
// Testing toBeInTheDOM with container
30+
expect(valueElement).toBeInTheDOM(containerElement)
31+
expect(containerElement).not.toBeInTheDOM(valueElement)
32+
33+
expect(() =>
34+
expect(valueElement).not.toBeInTheDOM(containerElement),
35+
).toThrowError()
36+
37+
expect(() =>
38+
expect(nonExistantElement).toBeInTheDOM(containerElement),
39+
).toThrowError()
40+
41+
expect(() =>
42+
expect(fakeElement).toBeInTheDOM(containerElement),
43+
).toThrowError()
44+
45+
expect(() => {
46+
expect(valueElement).toBeInTheDOM(fakeElement)
47+
}).toThrowError()
48+
})
49+
50+
test('.toContainElement', () => {
51+
const {queryByTestId} = render(`
52+
<span data-testid="grandparent">
53+
<span data-testid="parent">
54+
<span data-testid="child"></span>
55+
</span>
56+
</span>
57+
`)
958

10-
expect(queryByTestId('count-value')).toBeInTheDOM()
11-
expect(queryByTestId('count-value1')).not.toBeInTheDOM()
59+
const grandparent = queryByTestId('grandparent')
60+
const parent = queryByTestId('parent')
61+
const child = queryByTestId('child')
62+
const nonExistantElement = queryByTestId('not-exists')
63+
const fakeElement = {thisIsNot: 'an html element'}
64+
65+
expect(grandparent).toContainElement(parent)
66+
expect(grandparent).toContainElement(child)
67+
expect(parent).toContainElement(child)
68+
expect(parent).not.toContainElement(grandparent)
69+
expect(child).not.toContainElement(parent)
70+
expect(child).not.toContainElement(grandparent)
1271

1372
// negative test cases wrapped in throwError assertions for coverage.
1473
expect(() =>
15-
expect(queryByTestId('count-value')).not.toBeInTheDOM(),
74+
expect(nonExistantElement).not.toContainElement(child),
75+
).toThrowError()
76+
expect(() => expect(parent).toContainElement(grandparent)).toThrowError()
77+
expect(() =>
78+
expect(nonExistantElement).toContainElement(grandparent),
79+
).toThrowError()
80+
expect(() =>
81+
expect(grandparent).toContainElement(nonExistantElement),
82+
).toThrowError()
83+
expect(() =>
84+
expect(nonExistantElement).toContainElement(nonExistantElement),
85+
).toThrowError()
86+
expect(() =>
87+
expect(nonExistantElement).toContainElement(fakeElement),
1688
).toThrowError()
1789
expect(() =>
18-
expect(queryByTestId('count-value1')).toBeInTheDOM(),
90+
expect(fakeElement).toContainElement(nonExistantElement),
1991
).toThrowError()
2092
expect(() =>
21-
expect({thisIsNot: 'an html element'}).toBeInTheDOM(),
93+
expect(fakeElement).not.toContainElement(nonExistantElement),
2294
).toThrowError()
95+
expect(() => expect(fakeElement).toContainElement(grandparent)).toThrowError()
96+
expect(() => expect(grandparent).toContainElement(fakeElement)).toThrowError()
97+
expect(() => expect(fakeElement).toContainElement(fakeElement)).toThrowError()
98+
expect(() => expect(grandparent).not.toContainElement(child)).toThrowError()
2399
})
24100

25101
test('.toBeEmpty', () => {

‎src/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {toBeInTheDOM} from './to-be-in-the-dom'
22
import {toBeEmpty} from './to-be-empty'
3+
import {toContainElement} from './to-contain-element'
34
import {toHaveTextContent} from './to-have-text-content'
45
import {toHaveAttribute} from './to-have-attribute'
56
import {toHaveClass} from './to-have-class'
@@ -9,6 +10,7 @@ import {toBeVisible} from './to-be-visible'
910
export {
1011
toBeInTheDOM,
1112
toBeEmpty,
13+
toContainElement,
1214
toHaveTextContent,
1315
toHaveAttribute,
1416
toHaveClass,

‎src/to-be-in-the-dom.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import {matcherHint, printReceived} from 'jest-matcher-utils'
22
import {checkHtmlElement} from './utils'
33

4-
export function toBeInTheDOM(received) {
5-
if (received) {
6-
checkHtmlElement(received, toBeInTheDOM, this)
4+
export function toBeInTheDOM(element, container) {
5+
if (element) {
6+
checkHtmlElement(element, toBeInTheDOM, this)
77
}
8+
9+
if (container) {
10+
checkHtmlElement(container, toBeInTheDOM, this)
11+
}
12+
813
return {
9-
pass: !!received,
14+
pass: container ? container.contains(element) : !!element,
1015
message: () => {
1116
return [
1217
matcherHint(`${this.isNot ? '.not' : ''}.toBeInTheDOM`, 'element', ''),
1318
'',
1419
'Received:',
15-
` ${printReceived(received ? received.cloneNode(false) : received)}`,
20+
` ${printReceived(element ? element.cloneNode(false) : element)}`,
1621
].join('\n')
1722
},
1823
}

‎src/to-contain-element.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {matcherHint, printReceived} from 'jest-matcher-utils'
2+
import {checkHtmlElement} from './utils'
3+
4+
export function toContainElement(container, element) {
5+
checkHtmlElement(container, toContainElement, this)
6+
checkHtmlElement(element, toContainElement, this)
7+
8+
return {
9+
pass: container.contains(element),
10+
message: () => {
11+
return [
12+
matcherHint(
13+
`${this.isNot ? '.not' : ''}.toContainElement`,
14+
'element',
15+
'',
16+
),
17+
'',
18+
'Received:',
19+
` ${printReceived(container.cloneNode(false))}`,
20+
].join('\n')
21+
},
22+
}
23+
}

0 commit comments

Comments
 (0)
Please sign in to comment.