ReviewOS

stacks/very-happy-dom

fix(global-registrator): install the virtual FormData constructor

#1599
Closed glennmichael123 opened this 8 days ago ·
8 days ago

Problem

Window exposes VeryHappyFormData, which can populate from a virtual form, but GlobalRegistrator.register() does not replace the host FormData. FormData is absent from OVERRIDE_KEYS, so Bun native FormData remains active and silently ignores the virtual form controls.

Reproduction

import { GlobalRegistrator } from "very-happy-dom"

GlobalRegistrator.register()
document.body.innerHTML = `<form><input name="email" value="test@example.com"></form>`
const data = new FormData(document.querySelector("form")!)
console.log(data.get("email")) // null
console.log(new window.FormData(document.querySelector("form")!).get("email")) // test@example.com

Impact

Component tests using the documented global registration appear to submit an empty form. This is especially easy to miss when a test only checks that the submit event fired.

Acceptance

  • Registered global FormData is the Window implementation.
  • new FormData(virtualForm) includes successful controls.
  • unregister() restores the original host constructor rather than deleting it.
8 days ago

Fixed in b947a78, released in v0.1.11.

Verified against the published package rather than the source tree, using the reproduction from the issue:

new FormData(form).get(email) = "test@example.com"
globalThis.FormData === window.FormData = true
FormData.prototype is intact = object

Adding FormData to OVERRIDE_KEYS alone is not the fix — it overflows the stack. The wrapper delegates empty construction to globalThis.FormData, which is itself once registered, so new FormData() recursed until the stack ran out (RangeError: Maximum call stack size exceeded). It now holds the host constructor captured at module load.

A second defect surfaced while satisfying "the registered global FormData is the Window implementation": registration bound every function on its way to globalThis, constructors included. A bound function is a different object and has no prototype, so globalThis.FormData !== window.FormData and FormData.prototype was undefined. That was true of every class the Window exposes — Event, HTMLElement, Document and the rest — not only FormData. Constructors are now copied as they are.

Third acceptance item is done too: unregister() restores what it displaced instead of deleting it, so a host global survives a register/unregister cycle. Deleting had been removing Bun's own constructors outright.

Five tests cover it, including the no-argument construction that would otherwise recurse, and a navigator case proving the restore is general rather than special-cased for FormData. Suite is 1599 passing.

Sign in to comment on this issue.