use javascript::Value;
use javascript::evaluate_script;
#[ctor::ctor]
fn __init_test_logger() {
let _ = env_logger::Builder::from_env(env_logger::Env::default()).is_test(true).try_init();
}
#[cfg(test)]
mod object_literal_tests {
use super::*;
#[test]
fn test_basic_object_literal() {
let script = "let obj = {a: 1, b: 2}; obj.a + obj.b";
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 3.0),
_ => panic!("Expected number 3.0, got {:?}", result),
}
}
#[test]
fn test_object_property_access() {
let script = "let obj = {name: 'hello', value: 42}; obj.name";
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "hello".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string 'hello', got {:?}", result),
}
}
#[test]
fn test_empty_object() {
let script = "let empty = {}; empty";
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Object(map)) => {
assert_eq!(map.borrow().properties.len(), 0)
}
_ => panic!("Expected empty object, got {:?}", result),
}
}
#[test]
fn test_nested_object() {
let script = "let nested = {a: {b: 1}}; nested.a.b";
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 1.0),
_ => panic!("Expected number 1.0, got {:?}", result),
}
}
#[test]
fn test_object_with_string_keys() {
let script = "let obj = {'key': 123}; obj.key";
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 123.0),
_ => panic!("Expected number 123.0, got {:?}", result),
}
}
#[test]
fn test_console_log_with_object() {
let script = "let obj = {test: 'value'}; console.log(obj.test); obj.test";
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "value".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string 'value', got {:?}", result),
}
}
#[test]
fn test_intentionally_failing_object() {
let script = "let obj = {a: 1, b: 2}; obj.a + obj.b";
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 3.0), _ => panic!("Expected number 3.0, got {:?}", result),
}
}
#[test]
fn test_getter_setter_basic() {
let script = r#"
let obj = {
_value: 0,
get value() { return this._value; },
set value(v) { this._value = v * 2; }
};
obj.value = 5;
obj.value
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 10.0), _ => panic!("Expected number 10.0, got {:?}", result),
}
}
#[test]
fn test_getter_setter_with_computed_property() {
let script = r#"
let obj = {
_data: {},
get data() { return this._data; },
set data(value) { this._data = { processed: value * 10 }; }
};
obj.data = 3;
obj.data.processed
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 30.0), _ => panic!("Expected number 30.0, got {:?}", result),
}
}
#[test]
fn test_concise_method_parsing() {
let script = r#"
let obj = { foo() { return 7; } };
obj.foo();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 7.0),
_ => panic!("Expected number 7.0, got {:?}", result),
}
}
#[test]
fn test_computed_getter_parsing() {
let script = r#"
function CustomError() {}
let obj = { get [Symbol.toPrimitive]() { return 42; } };
42
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::Number(n)) => assert_eq!(n, 42.0),
_ => panic!("Expected number 42.0, got {:?}", result),
}
}
#[test]
fn test_object_to_string() {
let script = r#"
let obj = {a: 1, b: 2};
obj.toString();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "[object Object]".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string '[object Object]', got {:?}", result),
}
}
#[test]
fn test_object_to_string_with_super() {
let script = r#"
class Base {
toString() {
return "Base toString";
}
}
class Derived extends Base {}
let obj = new Derived();
obj.toString();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "Base toString".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string 'Base toString', got {:?}", result),
}
}
#[test]
fn test_object_to_string_with_super_2() {
let script = r#"
class A {
toString() {
return "A toString";
}
}
class B extends A {
toString() {
return "B " + super.toString();
}
}
let obj = new B();
obj.toString();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "B A toString".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string 'B A toString', got {:?}", result),
}
}
#[test]
fn test_object_to_string_with_super_3() {
let script = r#"
var obj = { toString() { return 'obj -> ' + super.toString(); } };
return obj.toString();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "obj -> [object Object]".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string 'obj -> [object Object]', got {:?}", result),
}
}
#[test]
fn test_object_to_string_with_super_4() {
let script = r#"
const proto = {
toString() {
return 'proto';
}
};
const obj = {
toString() {
return 'obj -> ' + super.toString();
}
};
Reflect.setPrototypeOf(obj, proto);
return obj.toString();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "obj -> proto".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string 'obj -> proto', got {:?}", result),
}
}
#[test]
fn test_object_to_string_with_super_5() {
let script = r#"
const proto = {
toString() {
return 'proto';
}
};
const obj = {
toString() {
return 'obj -> ' + super.toString();
}
};
// Reflect.setPrototypeOf(obj, proto);
return obj.toString();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Ok(Value::String(s)) => {
let expected = "obj -> [object Object]".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string 'obj -> [object Object]', got {:?}", result),
}
}
#[test]
fn test_object_with_reference_error() {
let script = r#"
let obj = {
get value() { return nonExistentVar; }
};
obj.value;
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Err(e) => {
let err_msg = format!("{:?}", e);
assert!(err_msg.contains("nonExistentVar"), "Expected nonExistentVar, got {:?}", err_msg);
}
_ => panic!("Expected nonExistentVar, got {:?}", result),
}
}
#[test]
fn test_object_with_reference_error_2() {
let script = r#"
const obj = {
__proto__: theProtoObj,
handler,
};
console.log(obj);
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match result {
Err(e) => {
let err_msg = format!("{:?}", e);
assert!(err_msg.contains("theProtoObj"), "Expected theProtoObj error, got {:?}", err_msg);
}
_ => panic!("Expected theProtoObj error, got {:?}", result),
}
}
}