use bao_engine::context::JsContext;
use bao_engine::value::JsValue;
fn eval_string(ctx: &mut JsContext, source: &str) -> String {
match ctx.eval(source, "<test>") {
Ok(JsValue::String(s)) => s,
Ok(JsValue::Number(n)) => format!("{}", n),
Ok(JsValue::Bool(b)) => if b { "true" } else { "false" }.to_string(),
_ => String::new(),
}
}
fn eval_number(ctx: &mut JsContext, source: &str) -> f64 {
match ctx.eval(source, "<test>") {
Ok(JsValue::Number(n)) => n,
_ => f64::NAN,
}
}
unsafe fn install_test_globals(
cx: &mut mozjs::context::JSContext,
global: mozjs::rust::Handle<*mut mozjs::jsapi::JSObject>,
) {
bao_engine::host_fn::install_console(cx, global);
}
#[test]
fn test_host_fn_all() {
let mut ctx = JsContext::for_test().expect("Failed to create JsContext");
ctx.set_global_setup(install_test_globals);
let result = eval_string(
&mut ctx,
r#"
var results = [];
console.time('test-timer');
console.timeEnd('test-timer');
results.push("timer_ok");
results.join("|")
"#,
);
assert!(result.contains("timer_ok"), "console timer should work");
let result = eval_string(
&mut ctx,
r#"
var results = [];
console.count('mycount');
console.count('mycount');
console.count('mycount');
console.countReset('mycount');
console.count('mycount');
results.push("count_ok");
results.join("|")
"#,
);
assert!(result.contains("count_ok"), "console count should work");
let result = eval_string(
&mut ctx,
r#"
console.assert(true, 'should not appear');
console.assert(false, 'should appear');
"assert_ok"
"#,
);
assert_eq!(result, "assert_ok");
let result = eval_string(
&mut ctx,
r#"
console.trace('trace-test');
"trace_ok"
"#,
);
assert_eq!(result, "trace_ok");
let result = eval_string(
&mut ctx,
r#"
console.dir({a: 1});
"dir_ok"
"#,
);
assert_eq!(result, "dir_ok");
let result = eval_string(
&mut ctx,
r#"
console.clear();
"clear_ok"
"#,
);
assert_eq!(result, "clear_ok");
let result = eval_string(
&mut ctx,
r#"
console.table([{name: 'a', val: 1}]);
"table_ok"
"#,
);
assert_eq!(result, "table_ok");
let result = eval_string(
&mut ctx,
r#"
var results = [];
results.push(typeof console.log);
results.push(typeof console.error);
results.push(typeof console.warn);
results.push(typeof console.info);
results.push(typeof console.debug);
results.push(typeof console.dir);
results.push(typeof console.time);
results.push(typeof console.timeEnd);
results.push(typeof console.trace);
results.push(typeof console.assert);
results.push(typeof console.clear);
results.push(typeof console.count);
results.push(typeof console.countReset);
results.push(typeof console.table);
results.join(",")
"#,
);
let parts: Vec<&str> = result.split(',').collect();
assert_eq!(parts.len(), 14, "should have 14 console methods");
for part in &parts {
assert_eq!(
*part, "function",
"console method should be function, got: {}",
part
);
}
let result = eval_string(
&mut ctx,
r#"
try {
throw new Error('test error message');
} catch(e) {
e.message
}
"#,
);
assert_eq!(result, "test error message");
let result = eval_string(
&mut ctx,
r#"
try {
null.foo;
} catch(e) {
e instanceof TypeError ? "TypeError" : e.message
}
"#,
);
assert_eq!(result, "TypeError");
let result = eval_string(
&mut ctx,
r#"
try {
eval("function(");
} catch(e) {
e instanceof SyntaxError ? "SyntaxError" : e.message
}
"#,
);
assert_eq!(result, "SyntaxError");
let result = eval_string(
&mut ctx,
r#"
try {
new Array(-1);
} catch(e) {
e instanceof RangeError ? "RangeError" : e.message
}
"#,
);
assert_eq!(result, "RangeError");
let result = eval_string(
&mut ctx,
r#"
typeof Promise.reject("err").catch(function(e){ return e; })
"#,
);
assert_eq!(result, "object");
let result = eval_string(
&mut ctx,
r#"
var result = "";
try {
result += "try ";
throw "err";
} catch(e) {
result += "catch ";
} finally {
result += "finally";
}
result
"#,
);
assert_eq!(result, "try catch finally");
let result = eval_string(
&mut ctx,
r#"
try {
throw new Error("stack test");
} catch(e) {
typeof e.stack
}
"#,
);
assert_eq!(result, "string", "Error.stack should be a string");
let result = eval_string(
&mut ctx,
r#"
class MyError extends Error {
constructor(msg) { super(msg); this.name = "MyError"; }
}
try { throw new MyError("custom"); }
catch(e) { e.name + ":" + e.message; }
"#,
);
assert_eq!(result, "MyError:custom");
assert_eq!(eval_number(&mut ctx, "((x) => x * 2)(21)"), 42.0);
let result = eval_string(
&mut ctx,
r#"
var {a, b} = {a: "hello", b: "world"};
a + " " + b
"#,
);
assert_eq!(result, "hello world");
assert_eq!(
eval_number(
&mut ctx,
"var arr = [1,2,3]; var [a,...rest] = arr; rest.length"
),
2.0
);
let result = eval_string(&mut ctx, "var x = 42; `value is ${x}`");
assert_eq!(result, "value is 42");
assert_eq!(
eval_number(
&mut ctx,
"var sum = 0; for (var x of [1,2,3,4,5]) sum += x; sum"
),
15.0
);
let result = eval_string(&mut ctx, "typeof Symbol('test')");
assert_eq!(result, "symbol");
let result = eval_string(
&mut ctx,
r#"
typeof new WeakMap() === 'object' && typeof new WeakSet() === 'object' ? "yes" : "no"
"#,
);
assert_eq!(result, "yes");
let result = eval_string(&mut ctx, "typeof Proxy");
assert_eq!(result, "function");
let result = eval_string(&mut ctx, "typeof Reflect");
assert_eq!(result, "object");
bao_engine::context::JsContext::shutdown_thread_sm();
}