use javascript::*;
#[ctor::ctor(unsafe)]
fn __init_test_logger() {
let _ = env_logger::Builder::from_env(env_logger::Env::default()).is_test(true).try_init();
}
#[cfg(test)]
mod timeout_tests {
use super::*;
#[test]
fn test_set_timeout_basic() {
let script = r#"
new Promise((resolve) => {
let result = "not called";
setTimeout(() => {
result = "called";
resolve(result);
}, 0);
})
"#;
let result = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert_eq!(result, "\"called\"");
}
#[test]
fn test_set_timeout_with_args() {
let script = r#"
new Promise((resolve) => {
setTimeout((x, y) => {
resolve(x + y);
}, 0, 5, 10);
})
"#;
let result = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert_eq!(result, "15");
}
#[test]
fn test_set_timeout_returns_id() {
let script = r#"
let id = setTimeout(() => {}, 0);
typeof id === "number" && id >= 0
"#;
let result = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert_eq!(result, "true");
}
#[test]
fn test_clear_timeout() {
let script = r#"
new Promise((resolve) => {
let result = "not called";
let id = setTimeout(() => { result = "called"; }, 0);
clearTimeout(id);
// Wait a bit to ensure timeout doesn't fire
setTimeout(() => { resolve(result); }, 1);
})
"#;
let result = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert_eq!(result, "\"not called\"");
}
#[test]
fn test_multiple_set_timeout() {
let script = r#"
new Promise((resolve) => {
let results = [];
setTimeout(() => { results.push(1); }, 0);
setTimeout(() => { results.push(2); }, 0);
setTimeout(() => { results.push(3); }, 0);
setTimeout(() => {
resolve(results.length === 3 && results[0] === 1 && results[1] === 2 && results[2] === 3);
}, 1);
})
"#;
let result = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert_eq!(result, "true");
}
#[test]
fn test_set_timeout_with_function_reference() {
let script = r#"
new Promise((resolve) => {
let result = 0;
function increment() { result += 1; }
setTimeout(increment, 0);
setTimeout(increment, 0);
setTimeout(() => { resolve(result); }, 1);
})
"#;
let result = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert_eq!(result, "2");
}
#[test]
fn timeout_closure_handles_error_constructor() {
let script = r#"
// Ensure an async callback that rejects with `new Error(...)` can resolve Error
function doSomethingCritical(){ return new Promise((r)=>{ setTimeout(()=>r('crit'), 5); }); }
function doSomethingOptional(){ return new Promise((resolve,reject)=>{ setTimeout(()=>reject(new Error('optional failed')), 5); }); }
function doSomethingExtraNice(x){ return new Promise((r)=>{ setTimeout(()=>r('extra-'+x), 5); }); }
function moreCriticalStuff(){ return new Promise((r)=>{ setTimeout(()=>r('done'), 5); }); }
// Chain where optional rejects but is caught; ensure no ReferenceError occurs
doSomethingCritical()
.then((result) =>
doSomethingOptional()
.then((optionalResult) => doSomethingExtraNice(optionalResult))
.catch((e) => { /* swallow optional error */ }),
)
.then(() => moreCriticalStuff())
.catch((e) => { throw new Error('serious error: ' + e.message); });
"#;
let res = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert_eq!(res, "\"done\"");
}
#[test]
fn awaited_function_called_once() {
let script = r#"
let cnt = 0;
function doSomething(){ cnt += 1; return new Promise((r)=>setTimeout(()=>r(1), 0)); }
async function f(){ const x = await doSomething(); return x; }
f();
// Wait briefly to let microtasks / timers run
new Promise((resolve)=>setTimeout(()=>resolve(cnt), 10));
"#;
let res = evaluate_script(script, false, None::<&std::path::Path>);
match res {
Ok(value) => assert_eq!(value, "1"),
Err(e) => panic!("Script evaluation failed: {e:?}"),
}
}
}
#[cfg(test)]
mod interval_tests {
use super::*;
#[test]
fn test_set_interval() {
let script = r#"
let count = 0;
let id = setInterval(() => {
count++;
}, 10);
new Promise((resolve) => {
setTimeout(() => {
clearInterval(id);
resolve(count);
}, 50);
})
"#;
let result = evaluate_script(script, false, None::<&std::path::Path>).unwrap();
assert!(result.parse::<i32>().unwrap() > 0);
}
}