browser_jswrap_test.go
1 package tools 2 3 import "testing" 4 5 func TestWrapJSForEvaluate(t *testing.T) { 6 cases := []struct { 7 name string 8 in string 9 wantWrapped bool 10 }{ 11 {"plain expression", "JSON.stringify(document.title)", false}, 12 {"arrow call", "[...document.querySelectorAll('a')].length", false}, 13 {"empty", "", false}, 14 {"whitespace only", " ", false}, 15 // Semicolon-terminated expressions and multi-line expression-only 16 // scripts must pass through: wrapping them in an IIFE without an 17 // explicit `return` would silently change the return value to 18 // `undefined`. Only top-level statement keywords (which are syntax 19 // errors in expression context) trigger wrapping. 20 {"expression with trailing semicolon", "JSON.stringify(document.title);", false}, 21 {"two expressions semicolon-separated", "foo(); bar()", false}, 22 {"two expressions newline-separated", "foo()\nbar()", false}, 23 {"return statement", "return 1", true}, 24 {"const + return", "const x = 1; return x", true}, 25 {"let", "let y = 2", true}, 26 {"var", "var z = 3", true}, 27 {"function declaration", "function f() { return 1 }", true}, 28 {"async function", "async function g() { return 1 }", true}, 29 {"async function with extra whitespace", "async function g() { return 1 }", true}, 30 // `async` alone must NOT trigger wrap — `async () => expr` is a 31 // valid expression; wrapping it without an explicit `return` in the 32 // outer IIFE would silently yield `undefined`. 33 {"async arrow expression", "async () => fetch('/x').then(r => r.json())", false}, 34 {"async arrow without space", "async()=>1", false}, 35 {"asyncFoo identifier", "asyncFoo", false}, 36 {"multiline const", "const a = 1\na", true}, 37 {"leading if", "if (true) { return 1 }", true}, 38 {"leading try", "try { return 1 } catch (e) {}", true}, 39 // Identifiers that happen to start with a keyword must NOT be wrapped. 40 {"returnValue identifier", "returnValue", false}, 41 {"constexpr identifier", "constexpr + 1", false}, 42 // Already wrapped IIFEs pass through unchanged. 43 {"user IIFE", "(() => { return 1 })()", false}, 44 {"user async IIFE", "(async () => { return 1 })()", false}, 45 {"user function IIFE", "(function() { return 1 })()", false}, 46 } 47 for _, tc := range cases { 48 t.Run(tc.name, func(t *testing.T) { 49 out := wrapJSForEvaluate(tc.in) 50 wrapped := out != tc.in 51 if wrapped != tc.wantWrapped { 52 t.Errorf("wrapJSForEvaluate(%q) = %q (wrapped=%v), wantWrapped=%v", tc.in, out, wrapped, tc.wantWrapped) 53 } 54 }) 55 } 56 }