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: suggestions for which query to use #586

Merged
merged 34 commits into from May 29, 2020

Conversation

benmonro
Copy link
Member

@benmonro benmonro commented May 25, 2020

What:
Adding suggestions for which query to use. (opt in via configure). When using configure({throwSuggestions: true}) tests will fail if they are not using the suggested queries. While this may seem harsh, the intent is more along the lines of the way that --detectLeaks works in jest.

Why:
To encourage people to follow best practices when using TL.

How:
a new getSuggestedQuery function that follows the order of 'which query should i use?'

Started going down the road of having a separate library but it became apparent pretty quick that it made more sense for this to live in DTL directly. We can export the suggestion from here for things like testing-playground.com

Docs PR: https://github.com/testing-library/testing-library-docs/pull/477/files

Checklist:

  • Support all use cases of 'which query should i use'
  • Documentation added to the
    docs site
  • I've prepared a PR for types targeting
    DefinitelyTyped
  • Tests
  • Ready to be merged

@benmonro benmonro added enhancement New feature or request needs discussion We need to discuss this to come up with a good solution needs investigation labels May 25, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented May 25, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 9cec773:

Sandbox Source
little-surf-usbk4 Configuration

@codecov
Copy link

codecov bot commented May 25, 2020

Codecov Report

Merging #586 into master will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##            master      #586   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           23        24    +1     
  Lines          468       535   +67     
  Branches       115       133   +18     
=========================================
+ Hits           468       535   +67     
Impacted Files Coverage Δ
src/config.js 100.00% <ø> (ø)
src/wait-for.js 100.00% <ø> (ø)
src/queries/alt-text.js 100.00% <100.00%> (ø)
src/queries/display-value.js 100.00% <100.00%> (ø)
src/queries/label-text.js 100.00% <100.00%> (ø)
src/queries/placeholder-text.js 100.00% <100.00%> (ø)
src/queries/role.js 100.00% <100.00%> (ø)
src/queries/test-id.js 100.00% <100.00%> (ø)
src/queries/text.js 100.00% <100.00%> (ø)
src/queries/title.js 100.00% <100.00%> (ø)
... and 3 more

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 709044b...9cec773. Read the comment docs.

suggestions.length === 1 &&
!allQuery.name.endsWith(rawSuggestions[0].queryName)
) {
throw getSuggestionError(suggestions[0], container)
Copy link
Member Author

Choose a reason for hiding this comment

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

@kentcdodds was pairing on this with my friend Kevin and he asked a good question. Should the suggestion Try to actually execute the query it's suggesting just to make sure it works? Curious to hear your thoughts.
..


function queryAllByPlaceholderText(...args) {
return queryAllByAttribute('placeholder', ...args)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

ICYW: all of the other queries use named functions and the suggestion feature needs them to be named.

queryAllByAttribute(getTestIdAttribute(), ...args)
function queryAllByTestId(...args) {
return queryAllByAttribute(getTestIdAttribute(), ...args)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

ICYW: all of the other queries use named functions and the suggestion feature needs them to be named.

@benmonro benmonro requested a review from kentcdodds May 26, 2020 06:01
src/query-helpers.js Outdated Show resolved Hide resolved
@smeijer
Copy link
Member

smeijer commented May 26, 2020

Copy from Twitter again 😇

@benmonro
We've mentioned something like "screen.getByLabelText('foo', { silent: true })" and agreed it's not the best solution.

How do you feel about using an "instance" level for it instead? screen.showSuggestions(true|false), where you can run queries in between:

screen.showSuggestions(false);

document.querySelector('input'); // don't throw this time

screen.showSuggestions(true);

Or an alternative, a wrapping method like within

Name it strict?

const { getByText } = strict(document.getElementById('messages'));
const helloMessage = getByText('hello')

This could then be accompanied by a screen.strict = true, which enables strict mode for all screen.x queries.

@benmonro
Copy link
Member Author

benmonro commented May 26, 2020

@kentcdodds @eps1lon @smeijer I tried using warn (screenshot below). I'm definitely not a fan for 2 reasons:

  1. you don't really know which test it's in. Failing the test will make it very obvious which test is using the query we're suggesting for improvement. If you know of a way to tie the warning to the line and/or test that's using the query I could be open to that, but otherwise how will people know where the suggestion is pointing?

  2. While the difference between getByLabel and getByRole might be slight, my biggest goal with this is to get people to stop using getByTestId which in my view does in fact warrant a fail when there's something better out there.

image

@smeijer
Copy link
Member

smeijer commented May 26, 2020

While the difference between getByLabel and getByRole might be slight, my biggest goal with this is to get people to stop using getByTestId which in my view does in fact warrant a fail when there's something better out there.

Do I understand correctly that you only throw for getByTestId queries? Because if throwing only happens for querySelector and getByTestId when the dev chose to opt-in, then I (as a developer) would have zero issues with throwing.

Heck, I can imagine something like that being default in a future version.

@benmonro
Copy link
Member Author

Do I understand correctly that you only throw for getByTestId queries?

no, I'm not currently having this warn on usages of querySelector, that would be a bit sketchy imo as we'd have to change the way querySelector works. Also I feel like that one can be done w/ a lint rule. As for getByTestId, no I do not only throw on that. It throws whenever a better option is available.

If we use warn we'd have to parse the stack trace (not sure when I'd have cycles to do that) have a look at my screenshot above (or feel free to pull my branch and play with it). If you can get the stack trace parsing working I'd be fine w/ having it use a warn (or maybe make that optional as well?). We can do new Error().stack but it would have to ultimately highlight the exact line in their test where the query was called or it's better to just fail the test imo.

I'll lay out the options here (in no particular order) for how to disable the suggestion for a single query, feel free to add more options, but maybe we can take a vote?

option 1: an additional query option in the query itself:

screen.getByTestId("foo", {suggest: false})

option 2: just use configure:

configure({showSuggestions:false});
screen.getByTestId("foo")
configure({showSuggestions:true});

option 3: Stephan's screen.showSuggestions idea

screen.showSuggestions(false);

document.querySelector('input'); // don't throw this time

screen.showSuggestions(true);

option 4: more granular configuration option for showSuggestions

configure({
  showSuggestions: { ByLabelText: "warn", ByTestId: "error" }
})

@smeijer
Copy link
Member

smeijer commented May 26, 2020

Option 5

// strict || strictA11y || ...

const { getByText } = strict(document.getElementById('messages'));
const helloMessage = getByText('hello')

A method like that fits nicely together with within in terms of code style.

Talking about within. If I'm not mistaken, that method also modifies the error stack to print the correct code frame. That's why we have the option showOriginalStackTrace.

I do agree, that is a must for this to work nicely. Showing incorrect code frames is nothing more than confusing.

@benmonro
Copy link
Member Author

@smeijer ah ok i'll look into how that prints the stack trace.

w/ regards to using strict(document.querySelector|getElementById) not sure I follow the point there... imo if you are using any of the primitive options to query the DOM that can be handled by a lint rule. can you elaborate on how you see that working? it's not clear to me...

@smeijer
Copy link
Member

smeijer commented May 26, 2020

edit Figured it out, and deleted some background info, you can still see it under the "edits".


What I'm proposing is an API like this:

import { screen, within } from '@testing-library/dom';

const view = within(document.getElementById('messages'), { strict: true });
view.getByText('hello'); // might throw for a11y issues

screen.getByText('hello'); // still not throwing

This would play nicely together with your option 1.

import { screen, getByText } from '@testing-library/dom';

screen.getByText('hello', { strict: true }); // might throw
getByText(document, 'hello', { strict: true }); // might throw

ps. I'm deliberately naming it strict and not suggest. As throwing errors isn't suggesting. It's enforcing. Naming it something like strictA11y would also be fine.

@benmonro benmonro removed the needs discussion We need to discuss this to come up with a good solution label May 27, 2020
@benmonro
Copy link
Member Author

@kentcdodds @smeijer all the feedback has been addressed. docs PR is good to merge as well as soon as this merges.

Copy link
Member

@timdeschryver timdeschryver left a comment

Choose a reason for hiding this comment

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

Really looking forward to this 👍
Should the extra config suggest also be added to the matcher types?

@kentcdodds
Copy link
Member

Yes 👍

@benmonro
Copy link
Member Author

@timdeschryver @kentcdodds added the type to matcher types.

types/matches.d.ts Outdated Show resolved Hide resolved
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.

Stellar! Thanks!

@kentcdodds kentcdodds merged commit 839dca3 into testing-library:master May 29, 2020
@benmonro benmonro deleted the feature/suggestions branch May 29, 2020 18:49
@kentcdodds
Copy link
Member

🎉 This PR is included in version 7.7.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@benmonro
Copy link
Member Author

@allcontributors please add @appleJax for code & tests

@allcontributors
Copy link
Contributor

@benmonro

I've put up a pull request to add @appleJax! 🎉

@benmonro
Copy link
Member Author

@allcontributors please add @benmonro for code, ideas, tests, docs

@allcontributors
Copy link
Contributor

@benmonro

I've put up a pull request to add @benmonro! 🎉

@smeijer
Copy link
Member

smeijer commented May 29, 2020

🎉

Nice work @benmonro ! Thanks.

@smeijer
Copy link
Member

smeijer commented May 29, 2020

I don't think that this pull-request would have been here if it wasn't for http://testing-playground.com, so how about this one as well?

@allcontributors please add @smeijer for ideas

@allcontributors
Copy link
Contributor

@smeijer

I've put up a pull request to add @smeijer! 🎉

@benmonro
Copy link
Member Author

@smeijer yep yep

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request released
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants