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

Improving api #13

Merged
merged 15 commits into from Mar 28, 2018
Merged

Improving api #13

merged 15 commits into from Mar 28, 2018

Conversation

antsmartian
Copy link
Collaborator

@antsmartian antsmartian commented Mar 22, 2018

What: Improving test API

Why: Discussed already here : #3

How: Again as discussed :)

Checklist:

  • Documentation
  • Tests
  • Ready to be merged
  • Added myself to contributors table

Related to this thread : #3

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.

Looking very good! Thanks! Just a few comments. Let's iterate and make this really amazing!


// Act
Simulate.click(getByTestId('load-greeting'))

await flushPromises()

// Assert
expect(queryByTestId('foo')).not.toBeInTheDOM()
Copy link
Member

Choose a reason for hiding this comment

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

I think people will look to these tests as an example of how to test their components with good practices.

With that in mind, let's do one of the following:

  1. Add a comment suggesting: "Here are a few assertions you could use, but you don't need all of them."
  2. Move these to a separate testing file that's intended to show off the different kinds of assertions.

I don't really care which we do.

@@ -1,6 +1,7 @@
import React from 'react'
import axiosMock from 'axios'
import {render, Simulate, flushPromises} from '../'
import '../../extend-expect' //eslint-disable-line import/no-unassigned-import
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 just disable this rule globally. It really annoys me and I should remove it from my base config eventually...

In the package.json, update the eslintConfig to have a rules property that disables this rule 👍

toBeInTheDOM(received) {
if (received) {
return {
message: () => 'expected the dom to be present',
Copy link
Member

Choose a reason for hiding this comment

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

Rather than calling it "dom" call it "element." The received is an element, not "dom" 👍

Same below.

const pass = textContent === checkWith
if (pass) {
return {
message: () => `expected ${textContent} not equals to ${checkWith}`,
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 use jest-matcher-utils here so it's colored properly.

},

toHaveTextContent(htmlElement, checkWith) {
const textContent = htmlElement.textContent
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 add an extra check here that htmlElement is actually instanceof HTMLElement? If not we can:

throw new Error(`The given subject is a ${getDisplayName(htmlElement)}, not an HTMLElement`)

Or return an object, but this should probably fail regardless of whether people use .not.

The getDisplayName function could be something like this:

function getDisplayName(subject) {
  if (subject && subject.constructor) {
    return subject.constructor.name
  } else {
    return typeof subject
  }
}

Or, maybe there's a better thing for this available on npm...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess the code that you had mentioned getDisplayName is enough. npm packages are there, but they offer many functionalities which we won't need at this time.

}
} else {
return {
message: () => `expected ${textContent} equals to ${checkWith}`,
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 use jest-matcher-utils here so it's colored properly.

},

toSatisfyDOM(element, fn) {
const pass = fn(element)
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be more like this: https://github.com/jest-community/jest-extended/blob/master/src/matchers/toSatisfy/index.js

In fact, it looks like we should probably be changing everything in this file to use jest-matcher-utils so it's colored properly etc. Unfortunately it looks like there are no docs, but here's the source.

@antsmartian
Copy link
Collaborator Author

@kentcdodds Hey, have addressed your comments also I have moved our assertions to element-queries, I guess now that's the apt place. Also all the assertions are moved to jest-color, so we get nice color when things get failed :) Let me know if everything is good to go..

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.

Looking awesome. Please address my few comments and let's add a section for these to the README please :) Thanks a ton!

@@ -1,5 +1,6 @@
import React from 'react'
import {render} from '../'
import '../../extend-expect'
Copy link
Member

Choose a reason for hiding this comment

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

The problem with this is it means you have to run the build before you can run the tests (because on a fresh clone the dist directory doesn't work). It also means that any changes to jest-extensions.js wont be picked up by tests until the next build.

Instead, you'll basically need to duplicate the code.

Alternatively... You could put the code that's currently in extend-expect.js into src/extend-expect.js and then change the contents of: extend-expect.js to be simply: require('./dist/extend-expect'). Then you could update this import to simply: '../extend-expect' 👍

<span data-testid="count-value">2</span>,
)

//other ways to assert your test cases, but you don't need all of them.
Copy link
Member

Choose a reason for hiding this comment

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

Not sure why prettier didn't add a space here, but please add a space before the o :)

//other ways to assert your test cases, but you don't need all of them.
expect(queryByTestId('count-value')).toBeInTheDOM()
expect(queryByTestId('count-value')).toHaveTextContent('2')
expect(getByTestId('count-value')).toSatisfyDOM(el => el.textContent === '2')
Copy link
Member

Choose a reason for hiding this comment

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

I'm starting to question the value of this matcher. Maybe instead we should just recommend people use jest-extend instead. This is basically the same as toSatisfy from there.

The other matchers are still very valuable though!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@kentcdodds toSatisfyDOM seems to be a fit for me in this library. Why we need to ask user to use jest-extend for a such a simple call? Yes, its almost the same code, but I feel it should be added. Anyways, let me know if you think otherwise.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that this matcher can be useful, but I'd much rather be very selective about what we include in this library. It's much easier to exclude something and add it later than add something and remove it later. So I lean on the side of excluding things. I'm thinking most people wont need this and those who do will be happy to include jest-extend I think.

)

const textContent = htmlElement.textContent
const pass = textContent === checkWith
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 extend this to use the matcher function that's in the queries.js file? Perhaps we should move that matcher function to a utils file.

Using the matcher function would allow the toHaveTextContent to use a regex/case insensitive substring/function which I think would make this more powerful.

Copy link
Collaborator 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 idea. Can take care of that, will move matcher to utils folder. Import matcher in queries.js and jest-extension.js files to use them. So fundamentally the call would be:

const pass =  matches(textContent,htmlElement,checkWith)

@antsmartian
Copy link
Collaborator Author

@kentcdodds Sure, will look into the changes and update the PR.

@kentcdodds
Copy link
Member

Also, let's figure out what what extra coverage we need for our tests. Open coverage/lcov-report/index.html in your browser to see the coverage report.

@antsmartian
Copy link
Collaborator Author

antsmartian commented Mar 27, 2018

@kentcdodds Yes, jest-extensions.js need to be tested. I can write few test cases for that. Seems to be a very interesting discussion on this PR, long thread!

@antsmartian
Copy link
Collaborator Author

@kentcdodds Update the PR. Few things that are pending:

  1. Add test cases for jest-extensions
  2. Write few more assertions for toHaveTextContent with regex etc, to show what user can actually do.
  3. Update the README on the following:
toHaveTextContent
toBeInTheDOM

their use cases etc.

Anything else is missing?

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.

Looks like we still need docs in the README and we're not at 100% code coverage.

Also, there's a merge conflict in the README and .all-contributorsrc. If you just deal with the conflict in .all-contributorsrc then run npx kcd-scripts contributors generate then that should fix the README 👍

Thank you so much for iterating on this!

src/utils.js Outdated
@@ -0,0 +1,9 @@
export default function matches(textToMatch, node, matcher) {
Copy link
Member

Choose a reason for hiding this comment

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

Rather than exporting this as the default (because this file is called utils), could we make it a named export (and feel free to disable the eslint warning, that's another one I've been meaning to disable).

function matches() {/* etc... */}

export {matches}

@antsmartian
Copy link
Collaborator Author

@kentcdodds This should be good to go. Let me know if anything else is needed.

@codecov
Copy link

codecov bot commented Mar 28, 2018

Codecov Report

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

Impacted file tree graph

@@          Coverage Diff          @@
##           master    #13   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           2      5    +3     
  Lines          53     72   +19     
  Branches       13     17    +4     
=====================================
+ Hits           53     72   +19
Impacted Files Coverage Δ
src/queries.js 100% <ø> (ø) ⬆️
src/utils.js 100% <100%> (ø)
src/extend-expect.js 100% <100%> (ø)
src/jest-extensions.js 100% <100%> (ø)

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 8403e84...e222206. Read the comment docs.

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 great! Thank you so much for these changes! Just a few docs changes please.

README.md Outdated
The first one `toBeInTheDOM` which allows you to assert whether an element present in the DOM or not

```javascript
import 'extend-expect' //adds few API to jest's extend
Copy link
Member

Choose a reason for hiding this comment

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

This will be import 'react-testing-library/extend-expect'

README.md Outdated
@@ -219,6 +219,31 @@ const usernameInputElement = getByTestId('username-input')
> Learn more about `data-testid`s from the blog post
> ["Making your UI tests resilient to change"][data-testid-blog-post]

#### `toBeInTheDOM`
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 put these in a different section of the README (maybe right where it is).

So just change this to:

## Custom Jest Matchers

There are two simple custom Jest matchers which you can use to extend the `expect` API of Jest for making assertions easier.

### `toBeInTheDOM`

This allows you to assert whether or not an element present in the DOM

... etc.

README.md Outdated
This API allows you to check whether the given element has a text content or not.

```javascript
import 'extend-expect' //adds few API to jest's extend
Copy link
Member

Choose a reason for hiding this comment

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

Here as well. Also, please add a space after the //

README.md Outdated

render(<span data-testid="count-value">2</span>)
expect(queryByTestId('count-value')).toHaveTextContent('2')
expect(queryByTestId('count-value')).not.toHaveTextContent('21')
Copy link
Member

Choose a reason for hiding this comment

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

For these two examples, could we use getByTestId rather than queryByTestId?

README.md Outdated

render(<span data-testid="count-value">2</span>)
expect(queryByTestId('count-value')).toBeInTheDOM()
expect(queryByTestId('count-value1')).not.toBeInTheDOM()
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a comment below that says: "Note: for this assertion, make sure you use a query function (like queryByTestId) rather than a get function (like getByTestId). Otherwise the get function could throw an error.

@@ -0,0 +1 @@
require('./dist/extend-expect')
Copy link
Member

Choose a reason for hiding this comment

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

Perfect

@antsmartian
Copy link
Collaborator Author

Sure Kent, will make these changes in sometime and update the PR.

@antsmartian
Copy link
Collaborator Author

@kentcdodds Addressed your comments. Let me know if anything else is needed?

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 great! Thanks!

@kentcdodds kentcdodds merged commit 18104fe into testing-library:master Mar 28, 2018
@kentcdodds
Copy link
Member

Thanks so much for your help! I've added you as a collaborator on the project. Please make sure that you review the other/MAINTAINING.md and CONTRIBUTING.md files (specifically the bit about the commit messages and the git hooks) and familiarize yourself with the code of conduct (we're using the contributor covenant). You might also want to watch the repo to be notified when someone files an issue/PR. Please continue to make PRs as you feel the need (you can make your branches directly on the repo rather than your fork if you want). Thanks! And welcome to the team :)

julienw pushed a commit to julienw/react-testing-library that referenced this pull request Dec 20, 2018
* added fireEvent util

* remove react logic

* 💯 coverage

* Update README.md
lucbpz pushed a commit to lucbpz/react-testing-library that referenced this pull request Jul 26, 2020
chore: 🤖 add dom-testing-library as npm keyword
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.

None yet

2 participants