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

getByText throwing when throwSuggestions is enforced recommending ambiguous getByRole('paragraph') approach. #1306

Closed
enmanuelduran opened this issue Apr 25, 2024 · 9 comments

Comments

@enmanuelduran
Copy link

enmanuelduran commented Apr 25, 2024

  • @testing-library/dom version: 10.0.0 (latest)
  • Testing Framework and version: react-scripts ^5.0.1 (Reproducible with Vitest as well)
  • DOM Environment: jsdom 16.7.0 or 16.6.0

Relevant code or config:

import React from "react";
import { render, screen, configure } from "@testing-library/react";

configure({ throwSuggestions: true  });

test("React Testing Library works!", () => {
  render(<p>Hello world!</p>);
  const p = screen.getByText('Hello world!');
  expect(p).toBeInTheDocument();
});

What you did:

Added this in all the places in the code base where the error appears, which made my tests noisy:

getByRole('paragraph', { name: (name, element) => element.textContent ===  'Hello world!' });

What happened:

I got this recommendation, which is contradictory with RTL's documentation and ambiguous (not simple to query paragraphs by text):

paragraph recommendation issue demonstration

Reproduction:

StackBlitz:

https://stackblitz.com/edit/dtl-template-zyki3r?file=src%2Fmain.test.ts

Problem description:

I have enabled suggestions in my code base, however, when trying to use getByText as recommended in the priority list for paragraphs outside of forms, I get an error, saying:

TestingLibraryElementError: A better query is available, try this:
    getByRole('paragraph')

Which makes sense, however, that recommendation made me think that I could use something like getByRole('paragraph', { name: 'Hello world!' }) but this wont work:

TestingLibraryElementError: Unable to find an accessible element with the role "paragraph" and name "Hello world!"

To solve the error I found this was possible:

getByRole('paragraph', { name: (name, element) => element.textContent ===  'Hello world!' });

However this is not a simple solution as in a large codebase with slightly larger texts this would potentially become a little more unmanageable/noisy, for other APIs like Buttons or Headings we already use the name to query for the textContent of the element, e.g screen.getByRole('heading', { name: 'Hello world!', level: 1 }) would work if the paragraph was an <h1> tag instead.

By doing some quick checks, I found that we match the name (or not) against the element using this matcher function, which uses computeAccessibleName from the dom-accesibility-api module. In the case of the paragraph naming is prohibited, hence that function will return an empty string which explains why we can't use the name to query texts.

I understand this is based on the specs from the w3c however, I believe we should clarify the path forward for this kind of case.

Suggested solution:

I believe this issue was introduced after the changes on #1241 which upgrades the aria-query library from 5.1.3 to 5.3.0 with several important changes to elements including the paragraph -- before this, getByText worked without issues and as per my tests it is because before the canSuggest call here returned undefined where now it returns true because the role argument passed is set to paragraph instead of undefined.

There are a few options as I can see for this issue:

  1. If this change only impacts paragraphs, we can follow the recommendation from the RTL priorities for the suggestions that are made on the library and avoid the failure on the getByText case for paragraphs, if we go down this path I could either add the p tag as part of the defaultIgnore "list" or add custom functionality to handle this in the getSuggestedQuery method.

  2. Update the documentation for RTL to suggest a path to move forward if we don't want to change the existing code, maybe using this approach or a better suggested approach by you/the community:

getByRole('paragraph', { name: (name, element) => element.textContent ===  'Hello world!' });
  1. Not a big fan of this option, but we could allow to query through the name for paragraphs within the dom-testing-library (assuming this ony affects paragraphs), by bypassing the check with the computeAccessibleName method from the dom-accesibility-api just for this case, in this way we could use the getByRole method with less complexity.

  2. This is a big miss-understanding, there is already a solution for this that I don't know of in which case I will be more than happy to close this one out and I apologize for the waste of time if that's the case.

Glad to do any changes in the code if we find this to be an actual issue and if we agree on a path forward!

Cheers!

Update:

After some more digging, I realized that this issue may affect more than just paragraphs (reference), so some of my initial suggestions might not work since they will need to be more generic. Wondering if getByText is progressively losing value as these new changes are happening, at least within the scope of throwSuggestions: true, maybe an update on the documentation would be best to clarify how to query paragraphs in an accesible manner to avoid the errors from getByText? so something close to suggestion 2.

Copy link

Uh oh! @enmanuelduran, the image you shared is missing helpful alt text. Check your issue body.

Alt text is an invisible description that helps screen readers describe images to blind or low-vision users. If you are using markdown to display images, add your alt text inside the brackets of the markdown image.

Learn more about alt text at Basic writing and formatting syntax: images on GitHub Docs.

@testing-library testing-library deleted a comment from github-actions bot Apr 26, 2024
@testing-library testing-library deleted a comment from github-actions bot Apr 26, 2024
@MatanBobi
Copy link
Member

Hi @enmanuelduran, thanks for taking the time to document this.
I'm leaning towards not changing the implementation as we're following the spec and if we'd start diverging from it, we'll end up with something that will be hard to maintain and will cause us to maybe make bad decisions in the future.
A documentation change might be a good idea, let's see what other maintainers think.

@enmanuelduran
Copy link
Author

enmanuelduran commented Sep 6, 2024

I am bit surprised that no-one else has reported this, I imagine it is because throwSuggestions is experimental, since we are not getting traction in the topic @MatanBobi, should we discuss the possibility of a documentation change/addition such as:


When the throwSuggestions feature is enabled, the library may provide recommendations that lack an intuitive solution for querying certain elements. This typically occurs for roles that do not support naming, most notably paragraphs. For instance, if you attempt to use getByText, you may encounter the following error:

TestingLibraryElementError: A better query is available, try this:
    getByRole('paragraph')

However, there is no direct way to query paragraphs using an object as the second parameter, such as in getByRole('paragraph', { name: 'Hello World' }).

To resolve this issue, you can create a custom query function to validate the element's structure and suppress the error. For example:

getByRole('paragraph', { name: (name, element) => element.textContent ===  'Hello world!' });

I could add that change in
https://testing-library.com/docs/dom-testing-library/api-configuration/#throwsuggestions-experimental.

Feel free to involve other people and suggest improvements if you think the message is verbose, or suggest other path if the
suggestions I have made are not feasible.

@MatanBobi
Copy link
Member

Thanks for taking the time on this @enmanuelduran.
I'm hesitating if this issue is enough or should we add the documentation.
I'd love to hear what @eps1lon and @timdeschryver think about this.

@timdeschryver
Copy link
Member

@MatanBobi adding it to the documentation is good for me.
IIRC this feature was added primarily to be used on Testing Playground.

@enmanuelduran
Copy link
Author

enmanuelduran commented Sep 14, 2024

Alright aligned, will create a PR for the documentation update and send it to you guys to review it.

@enmanuelduran
Copy link
Author

Have raised a pull request with updates to the documentation.

Feel free to review and suggest improvements @MatanBobi @timdeschryver

@enmanuelduran
Copy link
Author

enmanuelduran commented Sep 22, 2024

Friendly reminder to check the issue and Pull request associated @MatanBobi @timdeschryver

@enmanuelduran
Copy link
Author

Documentation has been updated!

For anyone in the future facing this issue, in summary, you can address this issue by using the getByRole method:

 getByRole('paragraph', { name: (name, element) => element.textContent === text });

If you want to make it more concise, you can create a global helper method in your global setup file to address this issue and avoid the extra noise (if you decide to enforce throwSuggestions that is), base idea:

global.getParagraphByText = (text) => {
    return getByRole('paragraph', { name: (name, element) => element.textContent === text });
};

this would improve your test's readability to global.getParagraphByText('Hello World').

If you are not interested on tracking paragraphs for some other reason, then you could just disable the suggestion for the cases when the error appears.

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

No branches or pull requests

3 participants