testing-library-docs: Missing documentation: differences between queries
It seems that it would be helpful to provide documentation about when to use getBy*, queryBy*, findBy* and why we actually need all 3 methods.
I ran into an issue in which I don’t know how to assert that an asynchronously rendered element is not in the document. I found a comparison of the query methods in the react-testing-library docs, which show what returns what type, but it hasn’t helped me because it seems that the only async method (findBy*) throws an error.
Relevant code or config:
I started with this, which throws an error from the screen.findByText method before the assertion is executed. Based on some documentation I found, I understand it happens because screen.findBy* throws an error.
const element = await screen.findByText('my text');
expect(element).not.toBeInTheDocument();
What you did:
I hacked together this solution (that doesn’t work), in an attempt to create a function that asynchronously finds an element and returns null if not found instead of throwing.
Example of an attempt that failed without guidance
import { screen } from '@testing-library/react';
type queryType = 'altText'
| 'displayValue'
| 'labelText'
| 'placeholderText'
| 'role'
| 'testId'
| 'text'
| 'title';
export const getAsyncElement = async (
finderText: string,
queryType: queryType,
) => {
try {
let element: HTMLElement | null;
switch (queryType) {
case 'altText':
element = await screen.findByAltText(finderText);
case 'displayValue':
element = await screen.findByDisplayValue(finderText);
case 'labelText':
element = await screen.findByLabelText(finderText);
case 'placeholderText':
element = await screen.findByPlaceholderText(finderText);
case 'role':
element = await screen.findByRole(finderText);
case 'testId':
element = await screen.findByTestId(finderText);
case 'text':
element = await screen.findByText(finderText);
case 'title':
element = await screen.findByTitle(finderText);
}
return element;
} catch (error) {
return null;
}
};
As a test, I first tried this on an element that is in the document, like so:
const element = await getAsyncElement('Create Connection', 'text');
expect(element).toBeInTheDocument();
What happened:
From the above I got exactly the opposite of what I expected.
expect(received).toBeInTheDocument()
received value must be an HTMLElement or an SVGElement.
Received has value: null
I’m not so sure why my hack didn’t work, but really that’s beside the point. I’d like to know what is recommended in this case and I think that is the documentation I’m missing.
Suggested solution:
Provide documentation for two similar things:
- The differences between DOM query methods
getBy*,queryBy*,findBy*and when to choose one over another - Enhance existing docs about how to assert for asynchronously rendered elements to not be in the document.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 16 (1 by maintainers)
For anyone who might be interested. @alexkrolick suggestion here should work 🙏 I ended up modifying a little bit and using it like the below. This is testing that element 1 shows after specified amount of time and element 2 does not show after a specified amount of time. Since I’m mocking API requests and I know the timing expectations, this solution works for me.
And tests that look something like this…
Yep, I’m realizing now how complicated this really. Thanks for providing your solution. I think mine didn’t work because on first render the element is not there and it then throws right away. With your solution the
timeboxparam would need to be the time that the element should definitely show after… which could make sense.Thanks again for helping me think through this @alexkrolick. I wish things would be easier (to read and write), but I guess that’s just the nature of all this.
I think I get it - are you trying to assert that an element that’s currently not rendered does not show up. It’s a very challenging assertion because there is no observable change in the DOM that can trigger the end condition in the passing state, other than the end of a time window. So it’s basically equivalent to a sleep and then requery.
Something like that. The minimum runtime of the test is the length of time you want to wait to be sure it didn’t appear. You could optimize the failure path with early retries if you want, and you’d have to handle it differently if the element can appear and disappear within the window.
Hi @adamhenson, I think that you’re looking for this page