Build1 distinct publisher3 min readPublished
Vue Testing Library's render() was never tied to jsdom, so a twd-js co-maintainer pointed it at the real page and it worked. The cost is one support file and the cleanup a browser will not do for you.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The helper's key choice is what it avoids doing. Emptying the app root with `app.innerHTML = ""` is the obvious way to clear the page, and according to the writeup it breaks Vue: the vnodes still point at the nodes you deleted, so the next router navigation patches elements that are no longer in the document [13]. Replacing the root element with a comment placeholder and putting the element back afterwards leaves that correspondence intact, which is why `restorePage()` hands back a live app instead of a husk [12][14].
`prepend` is doing layout work too. The page is already offset to make room for the twd sidebar, and anything left in normal flow inherits that offset, so a host div prepended to `body` sits where a user would actually see it [15]. Appending is what `render()` does by default, which is how the component ends up after `#app`, a full viewport below the layout [8], on a page where `screen` also still matches the running app's own elements [9].
Adoption cost, counted off the listing: one support file with two exported functions and two internal ones, and no changes to the application [22]. The cost that matters shows up at runtime instead. jsdom tears the environment down between test files; a browser does not, so renders accumulate and the app has to be back on the page before your flow tests run [17]. That is why `cleanup()` and `restorePage()` both sit in `afterEach` rather than `beforeEach` [16]. Per-test hygiene stops being the runner's job and becomes yours.
For this to transfer, a few things have to be true of your suite. Your component tests need to be Testing Library calls rather than runner-coupled mounts, because the whole move rests on `render()` delegating to `@vue/test-utils` and `@testing-library/dom`, neither of which is bound to jsdom [7]. Your app root needs to be `#app`, which the helper hardcodes as `APP_ROOT_ID` [20]. And you need the dev server in the loop, since twd-js runs the tests inside it and displays them in a sidebar [4]. The post also carries a second example on a view that fetches on mount and posts a form, mocked with `twd.mockReq` [21].
The evidence here comes with caveats. This is one person's report, and that person co-maintains the tool [5]. The demonstration test increments a counter from "Count is 0" to "Count is 1" on click [19], which is the assertion jsdom was never going to get wrong. So the sample proves the plumbing holds, not that it caught a bug. What the real page adds is the ability to assert on size and position at all, which is exactly the region a green jsdom run says nothing about [3].
Ranked by verification strength, evidence, and original report placement.
The demonstration test finds an element by test id counter-button, asserts it contains the text "Count is 0", clicks it with userEvent, then asserts it contains "Count is 1".
A second example applies the same approach to a view that fetches on mount and posts a form, using twd.mockReq.
A Vue component's job is to produce DOM in a browser, but most component tests ask it to do that in Node, against a DOM that jsdom simulates.
Running Vitest with jsdom has been the default since npm create vue@latest started offering that combination.
In the jsdom setup nothing is ever drawn, the CSS never runs and nothing has a size or a position, so a component can pass every assertion in the file and still be broken on screen.
twd-js runs tests inside the actual dev server, in a sidebar next to the app; it was built for flow testing (visit a route, click through, assert on what the user sees) and component testing was the thing it did not do.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 31, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
263 green tests, every value undefined: the schema mirror that agreed with itself1 distinct publisher
build
FFmpeg.wasm ships, but budget for a watchdog: one in ten heavy jobs deadlocked in silence1 distinct publisher
product
CodeQL 2.26.3 treats workflow files as code, and cache poisoning as a finding you must triage1 distinct publisher
build
Two and a half years of design system work ended with one adopting team1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Complete artifact, one hand holding it
The strongest thing in this story is that it hides nothing: the support file is printed in full, both tests are printed in full, and the claim that Vue Testing Library was never bound to jsdom is checkable against libraries anyone can read. What it lacks is a second pair of eyes. The one piece of reasoning with no artifact behind it — why emptying #app corrupts Vue's vnode references while moving the node does not — is also the piece most likely to break quietly on a version bump.
Nothing outside the author's own demo
We score no adoption because none is reported — not because it is absent. The technique appears in a counter and a todos view in what looks like a scaffolded example app, and that is the entire observed footprint. No other team, no install counts, no issue thread, no follow-on writeup.
Honest claim, unpriced bill
The headline argument is stated modestly — jsdom is called the right trade for plenty of tests, and the win is narrowed to real size, real position, real CSS. The tilt comes from what never gets costed. Trading jsdom for a browser means trading free per-file teardown for a helper you maintain, a dev server your suite depends on, and run characteristics nobody measured. This reporting names the first of those and skips the rest, so the ledger reads slightly better than it is.
Co-maintainer, said out loud in paragraph three
A twd-js co-maintainer is writing about extending twd-js into territory it did not previously cover, and the piece's rhetorical shape — jsdom's ceiling, then a discovery that his tool clears it — follows that interest exactly. The disclosure is early and unambiguous, which is why this sits in the middle rather than the top of the range, but the interest still shapes which alternatives went unmentioned.
One first-hand account you can rerun
We are moderately confident about the mechanics and much less confident about the verdict. A single interested publisher, no replication, and no measurement of the costs would normally push this lower; what holds it at the midpoint is that the claims are narrow, the code is complete, and any Vue developer can settle them in an afternoon.