Skip to content

Commit df66c0d

Browse files
smacpherson64gnapse
authored andcommitted
feat: toBeInTheDocument and toContainElement accept element to be null (#47)
In both cases, `null` is not considered to be contained in the provided context (the container or document) so these matchers will pass or not accordingly. And in the case of toCointainElement, the container is still required not to be null or undefined.
1 parent 3f351eb commit df66c0d

File tree

6 files changed

+58
-30
lines changed

6 files changed

+58
-30
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,13 @@ import 'jest-dom/extend-expect'
136136

137137
// const htmlElement = document.querySelector('[data-testid="html-element"]')
138138
// const svgElement = document.querySelector('[data-testid="svg-element"]')
139+
// const nonExistantElement = document.querySelector('does-not-exist')
139140
// const detachedElement = document.createElement('div')
140141

141142
expect(htmlElement).toBeInTheDocument()
142143
expect(svgElement).toBeInTheDocument()
143144
expect(detacthedElement).not.toBeInTheDocument()
145+
expect(nonExistantElement).not.toBeInTheDocument()
144146
// ...
145147
```
146148

@@ -149,7 +151,7 @@ expect(detacthedElement).not.toBeInTheDocument()
149151
### `toContainElement`
150152

151153
```typescript
152-
toContainElement(element: HTMLElement | SVGElement)
154+
toContainElement(element: HTMLElement | SVGElement | null)
153155
```
154156

155157
This allows you to assert whether an element contains another element as a descendant or not.
@@ -162,9 +164,11 @@ import 'jest-dom/extend-expect'
162164
// <span data-testid="ancestor"><span data-testid="descendant"></span></span>
163165
const ancestor = queryByTestId(container, 'ancestor')
164166
const descendant = queryByTestId(container, 'descendant')
167+
const nonExistantElement = queryByTestId(container, 'does-not-exist')
165168

166169
expect(ancestor).toContainElement(descendant)
167170
expect(descendant).not.toContainElement(ancestor)
171+
expect(ancestor).not.toContainElement(nonExistantElement)
168172
// ...
169173
```
170174

extend-expect.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ declare namespace jest {
88
toBeVisible(): R
99
toBeEmpty(): R
1010
toBeDisabled(): R
11-
toContainElement(element: HTMLElement | SVGElement): R
11+
toContainElement(element: HTMLElement | SVGElement | null): R
1212
toContainHTML(htmlText: string): R
1313
toHaveAttribute(attr: string, value?: string): R
1414
toHaveClass(...classNames: string[]): R

src/__tests__/to-be-in-the-document.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ test('.toBeInTheDocument', () => {
1313
expect(htmlElement).toBeInTheDocument()
1414
expect(svgElement).toBeInTheDocument()
1515
expect(detachedElement).not.toBeInTheDocument()
16+
expect(nullElement).not.toBeInTheDocument()
1617

1718
// negative test cases wrapped in throwError assertions for coverage.
1819
expect(() => expect(htmlElement).not.toBeInTheDocument()).toThrowError()

src/__tests__/to-contain-element.js

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import {render} from './helpers/test-utils'
22

3-
test('.toContainElement', () => {
4-
const {queryByTestId} = render(`
5-
<span data-testid="grandparent">
6-
<span data-testid="parent">
7-
<span data-testid="child"></span>
8-
</span>
9-
<svg data-testid="svg-element"></svg>
10-
</span>
11-
`)
3+
const {queryByTestId} = render(`
4+
<span data-testid="grandparent">
5+
<span data-testid="parent">
6+
<span data-testid="child"></span>
7+
</span>
8+
<svg data-testid="svg-element"></svg>
9+
</span>
10+
`)
1211

13-
const grandparent = queryByTestId('grandparent')
14-
const parent = queryByTestId('parent')
15-
const child = queryByTestId('child')
16-
const svgElement = queryByTestId('svg-element')
17-
const nonExistantElement = queryByTestId('not-exists')
18-
const fakeElement = {thisIsNot: 'an html element'}
12+
const grandparent = queryByTestId('grandparent')
13+
const parent = queryByTestId('parent')
14+
const child = queryByTestId('child')
15+
const svgElement = queryByTestId('svg-element')
16+
const nonExistantElement = queryByTestId('not-exists')
17+
const fakeElement = {thisIsNot: 'an html element'}
1918

19+
test('.toContainElement positive test cases', () => {
2020
expect(grandparent).toContainElement(parent)
2121
expect(grandparent).toContainElement(child)
2222
expect(grandparent).toContainElement(svgElement)
@@ -26,8 +26,10 @@ test('.toContainElement', () => {
2626
expect(child).not.toContainElement(parent)
2727
expect(child).not.toContainElement(grandparent)
2828
expect(child).not.toContainElement(svgElement)
29+
expect(grandparent).not.toContainElement(nonExistantElement)
30+
})
2931

30-
// negative test cases wrapped in throwError assertions for coverage.
32+
test('.toContainElement negative test cases', () => {
3133
expect(() =>
3234
expect(nonExistantElement).not.toContainElement(child),
3335
).toThrowError()
@@ -55,6 +57,9 @@ test('.toContainElement', () => {
5557
expect(() => expect(fakeElement).toContainElement(fakeElement)).toThrowError()
5658
expect(() => expect(grandparent).not.toContainElement(child)).toThrowError()
5759
expect(() =>
58-
expect(grandparent).not.toContainElement(svgElement),
60+
expect(grandparent).not.toContainElement(svgElement)
61+
).toThrowError()
62+
expect(() =>
63+
expect(grandparent).not.toContainElement(undefined)
5964
).toThrowError()
6065
})

src/to-be-in-the-document.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
import {matcherHint, printReceived} from 'jest-matcher-utils'
1+
import {
2+
matcherHint,
3+
stringify,
4+
RECEIVED_COLOR as receivedColor,
5+
} from 'jest-matcher-utils'
26
import {checkHtmlElement, checkDocumentKey} from './utils'
37

48
export function toBeInTheDocument(element) {
5-
checkHtmlElement(element, toBeInTheDocument, this)
9+
if (element !== null) {
10+
checkHtmlElement(element, toBeInTheDocument, this)
11+
}
12+
613
checkDocumentKey(global.document, 'documentElement', toBeInTheDocument)
714

815
return {
@@ -15,10 +22,12 @@ export function toBeInTheDocument(element) {
1522
'',
1623
),
1724
'',
18-
'Received:',
19-
` ${printReceived(
20-
element.hasChildNodes() ? element.cloneNode(false) : element,
21-
)}`,
25+
receivedColor(`${stringify(
26+
document.documentElement.cloneNode(false),
27+
)} ${this.isNot ? 'contains:' : 'does not contain:'} ${stringify(
28+
element ? element.cloneNode(false) : element,
29+
)}
30+
`),
2231
].join('\n')
2332
},
2433
}

src/to-contain-element.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import {matcherHint, printReceived} from 'jest-matcher-utils'
1+
import {
2+
matcherHint,
3+
stringify,
4+
RECEIVED_COLOR as receivedColor,
5+
} from 'jest-matcher-utils'
26
import {checkHtmlElement} from './utils'
37

48
export function toContainElement(container, element) {
59
checkHtmlElement(container, toContainElement, this)
6-
checkHtmlElement(element, toContainElement, this)
10+
11+
if (element !== null) {
12+
checkHtmlElement(element, toContainElement, this)
13+
}
714

815
return {
916
pass: container.contains(element),
@@ -12,11 +19,13 @@ export function toContainElement(container, element) {
1219
matcherHint(
1320
`${this.isNot ? '.not' : ''}.toContainElement`,
1421
'element',
15-
'',
22+
'element',
1623
),
1724
'',
18-
'Received:',
19-
` ${printReceived(container.cloneNode(false))}`,
25+
receivedColor(`${stringify(container.cloneNode(false))} ${
26+
this.isNot ? 'contains:' : 'does not contain:'
27+
} ${stringify(element ? element.cloneNode(false) : element)}
28+
`),
2029
].join('\n')
2130
},
2231
}

0 commit comments

Comments
 (0)