Amazing new locators in 2.1 beta don't seem to work with the interactivity API
Describe the bug
Also filed as vitest/vitest-browser-react#1 but I am cross posting here for visibility purposes.
It seems that @vitest/browser/context isn't properly coordinating with vitest-browser-react. The React rendering and verification work well by themselves, but when I try to fire any user events, the userEvent imported from @vitest/browser/context fails with an error.
Seems like the two pieces of @vitest/browser aren't communicating with ech other. (I know that vitest-browser-react is a separate package, but it integrates tightly with @vitest/browser, and in this case, the integration seems to be not quite right.)
Troubleshooting
👎 Tried swapping the order of render vs. setup
👎 Tried const user = userEvent; in case the setup is unnecessary
Reproduction
import { page, userEvent } from '@vitest/browser/context';
import { beforeEach, describe, expect, test } from 'vitest';
test('reproduction', () => {
const user = userEvent.setup();
const screen = page.render(<span>hello, world</span>);
await user.hover(page.getByRole('button'));
});
This results in:
reproduction
Error: Could not determine window of node. Node was [object Object]
- /src/__tests__/reproduction.test.tsx:7:2
System Info
System:
OS: macOS 14.6.1
CPU: (12) arm64 Apple M3 Pro
Memory: 1.79 GB / 36.00 GB
Shell: 3.7.0 - /opt/homebrew/bin/fish
Binaries:
Node: 20.11.0 - ~/.asdf/installs/nodejs/20.11.0/bin/node
npm: 10.8.2 - ~/.asdf/plugins/nodejs/shims/npm
Browsers:
Chrome: 128.0.6613.114
Chrome Canary: 130.0.6694.0
Safari: 17.6
npmPackages:
@vitejs/plugin-react: ^4.2.1 => 4.3.1
@vitest/browser: 2.1.0-beta.6 => 2.1.0-beta.6
@vitest/coverage-istanbul: 2.1.0-beta.6 => 2.1.0-beta.6
@vitest/runner: 2.1.0-beta.6 => 2.1.0-beta.6
vite: ^5.2.0 => 5.4.2
vitest: 2.1.0-beta.6 => 2.1.0-beta.6
Used Package Manager
npm
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
await user.hover(page.getByRole('button'));
There is no button in your reproduction. The error I get is different:
TimeoutError: locator.hover: Timeout 1000ms exceeded.
Please, provide a runnable reproduction (code repo or stackblitz link)
Hello @xeger. Please provide a minimal reproduction using a GitHub repository or StackBlitz (you can also use examples). Issues marked with needs reproduction will be closed if they have no activity within 3 days.
I am unable to create a standalone reproduction of the exact error.
Here's a gist that produces another, also seemingly valid issue.
While creating the reproduction I gained some insight into the original reported issue.
I am converting my package from vitest-2.0 + @testing-library/react to the new vitest-2.1 API.
If @testing-library has been imported into the project and used with other tests, the original error reproduces (but only in my repository, which is a large package that has a few dozen test files, only one of which is using the new API).
I will troubleshoot the original issue in situ; it seems there is some crosstalk between your API and testing-library. (My goal is to eliminate use of testing-library but it would be nice to support a gradual migration.)
Here's a gist that produces another, also seemingly valid issue.
Please, if you can publish it as a repository, that would be wonderful. I can't even open the link because the files are just too big.
Whoops, sorry about that. Here's the same thing in repo form.
To save you time, in case I am being a bonehead, I updated the reproduction to find a button and click it:
const screen = page.render(<button>hello, world</button>);
expect(page.getByRole('button')).toBeDefined();
expect(screen.getByRole('button')).toBeDefined();
await userEvent.click(page.getByRole('button'));
await userEvent.click(screen.getByRole('button'));
I expected to reproduce Could not determine window of node but instead, I see:
VitestBrowserElementError: Cannot find element with locator: getByRole('button')
Which is similar, but not identical to the original issue -- I use vitest to find a locator, verify the locator binds to an element with an expect, but when I try to use the same locator with vitest userEvent, it gets confused.
If I switch to using the playwright vitest driver (see branch playwright of repro repo) the test is green.
This second error may be more relevant to you, since I believe vitest's intention is to support a single locators, verification & interactivity API that works with all drivers (❤️).
expect(page.getByRole('button')).toBeDefined();
This doesn't do anything, getByRole always returns another locator. If you need to compare elements, you need to call .element() or await expect.element(locator)
My apologies; Playwright + locators are completely new to me, and it's clear that I should slow down and learn about locators vs. assuming they work just like @testing-library did. Still, it would be good to validate that the 2.1 beta will work in my environment and help find any bugs.
I created a playwright branch where I switch to using the playwright driver of vitest, and on that branch, all tests are passing after correcting my mistake and using expect.element for the verification.
Interestingly, the userEvent.click() call accepts a locator, resolves it to an element and clicks the button -- presumably this is part of its async API, to accept a locator and resolve before generating the DOM event.
The same test on the main branch (using webdriverio) continues to throw VitestBrowserElementError when I userEvent.click. It prints some HTML associated with the test failure or the exception:
<body>
<div />
</body>
Which reinforces the idea that userEvent when using webdriverio is somehow not seeing the same DOM that page and screen are seeing.
When using the preview driver with either branch, I continue to receive the original reported error:
Error: Could not determine window of node. Node was [object Object]
- /reproduction.test.tsx:12:2
I also tried copying the verbatim example test from vitest-browser-react README; you can find it in the reproduction repo as documented.test.tsx.
It seems to me that userEvent works quite differently between drivers, and/or that there is a bug in properly rendering the DOM with vitest-browser-react using webdriverio or preview drivers.
The vision of a single browser testing API that works with vitest both in preview + headless mode, and is similar to the API used in (playwright) E2E tests, is very compelling to me -- which is why I persist in troubleshooting. ❤️
I created a playwright branch where I switch to using the playwright driver of vitest, and on that branch, all tests are passing after correcting my mistake and using
expect.elementfor the verification.
Sorry, but your tests are still not correct. Please refer to the documentation for how poll and element work (element is just a wrapper around poll, so they work the same way): https://vitest.dev/api/expect.html#poll
test('using screen', async () => {
let clicked = false;
const screen = page.render(<button onClick={() => {clicked = true}}>hello, world</button>);
- expect.element(screen.getByRole('button'));
+ await expect.element(screen.getByRole('button')).toBeDefined();
await userEvent.click(screen.getByRole('button'));
- expect.poll(() => clicked).toBe(true);
+ await expect.poll(() => clicked).toBe(true);
});
I am looking into differences between providers right now.
Thank you for looking into it. I've upgraded the reproduction repository to 2.1.0 and corrected my tests; both the corrected test and the documented example from vitest-browser-react fail when using userEvent with webdriverio or preview drivers -- same issue, where the DOM seems to be empty to userEvent.
I will continue to troubleshoot it on my own & open a new issue if I find a root cause.
both the corrected test and the documented example from vitest-browser-react fail when using userEvent with webdriverio or preview drivers -- same issue, where the DOM seems to be empty to userEvent
I'll reopen the issue then. Both examples worked previously
Small correction: there is one test case succeeding now with the preview driver!
If we capture the render result and use it (and not the imported page) as the factory for locators, then userEvent works with playwright + preview.
If I'm using page to create locators, then the tests only pass with the playwright driver.
I've amended my reproduction to have an NPM command for each driver, and shown that the vitest-browser-react example passes once we modify it to use const screen = page.render.
I will try to reproduce this success in the project where I was encountering trouble. Your fix has definitely improved things with preview mode! (I am not opinionated about webdriverio vs. playwright for headless tests, but of course I want to help ensure vitest remains flexible as advertised.)
npm run test:playwright
✓ documented.test.tsx (2)
✓ reproduction.test.tsx (2)
npm run test:preview
❯ reproduction.test.tsx (2)
✓ using screen
× using page
❯ documented.test.tsx (2)
× example from vitest-dev/vitest-browser-react
✓ modified example
npm run test:webdriverio
❯ documented.test.tsx (2) 1355ms
× example from vitest-dev/vitest-browser-react
× modified example 1272ms
❯ reproduction.test.tsx (2) 1336ms
× using screen 1270ms
× using page
Quick update on some more troubleshooting steps that I have tried.
- Upgrade to vitest 2.1.1 & friends (see
vitest-2.1.1branch) - Also switch to PNPM as package manager (see
pnpmbranch)
The switch to PNPM revealed a missing direct dependency on react-dom, which I added. No change to the outcome.
The playwright driver continues to work well in all cases; the preview and WDIO drivers continue to be extremely flakey.
I also noticed that the playwright driver can run in headed mode and the vitest UI is available.
I appreciate that browsers and frontend are hell. I may just switch my private repository to use the vitest playwright provider and commit to using it in the future, as it is the only stable option. Maybe vitest should do the same thing, as a project. If the dream of vendor nenutrality is impossible to achieve, that's not your fault!
I'm running into a similar issue.
import {screen, render} from "@testing-library/angular"
import {userEvent} from "@vitest/browser/context"
// ...
const user = userEvent.setup()
await user.click(screen.getByLabelText(label))
TimeoutError: locator.click: Timeout 1000ms exceeded.
Even weirder, this varies depending on the machine. On Linux, everything runs as expected both in CI (Ubuntu 22) and on my local machine (WSL2 Ubuntu 24). When I switch over to my M2 Mac, it fails.
I'm able to fix this by increasing the locator.click timeout:
await user.click(screen.getByLabelText(label), {timeout: 3000})
It would be nice to be able to set this globally, but the providerOptions doesn't seem to pick up the actionTimeout config setting.
actionTimeout is added in https://github.com/vitest-dev/vitest/pull/6984 and it's currently available in v3 beta.
Let's close this in favor of https://github.com/vitest-dev/vitest/issues/7871 since the API itself is supported