use super::{eval, throws_error};
use tsrun::JsValue;
#[test]
fn test_eval_basic_expression() {
assert_eq!(eval("eval('1 + 2')"), JsValue::Number(3.0));
}
#[test]
fn test_eval_string_literal() {
assert_eq!(eval("eval('\"hello\"')"), JsValue::from("hello"));
}
#[test]
fn test_eval_number_literal() {
assert_eq!(eval("eval('42')"), JsValue::Number(42.0));
}
#[test]
fn test_eval_boolean_literal() {
assert_eq!(eval("eval('true')"), JsValue::Boolean(true));
assert_eq!(eval("eval('false')"), JsValue::Boolean(false));
}
#[test]
fn test_eval_null() {
assert_eq!(eval("eval('null')"), JsValue::Null);
}
#[test]
fn test_eval_undefined() {
assert_eq!(eval("eval('undefined')"), JsValue::Undefined);
}
#[test]
fn test_eval_empty_string() {
assert_eq!(eval("eval('')"), JsValue::Undefined);
}
#[test]
fn test_eval_variable_declaration() {
assert_eq!(
eval("eval('var x = 42'); typeof x"),
JsValue::from("undefined")
);
}
#[test]
fn test_eval_variable_inside_eval() {
assert_eq!(eval("eval('var x = 42; x')"), JsValue::Number(42.0));
}
#[test]
fn test_eval_let_declaration() {
assert_eq!(
eval("eval('let y = 100'); typeof y"),
JsValue::from("undefined")
);
}
#[test]
fn test_eval_multiple_statements() {
assert_eq!(eval("eval('1; 2; 3')"), JsValue::Number(3.0));
}
#[test]
fn test_eval_function_declaration() {
assert_eq!(
eval("eval('function add(a, b) { return a + b; }'); typeof add"),
JsValue::from("undefined")
);
}
#[test]
fn test_eval_function_inside_eval() {
assert_eq!(
eval("eval('function add(a, b) { return a + b; } add(2, 3)')"),
JsValue::Number(5.0)
);
}
#[test]
fn test_eval_access_outer_variable() {
assert_eq!(eval("let x = 10; eval('x')"), JsValue::Number(10.0));
}
#[test]
fn test_eval_modify_outer_variable() {
assert_eq!(eval("let x = 10; eval('x = 20'); x"), JsValue::Number(20.0));
}
#[test]
fn test_eval_access_function_scope() {
assert_eq!(
eval(
r#"
function test() {
let y = 42;
return eval('y');
}
test()
"#
),
JsValue::Number(42.0)
);
}
#[test]
fn test_eval_access_global() {
assert_eq!(
eval("var globalVar = 100; eval('globalVar')"),
JsValue::Number(100.0)
);
}
#[test]
fn test_eval_length() {
assert_eq!(eval("eval.length"), JsValue::Number(1.0));
}
#[test]
fn test_eval_name() {
assert_eq!(eval("eval.name"), JsValue::from("eval"));
}
#[test]
fn test_eval_typeof() {
assert_eq!(eval("typeof eval"), JsValue::from("function"));
}
#[test]
fn test_eval_is_callable() {
assert_eq!(eval("typeof eval('1 + 1')"), JsValue::from("number"));
}
#[test]
fn test_eval_non_string_number() {
assert_eq!(eval("eval(42)"), JsValue::Number(42.0));
}
#[test]
fn test_eval_non_string_boolean() {
assert_eq!(eval("eval(true)"), JsValue::Boolean(true));
}
#[test]
fn test_eval_non_string_null() {
assert_eq!(eval("eval(null)"), JsValue::Null);
}
#[test]
fn test_eval_non_string_undefined() {
assert_eq!(eval("eval(undefined)"), JsValue::Undefined);
}
#[test]
fn test_eval_non_string_object() {
assert_eq!(eval("let obj = {a: 1}; eval(obj).a"), JsValue::Number(1.0));
}
#[test]
fn test_eval_non_string_array() {
assert_eq!(eval("eval([1, 2, 3])[1]"), JsValue::Number(2.0));
}
#[test]
fn test_eval_syntax_error() {
assert!(throws_error("eval('{')", "SyntaxError"));
}
#[test]
fn test_eval_reference_error() {
assert!(throws_error("eval('undefinedVariable')", "ReferenceError"));
}
#[test]
fn test_eval_type_error() {
assert!(throws_error("eval('null.foo')", "TypeError"));
}
#[test]
fn test_eval_not_constructor() {
assert!(throws_error("new eval()", "TypeError"));
}
#[test]
fn test_eval_not_constructor_with_arg() {
assert!(throws_error("new eval('1 + 1')", "TypeError"));
}
#[test]
fn test_eval_object_literal() {
assert_eq!(eval("eval('({a: 1, b: 2})').a"), JsValue::Number(1.0));
}
#[test]
fn test_eval_array_literal() {
assert_eq!(eval("eval('[1, 2, 3]').length"), JsValue::Number(3.0));
}
#[test]
fn test_eval_arrow_function() {
assert_eq!(eval("eval('((x) => x * 2)(5)')"), JsValue::Number(10.0));
}
#[test]
fn test_eval_template_literal() {
assert_eq!(
eval("let x = 'world'; eval('`hello ${x}`')"),
JsValue::from("hello world")
);
}
#[test]
fn test_eval_regex() {
assert_eq!(eval("eval('/abc/g').source"), JsValue::from("abc"));
}
#[test]
fn test_eval_strict_mode_this() {
assert_eq!(
eval(
r#"
function test() {
return eval('this');
}
test()
"#
),
JsValue::Undefined
);
}
#[test]
fn test_eval_strict_assignment_to_undeclared() {
assert!(throws_error("eval('undeclaredVar = 1')", "ReferenceError"));
}
#[test]
fn test_indirect_eval_uses_global_scope() {
assert_eq!(
eval(
r#"
var globalX = 42;
function test() {
var localX = 100;
// Indirect eval using comma operator
return (1, eval)('globalX');
}
test()
"#
),
JsValue::Number(42.0)
);
}
#[test]
fn test_indirect_eval_via_variable() {
assert_eq!(
eval(
r#"
var globalY = 50;
var indirectEval = eval;
function test() {
var localY = 200;
return indirectEval('globalY');
}
test()
"#
),
JsValue::Number(50.0)
);
}
#[test]
fn test_eval_with_type_annotation() {
assert_eq!(eval("eval('let x: number = 42; x')"), JsValue::Number(42.0));
}
#[test]
fn test_eval_with_type_assertion() {
assert_eq!(
eval("eval('let x = 42 as number; x')"),
JsValue::Number(42.0)
);
}
#[test]
fn test_eval_with_interface() {
assert_eq!(
eval("eval('interface Foo { x: number }; 42')"),
JsValue::Number(42.0)
);
}
#[test]
fn test_eval_no_args() {
assert_eq!(eval("eval()"), JsValue::Undefined);
}
#[test]
fn test_eval_closure() {
assert_eq!(
eval(
r#"
function outer() {
let x = 10;
let f = eval('(function() { return x; })');
return f();
}
outer()
"#
),
JsValue::Number(10.0)
);
}
#[test]
fn test_eval_return_last_expression() {
assert_eq!(eval("eval('1; 2; 3')"), JsValue::Number(3.0));
}
#[test]
fn test_eval_statement_returns_undefined() {
assert_eq!(eval("eval('if (true) { 42 }')"), JsValue::Number(42.0));
}
#[test]
fn test_eval_empty_block() {
assert_eq!(eval("eval('{}')"), JsValue::Undefined);
}
#[test]
fn test_eval_whitespace_only() {
assert_eq!(eval("eval(' ')"), JsValue::Undefined);
}
#[test]
fn test_eval_comments_only() {
assert_eq!(eval("eval('// comment')"), JsValue::Undefined);
}
#[test]
fn test_eval_multiline() {
assert_eq!(
eval("eval('let a = 1;\\nlet b = 2;\\na + b')"),
JsValue::Number(3.0)
);
}
#[test]
fn test_eval_vs_function_scope() {
assert_eq!(
eval(
r#"
function test() {
let localVar = 123;
// eval can see localVar
let evalResult = eval('localVar');
// Function constructor cannot (uses global scope)
let fnResult = (new Function('return typeof localVar'))();
return evalResult + '-' + fnResult;
}
test()
"#
),
JsValue::from("123-undefined")
);
}
#[test]
fn test_eval_as_method() {
assert_eq!(
eval(
r#"
let obj = { eval: eval };
obj.eval('1 + 1')
"#
),
JsValue::Number(2.0)
);
}
#[test]
fn test_eval_property_writable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(globalThis, 'eval'); desc.writable"),
JsValue::Boolean(true)
);
}
#[test]
fn test_eval_property_enumerable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(globalThis, 'eval'); desc.enumerable"),
JsValue::Boolean(false)
);
}
#[test]
fn test_eval_property_configurable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(globalThis, 'eval'); desc.configurable"),
JsValue::Boolean(true)
);
}
#[test]
fn test_eval_name_property_writable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(eval, 'name'); desc.writable"),
JsValue::Boolean(false)
);
}
#[test]
fn test_eval_name_property_enumerable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(eval, 'name'); desc.enumerable"),
JsValue::Boolean(false)
);
}
#[test]
fn test_eval_name_property_configurable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(eval, 'name'); desc.configurable"),
JsValue::Boolean(true)
);
}
#[test]
fn test_eval_length_property_writable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(eval, 'length'); desc.writable"),
JsValue::Boolean(false)
);
}
#[test]
fn test_eval_length_property_enumerable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(eval, 'length'); desc.enumerable"),
JsValue::Boolean(false)
);
}
#[test]
fn test_eval_length_property_configurable() {
assert_eq!(
eval("let desc = Object.getOwnPropertyDescriptor(eval, 'length'); desc.configurable"),
JsValue::Boolean(true)
);
}
#[test]
fn test_eval_scope_debug() {
let result = eval(
r#"
function test() {
let localVar = 123;
console.log("localVar before eval:", localVar);
let result = eval('localVar');
console.log("eval result:", result);
return result;
}
test()
"#,
);
assert_eq!(result, JsValue::Number(123.0));
}
#[test]
fn test_eval_scope_with_function_constructor() {
let result = eval(
r#"
function test() {
let localVar = 123;
console.log("Step 1: localVar =", localVar);
// This is the line that fails in the original test
let evalResult = eval('localVar');
console.log("Step 2: evalResult =", evalResult);
// Now try Function constructor
let fnResult = (new Function('return typeof localVar'))();
console.log("Step 3: fnResult =", fnResult);
return evalResult + '-' + fnResult;
}
test()
"#,
);
assert_eq!(result, JsValue::from("123-undefined"));
}
#[test]
fn test_typeof_undeclared_variable() {
assert_eq!(
eval("typeof nonExistentVariable"),
JsValue::from("undefined")
);
}
#[test]
fn test_eval_scope_minimal() {
assert_eq!(
eval(
r#"
function f() {
let x = 42;
return eval('x');
}
f()
"#
),
JsValue::Number(42.0)
);
}
#[test]
fn test_eval_if_empty_block_completion() {
assert_eq!(eval("eval('1; if (true) { }')"), JsValue::Undefined);
}
#[test]
fn test_eval_if_expression_completion() {
assert_eq!(eval("eval('2; if (true) { 3; }')"), JsValue::Number(3.0));
}
#[test]
fn test_eval_switch_completion() {
assert_eq!(
eval("eval('1; switch (\"a\") { case \"a\": 2; }')"),
JsValue::Number(2.0)
);
}
#[test]
fn test_eval_for_completion() {
assert_eq!(
eval("eval('1; for (let i = 0; i < 3; i++) { i; }')"),
JsValue::Number(2.0)
);
}
#[test]
fn test_eval_while_completion() {
assert_eq!(
eval("eval('let i = 0; while (i < 3) { i++; }')"),
JsValue::Number(2.0)
);
}
#[test]
fn test_eval_if_else_completion() {
assert_eq!(
eval("eval('if (false) { 1 } else { 2 }')"),
JsValue::Number(2.0)
);
assert_eq!(
eval("eval('if (true) { 1 } else { 2 }')"),
JsValue::Number(1.0)
);
}
#[test]
fn test_eval_try_completion() {
assert_eq!(
eval("eval('try { 42 } catch(e) { }')"),
JsValue::Number(42.0)
);
}
#[test]
fn test_eval_try_catch_empty_completion() {
assert_eq!(
eval("eval('1; try { throw null; } catch (err) { }')"),
JsValue::Undefined
);
}
#[test]
fn test_eval_try_catch_expression_completion() {
assert_eq!(
eval("eval('2; try { throw null; } catch (err) { 3; }')"),
JsValue::Number(3.0)
);
}
#[test]
fn test_eval_try_no_throw_completion() {
assert_eq!(
eval("eval('try { 5 } catch(e) { 6 }')"),
JsValue::Number(5.0)
);
}
#[test]
fn test_eval_try_finally_no_throw() {
assert_eq!(eval("eval('try { 5 } finally { }')"), JsValue::Number(5.0));
}
#[test]
fn test_eval_try_catch_finally_completion() {
assert_eq!(
eval("eval('try { throw null; } catch(e) { 7 } finally { }')"),
JsValue::Number(7.0)
);
}
#[test]
fn test_eval_try_catch_break_completion() {
assert_eq!(
eval(
r#"eval("for (var i = 0; i < 2; ++i) { if (i) { try { throw null; } catch (e) { break; } } 'bad completion'; }")"#
),
JsValue::Undefined
);
}
#[test]
fn test_eval_try_catch_continue_completion() {
assert_eq!(
eval(
r#"eval("var last; for (var i = 0; i < 2; ++i) { if (i) { try { throw null; } catch (e) { last = i; continue; } } 'bad completion'; }")"#
),
JsValue::Undefined
);
}
#[test]
fn test_eval_block_completion() {
assert_eq!(eval("eval('{ 1; 2; 3; }')"), JsValue::Number(3.0));
}