use super::{create_test_runtime, run, run_to_completion};
use tsrun::{JsValue, OrderResponse, StepResult, api};
#[test]
fn test_is_undefined() {
assert!(JsValue::Undefined.is_undefined());
assert!(!JsValue::Null.is_undefined());
assert!(!JsValue::Boolean(true).is_undefined());
assert!(!JsValue::Number(42.0).is_undefined());
assert!(!JsValue::from("hello").is_undefined());
}
#[test]
fn test_is_null() {
assert!(JsValue::Null.is_null());
assert!(!JsValue::Undefined.is_null());
assert!(!JsValue::Boolean(false).is_null());
assert!(!JsValue::Number(0.0).is_null());
}
#[test]
fn test_is_nullish() {
assert!(JsValue::Undefined.is_nullish());
assert!(JsValue::Null.is_nullish());
assert!(!JsValue::Boolean(false).is_nullish());
assert!(!JsValue::Number(0.0).is_nullish());
assert!(!JsValue::from("").is_nullish());
}
#[test]
fn test_is_boolean() {
assert!(JsValue::Boolean(true).is_boolean());
assert!(JsValue::Boolean(false).is_boolean());
assert!(!JsValue::Undefined.is_boolean());
assert!(!JsValue::Number(1.0).is_boolean());
}
#[test]
fn test_is_number() {
assert!(JsValue::Number(42.0).is_number());
assert!(JsValue::Number(0.0).is_number());
assert!(JsValue::Number(f64::NAN).is_number());
assert!(JsValue::Number(f64::INFINITY).is_number());
assert!(!JsValue::Boolean(true).is_number());
assert!(!JsValue::from("42").is_number());
}
#[test]
fn test_is_string() {
assert!(JsValue::from("hello").is_string());
assert!(JsValue::from("").is_string());
assert!(!JsValue::Number(42.0).is_string());
assert!(!JsValue::Undefined.is_string());
}
#[test]
fn test_type_name() {
assert_eq!(JsValue::Undefined.type_name(), "undefined");
assert_eq!(JsValue::Null.type_name(), "null");
assert_eq!(JsValue::Boolean(true).type_name(), "boolean");
assert_eq!(JsValue::Number(42.0).type_name(), "number");
assert_eq!(JsValue::from("hello").type_name(), "string");
}
#[test]
fn test_as_bool() {
assert_eq!(JsValue::Boolean(true).as_bool(), Some(true));
assert_eq!(JsValue::Boolean(false).as_bool(), Some(false));
assert_eq!(JsValue::Number(1.0).as_bool(), None);
assert_eq!(JsValue::from("true").as_bool(), None);
assert_eq!(JsValue::Undefined.as_bool(), None);
}
#[test]
fn test_as_number() {
assert_eq!(JsValue::Number(42.0).as_number(), Some(42.0));
assert_eq!(JsValue::Number(0.0).as_number(), Some(0.0));
assert_eq!(JsValue::Number(-3.15).as_number(), Some(-3.15));
assert_eq!(JsValue::Boolean(true).as_number(), None);
assert_eq!(JsValue::from("42").as_number(), None);
let nan = JsValue::Number(f64::NAN).as_number();
assert!(nan.is_some());
assert!(nan.unwrap().is_nan());
}
#[test]
fn test_as_str() {
assert_eq!(JsValue::from("hello").as_str(), Some("hello"));
assert_eq!(JsValue::from("").as_str(), Some(""));
assert_eq!(JsValue::from("with spaces").as_str(), Some("with spaces"));
assert_eq!(JsValue::Number(42.0).as_str(), None);
assert_eq!(JsValue::Boolean(true).as_str(), None);
assert_eq!(JsValue::Undefined.as_str(), None);
}
#[test]
fn test_as_js_string() {
let value = JsValue::from("hello");
assert!(value.as_js_string().is_some());
assert_eq!(value.as_js_string().unwrap().as_str(), "hello");
assert!(JsValue::Number(42.0).as_js_string().is_none());
}
#[test]
fn test_from_bool() {
assert_eq!(JsValue::from(true), JsValue::Boolean(true));
assert_eq!(JsValue::from(false), JsValue::Boolean(false));
let v: JsValue = true.into();
assert_eq!(v, JsValue::Boolean(true));
}
#[test]
fn test_from_f64() {
assert_eq!(JsValue::from(42.0f64), JsValue::Number(42.0));
assert_eq!(JsValue::from(0.0f64), JsValue::Number(0.0));
assert_eq!(JsValue::from(-3.15f64), JsValue::Number(-3.15));
let v: JsValue = 42.0f64.into();
assert_eq!(v, JsValue::Number(42.0));
}
#[test]
fn test_from_i32() {
assert_eq!(JsValue::from(42i32), JsValue::Number(42.0));
assert_eq!(JsValue::from(0i32), JsValue::Number(0.0));
assert_eq!(JsValue::from(-100i32), JsValue::Number(-100.0));
}
#[test]
fn test_from_i64() {
assert_eq!(JsValue::from(42i64), JsValue::Number(42.0));
assert_eq!(JsValue::from(1_000_000i64), JsValue::Number(1_000_000.0));
}
#[test]
fn test_from_u32() {
assert_eq!(JsValue::from(42u32), JsValue::Number(42.0));
assert_eq!(JsValue::from(0u32), JsValue::Number(0.0));
}
#[test]
fn test_from_u64() {
assert_eq!(JsValue::from(42u64), JsValue::Number(42.0));
}
#[test]
fn test_from_usize() {
assert_eq!(JsValue::from(42usize), JsValue::Number(42.0));
}
#[test]
fn test_from_unit() {
assert_eq!(JsValue::from(()), JsValue::Undefined);
let v: JsValue = ().into();
assert_eq!(v, JsValue::Undefined);
}
#[test]
fn test_from_str() {
let v = JsValue::from("hello");
assert_eq!(v.as_str(), Some("hello"));
let v = JsValue::from("");
assert_eq!(v.as_str(), Some(""));
}
#[test]
fn test_from_string() {
let v = JsValue::from(String::from("hello"));
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn test_create_value_number() {
let v = api::create_value(42);
assert!(v.is_number());
assert_eq!(v.as_number(), Some(42.0));
}
#[test]
fn test_create_value_f64() {
let v = api::create_value(3.15f64);
assert!(v.is_number());
assert_eq!(v.as_number(), Some(3.15));
}
#[test]
fn test_create_value_bool() {
let v = api::create_value(true);
assert!(v.is_boolean());
assert_eq!(v.as_bool(), Some(true));
}
#[test]
fn test_create_value_string() {
let v = api::create_value("hello");
assert!(v.is_string());
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn test_create_value_string_owned() {
let v = api::create_value(String::from("world"));
assert!(v.is_string());
assert_eq!(v.as_str(), Some("world"));
}
#[test]
fn test_create_undefined() {
let v = api::create_undefined();
assert!(v.is_undefined());
}
#[test]
fn test_create_null() {
let v = api::create_null();
assert!(v.is_null());
}
#[test]
fn test_create_from_json_object() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let v = api::create_from_json(
&mut runtime,
&guard,
&serde_json::json!({
"name": "Alice",
"age": 30
}),
)
.unwrap();
assert!(v.is_object());
if let Some(obj) = v.as_object() {
let borrowed = obj.borrow();
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
let age_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("age"));
assert_eq!(
borrowed.get_property(&name_key).unwrap().as_str(),
Some("Alice")
);
assert_eq!(
borrowed.get_property(&age_key).unwrap().as_number(),
Some(30.0)
);
}
}
#[test]
fn test_create_from_json_array() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let v =
api::create_from_json(&mut runtime, &guard, &serde_json::json!([1, 2, 3, 4, 5])).unwrap();
assert!(v.is_object());
if let Some(obj) = v.as_object() {
let borrowed = obj.borrow();
if let Some(elements) = borrowed.array_elements() {
assert_eq!(elements.len(), 5);
assert_eq!(elements[0].as_number(), Some(1.0));
assert_eq!(elements[4].as_number(), Some(5.0));
}
}
}
#[test]
fn test_create_from_json_nested() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let v = api::create_from_json(
&mut runtime,
&guard,
&serde_json::json!({
"user": {
"name": "Bob",
"scores": [95, 87, 92]
}
}),
)
.unwrap();
assert!(v.is_object());
}
#[test]
fn test_create_object_empty() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let v = api::create_object(&mut runtime, &guard).unwrap();
assert!(v.is_object());
}
#[test]
fn test_create_array_empty() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let v = api::create_array(&mut runtime, &guard).unwrap();
assert!(v.is_object());
if let Some(obj) = v.as_object() {
let borrowed = obj.borrow();
assert_eq!(borrowed.array_length(), Some(0));
}
}
#[test]
fn test_create_from_json_primitives() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let v = api::create_from_json(&mut runtime, &guard, &serde_json::json!(null)).unwrap();
assert!(v.is_null());
let v = api::create_from_json(&mut runtime, &guard, &serde_json::json!(true)).unwrap();
assert!(v.is_boolean());
assert_eq!(v.as_bool(), Some(true));
let v = api::create_from_json(&mut runtime, &guard, &serde_json::json!(42)).unwrap();
assert!(v.is_number());
assert_eq!(v.as_number(), Some(42.0));
let v = api::create_from_json(&mut runtime, &guard, &serde_json::json!("hello")).unwrap();
assert!(v.is_string());
assert_eq!(v.as_str(), Some("hello"));
}
#[test]
fn test_display_undefined() {
assert_eq!(format!("{}", JsValue::Undefined), "undefined");
}
#[test]
fn test_display_null() {
assert_eq!(format!("{}", JsValue::Null), "null");
}
#[test]
fn test_display_boolean() {
assert_eq!(format!("{}", JsValue::Boolean(true)), "true");
assert_eq!(format!("{}", JsValue::Boolean(false)), "false");
}
#[test]
fn test_display_number() {
assert_eq!(format!("{}", JsValue::Number(42.0)), "42");
assert_eq!(format!("{}", JsValue::Number(3.15)), "3.15");
assert_eq!(format!("{}", JsValue::Number(0.0)), "0");
assert_eq!(format!("{}", JsValue::Number(-1.0)), "-1");
assert_eq!(format!("{}", JsValue::Number(f64::INFINITY)), "Infinity");
assert_eq!(
format!("{}", JsValue::Number(f64::NEG_INFINITY)),
"-Infinity"
);
assert_eq!(format!("{}", JsValue::Number(f64::NAN)), "NaN");
}
#[test]
fn test_display_string() {
assert_eq!(format!("{}", JsValue::from("hello")), "hello");
assert_eq!(format!("{}", JsValue::from("")), "");
assert_eq!(format!("{}", JsValue::from("with spaces")), "with spaces");
}
#[test]
fn test_runtime_value_is_number() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "42", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_number());
assert!(!rv.is_string());
assert!(!rv.is_undefined());
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_runtime_value_as_number() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "42", None).unwrap();
if let StepResult::Complete(rv) = result {
assert_eq!(rv.as_number(), Some(42.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_runtime_value_is_string() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "'hello'", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_string());
assert_eq!(rv.as_str(), Some("hello"));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_runtime_value_is_boolean() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "true", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_boolean());
assert_eq!(rv.as_bool(), Some(true));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_runtime_value_is_undefined() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "undefined", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_undefined());
assert!(rv.is_nullish());
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_runtime_value_is_null() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "null", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_null());
assert!(rv.is_nullish());
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_runtime_value_type_name() {
let mut runtime = create_test_runtime();
let mut check = |code: &str, expected: &str| {
let result = run(&mut runtime, code, None).unwrap();
if let StepResult::Complete(rv) = result {
assert_eq!(rv.type_name(), expected, "for code: {}", code);
} else {
panic!("Expected Complete for: {}", code);
}
};
check("undefined", "undefined");
check("null", "null");
check("true", "boolean");
check("42", "number");
check("'hello'", "string");
check("({})", "object"); check("[]", "object");
}
#[test]
fn test_runtime_value_display() {
let mut runtime = create_test_runtime();
let mut check = |code: &str, expected: &str| {
let result = run(&mut runtime, code, None).unwrap();
if let StepResult::Complete(rv) = result {
assert_eq!(format!("{}", rv), expected, "for code: {}", code);
} else {
panic!("Expected Complete for: {}", code);
}
};
check("undefined", "undefined");
check("null", "null");
check("true", "true");
check("false", "false");
check("42", "42");
check("3.14", "3.14");
check("'hello'", "hello");
check("({})", "[object Object]"); check("[]", "[object Object]");
}
#[test]
fn test_runtime_value_is_object() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "({})", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_object());
assert!(!rv.is_number());
assert!(!rv.is_string());
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_display_large_numbers() {
assert_eq!(format!("{}", JsValue::Number(1e21)), "1e+21");
assert_eq!(
format!("{}", JsValue::Number(999999999999999999999.0)),
"1e+21"
);
}
#[test]
fn test_display_small_numbers() {
assert_eq!(format!("{}", JsValue::Number(1e-7)), "1e-7");
}
#[test]
fn test_from_integer_precision() {
let large: i64 = 9007199254740993; let value = JsValue::from(large);
assert!(value.is_number());
}
#[test]
fn test_object_property_access() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "({ name: 'Alice', age: 30 })", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_object());
assert_eq!(rv.type_name(), "object");
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
let name = borrowed.get_property(&name_key);
assert!(name.is_some());
assert_eq!(name.unwrap().as_str(), Some("Alice"));
let age_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("age"));
let age = borrowed.get_property(&age_key);
assert!(age.is_some());
assert_eq!(age.unwrap().as_number(), Some(30.0));
} else {
panic!("Expected object");
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_nested_object() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"({ user: { name: 'Bob', settings: { theme: 'dark' } } })",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_object());
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
let user_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("user"));
let user = borrowed.get_property(&user_key);
assert!(user.is_some());
let user_val = user.unwrap();
assert!(user_val.is_object());
if let Some(user_obj) = user_val.as_object() {
let user_borrowed = user_obj.borrow();
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
let name = user_borrowed.get_property(&name_key);
assert_eq!(name.unwrap().as_str(), Some("Bob"));
let settings_key =
tsrun::value::PropertyKey::String(tsrun::JsString::from("settings"));
let settings = user_borrowed.get_property(&settings_key);
assert!(settings.is_some());
if let Some(settings_obj) = settings.unwrap().as_object() {
let settings_borrowed = settings_obj.borrow();
let theme_key =
tsrun::value::PropertyKey::String(tsrun::JsString::from("theme"));
let theme = settings_borrowed.get_property(&theme_key);
assert_eq!(theme.unwrap().as_str(), Some("dark"));
}
}
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_array_access() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "[1, 2, 3, 4, 5]", None).unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_object());
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
assert_eq!(borrowed.array_length(), Some(5));
if let Some(elements) = borrowed.array_elements() {
assert_eq!(elements.len(), 5);
assert_eq!(elements[0].as_number(), Some(1.0));
assert_eq!(elements[1].as_number(), Some(2.0));
assert_eq!(elements[2].as_number(), Some(3.0));
assert_eq!(elements[3].as_number(), Some(4.0));
assert_eq!(elements[4].as_number(), Some(5.0));
} else {
panic!("Expected array elements");
}
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_array_of_strings() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "['apple', 'banana', 'cherry']", None).unwrap();
if let StepResult::Complete(rv) = result {
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
if let Some(elements) = borrowed.array_elements() {
assert_eq!(elements.len(), 3);
assert_eq!(elements[0].as_str(), Some("apple"));
assert_eq!(elements[1].as_str(), Some("banana"));
assert_eq!(elements[2].as_str(), Some("cherry"));
}
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_array_of_objects() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"[{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
if let Some(elements) = borrowed.array_elements() {
assert_eq!(elements.len(), 2);
assert!(elements[0].is_object());
if let Some(first) = elements[0].as_object() {
let first_borrowed = first.borrow();
let id_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("id"));
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
assert_eq!(
first_borrowed.get_property(&id_key).unwrap().as_number(),
Some(1.0)
);
assert_eq!(
first_borrowed.get_property(&name_key).unwrap().as_str(),
Some("Alice")
);
}
if let Some(second) = elements[1].as_object() {
let second_borrowed = second.borrow();
let id_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("id"));
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
assert_eq!(
second_borrowed.get_property(&id_key).unwrap().as_number(),
Some(2.0)
);
assert_eq!(
second_borrowed.get_property(&name_key).unwrap().as_str(),
Some("Bob")
);
}
}
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_mixed_array() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"[1, 'two', true, null, undefined, { x: 3 }]",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
if let Some(elements) = borrowed.array_elements() {
assert_eq!(elements.len(), 6);
assert_eq!(elements[0].as_number(), Some(1.0));
assert_eq!(elements[1].as_str(), Some("two"));
assert_eq!(elements[2].as_bool(), Some(true));
assert!(elements[3].is_null());
assert!(elements[4].is_undefined());
assert!(elements[5].is_object());
}
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_function_result() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
function add(a, b) {
return a + b;
}
add(10, 20)
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_number());
assert_eq!(rv.as_number(), Some(30.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_function_returning_object() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
function createUser(name, age) {
return { name, age, active: true };
}
createUser('Charlie', 25)
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_object());
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
let age_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("age"));
let active_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("active"));
assert_eq!(
borrowed.get_property(&name_key).unwrap().as_str(),
Some("Charlie")
);
assert_eq!(
borrowed.get_property(&age_key).unwrap().as_number(),
Some(25.0)
);
assert_eq!(
borrowed.get_property(&active_key).unwrap().as_bool(),
Some(true)
);
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_class_instance() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return "Hello, " + this.name;
}
}
new Person('David', 35)
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_object());
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
let age_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("age"));
assert_eq!(
borrowed.get_property(&name_key).unwrap().as_str(),
Some("David")
);
assert_eq!(
borrowed.get_property(&age_key).unwrap().as_number(),
Some(35.0)
);
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_map_object() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const map = new Map();
map.set('key1', 'value1');
map.set('key2', 42);
map.size
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_number());
assert_eq!(rv.as_number(), Some(2.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_date_object() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const d = new Date(2024, 0, 15); // Jan 15, 2024
d.getFullYear()
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_number());
assert_eq!(rv.as_number(), Some(2024.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_json_parse_result() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const data = JSON.parse('{"name":"Eve","scores":[95,87,92]}');
data
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_object());
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
let name_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("name"));
assert_eq!(
borrowed.get_property(&name_key).unwrap().as_str(),
Some("Eve")
);
let scores_key = tsrun::value::PropertyKey::String(tsrun::JsString::from("scores"));
let scores = borrowed.get_property(&scores_key).unwrap();
assert!(scores.is_object());
if let Some(scores_arr) = scores.as_object() {
let scores_borrowed = scores_arr.borrow();
if let Some(elements) = scores_borrowed.array_elements() {
assert_eq!(elements.len(), 3);
assert_eq!(elements[0].as_number(), Some(95.0));
assert_eq!(elements[1].as_number(), Some(87.0));
assert_eq!(elements[2].as_number(), Some(92.0));
}
}
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_json_stringify() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"JSON.stringify({ a: 1, b: "hello", c: true })"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_string());
let s = rv.as_str().unwrap();
assert!(s.contains("\"a\":1"));
assert!(s.contains("\"b\":\"hello\""));
assert!(s.contains("\"c\":true"));
} else {
panic!("Expected Complete");
}
}
fn get_property(value: &JsValue, key: &str) -> Option<JsValue> {
if let Some(obj) = value.as_object() {
let borrowed = obj.borrow();
let prop_key = tsrun::value::PropertyKey::String(tsrun::JsString::from(key));
borrowed.get_property(&prop_key)
} else {
None
}
}
#[test]
fn test_helper_get_property() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "({ foo: 42, bar: 'baz' })", None).unwrap();
if let StepResult::Complete(rv) = result {
let foo = get_property(rv.value(), "foo");
assert_eq!(foo.unwrap().as_number(), Some(42.0));
let bar = get_property(rv.value(), "bar");
assert_eq!(bar.unwrap().as_str(), Some("baz"));
let missing = get_property(rv.value(), "missing");
assert!(missing.is_none());
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_deeply_nested_structure() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
({
level1: {
level2: {
level3: {
level4: {
value: "deep"
}
}
}
}
})
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let l1 = get_property(rv.value(), "level1").unwrap();
let l2 = get_property(&l1, "level2").unwrap();
let l3 = get_property(&l2, "level3").unwrap();
let l4 = get_property(&l3, "level4").unwrap();
let value = get_property(&l4, "value").unwrap();
assert_eq!(value.as_str(), Some("deep"));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_array_with_computed_values() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const arr = [];
for (let i = 0; i < 5; i++) {
arr.push(i * i);
}
arr
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
if let Some(elements) = borrowed.array_elements() {
assert_eq!(elements.len(), 5);
assert_eq!(elements[0].as_number(), Some(0.0)); assert_eq!(elements[1].as_number(), Some(1.0)); assert_eq!(elements[2].as_number(), Some(4.0)); assert_eq!(elements[3].as_number(), Some(9.0)); assert_eq!(elements[4].as_number(), Some(16.0)); }
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_object_with_methods() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const obj = {
value: 10,
double() {
return this.value * 2;
}
};
obj.double()
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
assert!(rv.is_number());
assert_eq!(rv.as_number(), Some(20.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_spread_operator_result() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const a = [1, 2];
const b = [3, 4];
[...a, ...b]
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
if let Some(obj) = rv.as_object() {
let borrowed = obj.borrow();
if let Some(elements) = borrowed.array_elements() {
assert_eq!(elements.len(), 4);
assert_eq!(elements[0].as_number(), Some(1.0));
assert_eq!(elements[1].as_number(), Some(2.0));
assert_eq!(elements[2].as_number(), Some(3.0));
assert_eq!(elements[3].as_number(), Some(4.0));
}
}
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_object_spread() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const a = { x: 1, y: 2 };
const b = { y: 3, z: 4 };
({ ...a, ...b })
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let x = get_property(rv.value(), "x");
let y = get_property(rv.value(), "y");
let z = get_property(rv.value(), "z");
assert_eq!(x.unwrap().as_number(), Some(1.0));
assert_eq!(y.unwrap().as_number(), Some(3.0)); assert_eq!(z.unwrap().as_number(), Some(4.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_api_get_property() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let user = api::create_from_json(
&mut runtime,
&guard,
&serde_json::json!({"name": "Alice", "age": 30}),
)
.unwrap();
let name_val = api::get_property(&user, "name").unwrap();
assert_eq!(name_val.as_str(), Some("Alice"));
let age_val = api::get_property(&user, "age").unwrap();
assert_eq!(age_val.as_number(), Some(30.0));
let missing = api::get_property(&user, "missing").unwrap();
assert!(missing.is_undefined());
}
#[test]
fn test_api_get_index() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr =
api::create_from_json(&mut runtime, &guard, &serde_json::json!([10, 20, 30])).unwrap();
assert_eq!(api::get_index(&arr, 0).unwrap().as_number(), Some(10.0));
assert_eq!(api::get_index(&arr, 1).unwrap().as_number(), Some(20.0));
assert_eq!(api::get_index(&arr, 2).unwrap().as_number(), Some(30.0));
assert!(api::get_index(&arr, 10).unwrap().is_undefined());
}
#[test]
fn test_api_len() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr =
api::create_from_json(&mut runtime, &guard, &serde_json::json!([1, 2, 3, 4, 5])).unwrap();
assert_eq!(api::len(&arr), Some(5));
let obj = api::create_from_json(&mut runtime, &guard, &serde_json::json!({"x": 1})).unwrap();
assert_eq!(api::len(&obj), None);
}
#[test]
fn test_api_is_empty() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let empty = api::create_array(&mut runtime, &guard).unwrap();
assert_eq!(api::is_empty(&empty), Some(true));
let non_empty = api::create_from_json(&mut runtime, &guard, &serde_json::json!([1])).unwrap();
assert_eq!(api::is_empty(&non_empty), Some(false));
}
#[test]
fn test_api_is_array() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr = api::create_from_json(&mut runtime, &guard, &serde_json::json!([1, 2, 3])).unwrap();
assert!(api::is_array(&arr));
let obj = api::create_from_json(&mut runtime, &guard, &serde_json::json!({"x": 1})).unwrap();
assert!(!api::is_array(&obj));
let num = api::create_value(42);
assert!(!api::is_array(&num));
}
#[test]
fn test_api_keys() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let obj = api::create_from_json(
&mut runtime,
&guard,
&serde_json::json!({"a": 1, "b": 2, "c": 3}),
)
.unwrap();
let keys = api::keys(&obj);
assert!(keys.contains(&"a".to_string()));
assert!(keys.contains(&"b".to_string()));
assert!(keys.contains(&"c".to_string()));
assert_eq!(keys.len(), 3);
}
#[test]
fn test_api_elements() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr =
api::create_from_json(&mut runtime, &guard, &serde_json::json!(["a", "b", "c"])).unwrap();
let elements = api::get_elements(&arr).unwrap();
assert_eq!(elements.len(), 3);
assert_eq!(elements[0].as_str(), Some("a"));
assert_eq!(elements[1].as_str(), Some("b"));
assert_eq!(elements[2].as_str(), Some("c"));
}
#[test]
fn test_set_property() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let obj = api::create_object(&mut runtime, &guard).unwrap();
api::set_property(&obj, "name", JsValue::from("Bob")).unwrap();
api::set_property(&obj, "age", JsValue::from(25)).unwrap();
let name = api::get_property(&obj, "name").unwrap();
assert_eq!(name.as_str(), Some("Bob"));
let age = api::get_property(&obj, "age").unwrap();
assert_eq!(age.as_number(), Some(25.0));
}
#[test]
fn test_set_index() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr = api::create_from_json(&mut runtime, &guard, &serde_json::json!([1, 2, 3])).unwrap();
api::set_index(&arr, 1, JsValue::from(20)).unwrap();
assert_eq!(api::get_index(&arr, 0).unwrap().as_number(), Some(1.0));
assert_eq!(api::get_index(&arr, 1).unwrap().as_number(), Some(20.0));
assert_eq!(api::get_index(&arr, 2).unwrap().as_number(), Some(3.0));
}
#[test]
fn test_set_index_extends() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr = api::create_array(&mut runtime, &guard).unwrap();
api::set_index(&arr, 2, JsValue::from(42)).unwrap();
assert_eq!(api::len(&arr), Some(3));
assert!(api::get_index(&arr, 0).unwrap().is_undefined());
assert!(api::get_index(&arr, 1).unwrap().is_undefined());
assert_eq!(api::get_index(&arr, 2).unwrap().as_number(), Some(42.0));
}
#[test]
fn test_push() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr = api::create_array(&mut runtime, &guard).unwrap();
api::push(&arr, JsValue::from(1)).unwrap();
api::push(&arr, JsValue::from(2)).unwrap();
api::push(&arr, JsValue::from(3)).unwrap();
assert_eq!(api::len(&arr), Some(3));
assert_eq!(api::get_index(&arr, 0).unwrap().as_number(), Some(1.0));
assert_eq!(api::get_index(&arr, 1).unwrap().as_number(), Some(2.0));
assert_eq!(api::get_index(&arr, 2).unwrap().as_number(), Some(3.0));
}
#[test]
fn test_push_mixed_types() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr = api::create_array(&mut runtime, &guard).unwrap();
api::push(&arr, JsValue::from("hello")).unwrap();
api::push(&arr, JsValue::from(42)).unwrap();
api::push(&arr, JsValue::from(true)).unwrap();
let elements = api::get_elements(&arr).unwrap();
assert_eq!(elements[0].as_str(), Some("hello"));
assert_eq!(elements[1].as_number(), Some(42.0));
assert_eq!(elements[2].as_bool(), Some(true));
}
#[test]
fn test_call_method_join() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr =
api::create_from_json(&mut runtime, &guard, &serde_json::json!(["a", "b", "c"])).unwrap();
let result =
api::call_method(&mut runtime, &guard, &arr, "join", &[JsValue::from("-")]).unwrap();
assert_eq!(result.as_str(), Some("a-b-c"));
}
#[test]
fn test_call_method_push() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr = api::create_from_json(&mut runtime, &guard, &serde_json::json!([1, 2])).unwrap();
let result = api::call_method(&mut runtime, &guard, &arr, "push", &[JsValue::from(3)]).unwrap();
assert_eq!(result.as_number(), Some(3.0));
assert_eq!(api::len(&arr), Some(3));
}
#[test]
fn test_call_method_tostring() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let arr = api::create_from_json(&mut runtime, &guard, &serde_json::json!([1, 2, 3])).unwrap();
let result = api::call_method(&mut runtime, &guard, &arr, "toString", &[]).unwrap();
assert_eq!(result.as_str(), Some("1,2,3"));
}
#[test]
fn test_call_method_map() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
const arr = [1, 2, 3];
arr.map(x => x * 2)
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let elements = api::get_elements(rv.value()).unwrap();
assert_eq!(elements[0].as_number(), Some(2.0));
assert_eq!(elements[1].as_number(), Some(4.0));
assert_eq!(elements[2].as_number(), Some(6.0));
}
}
#[test]
fn test_call_function() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let result = run(
&mut runtime,
"function add(a, b) { return a + b; } add",
None,
)
.unwrap();
if let StepResult::Complete(add_fn) = result {
let sum = api::call_function(
&mut runtime,
&guard,
add_fn.value(),
None,
&[JsValue::from(10), JsValue::from(20)],
)
.unwrap();
assert_eq!(sum.as_number(), Some(30.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_call_function_with_this() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let result = run(
&mut runtime,
"function getX() { return this.x; } getX",
None,
)
.unwrap();
if let StepResult::Complete(get_x) = result {
let obj =
api::create_from_json(&mut runtime, &guard, &serde_json::json!({"x": 42})).unwrap();
let x = api::call_function(&mut runtime, &guard, get_x.value(), Some(&obj), &[]).unwrap();
assert_eq!(x.as_number(), Some(42.0));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_call_method_error_on_missing() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let obj = api::create_object(&mut runtime, &guard).unwrap();
let result = api::call_method(&mut runtime, &guard, &obj, "nonexistent", &[]);
assert!(result.is_err());
}
#[test]
fn test_call_function_error_on_non_function() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let num = api::create_value(42);
let result = api::call_function(&mut runtime, &guard, &num, None, &[]);
assert!(result.is_err());
}
#[test]
fn test_guard_value() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let obj = api::create_from_json(
&mut runtime,
&guard,
&serde_json::json!({"nested": {"value": 42}}),
)
.unwrap();
let nested = api::get_property(&obj, "nested").unwrap();
api::guard_value(&guard, &nested);
let value = api::get_property(&nested, "value").unwrap();
assert_eq!(value.as_number(), Some(42.0));
}
#[test]
fn test_get_export_const() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
export const VERSION = "1.0.0";
export const COUNT = 42;
"#,
Some("/main.ts"),
)
.unwrap();
match result {
StepResult::Complete(_) => {
let version = api::get_export(&runtime, "VERSION");
assert!(version.is_some());
assert_eq!(version.unwrap().as_str(), Some("1.0.0"));
let count = api::get_export(&runtime, "COUNT");
assert!(count.is_some());
assert_eq!(count.unwrap().as_number(), Some(42.0));
let missing = api::get_export(&runtime, "MISSING");
assert!(missing.is_none());
}
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_get_export_function() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
export function add(a: number, b: number): number {
return a + b;
}
"#,
Some("/main.ts"),
)
.unwrap();
match result {
StepResult::Complete(_) => {
let guard = api::create_guard(&runtime);
let add_fn = api::get_export(&runtime, "add");
assert!(add_fn.is_some());
let add_fn = add_fn.unwrap();
assert!(add_fn.is_callable());
let result =
api::call_function(&mut runtime, &guard, &add_fn, None, &[10.into(), 32.into()])
.unwrap();
assert_eq!(result.as_number(), Some(42.0));
}
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_get_export_object() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
export interface Processor {
process: (x: number) => number;
}
export const processor: Processor = {
process: function(x: number): number {
return x * 2;
}
};
"#,
Some("/main.ts"),
)
.unwrap();
match result {
StepResult::Complete(_) => {
let guard = api::create_guard(&runtime);
let processor = api::get_export(&runtime, "processor");
assert!(processor.is_some());
let processor = processor.unwrap();
api::guard_value(&guard, &processor);
let result =
api::call_method(&mut runtime, &guard, &processor, "process", &[21.into()])
.unwrap();
assert_eq!(result.as_number(), Some(42.0));
}
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_get_export_class() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
export class Calculator {
value: number;
constructor(initial: number) {
this.value = initial;
}
add(x: number): number {
this.value += x;
return this.value;
}
}
"#,
Some("/main.ts"),
)
.unwrap();
match result {
StepResult::Complete(_) => {
let guard = api::create_guard(&runtime);
let calculator_class = api::get_export(&runtime, "Calculator");
assert!(calculator_class.is_some());
let calculator_class = calculator_class.unwrap();
api::guard_value(&guard, &calculator_class);
assert!(calculator_class.is_callable());
}
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_get_export_names() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
export const a = 1;
export const b = 2;
export function c() {}
export class D {}
"#,
Some("/main.ts"),
)
.unwrap();
match result {
StepResult::Complete(_) => {
let exports = api::get_export_names(&runtime);
assert!(exports.contains(&"a".to_string()));
assert!(exports.contains(&"b".to_string()));
assert!(exports.contains(&"c".to_string()));
assert!(exports.contains(&"D".to_string()));
assert_eq!(exports.len(), 4);
}
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_get_export_no_module() {
let interp = create_test_runtime();
let result = api::get_export(&interp, "anything");
assert!(result.is_none());
let names = api::get_export_names(&interp);
assert!(names.is_empty());
}
#[test]
fn test_get_export_with_default() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
export default function greet(name: string): string {
return "Hello, " + name + "!";
}
"#,
Some("/main.ts"),
)
.unwrap();
match result {
StepResult::Complete(_) => {
let guard = api::create_guard(&runtime);
let greet_fn = api::get_export(&runtime, "default");
assert!(greet_fn.is_some());
let greet_fn = greet_fn.unwrap();
let result = api::call_function(
&mut runtime,
&guard,
&greet_fn,
None,
&[JsValue::from("World")],
)
.unwrap();
assert_eq!(result.as_str(), Some("Hello, World!"));
}
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_get_export_live_binding() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
export let counter = 0;
export function increment(): void {
counter++;
}
"#,
Some("/main.ts"),
)
.unwrap();
match result {
StepResult::Complete(_) => {
let guard = api::create_guard(&runtime);
let counter = api::get_export(&runtime, "counter");
assert_eq!(counter.unwrap().as_number(), Some(0.0));
let increment_fn = api::get_export(&runtime, "increment").unwrap();
api::call_function(&mut runtime, &guard, &increment_fn, None, &[]).unwrap();
let counter = api::get_export(&runtime, "counter");
assert_eq!(counter.unwrap().as_number(), Some(1.0));
api::call_function(&mut runtime, &guard, &increment_fn, None, &[]).unwrap();
let counter = api::get_export(&runtime, "counter");
assert_eq!(counter.unwrap().as_number(), Some(2.0));
}
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_prepare_call_sync_function() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export function add(a: number, b: number): number {
return a + b;
}
"#,
Some("/main.ts"),
)
.unwrap();
let add_fn = api::get_export(&runtime, "add").unwrap();
api::prepare_call(
&mut runtime,
&add_fn,
None,
&[JsValue::from(10), JsValue::from(20)],
)
.unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => {
assert_eq!(rv.as_number(), Some(30.0));
}
_ => panic!("Expected Complete, got {:?}", result),
}
}
#[test]
fn test_prepare_call_async_function() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export async function greet(name: string): Promise<string> {
return `Hello, ${name}!`;
}
"#,
Some("/main.ts"),
)
.unwrap();
let greet_fn = api::get_export(&runtime, "greet").unwrap();
api::prepare_call(&mut runtime, &greet_fn, None, &[JsValue::from("World")]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => {
assert_eq!(rv.as_str(), Some("Hello, World!"));
}
_ => panic!("Expected Complete, got {:?}", result),
}
}
#[test]
fn test_prepare_call_with_this() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export function getValue(): number {
return this.value;
}
"#,
Some("/main.ts"),
)
.unwrap();
let guard = api::create_guard(&runtime);
let get_value_fn = api::get_export(&runtime, "getValue").unwrap();
let this_obj =
api::create_from_json(&mut runtime, &guard, &serde_json::json!({"value": 42})).unwrap();
api::prepare_call(&mut runtime, &get_value_fn, Some(&this_obj), &[]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => {
assert_eq!(rv.as_number(), Some(42.0));
}
_ => panic!("Expected Complete, got {:?}", result),
}
}
#[test]
fn test_prepare_call_error_not_callable() {
let mut runtime = create_test_runtime();
let num = JsValue::from(42);
let result = api::prepare_call(&mut runtime, &num, None, &[]);
assert!(result.is_err());
}
#[test]
fn test_prepare_call_thrown_error() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export function throwError(): void {
throw new Error("Test error");
}
"#,
Some("/main.ts"),
)
.unwrap();
let throw_fn = api::get_export(&runtime, "throwError").unwrap();
api::prepare_call(&mut runtime, &throw_fn, None, &[]).unwrap();
let result = run_to_completion(&mut runtime);
assert!(result.is_err());
let error = result.unwrap_err();
assert!(format!("{:?}", error).contains("Test error"));
}
#[test]
fn test_prepare_call_with_orders() {
use tsrun::{InterpreterConfig, create_eval_internal_module};
let config = InterpreterConfig {
internal_modules: vec![create_eval_internal_module()],
..Default::default()
};
let mut runtime = tsrun::Interpreter::with_config(config);
let gc_threshold = std::env::var("GC_THRESHOLD")
.ok()
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or(1);
runtime.set_gc_threshold(gc_threshold);
run(
&mut runtime,
r#"
import { order } from "tsrun:host";
export async function fetchData(url: string): Promise<string> {
const result = await order({ type: "fetch", url });
return result.data;
}
"#,
Some("/main.ts"),
)
.unwrap();
let fetch_fn = api::get_export(&runtime, "fetchData").unwrap();
api::prepare_call(&mut runtime, &fetch_fn, None, &[JsValue::from("/api")]).unwrap();
loop {
match runtime.step().unwrap() {
StepResult::Continue => continue,
StepResult::Suspended { pending, .. } => {
assert_eq!(pending.len(), 1);
let payload = &pending[0].payload;
assert!(payload.is_object());
let response = api::create_response_object(
&mut runtime,
&serde_json::json!({"data": "fetched"}),
)
.unwrap();
runtime.fulfill_orders(vec![OrderResponse {
id: pending[0].id,
result: Ok(response),
}]);
}
StepResult::Complete(rv) => {
assert_eq!(rv.as_str(), Some("fetched"));
break;
}
other => panic!("Unexpected step result: {:?}", other),
}
}
}
#[test]
fn test_prepare_call_multiple_args() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export function sum(a: number, b: number, c: number, d: number): number {
return a + b + c + d;
}
"#,
Some("/main.ts"),
)
.unwrap();
let sum_fn = api::get_export(&runtime, "sum").unwrap();
api::prepare_call(
&mut runtime,
&sum_fn,
None,
&[
JsValue::from(1),
JsValue::from(2),
JsValue::from(3),
JsValue::from(4),
],
)
.unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => {
assert_eq!(rv.as_number(), Some(10.0));
}
_ => panic!("Expected Complete, got {:?}", result),
}
}
#[test]
fn test_prepare_call_no_args() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export function getConstant(): number {
return 42;
}
"#,
Some("/main.ts"),
)
.unwrap();
let get_constant_fn = api::get_export(&runtime, "getConstant").unwrap();
api::prepare_call(&mut runtime, &get_constant_fn, None, &[]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => {
assert_eq!(rv.as_number(), Some(42.0));
}
_ => panic!("Expected Complete, got {:?}", result),
}
}
#[test]
fn test_prepare_call_same_function_multiple_times() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
let counter = 0;
export function increment(): number {
counter += 1;
return counter;
}
"#,
Some("/main.ts"),
)
.unwrap();
let increment_fn = api::get_export(&runtime, "increment").unwrap();
for expected in 1..=5 {
api::prepare_call(&mut runtime, &increment_fn, None, &[]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => {
assert_eq!(rv.as_number(), Some(expected as f64));
}
_ => panic!("Expected Complete, got {:?}", result),
}
}
}
#[test]
fn test_prepare_call_different_functions() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export function add(a: number, b: number): number {
return a + b;
}
export function multiply(a: number, b: number): number {
return a * b;
}
export function greet(name: string): string {
return `Hello, ${name}!`;
}
"#,
Some("/main.ts"),
)
.unwrap();
let add_fn = api::get_export(&runtime, "add").unwrap();
let multiply_fn = api::get_export(&runtime, "multiply").unwrap();
let greet_fn = api::get_export(&runtime, "greet").unwrap();
api::prepare_call(
&mut runtime,
&add_fn,
None,
&[JsValue::from(10), JsValue::from(20)],
)
.unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_number(), Some(30.0)),
_ => panic!("Expected Complete"),
}
api::prepare_call(
&mut runtime,
&multiply_fn,
None,
&[JsValue::from(5), JsValue::from(6)],
)
.unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_number(), Some(30.0)),
_ => panic!("Expected Complete"),
}
api::prepare_call(&mut runtime, &greet_fn, None, &[JsValue::from("World")]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_str(), Some("Hello, World!")),
_ => panic!("Expected Complete"),
}
api::prepare_call(
&mut runtime,
&add_fn,
None,
&[JsValue::from(100), JsValue::from(200)],
)
.unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_number(), Some(300.0)),
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_prepare_call_async_functions_multiple_times() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
let callCount = 0;
export async function asyncIncrement(): Promise<number> {
callCount += 1;
return callCount;
}
"#,
Some("/main.ts"),
)
.unwrap();
let async_fn = api::get_export(&runtime, "asyncIncrement").unwrap();
for expected in 1..=3 {
api::prepare_call(&mut runtime, &async_fn, None, &[]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => {
assert_eq!(rv.as_number(), Some(expected as f64));
}
_ => panic!("Expected Complete, got {:?}", result),
}
}
}
#[test]
fn test_prepare_call_interleaved_sync_and_async() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
let counter = 0;
export function syncAdd(x: number): number {
counter += x;
return counter;
}
export async function asyncAdd(x: number): Promise<number> {
counter += x;
return counter;
}
"#,
Some("/main.ts"),
)
.unwrap();
let sync_fn = api::get_export(&runtime, "syncAdd").unwrap();
let async_fn = api::get_export(&runtime, "asyncAdd").unwrap();
api::prepare_call(&mut runtime, &sync_fn, None, &[JsValue::from(1)]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_number(), Some(1.0)),
_ => panic!("Expected Complete"),
}
api::prepare_call(&mut runtime, &async_fn, None, &[JsValue::from(10)]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_number(), Some(11.0)),
_ => panic!("Expected Complete"),
}
api::prepare_call(&mut runtime, &sync_fn, None, &[JsValue::from(100)]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_number(), Some(111.0)),
_ => panic!("Expected Complete"),
}
api::prepare_call(&mut runtime, &async_fn, None, &[JsValue::from(1000)]).unwrap();
let result = run_to_completion(&mut runtime).unwrap();
match result {
StepResult::Complete(rv) => assert_eq!(rv.as_number(), Some(1111.0)),
_ => panic!("Expected Complete"),
}
}
#[test]
fn test_get_function_param_names_regular() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"function add(a, b, c) { return a + b + c; } add",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(
params,
Some(vec!["a".to_string(), "b".to_string(), "c".to_string()])
);
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_arrow() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "const fn = (x, y) => x + y; fn", None).unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, Some(vec!["x".to_string(), "y".to_string()]));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_no_params() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"function noArgs() { return 42; } noArgs",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, Some(vec![]));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_async() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"async function fetch(url, options) { return url; } fetch",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, Some(vec!["url".to_string(), "options".to_string()]));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_generator() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"function* gen(start, end) { yield start; } gen",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, Some(vec!["start".to_string(), "end".to_string()]));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_async_generator() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"async function* asyncGen(items) { for (const item of items) yield item; } asyncGen",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, Some(vec!["items".to_string()]));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_native() {
let mut runtime = create_test_runtime();
let result = run(&mut runtime, "[].push", None).unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, None);
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_bound() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"function greet(name, greeting) { return greeting + ' ' + name; } greet.bind(null, 'World')",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(
params,
Some(vec!["name".to_string(), "greeting".to_string()])
);
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_non_function() {
let num = JsValue::from(42);
assert_eq!(api::get_function_param_names(&num), None);
let s = JsValue::from("hello");
assert_eq!(api::get_function_param_names(&s), None);
assert_eq!(api::get_function_param_names(&JsValue::Null), None);
assert_eq!(api::get_function_param_names(&JsValue::Undefined), None);
}
#[test]
fn test_get_function_param_names_non_function_object() {
let mut runtime = create_test_runtime();
let guard = api::create_guard(&runtime);
let obj = api::create_from_json(&mut runtime, &guard, &serde_json::json!({"x": 1})).unwrap();
assert_eq!(api::get_function_param_names(&obj), None);
}
#[test]
fn test_get_function_param_names_typescript_types() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"function typed(name: string, age: number, active: boolean): string { return name; } typed",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(
params,
Some(vec![
"name".to_string(),
"age".to_string(),
"active".to_string()
])
);
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_default_params() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"function withDefaults(a, b = 10, c = 'hello') { return a; } withDefaults",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(
params,
Some(vec!["a".to_string(), "b".to_string(), "c".to_string()])
);
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_rest_param() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
"function withRest(first, ...rest) { return first; } withRest",
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, Some(vec!["first".to_string(), "rest".to_string()]));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_class_method() {
let mut runtime = create_test_runtime();
let result = run(
&mut runtime,
r#"
class Calculator {
add(a, b) { return a + b; }
}
const calc = new Calculator();
calc.add
"#,
None,
)
.unwrap();
if let StepResult::Complete(rv) = result {
let params = api::get_function_param_names(rv.value());
assert_eq!(params, Some(vec!["a".to_string(), "b".to_string()]));
} else {
panic!("Expected Complete");
}
}
#[test]
fn test_get_function_param_names_exported_function() {
let mut runtime = create_test_runtime();
run(
&mut runtime,
r#"
export function processData(input: string, options: object): string {
return input;
}
"#,
Some("/main.ts"),
)
.unwrap();
let func = api::get_export(&runtime, "processData").unwrap();
let params = api::get_function_param_names(&func);
assert_eq!(
params,
Some(vec!["input".to_string(), "options".to_string()])
);
}
#[test]
fn test_create_object_has_prototype() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let obj = api::create_object(&mut interp, &guard).unwrap();
let result = api::call_method(
&mut interp,
&guard,
&obj,
"hasOwnProperty",
&[JsValue::from("x")],
)
.unwrap();
assert_eq!(result.as_bool(), Some(false));
}
#[test]
fn test_create_array_has_prototype() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let arr = api::create_array(&mut interp, &guard).unwrap();
assert_eq!(api::len(&arr), Some(0));
api::push(&arr, JsValue::from(42)).unwrap();
assert_eq!(api::len(&arr), Some(1));
assert_eq!(api::get_index(&arr, 0).unwrap().as_number(), Some(42.0));
}
#[test]
fn test_create_object_with_capacity() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let obj = api::create_object_with_capacity(&mut interp, &guard, 4).unwrap();
api::set_property(&obj, "a", JsValue::from(1)).unwrap();
api::set_property(&obj, "b", JsValue::from(2)).unwrap();
api::set_property(&obj, "c", JsValue::from(3)).unwrap();
api::set_property(&obj, "d", JsValue::from(4)).unwrap();
assert_eq!(api::get_property(&obj, "a").unwrap().as_number(), Some(1.0));
assert_eq!(api::get_property(&obj, "d").unwrap().as_number(), Some(4.0));
}
#[test]
fn test_create_array_from() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let elements = vec![JsValue::from(10), JsValue::from(20), JsValue::from(30)];
let arr = api::create_array_from(&mut interp, &guard, elements).unwrap();
assert_eq!(api::len(&arr), Some(3));
assert_eq!(api::get_index(&arr, 0).unwrap().as_number(), Some(10.0));
assert_eq!(api::get_index(&arr, 1).unwrap().as_number(), Some(20.0));
assert_eq!(api::get_index(&arr, 2).unwrap().as_number(), Some(30.0));
}
#[test]
fn test_get_f64() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let obj = api::create_object(&mut interp, &guard).unwrap();
api::set_property(&obj, "x", JsValue::from(42.5)).unwrap();
assert_eq!(api::get_f64(&obj, "x").unwrap(), 42.5);
assert!(api::get_f64(&obj, "missing").is_err());
api::set_property(&obj, "name", JsValue::from("hello")).unwrap();
assert!(api::get_f64(&obj, "name").is_err());
}
#[test]
fn test_get_bool() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let obj = api::create_object(&mut interp, &guard).unwrap();
api::set_property(&obj, "active", JsValue::from(true)).unwrap();
assert_eq!(api::get_bool(&obj, "active").unwrap(), true);
assert!(api::get_bool(&obj, "missing").is_err());
api::set_property(&obj, "count", JsValue::from(1)).unwrap();
assert!(api::get_bool(&obj, "count").is_err());
}
#[test]
fn test_get_i32() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let obj = api::create_object(&mut interp, &guard).unwrap();
api::set_property(&obj, "count", JsValue::from(7)).unwrap();
assert_eq!(api::get_i32(&obj, "count").unwrap(), 7);
assert!(api::get_i32(&obj, "missing").is_err());
}
#[test]
fn test_get_string() {
let mut interp = create_test_runtime();
let guard = api::create_guard(&interp);
let obj = api::create_object(&mut interp, &guard).unwrap();
api::set_property(&obj, "name", JsValue::from("Alice")).unwrap();
assert_eq!(api::get_string(&obj, "name").unwrap().as_str(), "Alice");
assert!(api::get_string(&obj, "missing").is_err());
api::set_property(&obj, "count", JsValue::from(1)).unwrap();
assert!(api::get_string(&obj, "count").is_err());
}
#[test]
fn test_run_to_completion_simple() {
let mut interp = create_test_runtime();
interp.prepare("1 + 2", None).unwrap();
let result = interp.run_to_completion().unwrap();
assert_eq!(result.as_number(), Some(3.0));
}
#[test]
fn test_run_to_completion_object() {
let mut interp = create_test_runtime();
interp.prepare("({ x: 42 })", None).unwrap();
let result = interp.run_to_completion().unwrap();
assert!(result.is_object());
}
#[test]
fn test_run_to_completion_with_exports() {
let mut interp = create_test_runtime();
interp
.prepare(
"export function add(a: number, b: number): number { return a + b; }",
Some(tsrun::ModulePath::new("test.ts")),
)
.unwrap();
let _result = interp.run_to_completion().unwrap();
let add_fn = api::get_export(&interp, "add").unwrap();
let guard = api::create_guard(&interp);
let result = api::call_function(
&mut interp,
&guard,
&add_fn,
None,
&[JsValue::from(3), JsValue::from(4)],
)
.unwrap();
assert_eq!(result.as_number(), Some(7.0));
}