Skip to content

Lay out and paint form controls — they had no box at all until now - #95

Merged
tannevaled merged 2 commits into
mainfrom
feat/form-control-rendering
Sep 3, 2026
Merged

tannevaled merged 2 commits into
mainfrom
feat/form-control-rendering

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Writing the F6 capstone test (a login flow driven by pixel coordinates only — the way a real host, e.g. go-aiquota/tray's onboarding window, actually drives one, never a direct *dom.Node reference) failed immediately: #email/#password/#submit were missing from the element hit-map entirely.

Root cause: layout.isReplacedTag only recognizes "img"/"svg". input/button/select/textarea default to display:inline (css/ua.go) and have never been given a box or a paint step anywhere in this engine. This isn't specific to the F0–F5 interactive-input work (#89–93) — it means an <input> has been invisible and unclickable in this engine, full stop, independently of whether the JS/event mechanics work (they do, correctly).

Two-part layout fix, since replaced/atomic inline content has two entry points and only one was obvious from isReplacedTag's own doc comment:

  • contents() — the rarer path: a control given display:block by author CSS.
  • appendElementInline() — the common path: an inline-display control (the default) nested in ordinary flow content. Found by instrumenting the box tree directly when the "obvious" fix (only contents()) still left an empty tree.

Both reuse the same sizing helpers: explicit CSS width/height first, else a UA-shaped default per kind (checkbox/radio a small square, a button-like control sized to fit its own label via the engine's existing text Measurer, everything else a fixed text-field box).

Paint: a new InlineItem.FormControl field (sibling of Image) gets a real background + 1px border, checkbox/radio a state-colored square, and value/label text (password masked as bullets, placeholder drawn muted, button label centered) — close enough to real UA chrome to read as intentional, not pixel-perfect fidelity.

Test plan

  • Full existing suite + -race pass unmodified, go vet/gofmt clean
  • 100% coverage on every new function in both layout and paint
  • The one existing fixture using <input> (checkbox_hack.html) sets it display:none via its own CSS, so it never reaches either new code path — confirmed unaffected
  • e2e_login_test.go's TestEndToEndLoginByCoordinate — the capstone: OpenElements()/ElementAt (coordinate resolution) → Focus/Type/Click, entirely by pixel coordinate, correctly authenticates against a page whose own JS tracks the typed values (plus a wrong-password negative control)

🤖 Generated with Claude Code

tannevaled and others added 2 commits September 3, 2026 10:26
Writing the F6 capstone test (a login flow driven by pixel coordinates
only, the way any real host — go-aiquota/tray's onboarding window,
browserproxy's click forwarding — actually drives one, never a direct
*dom.Node reference) failed immediately: #email/#password/#submit were
missing from the element hit-map entirely.

Root cause traced to layout/layout.go's isReplacedTag, which only
recognizes "img"/"svg" — input/button/select/textarea default to
display:inline (css/ua.go) and have NEVER been given a box or a paint
step anywhere in this engine. This is not specific to the F0-F5 work
(#89-93): it means an <input> has been invisible and unclickable in
this engine, full stop, independent of the JS/event mechanics those
PRs correctly built.

Two-part fix, since replaced/atomic inline content actually has TWO
entry points in this codebase and only one was obvious from reading
isReplacedTag's own doc comment:
  - contents() (layout.go): the rarer path — a control given
    `display:block` (or similar) by author CSS routes through the
    normal block-box path.
  - appendElementInline(): the COMMON path — an inline-display control
    (the default) nested in ordinary flow content, discovered by
    instrumenting the box tree directly when the "obvious" fix (only
    touching contents()) still left the capstone test failing with an
    empty box tree.
Both reuse the same formControlSize/formControlDefaultSize (explicit
CSS width/height first, else a UA-shaped default per control kind —
checkbox/radio a small square, a button-like control sized to fit its
own label via the same Measurer already threaded through layout,
everything else a fixed text-field-shaped box) and controlLabel
helpers, proven to agree by TestFormControlDisplayBlockRoutesThrough
Contents.

paint/paint.go: it.FormControl (a new InlineItem field, the Image
field's sibling) gets a real background+1px-border box, checkbox/radio
a state-colored square, and — for anything else — its value/label text
(password masked as bullets, a placeholder drawn muted, a button's
label centered, matching real UA behavior close enough to read as
intentional rather than exact chrome fidelity).

100% coverage on every new function in both packages; full existing
suite + -race unaffected (the one existing fixture using <input>,
checkbox_hack.html, sets it display:none via its own CSS, so it never
reaches either new code path). The capstone test (e2e_login_test.go)
now passes: Open -> Elements()/ElementAt (coordinate resolution) ->
Focus/Type/Click, entirely by pixel coordinate, correctly authenticates
against a page whose own JS tracks the typed values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CI's coverage-gate.sh enforces a strict, ratcheted 100% floor on
layout (and paint, css, dom) — a floor my local check didn't
reproduce, since I'd been comparing against the unratcheted `engine`
package's own looser tolerance. Two fixes:

- formControlDefaultSize: restructured to named returns assigned by
  each case and returned once at the bottom, instead of each case
  returning directly — the trailing fallback after the switch was
  unreachable by construction (the only two call sites already gate on
  isFormControlTag), and 100% coverage can't be reached by testing
  around an unreachable line, only by removing it.
- Added tests for three real branches that were reachable but
  untested: a form control preceded by inline text with trailing
  whitespace (needs its own SpaceBefore, the same collapsible-space
  handling img/svg items already get — a real gap, "Label <input>" is
  the normal shape of an actual form), and the hidden-input check at
  BOTH its entry points (appendElementInline for the common inline
  case, contents() for the rarer display:block one) rather than just
  one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 5f1ec10 into main Sep 3, 2026
7 checks passed
@tannevaled
tannevaled deleted the feat/form-control-rendering branch September 3, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant