Eval-only tests, multi-class, async IO — 295/831 (35%)

- Generator: converts no-HTML tests with run("expr").toBe(val) patterns
  to (assert= val (eval-hs "expr")). 111→92 stubs (-19 converted).
- Parser: multi-class add/remove (.foo .bar collects into multi-add-class)
- Compiler: multi-add-class/multi-remove-class emit (do (dom-add-class..))
- Test runner: drives IO suspension in per-test evaluate for async tests
- Parser: catch/finally support in on handlers, cmd terminators

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 22:36:56 +00:00
parent 5fe97d8481
commit 52e4d38852
2 changed files with 97 additions and 21 deletions

View File

@@ -497,12 +497,54 @@ def generate_test_pw(test, elements, var_names, idx):
return '\n'.join(lines)
def generate_eval_only_test(test, idx):
"""Generate SX deftest for no-HTML tests using eval-hs.
Parses body field for run("expr").toBe(val) / expect(run("expr")).toBe(val) patterns."""
body = test.get('body', '')
lines = []
lines.append(f' (deftest "{test["name"]}"')
# Extract run("expr").toBe(val) or expect(await run("expr")).toBe(val) patterns
assertions = []
for m in re.finditer(r'(?:expect\()?(?:await\s+)?run\(["\x27]([^"\x27]+)["\x27]\)\)?\.toBe\(([^)]+)\)', body):
hs_expr = m.group(1).replace('\\', '').replace('"', '\\"')
expected = m.group(2).strip()
# Convert JS values to SX
if expected == 'true': expected_sx = 'true'
elif expected == 'false': expected_sx = 'false'
elif expected == 'null' or expected == 'undefined': expected_sx = 'nil'
elif expected.startswith('"') or expected.startswith("'"):
expected_sx = '"' + expected.strip("\"'") + '"'
else:
try:
float(expected)
expected_sx = expected
except ValueError:
expected_sx = f'"{expected}"'
assertions.append(f' (assert= {expected_sx} (eval-hs "{hs_expr}"))')
# Also handle toEqual patterns
for m in re.finditer(r'(?:expect\()?(?:await\s+)?run\(["\x27]([^"\x27]+)["\x27]\)\)?\.toEqual\(([^)]+)\)', body):
hs_expr = m.group(1).replace('\\', '').replace('"', '\\"')
expected = m.group(2).strip()
assertions.append(f' ;; toEqual: {expected[:40]}')
if not assertions:
return None # Can't convert this body pattern
for a in assertions:
lines.append(a)
lines.append(' )')
return '\n'.join(lines)
def generate_test(test, idx):
"""Generate SX deftest for an upstream test. Dispatches to Chai or PW parser."""
"""Generate SX deftest for an upstream test. Dispatches to Chai, PW, or eval-only."""
elements = parse_html(test['html'])
if not elements and not test.get('html', '').strip():
return None
# No HTML — try eval-only conversion
return generate_eval_only_test(test, idx)
if not elements:
return None