Skip to main content

Search...

Frontend Testing: A Real Browser Instead of jsdom

What does a real browser add to frontend testing? Vitest Browser Mode runs each component like a user would and flags contrast errors jsdom misses.

• • 8 min read
Cover of the expert talk on 'Frontend Testing: A Real Browser Instead of jsdom' with Alexander Opalic and Richard Seidl.

Frontend testing means checking UI components such as date pickers, modals, or tables for behavior, accessibility, and rendering. Vitest Browser Mode runs each component in isolation inside a real browser, whereas jsdom and happy-dom only emulate one in Node. Color contrast can then be checked automatically, and screenshots of a whole UI library compared without clicking through every variant.

Key Takeaways

  • Alexander Opalic considers frontend testing more complicated than backend testing, because a component has to meet accessibility and visual regression requirements on top of its behavior.
  • Vitest Browser Mode renders each component in isolation inside an iframe of a real browser, while jsdom and happy-dom emulate the browser in Node and often need mocks.
  • With jsdom, color contrast cannot be checked automatically; a real browser exposes gray text on a white background as an accessibility problem right away.
  • A click takes about 200 milliseconds in a real browser and about 20 with jsdom, so a five-minute test suite ends up roughly half a minute longer.
  • Visual regression testing with screenshots pays off for a core UI library with all its button and modal variants; applied to every single page, it turns flaky fast.

Why Frontend Testing Is Harder Than Backend Testing

Frontend testing carries more requirements than backend testing, and that makes it harder. Alexander Opalic, a developer for eight years and most of that time on the frontend, names two areas backend work simply does not have: accessibility and visual regression. At the end there is a browser, and somewhere at the end there are pixels. A date picker looks harmless, yet it brings a long list of requirements as soon as it has to work with a screen reader.

Then there is how tests come about in many projects. Alexander describes the typical case where a pull request needs 70 percent test coverage and developers hit that number somehow. The coverage looks right, confidence in the code stays low. His own first task as a working student was the same order, more test coverage, before he knew how to build a Vue application. It took a long time and was frustrating, he says today. It still got him into testing.

What Should a Component Test Check?

The behavior a user sees in the DOM. Alexander separates two ways of writing the same test. White box means checking the component’s internal state, ref in Vue, useState in React, plus its individual methods. Black box means operating the component the way a user would and checking the result in the DOM.

“The user doesn’t care what methods a component has or what they’re called. It just has to work.”

(Alexander Opalic)

The black-box approach matches how teams build frontends today. A core UI library delivers buttons, modals, tables, and date pickers, and an e-commerce site assembles them like Lego bricks. Each brick allows an isolated check. On the test level, this is component testing, one step below the end-to-end tests that load the whole page with Playwright. For a date picker, the end-to-end route is cumbersome, because the test first has to steer to the right page and find the element there.

What Does a Real Browser Add Over jsdom and happy-dom?

A real browser shows what jsdom and happy-dom can only imitate. Both tools run in Node and emulate a browser; that works for a lot and remains an emulation. Three or four years ago most frontend developers tested this way, and many projects still do. As soon as a component draws a chart, Node turns tedious, and a mock has to step in. Every mock makes the test more complicated and pushes it further from what the user sees.

Accessibility is the clearest example. An image needs alt text so a screen reader can read out what it shows. axe checks simple rules like that automatically. The harder cases need a real browser, and color contrast is one of them. In the browser it is obvious right away when gray text sits on a white background and nobody can read it. jsdom sees none of that.

In Alexander’s experience, writing tests becomes fun again once a team has tried Browser Mode. The problem lies elsewhere. Many frontend developers simply do not know it exists.

How Vitest Browser Mode Renders Components

Vitest Browser Mode renders each component in a real browser and runs its tests there. Vitest is the test runner around Vite, which replaced Webpack as the build tool because it runs faster and takes less configuration. The configuration allows an environment per test file. For plain TypeScript functions, Node is enough. For components, the setting is Browser.

Vitest then opens an iFrame, renders the date picker, for example, and runs the five or ten tests of that component. The next iFrame takes the next component, say a table. It looks a lot like Playwright, runs headless or visibly, and with timeouts it slows down enough to follow every click. On a processor with several cores, four components run in parallel; Vitest distributes the tests on its own.

Behind the tool stands VoidZero, the company Evan You co-founded. Vite and Vitest belong to it, as does Oxlint, a linter written in Rust that runs faster than ESLint before it. Browser Mode has existed for two or three years. Hardly anyone knows it.

Are Tests in a Real Browser Slower Than With jsdom?

Yes, but only slightly. Alexander recently migrated the tests of Reka UI, a headless component library from the Vue ecosystem, from jsdom to Browser Mode with AI’s help, mainly to get numbers. A click takes around 20 milliseconds with jsdom or happy-dom; in a real browser it can take 200. A suite that ran five minutes may need five and a half afterward.

jsdom / happy-domVitest Browser Mode
EnvironmentNode, emulated browserreal browser, one iFrame per component
One clickaround 20 msaround 200 ms
Reka UI test suitearound 5 minutesaround 5:30 minutes
Charts, slidersmocks neededrender directly
Color contrastnot testablevisible and testable

In return, the tests get easier to read. With jsdom, Reka UI had to mock a lot, especially for a slider, and those mocks disappear. On the accessibility side, contrast joins the checks, a test that was not possible before. Half a minute more runtime against fewer mocks and one extra layer of checks is a trade Alexander takes gladly.

Test Strategy First, Especially With AI

When building with AI, the test strategy has to stand from day one. Alexander tried this on his own workout tracking app, which he built entirely with AI. At the project’s start he invested time and worked out what to test and how.

Browser Mode helps on a second level. The models understand images. When a test fails, the agent sees the picture and finds it easier to understand why.

Flaky Tests, Selectors, and the Limits of Self-Healing

In Browser Mode, flaky tests are no bigger a problem than with Playwright. The tool waits asynchronously until an element appears, with a timeout as the upper bound. Trouble starts when selectors change. Once a test runs stable, it stays stable. Automatic retries exist, but a test that needs a retry deserves the question why first.

Alexander has also experimented with self-healing. In a GitHub Actions pipeline, an agent can spot a changed selector, adjust the test, and commit the fix. The downside is that the agent can quietly work a real bug out of the picture. Keep an eye on that before you switch such a pipeline on.

When Do Screenshot Tests Get Flaky?

Visual regression testing in a real browser pays off where many variants of the same component exist, and not for whole pages. A designer who changes a button should not have to hunt down every button variant across the website. With Browser Mode, an image of all variants emerges, and the test fires when something changes. Without such a test, even a small change to a modal gets tedious, because a website quickly has 15 to 20 modal variants and someone has to click through each by hand.

For whole pages Alexander advises against it, because that is where flakiness climbs. Screenshot tests belong on the core UI library and a few very important screens. Practice shows the rest. If a regression test keeps failing in one spot, take it out there.

A Tool That Needs Feedback

Alexander does not miss any features so far. What he does miss are projects that use Browser Mode and discover wishes along the way. Vitest 5 came out a month or two ago, and the maintainer opened an issue asking what should go in next. There is also a Discord. Feedback as an issue is welcome.

And then there is the old open source question, which Alexander leaves open. Companies use open source products like Vite, Vitest, or Oxlint, but which of them gives something back as a sponsor?

Share this page