diff --git a/spec/tests/test-hyperscript-behavioral.sx b/spec/tests/test-hyperscript-behavioral.sx index 040bd2e8..19732286 100644 --- a/spec/tests/test-hyperscript-behavioral.sx +++ b/spec/tests/test-hyperscript-behavioral.sx @@ -4341,8 +4341,8 @@ (assert= (eval-hs "'Hello World' contains 'missing' ignoring case") false) ) (deftest "contains works with arrays" - (assert= (eval-hs-locals "I contain that" (list (list (quote that) 1))) true) - (assert= (eval-hs-locals "that contains me" (list (list (quote that) "[1"))) true) + (assert= (eval-hs-locals "I contain that" (list (list (quote that) 1) (list (quote me) (list 1 2 3)))) true) + (assert= (eval-hs-locals "that contains me" (list (list (quote that) (list 1 2 3)) (list (quote me) 1))) true) ) (deftest "contains works with css literals" (hs-cleanup!) @@ -4501,8 +4501,8 @@ (assert= (eval-hs-locals "foobar does not include foo" (list (list (quote foo) "foo") (list (quote foobar) "foobar"))) false) ) (deftest "includes works with arrays" - (assert= (eval-hs-locals "I include that" (list (list (quote that) 1))) true) - (assert= (eval-hs-locals "that includes me" (list (list (quote that) "[1"))) true) + (assert= (eval-hs-locals "I include that" (list (list (quote that) 1) (list (quote me) (list 1 2 3)))) true) + (assert= (eval-hs-locals "that includes me" (list (list (quote that) (list 1 2 3)) (list (quote me) 1))) true) ) (deftest "includes works with css literals" (hs-cleanup!) diff --git a/tests/playwright/generate-sx-tests.py b/tests/playwright/generate-sx-tests.py index 120892ea..a4bef01b 100644 --- a/tests/playwright/generate-sx-tests.py +++ b/tests/playwright/generate-sx-tests.py @@ -1281,16 +1281,48 @@ def generate_eval_only_test(test, idx): hs_expr = extract_hs_expr(m.group(2)) opts_str = m.group(3) or '' expected_sx = js_val_to_sx(m.group(4)) - # Check for { me: X } or { locals: { x: X, y: Y } } in opts - me_match = re.search(r'\bme:\s*(\d+)', opts_str) - locals_match = re.search(r'locals:\s*\{([^}]+)\}', opts_str) + # Check for { me: X } or { locals: { x: X, y: Y } } in opts. + # Numeric me uses eval-hs-with-me; other me values get bound as a local. + me_num_match = re.search(r'\bme:\s*(\d+)\b', opts_str) + me_val_match = re.search(r'\bme:\s*(\[[^\]]*\]|\{[^}]*\}|"[^"]*"|\'[^\']*\')', opts_str) + # Locals: balanced-brace extraction so nested arrays/objects don't truncate. + locals_idx = opts_str.find('locals:') extra = [] - if locals_match: - for lm in re.finditer(r'(\w+)\s*:\s*([^,}]+)', locals_match.group(1)): - extra.append((lm.group(1), js_val_to_sx(lm.group(2).strip()))) - if me_match and not (window_setups or extra): - assertions.append(f' (assert= (eval-hs-with-me "{hs_expr}" {me_match.group(1)}) {expected_sx})') + if locals_idx >= 0: + open_idx = opts_str.find('{', locals_idx) + if open_idx >= 0: + depth, in_str, end_idx = 1, None, -1 + for i in range(open_idx + 1, len(opts_str)): + ch = opts_str[i] + if in_str: + if ch == in_str and opts_str[i - 1] != '\\': + in_str = None + continue + if ch in ('"', "'", '`'): + in_str = ch + continue + if ch == '{': + depth += 1 + elif ch == '}': + depth -= 1 + if depth == 0: + end_idx = i + break + if end_idx > open_idx: + for kv in split_top_level(opts_str[open_idx + 1:end_idx]): + kv = kv.strip() + m2 = re.match(r'^(\w+)\s*:\s*(.+)$', kv, re.DOTALL) + if m2: + extra.append((m2.group(1), js_val_to_sx(m2.group(2).strip()))) + if me_val_match: + extra.append(('me', js_val_to_sx(me_val_match.group(1)))) + if me_num_match and not (window_setups or extra): + assertions.append(f' (assert= (eval-hs-with-me "{hs_expr}" {me_num_match.group(1)}) {expected_sx})') else: + # If there are other locals/setups but `me: ` is present too, + # bind it as a local so the HS expression can see it. + if me_num_match and not me_val_match: + extra.append(('me', me_num_match.group(1))) assertions.append(emit_eval(hs_expr, expected_sx, extra)) # Pattern 1b: Inline — run("expr", opts).toEqual([...])