Why is RTL showing hidden elements as visible?
expect(screen.queryByText(/months in operation\?/i)).not.toBeVisible()
Received element is visible: <label aria-hidden="true" for="monthsDropdown" />
expect(screen.queryByText(/months in operation \?/i)).not.toBeInTheDocument()
expected document not to contain element, found <label aria-hidden="true" for="monthsDropdown">Months in operation?</label> instead
The parent div for this label also has the styles of display: none; visibility: hidden; height: 0;
Is this expected behavior?
TL;DR: The aria-hidden
attribute only hides the element from *ByRole
queries.
Let's assume we have the following component with two buttons.
const TestComponent = () => {
return (
<>
<div style={{ display: "none", visibility: "hidden", height: 0 }}>
<button>Hidden Button</button>
</div>
<button aria-hidden="true">Aria Hidden Button</button>
</>
);
};
Even though the first element is not visible due to the parent's style, and the second is being excluded from the accessibility tree with aria-hidden
, they'll both be accessible through getByText
queries since they're both present in the DOM.
// Both assertions will be pass
expect(screen.getByText("Hidden Button")).toBeInTheDocument();
expect(screen.getByText("Aria Hidden Button")).toBeInTheDocument();
However, trying to access them with getByRole
queries won't be possible as that query looks to the accessibility tree - unless the hidden
option is passed in the query.
// Will not find the elements
const [hiddenButton, visibleButton] = screen.getAllByRole("button")
// Will return both elements
const [hiddenButton, visibleButton] = screen.getAllByRole("button", { hidden: true })
In terms of visibility, the second button is visible while the first one is not.
// Both assertions will be pass
const [hiddenButton, visibleButton] = screen.getAllByRole("button", { hidden: true })
expect(hiddenButton).not.toBeVisible();
expect(visibleButton).toBeVisible();