Skip to content

Commit 4cf93d2

Browse files
Alex WendteKent C. Dodds
Alex Wendte
authored and
Kent C. Dodds
committed
feat(waitForDomChange): add new method (testing-library#118)
* waitForDomChange is implemented along with tests, documentation, and types. * Implements changes requested on 10-9-18. Removes matchSnapshots. Correctly formats README.md. Changes Promise to return a mutationsList. Adds documentation for mutationsList. * causes waitForElement to throw an error if callback is not defined. changes tests to account for new behavior. removes the documentation around the default callback. adds MutationObserver documentation for waitForDomChange
1 parent 3ab989f commit 4cf93d2

11 files changed

+362
-96
lines changed

.all-contributorsrc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,17 @@
336336
"ideas",
337337
"test"
338338
]
339+
},
340+
{
341+
"login": "themostcolm",
342+
"name": "Alex Wendte",
343+
"avatar_url": "https://avatars2.githubusercontent.com/u/5779538?v=4",
344+
"profile": "http://www.wendtedesigns.com/",
345+
"contributions": [
346+
"code",
347+
"doc",
348+
"test"
349+
]
339350
}
340351
]
341352
}

README.md

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
[![downloads][downloads-badge]][npmtrends]
1717
[![MIT License][license-badge]][license]
1818

19-
[![All Contributors](https://img.shields.io/badge/all_contributors-33-orange.svg?style=flat-square)](#contributors)
19+
[![All Contributors](https://img.shields.io/badge/all_contributors-34-orange.svg?style=flat-square)](#contributors)
2020
[![PRs Welcome][prs-badge]][prs]
2121
[![Code of Conduct][coc-badge]][coc]
2222

@@ -82,7 +82,9 @@ when a real user uses it.
8282
- [`getByTestId`](#getbytestid)
8383
- [`wait`](#wait)
8484
- [`waitForElement`](#waitforelement)
85+
- [`waitForDomChange`](#waitfordomchange)
8586
- [`fireEvent`](#fireevent)
87+
- [`getNodeText`](#getnodetext)
8688
- [Custom Jest Matchers](#custom-jest-matchers)
8789
- [Using other assertion libraries](#using-other-assertion-libraries)
8890
- [`TextMatch`](#textmatch)
@@ -492,7 +494,7 @@ intervals.
492494

493495
```typescript
494496
function waitForElement<T>(
495-
callback?: () => T | null | undefined,
497+
callback: () => T,
496498
options?: {
497499
container?: HTMLElement
498500
timeout?: number
@@ -502,9 +504,8 @@ function waitForElement<T>(
502504
```
503505

504506
When in need to wait for DOM elements to appear, disappear, or change you can use `waitForElement`.
505-
The `waitForElement` function is a small wrapper
506-
around the
507-
[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
507+
The `waitForElement` function is a small wrapper around the [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
508+
508509
Here's a simple example:
509510

510511
```javascript
@@ -536,8 +537,62 @@ const [usernameElement, passwordElement] = waitForElement(
536537

537538
Using [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) is more efficient than polling the DOM at regular intervals with `wait`. This library sets up a [`'mutationobserver-shim'`](https://github.com/megawac/MutationObserver.js) on the global `window` object for cross-platform compatibility with older browsers and the [`jsdom`](https://github.com/jsdom/jsdom/issues/639) that is usually used in Node-based tests.
538539

539-
The default `callback` is a no-op function (used like `await waitForElement()`). This can
540-
be helpful if you only need to wait for the next DOM change (see [`mutationObserverOptions`](#mutationobserveroptions) to learn which changes are detected).
540+
The default `container` is the global `document`. Make sure the elements you wait for will be attached to it, or set a different `container`.
541+
542+
The default `timeout` is `4500ms` which will keep you under
543+
[Jest's default timeout of `5000ms`](https://facebook.github.io/jest/docs/en/jest-object.html#jestsettimeouttimeout).
544+
545+
<a name="mutationobserveroptions"></a>The default `mutationObserverOptions` is `{subtree: true, childList: true, attributes: true, characterData: true}` which will detect
546+
additions and removals of child elements (including text nodes) in the `container` and any of its descendants. It will also detect attribute changes.
547+
548+
### `waitForDomChange`
549+
550+
```typescript
551+
function waitForDomChange<T>(options?: {
552+
container?: HTMLElement
553+
timeout?: number
554+
mutationObserverOptions?: MutationObserverInit
555+
}): Promise<T>
556+
```
557+
558+
When in need to wait for the DOM to change you can use `waitForDomChange`. The `waitForDomChange`
559+
function is a small wrapper around the
560+
[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
561+
562+
Here is an example where the promise will be resolved because the container is changed:
563+
564+
```javascript
565+
const container = document.createElement('div')
566+
waitForDomChange({container})
567+
.then(() => console.log('DOM changed!'))
568+
.catch(err => console.log(`Error you need to deal with: ${err}`))
569+
container.append(document.createElement('p'))
570+
// if 👆 was the only code affecting the container and it was not run,
571+
// waitForDomChange would throw an error
572+
```
573+
574+
The promise will resolve with a [`mutationsList`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver) which you can use to determine what kind of a change (or changes) affected the container
575+
576+
```javascript
577+
const container = document.createElement('div')
578+
container.setAttribute('data-cool', 'true')
579+
waitForDomChange({container}).then(mutationsList => {
580+
const mutation = mutationsList[0]
581+
console.log(
582+
`was cool: ${mutation.oldValue}\ncurrently cool: ${
583+
mutation.target.dataset.cool
584+
}`,
585+
)
586+
})
587+
container.setAttribute('data-cool', 'false')
588+
/*
589+
logs:
590+
was cool: true
591+
currently cool: false
592+
*/
593+
```
594+
595+
Using [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) is more efficient than polling the DOM at regular intervals with `wait`. This library sets up a [`'mutationobserver-shim'`](https://github.com/megawac/MutationObserver.js) on the global `window` object for cross-platform compatibility with older browsers and the [`jsdom`](https://github.com/jsdom/jsdom/issues/639) that is usually used in Node-based tests.
541596

542597
The default `container` is the global `document`. Make sure the elements you wait for will be attached to it, or set a different `container`.
543598

@@ -603,7 +658,7 @@ fireEvent.change(getByLabelText(/picture/i), {
603658
})
604659
```
605660

606-
#### `getNodeText`
661+
### `getNodeText`
607662

608663
```typescript
609664
getNodeText(node: HTMLElement)
@@ -1000,7 +1055,7 @@ Thanks goes to these people ([emoji key][emojis]):
10001055
| [<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/dom-testing-library/commits?author=antoaravinth "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=antoaravinth "Tests") [📖](https://github.com/kentcdodds/dom-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/dom-testing-library/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/kentcdodds/dom-testing-library/commits?author=lgandecki "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=lgandecki "Tests") [📖](https://github.com/kentcdodds/dom-testing-library/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/kentcdodds/dom-testing-library/issues?q=author%3Asompylasar "Bug reports") [🤔](#ideas-sompylasar "Ideas, Planning, & Feedback") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=sompylasar "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=sompylasar "Documentation") | [<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/kentcdodds/dom-testing-library/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 />[💬](#question-gnapse "Answering Questions") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=gnapse "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=gnapse "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/2747424?v=4" width="100px;"/><br /><sub><b>Josef Maxx Blake</b></sub>](http://jomaxx.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Tests") |
10011056
| [<img src="https://avatars3.githubusercontent.com/u/725236?v=4" width="100px;"/><br /><sub><b>Alex Cook</b></sub>](https://github.com/alecook)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=alecook "Documentation") [💡](#example-alecook "Examples") | [<img src="https://avatars3.githubusercontent.com/u/10348212?v=4" width="100px;"/><br /><sub><b>Daniel Cook</b></sub>](https://github.com/dfcook)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Tests") | [<img src="https://avatars2.githubusercontent.com/u/21194045?s=400&v=4" width="100px;"/><br /><sub><b>Thomas Chia</b></sub>](https://github.com/thchia)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Athchia "Bug reports") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=thchia "Code") | [<img src="https://avatars1.githubusercontent.com/u/28659384?v=4" width="100px;"/><br /><sub><b>Tim Deschryver</b></sub>](https://github.com/tdeschryver)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Tests") | [<img src="https://avatars3.githubusercontent.com/u/1571667?v=4" width="100px;"/><br /><sub><b>Alex Krolick</b></sub>](https://alexkrolick.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=alexkrolick "Code") | [<img src="https://avatars2.githubusercontent.com/u/2224291?v=4" width="100px;"/><br /><sub><b>Maddi Joyce</b></sub>](http://www.maddijoyce.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=maddijoyce "Code") | [<img src="https://avatars1.githubusercontent.com/u/25429764?v=4" width="100px;"/><br /><sub><b>Peter Kamps</b></sub>](https://github.com/npeterkamps)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Anpeterkamps "Bug reports") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=npeterkamps "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=npeterkamps "Tests") |
10021057
| [<img src="https://avatars2.githubusercontent.com/u/21689428?v=4" width="100px;"/><br /><sub><b>Jonathan Stoye</b></sub>](http://jonathanstoye.de)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JonathanStoye "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/4126644?v=4" width="100px;"/><br /><sub><b>Sanghyeon Lee</b></sub>](https://github.com/yongdamsh)<br />[💡](#example-yongdamsh "Examples") | [<img src="https://avatars3.githubusercontent.com/u/8015514?v=4" width="100px;"/><br /><sub><b>Justice Mba </b></sub>](https://github.com/Dajust)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Documentation") [🤔](#ideas-Dajust "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/340761?v=4" width="100px;"/><br /><sub><b>Wayne Crouch</b></sub>](https://github.com/wgcrouch)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wgcrouch "Code") | [<img src="https://avatars1.githubusercontent.com/u/4996462?v=4" width="100px;"/><br /><sub><b>Ben Elliott</b></sub>](http://benjaminelliott.co.uk)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=benelliott "Code") | [<img src="https://avatars3.githubusercontent.com/u/577921?v=4" width="100px;"/><br /><sub><b>Ruben Costa</b></sub>](http://nuances.co)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=rubencosta "Code") | [<img src="https://avatars2.githubusercontent.com/u/4982001?v=4" width="100px;"/><br /><sub><b>Robert Smith</b></sub>](http://rbrtsmith.com/)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Arbrtsmith "Bug reports") [🤔](#ideas-rbrtsmith "Ideas, Planning, & Feedback") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=rbrtsmith "Documentation") |
1003-
| [<img src="https://avatars3.githubusercontent.com/u/881986?v=4" width="100px;"/><br /><sub><b>dadamssg</b></sub>](https://github.com/dadamssg)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dadamssg "Code") | [<img src="https://avatars1.githubusercontent.com/u/186971?v=4" width="100px;"/><br /><sub><b>Neil Kistner</b></sub>](https://neilkistner.com/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wyze "Code") | [<img src="https://avatars3.githubusercontent.com/u/1448597?v=4" width="100px;"/><br /><sub><b>Ben Chauvette</b></sub>](http://bdchauvette.net/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=bdchauvette "Code") | [<img src="https://avatars2.githubusercontent.com/u/777527?v=4" width="100px;"/><br /><sub><b>Jeff Baumgardt</b></sub>](https://github.com/JeffBaumgardt)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/4658208?v=4" width="100px;"/><br /><sub><b>Matan Kushner</b></sub>](http://matchai.me)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Documentation") [🤔](#ideas-matchai "Ideas, Planning, & Feedback") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Tests") |
1058+
| [<img src="https://avatars3.githubusercontent.com/u/881986?v=4" width="100px;"/><br /><sub><b>dadamssg</b></sub>](https://github.com/dadamssg)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dadamssg "Code") | [<img src="https://avatars1.githubusercontent.com/u/186971?v=4" width="100px;"/><br /><sub><b>Neil Kistner</b></sub>](https://neilkistner.com/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wyze "Code") | [<img src="https://avatars3.githubusercontent.com/u/1448597?v=4" width="100px;"/><br /><sub><b>Ben Chauvette</b></sub>](http://bdchauvette.net/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=bdchauvette "Code") | [<img src="https://avatars2.githubusercontent.com/u/777527?v=4" width="100px;"/><br /><sub><b>Jeff Baumgardt</b></sub>](https://github.com/JeffBaumgardt)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/4658208?v=4" width="100px;"/><br /><sub><b>Matan Kushner</b></sub>](http://matchai.me)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Documentation") [🤔](#ideas-matchai "Ideas, Planning, & Feedback") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Tests") | [<img src="https://avatars2.githubusercontent.com/u/5779538?v=4" width="100px;"/><br /><sub><b>Alex Wendte</b></sub>](http://www.wendtedesigns.com/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=themostcolm "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=themostcolm "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=themostcolm "Tests") |
10041059

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`it waits for the attributes mutation 1`] = `
4+
Array [
5+
Array [
6+
Object {
7+
"addedNodes": Array [],
8+
"attributeName": "data-test-attribute",
9+
"attributeNamespace": null,
10+
"nextSibling": null,
11+
"oldValue": null,
12+
"previousSibling": null,
13+
"removedNodes": Array [],
14+
"target": <div
15+
data-test-attribute="PASSED"
16+
/>,
17+
"type": "attributes",
18+
},
19+
],
20+
]
21+
`;
22+
23+
exports[`it waits for the next DOM mutation 1`] = `
24+
Array [
25+
Array [
26+
Object {
27+
"addedNodes": Array [
28+
<div />,
29+
],
30+
"attributeName": null,
31+
"attributeNamespace": null,
32+
"nextSibling": null,
33+
"oldValue": null,
34+
"previousSibling": null,
35+
"removedNodes": Array [],
36+
"target": <body>
37+
<div />
38+
</body>,
39+
"type": "childList",
40+
},
41+
],
42+
]
43+
`;

src/__tests__/__snapshots__/wait-for-element.js.snap

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,3 @@ exports[`it waits for the callback to return a value and only reacts to DOM muta
7979
/>
8080
</div>
8181
`;
82-
83-
exports[`it waits for the next DOM mutation with default callback 1`] = `
84-
<body>
85-
<div />
86-
</body>
87-
`;
88-
89-
exports[`it waits for the next DOM mutation with default callback 2`] = `
90-
<body>
91-
<div />
92-
</body>
93-
`;

0 commit comments

Comments
 (0)