-
Notifications
You must be signed in to change notification settings - Fork 17
Description
Hi
The render function appears to generate element locators based on baseElement (by default document.body) instead of the container.
I’m not sure if this is intentional, but my expectation was that the container would be used as the base for the element selectors.
This seems to come from this line in pure.tsx:
vitest-browser-react/src/pure.tsx
Line 130 in 57c57b1
| ...getElementLocatorSelectors(baseElement), |
The main issue I’m seeing is that when page.elementLocator is called with document.body, it can create a selector that isn’t stable.
Instead of a simple body selector, the generated selector uses the body’s text content.
If the body content changes over time, the base selector can become invalid, which then causes tests to fail even though the DOM is in the expected state.
Locally, I was able to resolve this by changing the line in pure.tsx to use container instead of baseElement.
But Im not sure if this is the right solution and if it was intended or not.
Adding a simple code reproduction here:
import React from "react";
import { expect, test } from "vitest";
import { render } from "vitest-browser-react";
import { page } from "vitest/browser";
const LoadingComponent = ({ timeout = 1500 }: { timeout?: number }) => {
const [show, setShow] = React.useState(false);
React.useEffect(() => {
const timer = setTimeout(() => setShow(true), timeout);
return () => clearTimeout(timer);
}, [timeout]);
return <div>{show ? "Hello Vitest!" : "Loading..."}</div>;
};
test("renders name", async () => {
// Put some stuff in the body
const stuff = document.createElement("div");
stuff.textContent = "Stuff That Changes";
document.body.appendChild(stuff);
// Stuff changed
setTimeout(() => {
stuff.textContent = "Im no longer there";
}, 1000);
const res = await render(<LoadingComponent />);
console.log("The expected container locator", res.locator); // "div >> nth=1"
const baseElementSelector = page.elementLocator(res.baseElement).selector;
console.log(baseElementSelector); // Expected "body" but got "internal:text="Stuff That ChangesLoading...!"i"
const theProblematicSelector = page.getByText("Hello Vitest!").selector;
console.log(theProblematicSelector); // internal:text="Stuff That ChangesHello Vitest!"i >> internal:text="Hello Vitest!"i
// ^^ This does not use the "container" as the base locator
// ^^ The selector generated by page.elementLocator(baseElement) where baseElement is document.body results in a flaky locator
await expect
.element(res.getByText("Hello Vitest!"), {
timeout: 3000,
interval: 200,
})
.toBeVisible(); // Fails even though "Hello Vitest!" is there
// Cannot find element with locator: getByText('Stuff That ChangesLoading...').getByText('Hello Vitest!')
});Thanks