use javascript::{Value, evaluate_script};
#[test]
fn test_prototype_assignment() {
let script = r#"
var proto = { inheritedProp: "inherited value" };
var obj = { ownProp: "own value" };
obj.__proto__ = proto;
obj
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
println!("Object after __proto__ assignment: {:?}", result);
match result {
Value::Object(obj) => {
let obj = obj.borrow();
assert!(obj.prototype.is_some());
if let Some(proto_rc) = &obj.prototype {
let proto = proto_rc.borrow();
assert!(proto.contains_key(&"inheritedProp".into()));
}
}
_ => panic!("Expected object"),
}
}
#[test]
fn test_prototype_chain_lookup() {
let script = r#"
var proto = { inheritedProp: "inherited value" };
var obj = { ownProp: "own value" };
obj.__proto__ = proto;
[obj.ownProp, obj.inheritedProp]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let own_prop = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
match own_prop {
Value::String(s) => {
let expected = "own value".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string for own property"),
}
let inherited_prop = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
match inherited_prop {
Value::String(s) => {
let expected = "inherited value".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string for inherited property"),
}
}
_ => panic!("Expected array"),
}
}
#[test]
fn test_multi_level_prototype_chain() {
let script = r#"
var grandparent = { grandparentProp: "grandparent value" };
var parent = { parentProp: "parent value" };
parent.__proto__ = grandparent;
var child = { childProp: "child value" };
child.__proto__ = parent;
[child.childProp, child.parentProp, child.grandparentProp]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let child_prop = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
match child_prop {
Value::String(s) => {
let expected = "child value".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string for child property"),
}
let parent_prop = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
match parent_prop {
Value::String(s) => {
let expected = "parent value".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string for parent property"),
}
let grandparent_prop = arr.borrow().get(&"2".into()).unwrap().borrow().clone();
match grandparent_prop {
Value::String(s) => {
let expected = "grandparent value".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string for grandparent property"),
}
}
_ => panic!("Expected array"),
}
}
#[test]
fn test_has_own_property_symbol_and_inherited() {
let script = r#"
var s = Symbol('x');
var proto = { inherited: 'yes' };
var obj = { own: 'ok' };
obj.__proto__ = proto;
obj[s] = 42;
[ obj.hasOwnProperty('own'), obj.hasOwnProperty('inherited'), obj.hasOwnProperty(s) ]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let a = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
let b = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
let c = arr.borrow().get(&"2".into()).unwrap().borrow().clone();
match (a, b, c) {
(Value::Boolean(true), Value::Boolean(false), Value::Boolean(true)) => {}
_ => panic!("Unexpected hasOwnProperty results"),
}
}
_ => panic!("Expected array"),
}
}
#[test]
fn test_is_prototype_of_and_property_is_enumerable() {
let script = r#"
var proto = { p: 1 };
var obj = Object.create(proto);
obj.q = 2;
[ proto.isPrototypeOf(obj), obj.isPrototypeOf(proto), obj.propertyIsEnumerable('q'), obj.propertyIsEnumerable('p') ]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let a = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
let b = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
let c = arr.borrow().get(&"2".into()).unwrap().borrow().clone();
let d = arr.borrow().get(&"3".into()).unwrap().borrow().clone();
match (a, b, c, d) {
(Value::Boolean(true), Value::Boolean(false), Value::Boolean(true), Value::Boolean(false)) => {}
_ => panic!("Unexpected prototype/propertyIsEnumerable results"),
}
}
_ => panic!("Expected array"),
}
}
#[test]
fn test_override_has_own_property() {
let script = r#"
var proto = { inherited: 'yes' };
var obj = { own: 'ok' };
obj.__proto__ = proto;
obj.hasOwnProperty = function(k) { return 'override-' + k; };
var keys = Object.keys(obj);
var descs = Object.getOwnPropertyDescriptors(obj);
[obj.hasOwnProperty === obj.__proto__.hasOwnProperty, keys, descs.hasOwnProperty ? 'own' : 'none', obj.hasOwnProperty('own')]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let eq = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
let keys_val = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
let desc_presence = arr.borrow().get(&"2".into()).unwrap().borrow().clone();
let call_res = arr.borrow().get(&"3".into()).unwrap().borrow().clone();
assert!(
matches!(eq, Value::Boolean(false)) || matches!(eq, Value::Number(n) if n == 0.0),
"unexpected eq value: {:?}",
eq
);
if let Value::Object(keys_arr) = keys_val {
let mut found = false;
for (_k, v) in keys_arr.borrow().properties.iter() {
if let Value::String(s) = &*v.borrow()
&& String::from_utf16_lossy(s) == "hasOwnProperty"
{
found = true;
break;
}
}
assert!(found, "hasOwnProperty not present in Object.keys(obj)");
} else {
panic!("Expected keys to be an object/array");
}
match desc_presence {
Value::String(s) => {
let expected = "own".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected descriptor presence to be 'own'"),
}
match call_res {
Value::String(s) => {
let expected = "override-own".encode_utf16().collect::<Vec<u16>>();
assert_eq!(s, expected);
}
_ => panic!("Expected string from overridden hasOwnProperty call"),
}
}
_ => panic!("Expected array result from test script"),
}
}
#[test]
fn test_to_string_default_and_tag() {
let script = r#"
var o1 = {};
var tag = Symbol.toStringTag;
var o2 = {};
o2[tag] = 'Custom';
[ o1.toString(), o2.toString() ]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let a = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
let b = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
match (a, b) {
(Value::String(sa), Value::String(sb)) => {
assert_eq!(String::from_utf16_lossy(&sa), "[object Object]");
assert_eq!(String::from_utf16_lossy(&sb), "[object Custom]");
}
_ => panic!("Expected strings from toString"),
}
}
_ => panic!("Expected array"),
}
}
#[test]
fn test_to_locale_string_defaults_and_override() {
let script = r#"
var o1 = {};
var tag = Symbol.toStringTag;
var o2 = {};
o2[tag] = 'Custom';
var o3 = {};
o3.toLocaleString = function() { return 'my-locale'; };
[ o1.toLocaleString(), o2.toLocaleString(), o3.toLocaleString() ]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let a = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
let b = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
let c = arr.borrow().get(&"2".into()).unwrap().borrow().clone();
match (a, b, c) {
(Value::String(sa), Value::String(sb), Value::String(sc)) => {
assert_eq!(String::from_utf16_lossy(&sa), "[object Object]");
assert_eq!(String::from_utf16_lossy(&sb), "[object Custom]");
assert_eq!(String::from_utf16_lossy(&sc), "my-locale");
}
_ => panic!("Expected strings from toLocaleString"),
}
}
_ => panic!("Expected array"),
}
}
#[test]
fn test_to_string_override_and_valueof() {
let script = r#"
var proto = {};
var o = { own: 1 };
o.__proto__ = proto;
o.toString = function() { return 'my-toString'; };
var v = o.valueOf();
[ o.toString(), proto.toString(), o === v ]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let t = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
let pt = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
let eq = arr.borrow().get(&"2".into()).unwrap().borrow().clone();
match (t, pt, eq) {
(Value::String(ts), Value::String(pts), Value::Boolean(b)) => {
assert_eq!(String::from_utf16_lossy(&ts), "my-toString");
assert_eq!(String::from_utf16_lossy(&pts), "[object Object]");
assert!(b);
}
_ => panic!("Unexpected types from toString/valueOf test"),
}
}
_ => panic!("Expected array"),
}
}
#[test]
fn test_class_instance_to_string_inherits_object_prototype() {
let script = r#"
class C {}
[ (new C()).toString(), Object.prototype.toString.call(new C()) ]
"#;
let result = evaluate_script(script, None::<&std::path::Path>).unwrap();
match result {
Value::Object(arr) => {
let a = arr.borrow().get(&"0".into()).unwrap().borrow().clone();
let b = arr.borrow().get(&"1".into()).unwrap().borrow().clone();
match (a, b) {
(Value::String(sa), Value::String(sb)) => {
assert_eq!(String::from_utf16_lossy(&sa), "[object Object]");
assert_eq!(String::from_utf16_lossy(&sb), "[object Object]");
}
_ => panic!("Expected strings from class instance toString tests"),
}
}
_ => panic!("Expected array"),
}
}