HS: MutationObserver mock + on mutation dispatch (+7 tests)
Parser: parse-on-feat now consumes `of FILTER` after `mutation` event-name, where FILTER is `attributes`/`childList`/`characterData` ident or `@a [or @b]*` attr-token chain. Emits :of-filter dict on parts. Compiler: scan-on threads of-filter-info; mutation event-name emits `(do (hs-on …) (hs-on-mutation-attach! TARGET MODE ATTRS))`. Runtime: hs-on-mutation-attach! constructs a real MutationObserver with config matched to filter and dispatches "mutation" event with records detail. Runner: HsMutationObserver mock with global registry; prototype hooks on El.setAttribute/appendChild/removeChild/_setInnerHTML fire matching observers synchronously, with __hsMutationActive guard preventing recursion. Generator: dropped 7 mutation tests from skip-list, added evaluate(setAttribute) and evaluate(appendChild) body patterns. hs-upstream-on: 36/70 → 43/70. Smoke 0-195 unchanged at 170/195. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -114,13 +114,6 @@ SKIP_TEST_NAMES = {
|
||||
"can filter events based on count range",
|
||||
"can filter events based on unbounded count range",
|
||||
"can mix ranges",
|
||||
"can listen for general mutations",
|
||||
"can listen for attribute mutations",
|
||||
"can listen for specific attribute mutations",
|
||||
"can listen for childList mutations",
|
||||
"can listen for multiple mutations",
|
||||
"can listen for multiple mutations 2",
|
||||
"can listen for attribute mutations on other elements",
|
||||
"each behavior installation has its own event queue",
|
||||
"can catch exceptions thrown in js functions",
|
||||
"can catch exceptions thrown in hyperscript functions",
|
||||
@@ -1166,6 +1159,32 @@ def parse_dev_body(body, elements, var_names):
|
||||
ops.append(f'(if (dom-has-class? {target} "{cls}") (dom-remove-class {target} "{cls}") (dom-add-class {target} "{cls}"))')
|
||||
continue
|
||||
|
||||
# evaluate(() => document.querySelector(SEL).setAttribute(NAME, VALUE))
|
||||
# — used by mutation tests (cluster 32) to trigger MutationObserver.
|
||||
m = re.match(
|
||||
r'''evaluate\(\s*\(\)\s*=>\s*document\.querySelector\(\s*([\'"])([^\'"]+)\1\s*\)'''
|
||||
r'''\.setAttribute\(\s*([\'"])([\w-]+)\3\s*,\s*([\'"])([^\'"]*)\5\s*\)\s*\)\s*$''',
|
||||
stmt_na, re.DOTALL,
|
||||
)
|
||||
if m and seen_html:
|
||||
sel = re.sub(r'^#work-area\s+', '', m.group(2))
|
||||
target = selector_to_sx(sel, elements, var_names)
|
||||
ops.append(f'(dom-set-attr {target} "{m.group(4)}" "{m.group(6)}")')
|
||||
continue
|
||||
|
||||
# evaluate(() => document.querySelector(SEL).appendChild(document.createElement(TAG)))
|
||||
# — used by mutation childList tests (cluster 32).
|
||||
m = re.match(
|
||||
r'''evaluate\(\s*\(\)\s*=>\s*document\.querySelector\(\s*([\'"])([^\'"]+)\1\s*\)'''
|
||||
r'''\.appendChild\(\s*document\.createElement\(\s*([\'"])([\w-]+)\3\s*\)\s*\)\s*\)\s*$''',
|
||||
stmt_na, re.DOTALL,
|
||||
)
|
||||
if m and seen_html:
|
||||
sel = re.sub(r'^#work-area\s+', '', m.group(2))
|
||||
target = selector_to_sx(sel, elements, var_names)
|
||||
ops.append(f'(dom-append {target} (dom-create-element "{m.group(4)}"))')
|
||||
continue
|
||||
|
||||
# evaluate(() => { var range = document.createRange();
|
||||
# var textNode = document.getElementById(ID).firstChild;
|
||||
# range.setStart(textNode, N); range.setEnd(textNode, M);
|
||||
|
||||
Reference in New Issue
Block a user