diff --git a/spec/tests/test-hyperscript-behavioral.sx b/spec/tests/test-hyperscript-behavioral.sx index 5c610e81..3a867216 100644 --- a/spec/tests/test-hyperscript-behavioral.sx +++ b/spec/tests/test-hyperscript-behavioral.sx @@ -4183,7 +4183,7 @@ (assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")) (list "a" "c" "b")) ) (deftest "split by on null returns null" - (eval-hs "set x to null then return x split by ','n") + (eval-hs "set x to null then return x split by ','\\n") ) (deftest "the result inside where refers to previous command result, not current element" (assert= (eval-hs "get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result") (list 4 5)) @@ -9135,13 +9135,13 @@ (assert= (eval-hs-locals "pick item 2 from arr set $test to it" (list (list (quote arr) (list 10 11 12 13 14 15 16)))) (list 12)) ) (deftest "can pick a single regex match" - (assert= (eval-hs-locals "pick match of \"d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list "32")) + (assert= (eval-hs-locals "pick match of \"\\d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list "32")) ) (deftest "can pick a single regex match w/ a flag" (assert= (eval-hs-locals "pick match of \"t.e\" | i from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list "The")) ) (deftest "can pick all regex matches" - (assert= (eval-hs-locals "pick matches of \"d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list (list "32") (list "12") (list "149"))) + (assert= (eval-hs-locals "pick matches of \"\\d+\" from haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes jumped 12 times over the 149 lazy dogs"))) (list (list "32") (list "12") (list "149"))) ) (deftest "can pick first n items" (assert= (eval-hs-locals "pick first 3 of arr set $test to it" (list (list (quote arr) (list 10 20 30 40 50)))) (list 10 20 30)) @@ -9159,7 +9159,7 @@ (assert= (eval-hs-locals "pick last 2 of arr set $test to it" (list (list (quote arr) (list 10 20 30 40 50)))) (list 40 50)) ) (deftest "can pick match using 'of' syntax" - (assert= (eval-hs-locals "pick match of \"d+\" of haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes"))) (list "32")) + (assert= (eval-hs-locals "pick match of \"\\d+\" of haystack set window.test to it" (list (list (quote haystack) "The 32 quick brown foxes"))) (list "32")) ) (deftest "can pick random item" (assert (not (nil? (eval-hs-locals "pick random of arr set $test to it" (list (list (quote arr) (list 10 20 30))))))) @@ -9186,7 +9186,7 @@ (assert= (eval-hs-locals "pick items 0 to -4 from arr set $test to it" (list (list (quote arr) (list 10 11 12 13 14 15 16)))) (list 10 11 12)) ) (deftest "does not hang on zero-length regex matches" - (assert (not (nil? (eval-hs-locals "pick matches of \"d*\" from haystack set window.test to it" (list (list (quote haystack) "a1b")))))) + (assert (not (nil? (eval-hs-locals "pick matches of \"\\d*\" from haystack set window.test to it" (list (list (quote haystack) "a1b")))))) ) (deftest "pick first from null returns null" (eval-hs "set x to null then pick first 3 from x then return it") @@ -9195,7 +9195,7 @@ (eval-hs "set x to null then pick last 2 from x then return it") ) (deftest "pick match from null returns null" - (eval-hs "set x to null then pick match of \"d+\" from x then return it") + (eval-hs "set x to null then pick match of \"\\d+\" from x then return it") ) (deftest "pick random from null returns null" (eval-hs "set x to null then pick random from x then return it") diff --git a/tests/playwright/generate-sx-tests.py b/tests/playwright/generate-sx-tests.py index 1ce2f910..3efec6bc 100644 --- a/tests/playwright/generate-sx-tests.py +++ b/tests/playwright/generate-sx-tests.py @@ -1768,14 +1768,49 @@ def _js_window_expr_to_sx(expr): return None +def _decode_js_escapes(s): + """Decode JS string escape sequences. + - \\" -> " (escaped quote) + - \\' -> ' + - \\` -> ` + - \\\\ -> \\ (escaped backslash) + - \\n, \\t -> space (already normalized) + - Other \\X sequences (e.g. \\d for regex) are preserved literally, + matching String.raw semantics for unknown escapes. + """ + out = [] + i = 0 + while i < len(s): + c = s[i] + if c == '\\' and i + 1 < len(s): + nxt = s[i + 1] + if nxt in ('"', "'", '`'): + out.append(nxt) + i += 2 + continue + if nxt == '\\': + out.append('\\') + i += 2 + continue + # Unknown escape: preserve both chars (regex \\d, CSS \\:, lambda \\ -> ) + out.append(c) + i += 1 + continue + out.append(c) + i += 1 + return ''.join(out) + + def extract_hs_expr(raw): """Clean a HS expression extracted from run() call.""" # Remove surrounding whitespace and newlines expr = raw.strip().replace('\n', ' ').replace('\t', ' ') # Collapse multiple spaces expr = re.sub(r'\s+', ' ', expr) - # Escape backslashes (preserve regex escapes like \d, CSS escapes, lambda \) - # then escape quotes for SX string. + # Decode JS-level escape sequences while preserving regex/CSS/lambda + # backslashes. \" -> ", \\ -> \, \d -> \d (unchanged). + expr = _decode_js_escapes(expr) + # Re-escape for SX string literal: backslashes, then quotes. expr = expr.replace('\\', '\\\\').replace('"', '\\"') return expr