-
Notifications
You must be signed in to change notification settings - Fork 470
Improve the 'byText' queries capability. #53
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
Comments
Also see getVisuallyBelow in the comment here: testing-library/cypress-testing-library#6 (comment) |
Hey, thanks so much for the quick response @sompylasar. I'm not sure, but I think the solutions you're pointing to are all for cypress-testing-library. I'm suggesting this feature for react-testing-library. I've not used others, but I think if we could add just that one line here, the rest dependants shouldn't bother changing a thing. |
@Dajust Yes, as I understood, you wanted a field in the "options" argument to override the "container" argument to serve the same purpose that "container" already does. First of all, I suggest "within" for a name, "in" is too short to be searchable, it's also a JS keyword in some contexts. Second, could you please elaborate why do you want it to be in the "options" and why "container" does not work for you? |
P.S. The solutions I show are implemented in cypress-testing-library because I now use Cypress at work for UI end-to-end tests and that's where I was given enough time to apply my ideas. Previously I played with Puppeteer and made a POC implementing the same ideas with injected scripts (for context, this is how Cypress works, by injecting scripts next to your running app). |
Specifying |
Sure, I don't have a problem with whatever name that works 😄 I'm also not doing end-to-end testing at this time, just integration tests. |
Okay, you could override the container during the render phase, but that doesn't change anything to what we want, because whatever container that is provided needs to 'contain' what was rendered before we can even start querying stuffs. |
@Dajust Okay, if you want to query within a node that you got (how?), you could use dom-testing-library queries directly to provide that node as a container, not use the pre-baked query functions provided by |
Yeaa, makes sense! 😄 But on a second thought, then that means we have to import both react-testing-library and dom-testing-library in our tests. That kinda kills the beauty of having react-test-library as a wrapper around dom-testing-library. (Same applies to other dependants) Also, how about teachability? I was actually writing a thing on react-testing-library that inspired this suggestion. But now having to start explaining the limitations on react-testing-library and why you need to import the core dom-testing-library to text X kinda sucks. To be clear, it works just fine for me, but teaching it will suck, especially when I know we could effortlessly simplify that process. My thoughts though. |
I don't have a lot of real teaching / course writing experience, but when I explain things to my colleagues, my goal is to make them understand how the thing works, not to abstract things away from them pretending there's nothing down there. I'd start with explaining that react-testing-library is a thin wrapper around dom-testing-library, which in turn is a thin wrapper around the DOM; their purpose is to avoid boilerplate by these nice abstractions for querying based on how a user uses the app, not on DOM structure. |
What about: const sidebar = container.querySelector('.sidebar')
getByText(sidebar, 'some text') You don't have to use the container obtained from |
Sorry, I don't have time to catch up on the thread, but I think it'd be totally cool to add some kind of helper function to make this more straightforward and easy for people. Even if it's very simple to workaround. |
I think this should work (on my phone): import {render, queries} from 'react-testing-library'
// ....
const {getByTestId} = render(/* stuff */)
const lolMessage = queries.getByText(getByTestId('messages'), 'lol') |
And by that, I'm pretty sure that'll work right now. |
I think it'd maybe make more sense though to support an |
I initially thought this would be unwarranted, but since the version of the Both |
I vote for Who wants to make the PR? |
What if we did: const {getByTestId, within} = render(/* stuff */)
within(getByTestId('messages')).getByText('lol') Reads more like a sentence I think... What's returned by within is all the queries with the given element as the container. |
I think I like that better |
I can do it, but only if @Dajust is not interested in doing it himself. Regarding the other option you just suggested, what happens if you call |
lol, now that I think of it, import {getQueriesForElement as within, render} from 'react-testing-library'
// ...
const {getByTestId} = render(/* stuff */)
within(getByTestId('messages')).getByText('lol') Sooo.... Hmmm.... considering this, I'm thinking that no change is best and rather we should add examples/docs to help people avoid this question. |
It's always best when no api changes are needed. Documentation then, a FAQ entry maybe? Or something more explicit? In any case, I really think @Dajust should be the one doing it. |
I agree. @Dajust, what would have made this approach more obvious? How can we document this better? |
This works just fine for me @kentcdodds 😄 And now that I gave a broader thought, same is applicable to all queries, not just the "byTexts". Docs: We could add a new section, maybe somewhere after the queryAll and getAll APIs and before the Exaples section, that documents withinSometimes, there is no garauntee that the text, placeholder, or label you want to query is unique on the page. So you might want to explicity tell react-render-dom to get an element only within a particular section of the page. Example: To get the text 'hello' only within a secton called 'messages', you could do: import {getQueriesForElement as within, render} from 'react-testing-library'
// ...
const {getByTestId} = render(/* stuff */)
const messagesSection = getByTestId('messages')
const hello = within(messagesSection).getByText('hello') Note that the actuall helper function here is |
Umm... 🤔 How about we export |
We could export it with both names as it makes sense as it's named for the current use case and makes more sense as within for the new use case. Then we can document it as you described. |
Can I help do this please? |
Please do! |
Wasn't this solved in #54? If so the issue should be closed manually. |
Describe the feature you'd like:
'byText' queires,
getByText()
, for example, works greate in writing tests that closely resembles users actions, but it has a drawback, expecially on integration tests, which usually requires many components to be rendered.Sometimes a user might be interested in a particular text, but only on a prticular section of the page. Take the bellow screenshot for example.
So the user sends a 'lol' and 'today' message and wants to make sure the message is sent. She'll have to look for the message "byText" 'lol' to see when/if the message has been markded as sent. It is only the "byText" queries that can help us do this job, but here's the problem: There are many 'lol' and 'today' texts on the page, but our user is only looking at or interested in the 'lol' text INSIDE-THE-MESSAGES-SECTION.
getByText
in it's current form will not help us in such case, because it will grab the text 'lol' anywhere it founds it on the page, which in turn require our tests to rely on DOM structure to get the text where're interested in.Suggested API:
Provide an option to tell the 'byText' queries where-in-the-document they should get the text. Something like this:
getByText('some text', {in: node})
Suggested implementation:
I think this could be easily implemented by some minor editting the queryAllByText fucntion.
We could simply check to see if the
in
option is present, then use it as the baseElement of the querySelectorAll(). We only need to touch too lines in the function. Something like this:Describe alternatives you've considered:
N/A
Teachability, Documentation, Adoption, Migration Strategy:
To use this feature, user will need to first grab the
in
node, then use it as thein
option. Like this:const messageArea = getByTestId('message-area');
getByText('some text', {in: messageArea})
The text was updated successfully, but these errors were encountered: