Skip to content

Commit 8748c89

Browse files
gnapseKent C. Dodds
authored and
Kent C. Dodds
committed
feat(prettyDOM): Expose pretty-dom utility (testing-library#25)
* Expose pretty-dom utility * Allow users of prettyDOM to set max length * Remove logDOM * Add tests for prettyDOM * Add yarn-error.log to .gitignore * Add documencation for prettyDOM * Update pretty-dom.js
1 parent 131a20b commit 8748c89

File tree

7 files changed

+78
-20
lines changed

7 files changed

+78
-20
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ dist
55
.opt-out
66
.DS_Store
77
.eslintcache
8+
yarn-error.log
89

910
# these cause more harm than good
1011
# when working with contributors

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ when a real user uses it.
8585
* [`query` APIs](#query-apis)
8686
* [`bindElementToQueries`](#bindelementtoqueries)
8787
* [Debugging](#debugging)
88+
* [`prettyDOM`](#prettydom)
8889
* [Implementations](#implementations)
8990
* [FAQ](#faq)
9091
* [Other Solutions](#other-solutions)
@@ -530,6 +531,34 @@ DEBUG_PRINT_LIMIT=10000 npm test
530531
This works on macOS/linux, you'll need to do something else for windows. If you'd
531532
like a solution that works for both, see [`cross-env`](https://www.npmjs.com/package/cross-env)
532533

534+
### `prettyDOM`
535+
536+
This helper function can be used to print out readable representation of the DOM
537+
tree of a node. This can be helpful for instance when debugging tests.
538+
539+
It is defined as:
540+
541+
```typescript
542+
function prettyDOM(node: HTMLElement, maxLength?: number): string
543+
```
544+
545+
It receives the root node to print out, and an optional extra argument to limit
546+
the size of the resulting string, for cases when it becomes too large.
547+
548+
This function is usually used alongside `console.log` to temporarily print out
549+
DOM trees during tests for debugging purposes:
550+
551+
```javascript
552+
const div = document.createElement('div')
553+
div.innerHTML = '<div><h1>Hello World</h1></div>'
554+
console.log(prettyDOM(div))
555+
// <div>
556+
// <h1>Hello World</h1>
557+
// </div>
558+
```
559+
560+
This function is what also powers [the automatic debugging output described above](#debugging).
561+
533562
## Implementations
534563

535564
This library was not built to be used on its own. The original implementation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`it prints out the given DOM element tree 1`] = `
4+
"<div>
5+
<div>
6+
Hello World!
7+
</div>
8+
</div>"
9+
`;

src/__tests__/pretty-dom.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {prettyDOM} from '../pretty-dom'
2+
import {render} from './helpers/test-utils'
3+
4+
test('it prints out the given DOM element tree', () => {
5+
const {container} = render('<div>Hello World!</div>')
6+
expect(prettyDOM(container)).toMatchSnapshot()
7+
})
8+
9+
test('it supports truncating the output length', () => {
10+
const {container} = render('<div>Hello World!</div>')
11+
expect(prettyDOM(container, 5)).toMatch(/\.\.\./)
12+
})

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export * from './matches'
1111
export * from './get-node-text'
1212
export * from './events'
1313
export * from './bind-element-to-queries'
14+
export * from './pretty-dom'

src/pretty-dom.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import prettyFormat from 'pretty-format'
2+
3+
const {DOMElement, DOMCollection} = prettyFormat.plugins
4+
5+
function prettyDOM(htmlElement, maxLength) {
6+
const debugContent = prettyFormat(htmlElement, {
7+
plugins: [DOMElement, DOMCollection],
8+
printFunctionName: false,
9+
highlight: true,
10+
})
11+
return maxLength !== undefined && htmlElement.outerHTML.length > maxLength
12+
? `${debugContent.slice(0, maxLength)}...`
13+
: debugContent
14+
}
15+
16+
export {prettyDOM}

src/queries.js

+10-20
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import prettyFormat from 'pretty-format'
21
import {matches} from './matches'
32
import {getNodeText} from './get-node-text'
3+
import {prettyDOM} from './pretty-dom'
44

5-
const {DOMElement, DOMCollection} = prettyFormat.plugins
5+
function debugDOM(htmlElement) {
6+
return prettyDOM(htmlElement, process.env.DEBUG_PRINT_LIMIT || 7000)
7+
}
68

79
// Here are the queries for the library.
810
// The queries here should only be things that are accessible to both users who are using a screen reader
@@ -74,7 +76,7 @@ function getByTestId(container, id, ...rest) {
7476
const el = queryByTestId(container, id, ...rest)
7577
if (!el) {
7678
throw new Error(
77-
`Unable to find an element by: [data-testid="${id}"] \n\n${htmlElementToDisplay(
79+
`Unable to find an element by: [data-testid="${id}"] \n\n${debugDOM(
7880
container,
7981
)}`,
8082
)
@@ -86,7 +88,7 @@ function getByPlaceholderText(container, text, ...rest) {
8688
const el = queryByPlaceholderText(container, text, ...rest)
8789
if (!el) {
8890
throw new Error(
89-
`Unable to find an element with the placeholder text of: ${text} \n\n${htmlElementToDisplay(
91+
`Unable to find an element with the placeholder text of: ${text} \n\n${debugDOM(
9092
container,
9193
)}`,
9294
)
@@ -100,13 +102,13 @@ function getByLabelText(container, text, ...rest) {
100102
const label = queryLabelByText(container, text)
101103
if (label) {
102104
throw new Error(
103-
`Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. \n\n${htmlElementToDisplay(
105+
`Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. \n\n${debugDOM(
104106
container,
105107
)}`,
106108
)
107109
} else {
108110
throw new Error(
109-
`Unable to find a label with the text of: ${text} \n\n${htmlElementToDisplay(
111+
`Unable to find a label with the text of: ${text} \n\n${debugDOM(
110112
container,
111113
)}`,
112114
)
@@ -119,7 +121,7 @@ function getByText(container, text, ...rest) {
119121
const el = queryByText(container, text, ...rest)
120122
if (!el) {
121123
throw new Error(
122-
`Unable to find an element with the text: ${text}. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. \n\n${htmlElementToDisplay(
124+
`Unable to find an element with the text: ${text}. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. \n\n${debugDOM(
123125
container,
124126
)}`,
125127
)
@@ -139,26 +141,14 @@ function getByAltText(container, alt) {
139141
const el = queryByAltText(container, alt)
140142
if (!el) {
141143
throw new Error(
142-
`Unable to find an element with the alt text: ${alt} \n\n${htmlElementToDisplay(
144+
`Unable to find an element with the alt text: ${alt} \n\n${debugDOM(
143145
container,
144146
)}`,
145147
)
146148
}
147149
return el
148150
}
149151

150-
function htmlElementToDisplay(htmlElement) {
151-
const debugContent = prettyFormat(htmlElement, {
152-
plugins: [DOMElement, DOMCollection],
153-
printFunctionName: false,
154-
highlight: true,
155-
})
156-
const maxLength = process.env.DEBUG_PRINT_LIMIT || 7000
157-
return htmlElement.outerHTML.length > maxLength
158-
? `${debugContent.slice(0, maxLength)}...`
159-
: debugContent
160-
}
161-
162152
export {
163153
queryByPlaceholderText,
164154
getByPlaceholderText,

0 commit comments

Comments
 (0)