diff --git a/README.md b/README.md
index 69809c09..85ce9bee 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,7 @@ when a real user uses it.
* [Custom Jest Matchers - Typescript](#custom-jest-matchers---typescript)
* [`TextMatch`](#textmatch)
* [`query` APIs](#query-apis)
+* [Debugging](#debugging)
* [Implementations](#implementations)
* [FAQ](#faq)
* [Other Solutions](#other-solutions)
@@ -540,6 +541,42 @@ expect(submitButton).toBeNull() // it doesn't exist
expect(submitButton).not.toBeInTheDOM()
```
+## Debugging
+
+When you use any `get` calls in your test cases, the current state of the `container`
+(DOM) gets printed on the console. For example:
+
+```javascript
+//
Hello world
+getByText(container, 'Goodbye world') // will fail by throwing error
+```
+
+The above test case will fail, however it prints the state of your DOM under test,
+so you will get to see:
+
+```
+Unable to find an element with the text: Goodbye world. 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.
+Here is the state of your container:
+
+
+ Hello World!
+
+
+```
+
+Note: Since the DOM size can get really large, you can set the limit of DOM content
+to be printed via environment variable `DEBUG_PRINT_LIMIT`. The default value is
+`7000`. You will see `...` in the console, when the DOM content is stripped off,
+because of the length you have set or due to default size limit. Here's how you
+might increase this limit when running tests:
+
+```
+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)
+
## Implementations
This library was not built to be used on its own. The original implementation
diff --git a/package.json b/package.json
index 1ba1cfee..c6f7fc92 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
],
"dependencies": {
"jest-matcher-utils": "^22.4.3",
+ "pretty-format": "^22.4.3",
"mutationobserver-shim": "^0.3.2",
"wait-for-expect": "^0.4.0"
},
diff --git a/src/__tests__/__snapshots__/element-queries.js.snap b/src/__tests__/__snapshots__/element-queries.js.snap
index 6d636275..a026cde0 100644
--- a/src/__tests__/__snapshots__/element-queries.js.snap
+++ b/src/__tests__/__snapshots__/element-queries.js.snap
@@ -1,15 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`get throws a useful error message 1`] = `"Unable to find a label with the text of: LucyRicardo"`;
+exports[`get throws a useful error message 1`] = `
+"Unable to find a label with the text of: LucyRicardo
-exports[`get throws a useful error message 2`] = `"Unable to find an element with the placeholder text of: LucyRicardo"`;
+[36m
[39m
+ [36m[39m
+[36m
[39m"
+`;
-exports[`get throws a useful error message 3`] = `"Unable to find an element with the text: LucyRicardo. 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."`;
+exports[`get throws a useful error message 2`] = `
+"Unable to find an element with the placeholder text of: LucyRicardo
-exports[`get throws a useful error message 4`] = `"Unable to find an element by: [data-testid=\\"LucyRicardo\\"]"`;
+[36m
[39m
+ [36m[39m
+[36m
[39m"
+`;
-exports[`get throws a useful error message 5`] = `"Unable to find an element with the alt text: LucyRicardo"`;
+exports[`get throws a useful error message 3`] = `
+"Unable to find an element with the text: LucyRicardo. 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.
-exports[`label with no form control 1`] = `"Found a label with the text of: alone, however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly."`;
+[36m
[39m
+ [36m[39m
+[36m
[39m"
+`;
-exports[`totally empty label 1`] = `"Found a label with the text of: , however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly."`;
+exports[`get throws a useful error message 4`] = `
+"Unable to find an element by: [data-testid=\\"LucyRicardo\\"]
+
+[36m
[39m
+ [36m[39m
+[36m
[39m"
+`;
+
+exports[`get throws a useful error message 5`] = `
+"Unable to find an element with the alt text: LucyRicardo
+
+[36m
[39m
+ [36m[39m
+[36m
[39m"
+`;
+
+exports[`label with no form control 1`] = `
+"Found a label with the text of: alone, however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly.
+
+[36m
[39m
+ [36m[39m
+[36m
[39m"
+`;
+
+exports[`totally empty label 1`] = `
+"Found a label with the text of: , however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly.
+
+[36m
[39m
+ [36m[39m
+[36m
[39m"
+`;
diff --git a/src/__tests__/__snapshots__/wait-for-element.js.snap b/src/__tests__/__snapshots__/wait-for-element.js.snap
index 9ca53ee7..aa5286c1 100644
--- a/src/__tests__/__snapshots__/wait-for-element.js.snap
+++ b/src/__tests__/__snapshots__/wait-for-element.js.snap
@@ -1,8 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[
- `it returns immediately if the callback returns the value before any mutations 1`
-] = `
+exports[`it returns immediately if the callback returns the value before any mutations 1`] = `
-`
+`;
exports[`it throws if timeout is exceeded 1`] = `
Array [
[Error: Timed out in waitForElement.],
]
-`
+`;
exports[`it throws if timeout is exceeded 2`] = `
-`
+`;
-exports[
- `it throws the same error that the callback has thrown if timeout is exceeded 1`
-] = `
+exports[`it throws the same error that the callback has thrown if timeout is exceeded 1`] = `
Array [
- [Error: Unable to find an element by: [data-testid="test"]],
+ [Error: Unable to find an element by: [data-testid="test"]
+
+[36m[39m],
]
-`
+`;
-exports[
- `it throws the same error that the callback has thrown if timeout is exceeded 2`
-] = `
+exports[`it throws the same error that the callback has thrown if timeout is exceeded 2`] = `
-`
+`;
-exports[
- `it waits for the callback to return a value and only reacts to DOM mutations 1`
-] = `
+exports[`it waits for the callback to return a value and only reacts to DOM mutations 1`] = `
-`
+`;
exports[`it waits for the next DOM mutation with default callback 1`] = `
-`
+`;
exports[`it waits for the next DOM mutation with default callback 2`] = `
-`
+`;
diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js
index 33ff6100..e84d6f8b 100644
--- a/src/__tests__/element-queries.js
+++ b/src/__tests__/element-queries.js
@@ -193,4 +193,36 @@ test('using jest helpers to check element class names', () => {
).toThrowError()
})
+test('test the debug helper prints the dom state here', () => {
+ const originalDebugPrintLimit = process.env.DEBUG_PRINT_LIMIT
+ const Large = `
`
+
+ const {getByText} = render(Large) // render large DOM which exceeds 7000 limit
+ expect(() => expect(getByText('not present')).toBeInTheDOM()).toThrowError()
+
+ const Hello = `
+ Hello World!
+
`
+ const {getByTestId} = render(Hello)
+ process.env.DEBUG_PRINT_LIMIT = 5 // user should see `...`
+ expect(() => expect(getByTestId('not present')).toBeInTheDOM()).toThrowError(
+ /\.\.\./,
+ )
+
+ const {getByLabelText} = render(Hello)
+ process.env.DEBUG_PRINT_LIMIT = 10000 // user shouldn't see `...`
+ expect(() =>
+ expect(getByLabelText('not present')).toBeInTheDOM(/^((?!\.\.\.).)*$/),
+ ).toThrowError()
+
+ //all good replacing it with old value
+ process.env.DEBUG_PRINT_LIMIT = originalDebugPrintLimit
+})
+
/* eslint jsx-a11y/label-has-for:0 */
diff --git a/src/queries.js b/src/queries.js
index bddf781a..86b1a529 100644
--- a/src/queries.js
+++ b/src/queries.js
@@ -1,5 +1,8 @@
+import prettyFormat from 'pretty-format'
import {matches} from './matches'
+const {DOMElement, DOMCollection} = prettyFormat.plugins
+
// 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
// and those who are not using a screen reader (with the exception of the data-testid attribute query).
@@ -78,7 +81,11 @@ function getText(node) {
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}"]`)
+ throw new Error(
+ `Unable to find an element by: [data-testid="${id}"] \n\n${htmlElementToDisplay(
+ container,
+ )}`,
+ )
}
return el
}
@@ -87,7 +94,9 @@ 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}`,
+ `Unable to find an element with the placeholder text of: ${text} \n\n${htmlElementToDisplay(
+ container,
+ )}`,
)
}
return el
@@ -99,10 +108,16 @@ 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.`,
+ `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(
+ container,
+ )}`,
)
} else {
- throw new Error(`Unable to find a label with the text of: ${text}`)
+ throw new Error(
+ `Unable to find a label with the text of: ${text} \n\n${htmlElementToDisplay(
+ container,
+ )}`,
+ )
}
}
return el
@@ -112,7 +127,9 @@ 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.`,
+ `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(
+ container,
+ )}`,
)
}
return el
@@ -129,11 +146,27 @@ function queryByAltText(container, alt) {
function getByAltText(container, alt) {
const el = queryByAltText(container, alt)
if (!el) {
- throw new Error(`Unable to find an element with the alt text: ${alt}`)
+ throw new Error(
+ `Unable to find an element with the alt text: ${alt} \n\n${htmlElementToDisplay(
+ 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,