Skip to content

Commit

Permalink
feat(prettyDOM): Expose pretty-dom utility (#25)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
gnapse authored and Kent C. Dodds committed Apr 19, 2018
1 parent 131a20b commit 8748c89
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -5,6 +5,7 @@ dist
.opt-out
.DS_Store
.eslintcache
yarn-error.log

# these cause more harm than good
# when working with contributors
Expand Down
29 changes: 29 additions & 0 deletions README.md
Expand Up @@ -85,6 +85,7 @@ when a real user uses it.
* [`query` APIs](#query-apis)
* [`bindElementToQueries`](#bindelementtoqueries)
* [Debugging](#debugging)
* [`prettyDOM`](#prettydom)
* [Implementations](#implementations)
* [FAQ](#faq)
* [Other Solutions](#other-solutions)
Expand Down Expand Up @@ -530,6 +531,34 @@ DEBUG_PRINT_LIMIT=10000 npm test
This works on macOS/linux, you'll need to do something else for windows. If you'd
like a solution that works for both, see [`cross-env`](https://www.npmjs.com/package/cross-env)

### `prettyDOM`

This helper function can be used to print out readable representation of the DOM
tree of a node. This can be helpful for instance when debugging tests.

It is defined as:

```typescript
function prettyDOM(node: HTMLElement, maxLength?: number): string
```

It receives the root node to print out, and an optional extra argument to limit
the size of the resulting string, for cases when it becomes too large.

This function is usually used alongside `console.log` to temporarily print out
DOM trees during tests for debugging purposes:

```javascript
const div = document.createElement('div')
div.innerHTML = '<div><h1>Hello World</h1></div>'
console.log(prettyDOM(div))
// <div>
// <h1>Hello World</h1>
// </div>
```

This function is what also powers [the automatic debugging output described above](#debugging).

## Implementations

This library was not built to be used on its own. The original implementation
Expand Down
9 changes: 9 additions & 0 deletions src/__tests__/__snapshots__/pretty-dom.js.snap
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`it prints out the given DOM element tree 1`] = `
"<div>
<div>
Hello World!
</div>
</div>"
`;
12 changes: 12 additions & 0 deletions src/__tests__/pretty-dom.js
@@ -0,0 +1,12 @@
import {prettyDOM} from '../pretty-dom'
import {render} from './helpers/test-utils'

test('it prints out the given DOM element tree', () => {
const {container} = render('<div>Hello World!</div>')
expect(prettyDOM(container)).toMatchSnapshot()
})

test('it supports truncating the output length', () => {
const {container} = render('<div>Hello World!</div>')
expect(prettyDOM(container, 5)).toMatch(/\.\.\./)
})
1 change: 1 addition & 0 deletions src/index.js
Expand Up @@ -11,3 +11,4 @@ export * from './matches'
export * from './get-node-text'
export * from './events'
export * from './bind-element-to-queries'
export * from './pretty-dom'
16 changes: 16 additions & 0 deletions src/pretty-dom.js
@@ -0,0 +1,16 @@
import prettyFormat from 'pretty-format'

const {DOMElement, DOMCollection} = prettyFormat.plugins

function prettyDOM(htmlElement, maxLength) {
const debugContent = prettyFormat(htmlElement, {
plugins: [DOMElement, DOMCollection],
printFunctionName: false,
highlight: true,
})
return maxLength !== undefined && htmlElement.outerHTML.length > maxLength
? `${debugContent.slice(0, maxLength)}...`
: debugContent
}

export {prettyDOM}
30 changes: 10 additions & 20 deletions src/queries.js
@@ -1,8 +1,10 @@
import prettyFormat from 'pretty-format'
import {matches} from './matches'
import {getNodeText} from './get-node-text'
import {prettyDOM} from './pretty-dom'

const {DOMElement, DOMCollection} = prettyFormat.plugins
function debugDOM(htmlElement) {
return prettyDOM(htmlElement, process.env.DEBUG_PRINT_LIMIT || 7000)
}

// Here are the queries for the library.
// The queries here should only be things that are accessible to both users who are using a screen reader
Expand Down Expand Up @@ -74,7 +76,7 @@ function getByTestId(container, id, ...rest) {
const el = queryByTestId(container, id, ...rest)
if (!el) {
throw new Error(
`Unable to find an element by: [data-testid="${id}"] \n\n${htmlElementToDisplay(
`Unable to find an element by: [data-testid="${id}"] \n\n${debugDOM(
container,
)}`,
)
Expand All @@ -86,7 +88,7 @@ function getByPlaceholderText(container, text, ...rest) {
const el = queryByPlaceholderText(container, text, ...rest)
if (!el) {
throw new Error(
`Unable to find an element with the placeholder text of: ${text} \n\n${htmlElementToDisplay(
`Unable to find an element with the placeholder text of: ${text} \n\n${debugDOM(
container,
)}`,
)
Expand All @@ -100,13 +102,13 @@ function getByLabelText(container, text, ...rest) {
const label = queryLabelByText(container, text)
if (label) {
throw new Error(
`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(
`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(
container,
)}`,
)
} else {
throw new Error(
`Unable to find a label with the text of: ${text} \n\n${htmlElementToDisplay(
`Unable to find a label with the text of: ${text} \n\n${debugDOM(
container,
)}`,
)
Expand All @@ -119,7 +121,7 @@ function getByText(container, text, ...rest) {
const el = queryByText(container, text, ...rest)
if (!el) {
throw new Error(
`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(
`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(
container,
)}`,
)
Expand All @@ -139,26 +141,14 @@ function getByAltText(container, alt) {
const el = queryByAltText(container, alt)
if (!el) {
throw new Error(
`Unable to find an element with the alt text: ${alt} \n\n${htmlElementToDisplay(
`Unable to find an element with the alt text: ${alt} \n\n${debugDOM(
container,
)}`,
)
}
return el
}

function htmlElementToDisplay(htmlElement) {
const debugContent = prettyFormat(htmlElement, {
plugins: [DOMElement, DOMCollection],
printFunctionName: false,
highlight: true,
})
const maxLength = process.env.DEBUG_PRINT_LIMIT || 7000
return htmlElement.outerHTML.length > maxLength
? `${debugContent.slice(0, maxLength)}...`
: debugContent
}

export {
queryByPlaceholderText,
getByPlaceholderText,
Expand Down

0 comments on commit 8748c89

Please sign in to comment.