HS: resize observer mock + on resize (+3 tests)

Cluster 26. Three parts:
(a) `tests/hs-run-filtered.js`: mock style is now a Proxy that dispatches
    a synthetic `resize` DOM event on the owning element whenever
    `width` / `height` changes (via `setProperty` or direct assignment).
    Detail carries numeric `width` / `height` parsed from the current
    inline style. Strengthens the old no-op ResizeObserver stub into an
    `HsResizeObserver` class with a per-element callback registry
    (collision-proof name vs. cluster 27's IntersectionObserver); HS's
    `on resize` uses the plain DOM event path, not the observer API.
    Adds `ResizeObserverEntry` for code that references it.
(b) `tests/playwright/generate-sx-tests.py`: new pattern for
    `(page.)?evaluate(() => [{] document.{getElementById|querySelector}(…).style.PROP = 'VAL'; [}])`
    emitting `(host-set! (host-get target "style") "PROP" "VAL")`.
(c) `spec/tests/test-hyperscript-behavioral.sx`: regenerated — the three
    resize fixtures now carry the style mutation step between activate
    and assert.

No parser/compiler/runtime changes: `on resize` already parses via
`parse-compound-event-name`, and `hs-on` binds via `dom-listen` which is
plain `addEventListener("resize", …)`.

Suite hs-upstream-resize: 0/3 → 3/3. Smoke 0-195: 164/195 → 165/195
(the +1 smoke bump is logAll-generator work uncommitted in the main tree
at verification time, unrelated to this cluster).
This commit is contained in:
2026-04-24 10:08:11 +00:00
parent 99c5911347
commit 304a52d2cf
3 changed files with 96 additions and 4 deletions

View File

@@ -1008,6 +1008,30 @@ def parse_dev_body(body, elements, var_names):
ops.append(f'(dom-set-inner-html {target} "{val}")')
continue
# evaluate(() => document.getElementById(ID).style.PROP = 'VALUE')
# or document.querySelector(SEL).style.PROP = 'VALUE'. Used by resize
# tests (cluster 26): writing style.width/height dispatches a synthetic
# `resize` event via the mock style proxy. Accepts both arrow-expr
# and block form: `() => expr` and `() => { expr; }`. Also accepts
# the `page.evaluate` Playwright prefix.
m = re.match(
r"(?:page\.)?evaluate\(\s*\(\)\s*=>\s*\{?\s*"
r"document\.(?:getElementById|querySelector)\("
r"\s*(['\"])([^'\"]+)\1\s*\)"
r"\.style\.(\w+)\s*=\s*(['\"])(.*?)\4\s*;?\s*\}?\s*\)\s*$",
stmt_na, re.DOTALL,
)
if m and seen_html:
sel = m.group(2)
if sel and not sel.startswith(('#', '.', '[')):
sel = '#' + sel
sel = re.sub(r'^#work-area\s+', '', sel)
target = selector_to_sx(sel, elements, var_names)
prop = m.group(3)
val = m.group(5).replace('\\', '\\\\').replace('"', '\\"')
ops.append(f'(host-set! (host-get {target} "style") "{prop}" "{val}")')
continue
# clickAndReadStyle(evaluate, SEL, PROP) — upstream helper that
# dispatches a click on SEL and returns its computed style[PROP].
# Materialize the click; downstream toHaveCSS assertions then test