use javascript::{Value, evaluate_script, obj_get_key_value};
#[ctor::ctor]
fn __init_test_logger() {
let _ = env_logger::Builder::from_env(env_logger::Env::default()).is_test(true).try_init();
}
#[cfg(test)]
mod promise_tests {
use super::*;
#[test]
fn test_promise_then_basic() {
let code = r#"
let result = null;
let p = new Promise(function(resolve, reject) {
resolve(100);
});
p.then(function(val) {
result = val * 2;
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_chaining() {
let code = r#"
let finalResult = null;
let p = new Promise(function(resolve, reject) {
resolve(10);
});
p.then(function(val) {
return val + 5;
}).then(function(val) {
return val * 2;
}).then(function(val) {
finalResult = val;
});
finalResult
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_all_resolved() {
let code = r#"
let result = null;
let p1 = new Promise(function(resolve, reject) {
resolve(1);
});
let p2 = new Promise(function(resolve, reject) {
resolve(2);
});
let p3 = new Promise(function(resolve, reject) {
resolve(3);
});
Promise.all([p1, p2, p3]).then(function(values) {
result = values[0] + values[1] + values[2];
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_race_resolved() {
let code = r#"
let result = null;
let p1 = new Promise(function(resolve, reject) {
resolve(1);
});
let p2 = new Promise(function(resolve, reject) {
resolve(2);
});
Promise.race([p1, p2]).then(function(value) {
result = value;
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_async_execution_order() {
let code = r#"
let executionOrder = [];
new Promise((resolve, reject) => {
let p = new Promise((res, rej) => res("async result"));
p.then((value) => {
executionOrder.push(value);
resolve(executionOrder);
});
});
executionOrder.push("sync");
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
match result {
Ok(Value::Object(arr)) => {
if let Ok(Some(length_val)) = obj_get_key_value(&arr, &"length".into()) {
if let Value::Number(len) = *length_val.borrow() {
assert_eq!(len, 2.0, "Array should have 2 elements");
if let Ok(Some(first_val)) = obj_get_key_value(&arr, &"0".into()) {
if let Value::String(first) = &*first_val.borrow() {
assert_eq!(String::from_utf16_lossy(first), "sync");
} else {
panic!("First element should be string 'sync'");
}
}
if let Ok(Some(second_val)) = obj_get_key_value(&arr, &"1".into()) {
if let Value::String(second) = &*second_val.borrow() {
assert_eq!(String::from_utf16_lossy(second), "async result");
} else {
panic!("Second element should be string 'async result'");
}
}
} else {
panic!("Array length should be a number");
}
} else {
panic!("Array should have length property");
}
}
_ => panic!("Expected array result, got {:?}", result),
}
}
#[test]
fn test_promise_finally() {
let code = r#"new Promise(function(resolve, reject) { resolve(42); }).finally(function() { console.log('finally executed'); })"#;
let result = evaluate_script(code, None::<&std::path::Path>);
match result {
Ok(Value::Number(42.0)) => {
}
_ => panic!("Test failed: {:?}", result),
}
}
#[test]
fn test_promise_allsettled_mixed() {
let code = r#"
let p1 = new Promise(function(resolve, reject) {
console.log("Resolving p1");
resolve(1);
});
let p2 = new Promise(function(resolve, reject) {
console.log("Rejecting p2");
reject("error");
});
let p3 = new Promise(function(resolve, reject) {
console.log("Resolving p3");
resolve(3);
});
Promise.allSettled([p1, p2, p3])
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
match result {
Ok(Value::Object(arr)) => {
if let Ok(Some(length_val)) = obj_get_key_value(&arr, &"length".into()) {
if let Value::Number(len) = *length_val.borrow() {
assert_eq!(len, 3.0, "Array should have 3 elements");
if let Ok(Some(first_val)) = obj_get_key_value(&arr, &"0".into())
&& let Value::Object(settled) = &*first_val.borrow()
{
if let Ok(Some(status_val)) = obj_get_key_value(settled, &"status".into())
&& let Value::String(status) = &*status_val.borrow()
{
assert_eq!(String::from_utf16_lossy(status), "fulfilled");
}
if let Ok(Some(value_val)) = obj_get_key_value(settled, &"value".into())
&& let Value::Number(val) = *value_val.borrow()
{
assert_eq!(val, 1.0);
}
}
if let Ok(Some(second_val)) = obj_get_key_value(&arr, &"1".into())
&& let Value::Object(settled) = &*second_val.borrow()
{
if let Ok(Some(status_val)) = obj_get_key_value(settled, &"status".into())
&& let Value::String(status) = &*status_val.borrow()
{
assert_eq!(String::from_utf16_lossy(status), "rejected");
}
if let Ok(Some(reason_val)) = obj_get_key_value(settled, &"reason".into())
&& let Value::String(reason) = &*reason_val.borrow()
{
assert_eq!(String::from_utf16_lossy(reason), "error");
}
}
if let Ok(Some(third_val)) = obj_get_key_value(&arr, &"2".into())
&& let Value::Object(settled) = &*third_val.borrow()
{
if let Ok(Some(status_val)) = obj_get_key_value(settled, &"status".into())
&& let Value::String(status) = &*status_val.borrow()
{
assert_eq!(String::from_utf16_lossy(status), "fulfilled");
}
if let Ok(Some(value_val)) = obj_get_key_value(settled, &"value".into())
&& let Value::Number(val) = *value_val.borrow()
{
assert_eq!(val, 3.0);
}
}
} else {
panic!("Array length should be a number");
}
} else {
panic!("Array should have length property");
}
}
_ => panic!("Expected array result, got {:?}", result),
}
}
#[test]
fn test_promise_constructor_direct_functionality() {
let code = r#"
let result = null;
new Promise(function(resolve, reject) {
resolve("direct test");
}).then(function(value) {
result = value;
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_then_direct_functionality() {
let code = r#"
let result = null;
let p = new Promise(function(resolve, reject) {
resolve(42);
});
p.then(function(value) {
result = value * 2;
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_catch_direct_functionality() {
let code = r#"
let result = null;
let p = new Promise(function(resolve, reject) {
reject("test error");
});
p.catch(function(reason) {
result = "caught: " + reason;
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_finally_direct_functionality() {
let code = r#"
let result = null;
new Promise(function(resolve, reject) {
resolve(100);
}).finally(function() {
result = "cleanup done";
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_resolve_reject_functions_direct() {
let code = r#"
let resolveResult = null;
let rejectResult = null;
new Promise(function(resolve, reject) {
resolve("resolved");
}).then(function(value) {
resolveResult = value;
});
new Promise(function(resolve, reject) {
reject("rejected");
}).catch(function(reason) {
rejectResult = reason;
});
[resolveResult, rejectResult]
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
assert!(result.is_ok());
}
#[test]
fn test_promise_constructor_with_arrow_function() {
let code = r#"
let result = null;
new Promise(resolve => resolve(42)).then(value => {
result = value * 2;
});
result
"#;
let result = evaluate_script(code, None::<&std::path::Path>);
match result {
Ok(_) => {
}
Err(e) => {
panic!("Arrow function in Promise constructor failed: {:?}", e);
}
}
}
}