use crate::{
forward, forward_val,
object::FunctionBuilder,
property::{Attribute, PropertyDescriptor},
Context, JsString,
};
#[allow(clippy::float_cmp)]
#[test]
fn arguments_object() {
let mut context = Context::new();
let init = r#"
function jason(a, b) {
return arguments[0];
}
var val = jason(100, 6);
"#;
eprintln!("{}", forward(&mut context, init));
let return_val = forward_val(&mut context, "val").expect("value expected");
assert!(return_val.is_integer());
assert_eq!(
return_val
.to_i32(&mut context)
.expect("Could not convert value to i32"),
100
);
}
#[test]
fn self_mutating_function_when_calling() {
let mut context = Context::new();
let func = r#"
function x() {
x.y = 3;
}
x();
"#;
eprintln!("{}", forward(&mut context, func));
let y = forward_val(&mut context, "x.y").expect("value expected");
assert!(y.is_integer());
assert_eq!(
y.to_i32(&mut context)
.expect("Could not convert value to i32"),
3
);
}
#[test]
fn self_mutating_function_when_constructing() {
let mut context = Context::new();
let func = r#"
function x() {
x.y = 3;
}
new x();
"#;
eprintln!("{}", forward(&mut context, func));
let y = forward_val(&mut context, "x.y").expect("value expected");
assert!(y.is_integer());
assert_eq!(
y.to_i32(&mut context)
.expect("Could not convert value to i32"),
3
);
}
#[test]
fn call_function_prototype() {
let mut context = Context::new();
let func = r#"
Function.prototype()
"#;
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_undefined());
}
#[test]
fn call_function_prototype_with_arguments() {
let mut context = Context::new();
let func = r#"
Function.prototype(1, "", new String(""))
"#;
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_undefined());
}
#[test]
fn call_function_prototype_with_new() {
let mut context = Context::new();
let func = r#"
new Function.prototype()
"#;
let value = forward_val(&mut context, func);
assert!(value.is_err());
}
#[test]
fn function_prototype_name() {
let mut context = Context::new();
let func = r#"
Function.prototype.name
"#;
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_string());
assert!(value.as_string().unwrap().is_empty());
}
#[test]
#[allow(clippy::float_cmp)]
fn function_prototype_length() {
let mut context = Context::new();
let func = r#"
Function.prototype.length
"#;
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_number());
assert_eq!(value.as_number().unwrap(), 0.0);
}
#[test]
fn function_prototype_call() {
let mut context = Context::new();
let func = r#"
let e = new Error()
Object.prototype.toString.call(e)
"#;
let value = forward_val(&mut context, func).unwrap();
assert!(value.is_string());
assert_eq!(value.as_string().unwrap(), "[object Error]");
}
#[test]
fn function_prototype_call_throw() {
let mut context = Context::new();
let throw = r#"
let call = Function.prototype.call;
call(call)
"#;
let value = forward_val(&mut context, throw).unwrap_err();
assert!(value.is_object());
let string = value.to_string(&mut context).unwrap();
assert!(string.starts_with("TypeError"))
}
#[test]
fn function_prototype_call_multiple_args() {
let mut context = Context::new();
let init = r#"
function f(a, b) {
this.a = a;
this.b = b;
}
let o = {a: 0, b: 0};
f.call(o, 1, 2);
"#;
forward_val(&mut context, init).unwrap();
let boolean = forward_val(&mut context, "o.a == 1")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut context, "o.b == 2")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
}
#[test]
fn function_prototype_apply() {
let mut context = Context::new();
let init = r#"
const numbers = [6, 7, 3, 4, 2];
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
"#;
forward_val(&mut context, init).unwrap();
let boolean = forward_val(&mut context, "max == 7")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut context, "min == 2")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
}
#[test]
fn function_prototype_apply_on_object() {
let mut context = Context::new();
let init = r#"
function f(a, b) {
this.a = a;
this.b = b;
}
let o = {a: 0, b: 0};
f.apply(o, [1, 2]);
"#;
forward_val(&mut context, init).unwrap();
let boolean = forward_val(&mut context, "o.a == 1")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut context, "o.b == 2")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
}
#[test]
fn closure_capture_clone() {
let mut context = Context::new();
let string = JsString::from("Hello");
let object = context.construct_object();
object
.define_property_or_throw(
"key",
PropertyDescriptor::builder()
.value(" world!")
.writable(false)
.enumerable(false)
.configurable(false),
&mut context,
)
.unwrap();
let func = FunctionBuilder::closure_with_captures(
&mut context,
|_, _, context, captures| {
let (string, object) = &captures;
let hw = JsString::concat(
string,
object
.__get_own_property__(&"key".into(), context)?
.and_then(|prop| prop.value().cloned())
.and_then(|val| val.as_string().cloned())
.ok_or_else(|| context.construct_type_error("invalid `key` property"))?,
);
Ok(hw.into())
},
(string.clone(), object.clone()),
)
.name("closure")
.build();
context.register_global_property("closure", func, Attribute::default());
assert_eq!(forward(&mut context, "closure()"), "\"Hello world!\"");
}