Repository navigation
bun:dom and the Vitest work in one tree (not for merging) - #45415
Jarred-Sumner wants to merge 1 commit into
Conversation
|
Updated 3:14 PM PT - Oct 20th, 2026
❌ @Jarred-Sumner, your commit b1530fe has 1 failures in
Add 🧪 To try this PR locally: bunx bun-pr 45415That installs a local version of the PR into your bun-45415 --bun |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
6ae7a6b to
c7cbb3d
Compare
b058683 to
94c7384
Compare
f5f0d54 to
7ab0f0e
Compare
132bfee to
0bbe8f0
Compare
…e check] Not for merging. The two parts have their own branches.
|
Tested Results: 2,196/2,439 pass with native vs 2,434/2,439 with Happy DOM 20.14.5. Compatibility gaps observed:
Encouraging subset result: an entire 145-test workspace passed both, with median wall 2.804 → 2.224 s and 29.1% less CPU across six alternating pairs. Busy shared host; no full-suite speedup claim. |
What does this PR do?
Not for merging. It puts two branches in one tree so that CI makes a build of both to try:
bun test. Its change tosetTimeout(callback, 0)is off here unlessBUN_TEST_ZERO_DELAY_TIMERS=1is set, so that one build can be timed both ways.bun:dom, a DOM built into Bun (WebKit's DOM and HTML parser, without loading, layout and rendering), which will get a pull request of its ownOn top of the two there is a test environment for the built-in DOM:
bun test --environment=bun:dom--environment=jsdomdoes, with no package to install: a new window per test file athttp://localhost:3000, the window's members onglobalThis, and an error in a listener fails the test.@vitest-environment jsdomorhappy-domcomment in a test file. The comment says that the file needs a DOM, and the flag says which one. Anodecomment still wins.toEqualtreat its nodes like those of jsdom and happy-dom.useFakeTimers()replaces the globalrequestAnimationFrame. The window's own function stays on the real clock, as in jsdom, so a library that took it when it loaded keeps getting frames.URLis a subclass of Bun's there, as under--environment=jsdom. A test that definesURL.createObjectURLand deletes it afterwards gets Bun's back.--isolate=modulesundoes what a file does to the prototypes of the DOM classes, which every file's window shares.bun:dom's copy of WebCore is from the same WebKit revision as the JavaScriptCore thatmainlinks now (upstream23b0720b40).New in
bun:domsince the last build:activeElementof the documents above it, which give up their own focus (blur,focusout). When a document above takes the focus back, those below give up theirs. As in Chrome.requestAnimationFrame()under a fakedperformance. It kept its pace byperformance.now(), which starts again at 0 when a test fakes it, so a frame could wait for as long as the process had run. It goes by a monotonic clock now.spyOn(window, "setTimeout")on a window that isn't the global object (an iframe's) left the propertyundefined.nameis a property of its document only if the document has a window, as in Chrome. On whatDOMParserorcreateHTMLDocument()made,<img name=body>shadoweddocument.body, andObject.defineProperty()for such a name threw.window.reportError(). It fires anerrorevent at the window, and goes toconsole.error()if nobody cancels that. Under the test environment the globalreportErroris the window's and no longer Bun's, which knows nothing of the window. React 19 reports what a component throws through it.<iframe>and<frame>have acontentWindowand acontentDocument(about:blankandsrcdoc; nothing is loaded).window.open()returns a window.parent,top,opener,frames,length,frameElement,closedandpostMessage()between windows work. Under the test environment native code hands outglobalThiswhere it would hand out the window.DOMParserparses the XML types andXMLHttpRequesthasresponseXML, on Bun's XML parser.TouchEvent,SVGSVGElementand 15 more SVG classes,NavigatorwithPluginArrayandMimeTypeArray,CSSLayerBlockRule,CSSLayerStatementRule.Navigatoris also a global of the runtime, as in Node. The runtime'snavigatorno longer hasonLine, which the last build gave it and Node's doesn't have. A window's keeps it.MessageEvent'ssourcecan be any object where the global object stands for a window.elementFromPoint()andelementsFromPoint()are gone. They cannot answer without layout, jsdom has neither, and libraries test for them.styleattribute stands whereelement.stylefirst wrote to it.submitorresetevent that script dispatches at a control reaches the form's listeners, as in jsdom.var()that refers to nothing stays as it was declared in computed style.background: transparentreads back as written. An SVG<a href>takes focus.target="_self",_parentor_topnavigates its window.requestAnimationFrame()calls back at most 60 times a second, and the first frame after a pause does not wait. With a DOM this fast, a frame 16 ms later came afterawait user.click()had resolved, where under jsdom it falls inside the click.Changed outside the DOM:
new Blob(),new File()andslice():typeis the type the script gave, in lower case. It used to come back astext/plain;charset=utf-8fortext/plain, and likewise for HTML, CSS, JSON, JavaScript and form data. AContent-Typeheader made from such a blob still names the charset.calc(0% + 0px)andcalc(50% - 0px)keep both terms, in the bundler too. A sum with a percentage in it is not the same as a length. The sign of a zero was read the wrong way round.The binary is about 4.1 MB bigger, so the commit message has
[skip size check].How did you verify your code works?
On a release build for Linux x64:
--environment=bun:dom, under--isolate,--isolate=modules, and both with--parallel=8.