Are you trying to target a specific element inside the DOM in Jest to make testing simpler and not sure how to do it?
This post on querying within components using within
will hopefully point you in the right direction.
This article was originally posted (and is more up to date) at https://robertmarshall.dev/blog/how-to-query-and-test-within-a-specific-component-in-jest/
Context
There is an component that has three cards outlining a product. Each card has a title element and a button that fires a function. We want to test if the correct function fires, when the Cat Food “Buy Item” button is clicked.
This looks like this:
import "./styles.css";
import { purchaseFunction } from "./api";
const Card = ({ title, slug }) => (
<div className="card">
<h2>{title}</h2>
<button className="button" onClick={() => purchaseFunction(slug)}>
Buy Item
</button>
</div>
);
const Products = ({ items }) => (
<div className="products">
{items.map(({ title, slug }) => (
<Card key={slug} title={title} slug={slug} />
))}
</div>
);
export default function App() {
const items = [
{ title: "Cat Food", slug: "cat-food" },
{ title: "Dog Food", slug: "dog-food" },
{ title: "Waffles", slug: "waffles" }
];
return (
<div className="app">
<h1>Shop</h1>
<Products items={items} />
</div>
);
}
A working example of the above, so you can see the code: React Testing Library Within CodeSandbox.
The issue is that each button has the same label “Buy Item”. How can the jest test target a specific section of the component? How can we use React Testing Library focus down into a DOM node and only use a section in the test?
What To Do?
There are several options at this point. It would be easy to apply a testid
to each button with a unique name, but this wouldn’t be testing the proper component. Using the testid
query is only recommended for cases where you can’t match by role or text or it doesn’t make sense. More detail on this can be found in the React Testing Library priority page.
It would also be possible to use queryAllByText
to find each ‘Buy Item’ button, then find the second button. Like so:
test("purchaseFunction called with correct product slug on second button click", () => {
render(<App />);
// Get the dog food buy button from an array of all buy buttons.
const DogFoodButton = screen.queryAllByText("Buy Item")[1];
userEvent.click(DogFoodButton);
expect(purchaseFunction).toHaveBeenCalledWith("dog-food");
});
However this is very brittle and if the card positions are switched/moved the test will break. We do not want to test the order of the cards. We want to test if a particular button is pressed.
The Cleaner Way To Test
This is where within
is super handy. It allows the test to be focused within a particular DOM element. To run a jest test inside an element block.
This would be handled like so:
test("purchaseFunction called with correct product slug on Dog Food click", () => {
render(<App />);
// Get the specific card and bind to query functions using within.
const DogFoodCard = within(screen.getByText("Dog Food").parentElement);
// Now we can click the specific button within that card.
userEvent.click(DogFoodCard.getByRole("button", { name: "Buy Item" }));
expect(purchaseFunction).toHaveBeenCalledWith("dog-food");
});
Although the test is a little longer, it is more explicit in what is happening. It allows jest queries to be restricted with within a particular element meaning that the tests themselves can be far more succinct and cleaner. Hopefully this answers the question of how to make queries in Jest test within context of particular element.
For more React and Jest hints and tips take a look at the React Category, and Jest Testing Category!
Top comments (1)
thanks for info.