use javascript::{Value, evaluate_script};
#[ctor::ctor]
fn __init_test_logger() {
let _ = env_logger::Builder::from_env(env_logger::Env::default()).is_test(true).try_init();
}
#[cfg(test)]
mod class_tests {
use super::*;
#[test]
fn test_simple_class_declaration() {
let script = r#"
class Person {
}
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
if let Err(e) = &result {
println!("Error: {:?}", e);
}
assert!(result.is_ok(), "Simple class declaration should work");
}
#[test]
fn test_class_new_simple() {
let script = r#"
class Person {
constructor() {
}
}
let person = new Person();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(val) => println!("Success: {:?}", val),
Err(e) => println!("Error: {:?}", e),
}
assert!(result.is_ok(), "Simple new expression should work");
}
#[test]
fn test_class_constructor_with_this() {
let script = r#"
class Person {
constructor(name) {
this.name = name;
}
}
let person = new Person("Alice");
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(val) => println!("Success: {:?}", val),
Err(e) => println!("Error: {:?}", e),
}
assert!(result.is_ok(), "Class constructor with this should work");
}
#[test]
fn test_class_method_call() {
let script = r#"
class Person {
constructor(name) {
this.name = name;
}
greet() {
return "Hello, " + this.name;
}
}
let person = new Person("Alice");
let greeting = person.greet();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(val) => println!("Success: {:?}", val),
Err(e) => println!("Error: {:?}", e),
}
assert!(result.is_ok(), "Class method call should work");
}
#[test]
fn test_is_class_instance() {
let script = r#"
class Person {
constructor(name) {
this.name = name;
}
}
let person = new Person("Alice");
let obj = {};
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
assert!(result.is_ok(), "Script should execute successfully");
}
#[test]
fn test_instanceof_operator() {
let script = r#"
class Person {
constructor(name) {
this.name = name;
}
}
class Animal {
constructor(type) {
this.type = type;
}
}
let person = new Person("Alice");
let animal = new Animal("Dog");
let obj = {};
let is_person_instance = person instanceof Person;
let is_animal_instance = animal instanceof Animal;
let is_person_animal = person instanceof Animal;
let is_obj_person = obj instanceof Person;
"is_person_instance: " + is_person_instance + "\n" +
"is_animal_instance: " + is_animal_instance + "\n" +
"is_person_animal: " + is_person_animal + "\n" +
"is_obj_person: " + is_obj_person;
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(val) => {
if let Value::String(s) = val {
let s = String::from_utf16_lossy(s);
println!("{}", s);
assert!(s.contains("is_person_instance: true"));
assert!(s.contains("is_animal_instance: true"));
assert!(s.contains("is_person_animal: false"));
assert!(s.contains("is_obj_person: false"));
} else {
println!("Unexpected result type: {:?}", val);
}
}
Err(e) => println!("Error: {:?}", e),
}
assert!(result.is_ok(), "instanceof operator should work");
}
#[test]
fn test_class_inheritance() {
let script = r#"
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " makes a sound";
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
return super.speak() + " - Woof!";
}
getBreed() {
return this.breed;
}
}
let dog = new Dog("Buddy", "Golden Retriever");
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
assert!(result.is_ok(), "Class inheritance should work");
}
#[test]
fn test_super_in_constructor() {
let script = r#"
class Parent {
constructor(value) {
this.value = value;
}
}
class Child extends Parent {
constructor(value, extra) {
super(value);
this.extra = extra;
}
}
let child = new Child("test", "more");
child.value + " " + child.extra;
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(val) => println!("Success: {:?}", val),
Err(e) => println!("Error: {:?}", e),
}
assert!(result.is_ok(), "super() in constructor should work");
}
#[test]
fn test_super_method_call() {
let script = r#"
class Calculator {
add(a, b) {
return a + b;
}
}
class AdvancedCalculator extends Calculator {
add(a, b) {
let base = super.add(a, b);
return base * 2;
}
}
let calc = new AdvancedCalculator();
calc.add(3, 4);
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(val) => println!("Success: {:?}", val),
Err(e) => println!("Error: {:?}", e),
}
assert!(result.is_ok(), "super.method() should work");
}
#[test]
fn test_static_members() {
let script = r#"
class Test {
static staticProp = "static value";
static staticMethod() {
return "static method result";
}
constructor(name) {
this.name = name;
}
}
let staticProp = Test.staticProp;
let staticResult = Test.staticMethod();
let instance = new Test("test");
let instanceName = instance.name;
staticProp + ", " + staticResult + ", " + instanceName;
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(val) => println!("Success: {:?}", val),
Err(e) => println!("Error: {:?}", e),
}
assert!(result.is_ok(), "Static property, method access and instance properties should work");
}
#[test]
fn test_super_missing_method_throws() {
let script = r#"
class P {}
class C extends P {
m() { return super.foo(); }
}
let c = new C();
c.m();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
assert!(result.is_err(), "Calling missing super method should throw an error");
}
#[test]
fn test_super_non_function_property_throws() {
let script = r#"
class P {}
P.prototype.foo = 5;
class C extends P { m() { return super.foo(); } }
let c = new C();
c.m();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
assert!(result.is_err(), "Calling a non-function super property should throw a TypeError");
}
#[test]
fn test_super_getter_property() {
let script = r#"
class P { get value() { return "parent"; } }
class C extends P { m() { return super.value; } }
new C().m();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(Value::String(s)) => {
let s = String::from_utf16_lossy(s);
assert_eq!(s, "parent");
}
Ok(v) => panic!("Unexpected result: {:?}", v),
Err(e) => panic!("Error: {:?}", e),
}
}
#[test]
fn test_super_deep_chain() {
let script = r#"
class A { toString() { return "A"; } }
class B extends A { toString() { return "B " + super.toString(); } }
class C extends B { toString() { return "C " + super.toString(); } }
let x = new C();
x.toString();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(Value::String(s)) => {
let s = String::from_utf16_lossy(s);
assert_eq!(s, "C B A");
}
Ok(v) => panic!("Unexpected result: {:?}", v),
Err(e) => panic!("Error: {:?}", e),
}
}
#[test]
fn test_super_in_arrow() {
let script = r#"
class P { m() { return "P"; } }
class C extends P { m() { let f = () => super.m(); return f(); } }
new C().m();
"#;
let result = evaluate_script(script, None::<&std::path::Path>);
match &result {
Ok(Value::String(s)) => {
let s = String::from_utf16_lossy(s);
assert_eq!(s, "P");
}
Ok(v) => panic!("Unexpected result: {:?}", v),
Err(e) => panic!("Error: {:?}", e),
}
}
}