Skip to content
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

feat: update toBeInTheDOM #25

Merged
merged 11 commits into from
Jul 5, 2018
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ to maintain.
- [Usage](#usage)
- [Custom matchers](#custom-matchers)
- [`toBeInTheDOM`](#tobeinthedom)
- [`toContainElement`](#tocontainelement)
- [`toHaveTextContent`](#tohavetextcontent)
- [`toHaveAttribute`](#tohaveattribute)
- [`toHaveClass`](#tohaveclass)
Expand Down Expand Up @@ -95,7 +96,9 @@ expect.extend({toBeInTheDOM, toHaveClass})

### `toBeInTheDOM`

This allows you to assert whether an element present in the DOM or not.
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.

#### Using the default DOM container

```javascript
// add the custom expect matchers once
Expand All @@ -108,6 +111,23 @@ expect(queryByTestId(container, 'count-value1')).not.toBeInTheDOM()
// ...
```

#### Using a specified DOM container

```javascript
// add the custom expect matchers once
import 'jest-dom/extend-expect'

// ...
// <span data-testid="ancestor"><span data-testid="descendant"></span></span>
expect(queryByTestId(container, 'descendant')).toBeInTheDOM(
queryByTestId(container, 'ancestor'),
)
expect(queryByTestId(container, 'ancestor')).not.toBeInTheDOM(
queryByTestId(container, 'descendant'),
)
// ...
```

> 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.
Expand All @@ -127,6 +147,24 @@ expect(queryByTestId(container, 'not-empty')).not.toBeEmpty()
// ...
```

### `toContainElement`

This allows you to assert whether an element contains another element as a descendant or not.

```javascript
// add the custom expect matchers once
import 'jest-dom/extend-expect'

// ...
// <span data-testid="ancestor"><span data-testid="descendant"></span></span>
const ancestor = queryByTestId(container, 'ancestor')
const descendant = queryByTestId(container, 'descendant')

expect(ancestor).toContainElement(descendant)
expect(descendant).not.toContainElement(ancestor)
// ...
```

### `toHaveTextContent`

This API allows you to check whether the given element has a text content or not.
Expand Down Expand Up @@ -281,6 +319,7 @@ Thanks goes to these people ([emoji key][emojis]):
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<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") |
| [<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") |

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

This project follows the [all-contributors][all-contributors] specification.
Expand Down
11 changes: 7 additions & 4 deletions extend-expect.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {toContainElement} from './src'

declare namespace jest {
interface Matchers<R> {
toHaveAttribute: (attr: string, value?: string) => R
toHaveTextContent: (text: string) => R
toHaveClass: (className: string) => R
toBeInTheDOM: () => R
toBeInTheDOM: (container?: HTMLElement) => R
toBeVisible: () => R
toBeEmpty: () => R
toContainElement: (element: HTMLElement) => R
toHaveAttribute: (attr: string, value?: string) => R
toHaveClass: (className: string) => R
toHaveStyle: (css: string) => R
toHaveTextContent: (text: string) => R
}
}
88 changes: 82 additions & 6 deletions src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,97 @@ import {render} from './helpers/test-utils'
expect.addSnapshotSerializer(plugins.ConvertAnsi)

test('.toBeInTheDOM', () => {
const {queryByTestId} = render(`<span data-testid="count-value">2</span>`)
const {queryByTestId} = render(`
<span data-testid="count-container">
<span data-testid="count-value"></span>
</span>`)

const containerElement = queryByTestId('count-container')
const valueElement = queryByTestId('count-value')
const nonExistantElement = queryByTestId('not-exists')
const fakeElement = {thisIsNot: 'an html element'}

// Testing toBeInTheDOM without container
expect(valueElement).toBeInTheDOM()
expect(nonExistantElement).not.toBeInTheDOM()

// negative test cases wrapped in throwError assertions for coverage.
expect(() => expect(valueElement).not.toBeInTheDOM()).toThrowError()

expect(() => expect(nonExistantElement).toBeInTheDOM()).toThrowError()

expect(() => expect(fakeElement).toBeInTheDOM()).toThrowError()

// Testing toBeInTheDOM with container
expect(valueElement).toBeInTheDOM(containerElement)
expect(containerElement).not.toBeInTheDOM(valueElement)

expect(() =>
expect(valueElement).not.toBeInTheDOM(containerElement),
).toThrowError()

expect(() =>
expect(nonExistantElement).toBeInTheDOM(containerElement),
).toThrowError()

expect(() =>
expect(fakeElement).toBeInTheDOM(containerElement),
).toThrowError()

expect(() => {
expect(valueElement).toBeInTheDOM(fakeElement)
}).toThrowError()
})

test('.toContainElement', () => {
const {queryByTestId} = render(`
<span data-testid="grandparent">
<span data-testid="parent">
<span data-testid="child"></span>
</span>
</span>
`)

expect(queryByTestId('count-value')).toBeInTheDOM()
expect(queryByTestId('count-value1')).not.toBeInTheDOM()
const grandparent = queryByTestId('grandparent')
const parent = queryByTestId('parent')
const child = queryByTestId('child')
const nonExistantElement = queryByTestId('not-exists')
const fakeElement = {thisIsNot: 'an html element'}

expect(grandparent).toContainElement(parent)
expect(grandparent).toContainElement(child)
expect(parent).toContainElement(child)
expect(parent).not.toContainElement(grandparent)
expect(child).not.toContainElement(parent)
expect(child).not.toContainElement(grandparent)

// negative test cases wrapped in throwError assertions for coverage.
expect(() =>
expect(queryByTestId('count-value')).not.toBeInTheDOM(),
expect(nonExistantElement).not.toContainElement(child),
).toThrowError()
expect(() => expect(parent).toContainElement(grandparent)).toThrowError()
expect(() =>
expect(nonExistantElement).toContainElement(grandparent),
).toThrowError()
expect(() =>
expect(grandparent).toContainElement(nonExistantElement),
).toThrowError()
expect(() =>
expect(nonExistantElement).toContainElement(nonExistantElement),
).toThrowError()
expect(() =>
expect(nonExistantElement).toContainElement(fakeElement),
).toThrowError()
expect(() =>
expect(queryByTestId('count-value1')).toBeInTheDOM(),
expect(fakeElement).toContainElement(nonExistantElement),
).toThrowError()
expect(() =>
expect({thisIsNot: 'an html element'}).toBeInTheDOM(),
expect(fakeElement).not.toContainElement(nonExistantElement),
).toThrowError()
expect(() => expect(fakeElement).toContainElement(grandparent)).toThrowError()
expect(() => expect(grandparent).toContainElement(fakeElement)).toThrowError()
expect(() => expect(fakeElement).toContainElement(fakeElement)).toThrowError()
expect(() => expect(grandparent).not.toContainElement(child)).toThrowError()
})

test('.toBeEmpty', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {toBeInTheDOM} from './to-be-in-the-dom'
import {toBeEmpty} from './to-be-empty'
import {toContainElement} from './to-contain-element'
import {toHaveTextContent} from './to-have-text-content'
import {toHaveAttribute} from './to-have-attribute'
import {toHaveClass} from './to-have-class'
Expand All @@ -9,6 +10,7 @@ import {toBeVisible} from './to-be-visible'
export {
toBeInTheDOM,
toBeEmpty,
toContainElement,
toHaveTextContent,
toHaveAttribute,
toHaveClass,
Expand Down
15 changes: 10 additions & 5 deletions src/to-be-in-the-dom.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import {matcherHint, printReceived} from 'jest-matcher-utils'
import {checkHtmlElement} from './utils'

export function toBeInTheDOM(received) {
if (received) {
checkHtmlElement(received, toBeInTheDOM, this)
export function toBeInTheDOM(element, container) {
if (element) {
checkHtmlElement(element, toBeInTheDOM, this)
}

if (container) {
checkHtmlElement(container, toBeInTheDOM, this)
}

return {
pass: !!received,
pass: container ? container.contains(element) : !!element,
message: () => {
return [
matcherHint(`${this.isNot ? '.not' : ''}.toBeInTheDOM`, 'element', ''),
'',
'Received:',
` ${printReceived(received ? received.cloneNode(false) : received)}`,
` ${printReceived(element ? element.cloneNode(false) : element)}`,
].join('\n')
},
}
Expand Down
23 changes: 23 additions & 0 deletions src/to-contain-element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {matcherHint, printReceived} from 'jest-matcher-utils'
import {checkHtmlElement} from './utils'

export function toContainElement(container, element) {
checkHtmlElement(container, toContainElement, this)
checkHtmlElement(element, toContainElement, this)

return {
pass: container.contains(element),
message: () => {
return [
matcherHint(
`${this.isNot ? '.not' : ''}.toContainElement`,
'element',
'',
),
'',
'Received:',
` ${printReceived(container.cloneNode(false))}`,
].join('\n')
},
}
}