diff --git a/.all-contributorsrc b/.all-contributorsrc index d984844d..08ca11f2 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -209,6 +209,17 @@ "contributions": [ "code" ] + }, + { + "login": "npeterkamps", + "name": "Peter Kamps", + "avatar_url": "https://avatars1.githubusercontent.com/u/25429764?v=4", + "profile": "https://github.com/npeterkamps", + "contributions": [ + "bug", + "code", + "test" + ] } ] } diff --git a/.gitignore b/.gitignore index 4693e52b..cbf34c78 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ dist .DS_Store .eslintcache yarn-error.log +.idea/ # these cause more harm than good # when working with contributors diff --git a/README.md b/README.md index b39a6e70..8b3171ac 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ [![downloads][downloads-badge]][npmtrends] [![MIT License][license-badge]][license] -[![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-21-orange.svg?style=flat-square)](#contributors) [![PRs Welcome][prs-badge]][prs] [![Code of Conduct][coc-badge]][coc] @@ -857,13 +857,11 @@ light-weight, simple, and understandable. Thanks goes to these people ([emoji key][emojis]): - | [
Kent C. Dodds](https://kentcdodds.com)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=kentcdodds "Code") [πŸ“–](https://github.com/kentcdodds/dom-testing-library/commits?author=kentcdodds "Documentation") [πŸš‡](#infra-kentcdodds "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=kentcdodds "Tests") | [
Ryan Castner](http://audiolion.github.io)
[πŸ“–](https://github.com/kentcdodds/dom-testing-library/commits?author=audiolion "Documentation") | [
Daniel Sandiego](https://www.dnlsandiego.com)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=dnlsandiego "Code") | [
PaweΕ‚ MikoΕ‚ajczyk](https://github.com/Miklet)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=Miklet "Code") | [
Alejandro ÑÑñez Ortiz](http://co.linkedin.com/in/alejandronanez/)
[πŸ“–](https://github.com/kentcdodds/dom-testing-library/commits?author=alejandronanez "Documentation") | [
Matt Parrish](https://github.com/pbomb)
[πŸ›](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Apbomb "Bug reports") [πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=pbomb "Code") [πŸ“–](https://github.com/kentcdodds/dom-testing-library/commits?author=pbomb "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=pbomb "Tests") | [
Justin Hall](https://github.com/wKovacs64)
[πŸ“¦](#platform-wKovacs64 "Packaging/porting to new platform") | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | [
Anto Aravinth](https://github.com/antoaravinth)
[πŸ’»](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") | [
Jonah Moses](https://github.com/JonahMoses)
[πŸ“–](https://github.com/kentcdodds/dom-testing-library/commits?author=JonahMoses "Documentation") | [
Łukasz Gandecki](http://team.thebrain.pro)
[πŸ’»](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") | [
Ivan Babak](https://sompylasar.github.io)
[πŸ›](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") | [
Jesse Day](https://github.com/jday3)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=jday3 "Code") | [
Ernesto GarcΓ­a](http://gnapse.github.io)
[πŸ’¬](#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") | [
Josef Maxx Blake](http://jomaxx.com)
[πŸ’»](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") | -| [
Alex Cook](https://github.com/alecook)
[πŸ“–](https://github.com/kentcdodds/dom-testing-library/commits?author=alecook "Documentation") [πŸ’‘](#example-alecook "Examples") | [
Daniel Cook](https://github.com/dfcook)
[πŸ’»](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") | [
Thomas Chia](https://github.com/thchia)
[πŸ›](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Athchia "Bug reports") [πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=thchia "Code") | [
Tim Deschryver](https://github.com/tdeschryver)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Tests") | [
Alex Krolick](https://alexkrolick.com)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=alexkrolick "Code") | [
Maddi Joyce](http://www.maddijoyce.com)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=maddijoyce "Code") | - +| [
Alex Cook](https://github.com/alecook)
[πŸ“–](https://github.com/kentcdodds/dom-testing-library/commits?author=alecook "Documentation") [πŸ’‘](#example-alecook "Examples") | [
Daniel Cook](https://github.com/dfcook)
[πŸ’»](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") | [
Thomas Chia](https://github.com/thchia)
[πŸ›](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Athchia "Bug reports") [πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=thchia "Code") | [
Tim Deschryver](https://github.com/tdeschryver)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Tests") | [
Alex Krolick](https://alexkrolick.com)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=alexkrolick "Code") | [
Maddi Joyce](http://www.maddijoyce.com)
[πŸ’»](https://github.com/kentcdodds/dom-testing-library/commits?author=maddijoyce "Code") | [
Peter Kamps](https://github.com/npeterkamps)
[πŸ›](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") | This project follows the [all-contributors][all-contributors] specification. diff --git a/src/__tests__/__snapshots__/element-queries.js.snap b/src/__tests__/__snapshots__/element-queries.js.snap index d2953868..0ff24479 100644 --- a/src/__tests__/__snapshots__/element-queries.js.snap +++ b/src/__tests__/__snapshots__/element-queries.js.snap @@ -1,7 +1,7 @@ // 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 +"Unable to find a label with the text of: LucyRicardo 
 
 @@ -9,7 +9,7 @@ exports[`get throws a useful error message 1`] = ` `; exports[`get throws a useful error message 2`] = ` -"Unable to find an element with the placeholder text of: LucyRicardo +"Unable to find an element with the placeholder text of: LucyRicardo 
 
 @@ -17,7 +17,7 @@ exports[`get throws a useful error message 2`] = ` `; 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. +"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. 
 
 @@ -25,7 +25,7 @@ exports[`get throws a useful error message 3`] = ` `; exports[`get throws a useful error message 4`] = ` -"Unable to find an element by: [data-testid=\\"LucyRicardo\\"] +"Unable to find an element by: [data-testid=\\"LucyRicardo\\"] 
 
 @@ -33,7 +33,7 @@ exports[`get throws a useful error message 4`] = ` `; exports[`get throws a useful error message 5`] = ` -"Unable to find an element with the alt text: LucyRicardo +"Unable to find an element with the alt text: LucyRicardo 
 
 @@ -41,7 +41,7 @@ exports[`get throws a useful error message 5`] = ` `; exports[`get throws a useful error message 6`] = ` -"Unable to find an element with the title: LucyRicardo. +"Unable to find an element with the title: LucyRicardo. 
 
 @@ -49,15 +49,29 @@ exports[`get throws a useful error message 6`] = ` `; exports[`get throws a useful error message 7`] = ` -"Unable to find an element with the value: LucyRicardo. +"Unable to find an element with the value: LucyRicardo. 
 
 
" `; +exports[`get throws a useful error message without DOM in Cypress 1`] = `"Unable to find a label with the text of: LucyRicardo"`; + +exports[`get throws a useful error message without DOM in Cypress 2`] = `"Unable to find an element with the placeholder text of: LucyRicardo"`; + +exports[`get throws a useful error message without DOM in Cypress 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 without DOM in Cypress 4`] = `"Unable to find an element by: [data-testid=\\"LucyRicardo\\"]"`; + +exports[`get throws a useful error message without DOM in Cypress 5`] = `"Unable to find an element with the alt text: LucyRicardo"`; + +exports[`get throws a useful error message without DOM in Cypress 6`] = `"Unable to find an element with the title: LucyRicardo."`; + +exports[`get throws a useful error message without DOM in Cypress 7`] = `"Unable to find an element with the value: LucyRicardo."`; + 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. +"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. 
 
" `; + +exports[`it supports receiving the document element 1`] = ` +" +  +  +" +`; diff --git a/src/__tests__/__snapshots__/wait-for-element.js.snap b/src/__tests__/__snapshots__/wait-for-element.js.snap index aa5286c1..03db14f6 100644 --- a/src/__tests__/__snapshots__/wait-for-element.js.snap +++ b/src/__tests__/__snapshots__/wait-for-element.js.snap @@ -24,7 +24,7 @@ exports[`it throws if timeout is exceeded 2`] = ` 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"]  { + window.Cypress = null; +}); + test('query can return null', () => { const { queryByLabelText, @@ -368,4 +372,26 @@ test('test the debug helper prints the dom state here', () => { process.env.DEBUG_PRINT_LIMIT = originalDebugPrintLimit }) +test('get throws a useful error message without DOM in Cypress', () => { + window.Cypress = {} + const { + getByLabelText, + getByPlaceholderText, + getByText, + getByTestId, + getByAltText, + getByTitle, + getByValue, + } = render('
') + expect(() => getByLabelText('LucyRicardo')).toThrowErrorMatchingSnapshot() + expect(() => + getByPlaceholderText('LucyRicardo'), + ).toThrowErrorMatchingSnapshot() + expect(() => getByText('LucyRicardo')).toThrowErrorMatchingSnapshot() + expect(() => getByTestId('LucyRicardo')).toThrowErrorMatchingSnapshot() + expect(() => getByAltText('LucyRicardo')).toThrowErrorMatchingSnapshot() + expect(() => getByTitle('LucyRicardo')).toThrowErrorMatchingSnapshot() + expect(() => getByValue('LucyRicardo')).toThrowErrorMatchingSnapshot() +}) + /* eslint jsx-a11y/label-has-for:0 */ diff --git a/src/__tests__/pretty-dom.js b/src/__tests__/pretty-dom.js index 4d812675..312cf78b 100644 --- a/src/__tests__/pretty-dom.js +++ b/src/__tests__/pretty-dom.js @@ -10,3 +10,7 @@ test('it supports truncating the output length', () => { const {container} = render('
Hello World!
') expect(prettyDOM(container, 5)).toMatch(/\.\.\./) }) + +test('it supports receiving the document element', () => { + expect(prettyDOM(document)).toMatchSnapshot() +}) diff --git a/src/pretty-dom.js b/src/pretty-dom.js index 5e78b6c8..4906a238 100644 --- a/src/pretty-dom.js +++ b/src/pretty-dom.js @@ -2,12 +2,16 @@ import prettyFormat from 'pretty-format' const {DOMElement, DOMCollection} = prettyFormat.plugins -function prettyDOM(htmlElement, maxLength) { - const debugContent = prettyFormat(htmlElement, { +function prettyDOM(htmlElement, maxLength, options) { + if (htmlElement.documentElement) { + htmlElement = htmlElement.documentElement + } + + const debugContent = prettyFormat(htmlElement, Object.assign({ plugins: [DOMElement, DOMCollection], printFunctionName: false, highlight: true, - }) + }, options)) return maxLength !== undefined && htmlElement.outerHTML.length > maxLength ? `${debugContent.slice(0, maxLength)}...` : debugContent diff --git a/src/queries.js b/src/queries.js index 9f648b73..6ea1a20e 100644 --- a/src/queries.js +++ b/src/queries.js @@ -3,7 +3,21 @@ import {getNodeText} from './get-node-text' import {prettyDOM} from './pretty-dom' function debugDOM(htmlElement) { - return prettyDOM(htmlElement, process.env.DEBUG_PRINT_LIMIT || 7000) + const limit = process.env.DEBUG_PRINT_LIMIT || 7000 + const inNode = (typeof module !== 'undefined' && module.exports) + const inCypress = (typeof window !== 'undefined' && window.Cypress) + /* istanbul ignore else */ + if (inCypress) { + return '' + } else if (inNode) { + return prettyDOM(htmlElement, limit) + } else { + return prettyDOM(htmlElement, limit, {highlight: false}) + } +} + +function getElementError(message, container) { + return new Error([message, debugDOM(container)].filter(Boolean).join('\n\n')) } // Here are the queries for the library. @@ -37,27 +51,29 @@ function queryAllByLabelText( const labels = queryAllLabelsByText(container, text, {exact, ...matchOpts}) const labelledElements = labels .map(label => { - /* istanbul ignore if */ if (label.control) { - // appears to be unsupported in jsdom: https://github.com/jsdom/jsdom/issues/2175 - // but this would be the proper way to do things return label.control - } else if (label.getAttribute('for')) { + } + /* istanbul ignore if */ + if (label.getAttribute('for')) { // we're using this notation because with the # selector we would have to escape special characters e.g. user.name // see https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector#Escaping_special_characters // + + // .control support has landed in jsdom (https://github.com/jsdom/jsdom/issues/2175) return container.querySelector(`[id="${label.getAttribute('for')}"]`) - } else if (label.getAttribute('id')) { + } + if (label.getAttribute('id')) { // return container.querySelector( `[aria-labelledby="${label.getAttribute('id')}"]`, ) - } else if (label.childNodes.length) { + } + if (label.childNodes.length) { // return label.querySelector(selector) - } else { - return null } + return null }) .filter(label => label !== null) .concat(queryAllByAttribute('aria-label', container, text, {exact})) @@ -139,11 +155,7 @@ function queryByAltText(...args) { function getAllByTestId(container, id, ...rest) { const els = queryAllByTestId(container, id, ...rest) if (!els.length) { - throw new Error( - `Unable to find an element by: [data-testid="${id}"] \n\n${debugDOM( - container, - )}`, - ) + throw getElementError(`Unable to find an element by: [data-testid="${id}"]`, container) } return els } @@ -155,11 +167,7 @@ function getByTestId(...args) { function getAllByTitle(container, title, ...rest) { const els = queryAllByTitle(container, title, ...rest) if (!els.length) { - throw new Error( - `Unable to find an element with the title: ${title}. \n\n${debugDOM( - container, - )}`, - ) + throw getElementError(`Unable to find an element with the title: ${title}.`, container) } return els } @@ -171,11 +179,7 @@ function getByTitle(...args) { function getAllByValue(container, value, ...rest) { const els = queryAllByValue(container, value, ...rest) if (!els.length) { - throw new Error( - `Unable to find an element with the value: ${value}. \n\n${debugDOM( - container, - )}`, - ) + throw getElementError(`Unable to find an element with the value: ${value}.`, container) } return els } @@ -187,11 +191,7 @@ function getByValue(...args) { function getAllByPlaceholderText(container, text, ...rest) { const els = queryAllByPlaceholderText(container, text, ...rest) if (!els.length) { - throw new Error( - `Unable to find an element with the placeholder text of: ${text} \n\n${debugDOM( - container, - )}`, - ) + throw getElementError(`Unable to find an element with the placeholder text of: ${text}`, container) } return els } @@ -205,17 +205,9 @@ function getAllByLabelText(container, text, ...rest) { if (!els.length) { const labels = queryAllLabelsByText(container, text, ...rest) if (labels.length) { - 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${debugDOM( - container, - )}`, - ) + throw getElementError(`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.`, container) } else { - throw new Error( - `Unable to find a label with the text of: ${text} \n\n${debugDOM( - container, - )}`, - ) + throw getElementError(`Unable to find a label with the text of: ${text}`, container) } } return els @@ -228,11 +220,7 @@ function getByLabelText(...args) { function getAllByText(container, text, ...rest) { const els = queryAllByText(container, text, ...rest) if (!els.length) { - 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${debugDOM( - container, - )}`, - ) + throw getElementError(`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.`, container) } return els } @@ -244,11 +232,7 @@ function getByText(...args) { function getAllByAltText(container, alt, ...rest) { const els = queryAllByAltText(container, alt, ...rest) if (!els.length) { - throw new Error( - `Unable to find an element with the alt text: ${alt} \n\n${debugDOM( - container, - )}`, - ) + throw getElementError(`Unable to find an element with the alt text: ${alt}`, container) } return els }