Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(debug_helper): Adding debug helper when a get call fails #3

Merged
merged 8 commits into from
Apr 10, 2018

Conversation

antsmartian
Copy link
Contributor

Fix for testing-library/react-testing-library#31

@kentcdodds Let me know if things are good to go!

@codecov
Copy link

codecov bot commented Apr 6, 2018

Codecov Report

Merging #3 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master     #3   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           7      5    -2     
  Lines         127    102   -25     
  Branches       33     27    -6     
=====================================
- Hits          127    102   -25
Impacted Files Coverage Δ
src/queries.js 100% <100%> (ø) ⬆️
src/extend-expect.js 100% <0%> (ø) ⬆️
src/jest-extensions.js 100% <0%> (ø) ⬆️
src/wait-for-element.js
src/events.js

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d45b449...b681a47. Read the comment docs.

src/queries.js Outdated
@@ -1,4 +1,6 @@
import {matches} from './matches'
import prettyFormat from 'pretty-format' // eslint-disable-line
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why eslint-disable-line here?

Copy link
Contributor Author

@antsmartian antsmartian Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically pretty-format is not part of the package.json - dependencies but its part of jest I believe. Hence I have added the eslint-disable-line, yes, I need to clean up a bit by adding the exact eslint-ignore for the same.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what if I don't use jest with dom-testing-library?

Copy link
Contributor Author

@antsmartian antsmartian Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, pretty-format is from jest-matcher-utils (https://github.com/facebook/jest/blob/master/packages/jest-matcher-utils/package.json#L14) which is in our dependencies. Sorry for the rushed comment, saying it as jest in first place!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. Is there a reason for not depending on pretty-format directly as well? :)

Copy link
Contributor Author

@antsmartian antsmartian Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lgandecki We use custom jest assertions, for which we depend on jest-matcher-utils. As part of printing the DOM, we thought we could use pretty-format as it was already part of the jest-matcher-utils and it does actually a good job. It works well for our use case, so why not? :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not against using pretty-format, I'm just trying to understand if there is a reason to not put it in package.json as a direct dependency. Then there is no need to fight around eslint complaining (rightly). And if jest-matcher-utils switch to something else internally and stop requiring pretty-format, our package will still work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lgandecki Yes, I agree. Will make the changes. Thanks your comments!

src/queries.js Outdated
}
return el
}

function checkDebugLimit(debugContent) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is confusong: it shouldn't be "check" (would return boolean), it is more of a "trimToDebugLimit".

src/queries.js Outdated
}
return el
}

function checkDebugLimit(debugContent) {
const limit = process.env.DEBUG_PRINT_LIMIT
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the limit needed in browser where there's no env, e.g. via Cypress?

I suggest exporting a mutable "defaultOptions" object from the module instead, like Node's util.inspect.defaultOptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thats a good point.

src/queries.js Outdated
debugContent = debugContent.slice(0, 7000)
if (contentLength >= 7000)
// there are more chars we have stripped, just show to the user
debugContent += '\n . . .'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above "if" and "else" are essentially copy-paste except limit is replaced with a default value.

I suggest pulling the "magic number" into a "defaultOptions" object and use it like so:

    const limit = defaultOptions.limit;
    // OR:  const limit = defaultOptions.limit || 7000;
    const contentLength = debugContent.length
    debugContent = debugContent.slice(0, limit)
    if (limit <= contentLength)
      // there are more chars we have stripped, just show to the user
      debugContent += '\n . . .'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. Can be improved. This is just a rough draft, I have to clean up few things. Thanks for your comments.

@antsmartian
Copy link
Contributor Author

antsmartian commented Apr 6, 2018

@kentcdodds Hey Kent.. @sompylasar suggested the following:

What if the limit needed in browser where there's no env, e.g. via Cypress?

I suggest exporting a mutable "defaultOptions" object from the module instead, like Node's util.inspect.defaultOptions.

w.r.t to process env for DEBUG_PRINT_LIMIT. I was thinking it for a moment, if we do like defaultOptions, then we need to export it, as part of this lib. So was thinking, what your thoughts on that? Yes to me, thats a good way to support across all the env.

@antsmartian
Copy link
Contributor Author

Ping, any thoughts on my last comment? Guess people missed it ;) cc @kentcdodds

@kentcdodds
Copy link
Member

I have thoughts. Patience. Not all of us have time to address this within hours or days. We'll get to it when we can 😉

One thing you could do is rebase your branch and fix merge conflicts 😀

@antsmartian
Copy link
Contributor Author

antsmartian commented Apr 9, 2018

@kentcdodds Oops! Sorry thought people had missed it. will do the rebase and update the branch.

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts and ideas :)

README.md Outdated
For example:

```javascript
;`<div data-testid="debugging" data-otherid="debugging">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prettier added the ; to make this valid javascript.

Why don't we replace this whole block with:

```javascript
// <div>Hello world</div>
getByText(container, 'Goodbye world') // will fail by throwing error
```

This way:

  1. There's less noise that could distract from people's understanding of what's happening
  2. There's no weird ;
  3. We're using getByText properly (with container as the first arg).

README.md Outdated
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: Test value.. 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Test value." -> "Goodbye world"

README.md Outdated
>
Hello World!
</div>
</div>

This comment was marked as resolved.

README.md Outdated
</div>
```

Note: Since the DOM size can go really large, you can set the limit of DOM content to be printed via environment variable `DEBUG_PRINT_LIMIT`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"go" -> "get"

src/queries.js Outdated
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} \nHere is the state of your container: \n${htmlElement}`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the text: "Here is the state of your container" entirely. I think people will know what the printed HTML is without that.

src/queries.js Outdated
}
return el
}

function getByPlaceholderText(container, text, ...rest) {
const el = queryByPlaceholderText(container, text, ...rest)
if (!el) {
const htmlElement = trimToDebugLimit(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make a function called htmlElementToDisplay that trims and formats it, so we don't have to duplicate this everywhere.. Also, make sure that it compares the length of the html string rather than the formatted version because the formatted version will have extra characters for the highlighting.

I envision the error code below looking like:

throw new Error(
  `Unable to find an element with the placeholder text of: ${text}\n\n${htmlElementToDisplay(htmlElement)}`,
)

And the htmlElementToDisplay should be something like:

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

Note the addition of DOMCollection to the plugins (handles an array of nodes I think). Also note that we checking the maxLength against htmlElement.outerHTML. If we checked it against debugContent then we'd get a length that wasn't quite right (because the debugContent includes a bunch of characters for the coloring).

process.env.DEBUG_PRINT_LIMIT = 10000 // user shouldn't see `. . .`
expect(() =>
expect(getByLabelText('not present')).toBeInTheDOM(),
).toThrowError()

This comment was marked as resolved.

</div>`
const {getByTestId} = render(Hello)
process.env.DEBUG_PRINT_LIMIT = 10 // user should see `. . .`
expect(() => expect(getByTestId('not present')).toBeInTheDOM()).toThrowError()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good assertion, but we should add another assertion to make sure the error includes the ...

You can do this by adding a regex to the toThrowError call:

.toThrowError(/\.\.\./)

In the one below we should do the opposite

.toThrowError(/^((?!\.\.\.).)*$/)

I'm pretty sure that should verify that there is no ... in the error message.

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, meant to set my review to "Request changes" rather than "Approve"

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, meant to set my review to "Request changes" rather than "Approve"

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super! Thanks so much for your help on this!

@kentcdodds kentcdodds merged commit c8069e3 into testing-library:master Apr 10, 2018
@sompylasar
Copy link
Collaborator

@kentcdodds Did we go with unchecked process.env for configuration?

@kentcdodds
Copy link
Member

🎉 This PR is included in version 1.4.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@kentcdodds
Copy link
Member

Yes, for this first release I thought it'd be fine to go with the process.env-only approach. Though now that I think about it that will break the browser case. We should probably protect that reference 🤔

I was planning on accepting a PR for enhancing the configurability of this feature. I didn't consider that this would break the browser use-case.

@kentcdodds
Copy link
Member

That said, anyone using this in the browser will be using a bundler so I'm pretty sure this'll be fine.

@sompylasar
Copy link
Collaborator

That said, anyone using this in the browser will be using a bundler so I'm pretty sure this'll be fine.

I thought about this, too. Although I'm not 100% sure.

@antoaravinth Could you please check if this holds true at least for Cypress? You can do so from cypress-testing-library: clone it, update dom-testing-library dependency, npm run -s test:cypress or test:cypress:open. Thanks!

@antsmartian
Copy link
Contributor Author

@sompylasar Yes it will. I have checked anyways:

Opening Cypress...
  (Tests Starting)


  dom-testing-library commands
    ✓ getByPlaceholderText (537ms)
    ✓ getByText (129ms)
    ✓ getByLabelText (495ms)
    ✓ getByAltText (134ms)
    ✓ getByTestId (134ms)


  5 passing (2s)


  (Tests Finished)

  - Tests:           5
  - Passes:          5
  - Failures:        0
  - Pending:         0
  - Duration:        2 seconds
  - Screenshots:     0
  - Video Recorded:  false
  - Cypress Version: 2.1.0


  (All Done)

@sompylasar
Copy link
Collaborator

@antoaravinth Awesome! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants