jetcrab/
test_utils.rs

1//! # Test Utilities Module
2//!
3//! Provides testing utilities and test runner functionality for the JetCrab engine,
4//! enabling automated testing of JavaScript code execution and compliance.
5//!
6//! ## Overview
7//!
8//! The test utilities module includes:
9//!
10//! - **Test Results**: Tracking of test outcomes and statistics
11//! - **Test Runner**: Automated test execution engine
12//! - **Compliance Testing**: JavaScript specification compliance validation
13//! - **Result Reporting**: Detailed test result formatting
14//!
15//! ## Features
16//!
17//! - **Pass/Fail Tracking**: Count successful and failed tests
18//! - **Compliance Rate**: Calculate percentage of compliant features
19//! - **Batch Testing**: Run multiple tests in sequence
20//! - **Error Handling**: Graceful handling of test failures
21//!
22//! ## Usage
23//!
24//! ```rust
25//! use jetcrab::test_utils::{TestRunner, TestResult};
26//!
27//! let mut runner = TestRunner::new();
28//! let tests = vec![
29//!     ("addition", "2 + 2", "4"),
30//!     ("string", "'hello'", "hello"),
31//! ];
32//! let results = runner.run_tests(tests);
33//! println!("{}", results);
34//! ```
35
36use std::fmt;
37
38#[derive(Default)]
39pub struct TestResult {
40    pub passed: i32,
41    pub failed: i32,
42    pub skipped: i32,
43}
44
45impl TestResult {
46    pub fn new() -> Self {
47        Self::default()
48    }
49
50    pub fn add_passed(&mut self) {
51        self.passed += 1;
52    }
53
54    pub fn add_failed(&mut self) {
55        self.failed += 1;
56    }
57
58    pub fn add_skipped(&mut self) {
59        self.skipped += 1;
60    }
61
62    pub fn total(&self) -> i32 {
63        self.passed + self.failed + self.skipped
64    }
65
66    pub fn compliance_rate(&self) -> f64 {
67        if self.passed + self.failed > 0 {
68            (self.passed as f64 / (self.passed + self.failed) as f64) * 100.0
69        } else {
70            0.0
71        }
72    }
73}
74
75impl fmt::Display for TestResult {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        writeln!(f, "=== Final Results ===")?;
78        writeln!(f, "Passed: {}", self.passed)?;
79        writeln!(f, "Failed: {}", self.failed)?;
80        writeln!(f, "Skipped: {}", self.skipped)?;
81        writeln!(f, "Total: {}", self.total())?;
82        writeln!(f, "Compliance Rate: {:.1}%", self.compliance_rate())?;
83
84        if self.failed == 0 {
85            writeln!(f, "🎉 All implemented features are compliant!")?;
86        } else {
87            writeln!(f, "⚠️  Some features need improvements.")?;
88        }
89        Ok(())
90    }
91}
92
93#[derive(Default)]
94pub struct TestRunner {
95    pub engine: crate::api::Engine,
96}
97
98impl TestRunner {
99    pub fn new() -> Self {
100        Self::default()
101    }
102
103    pub fn run_test(&mut self, _test_name: &str, code: &str, expected: &str) -> TestResult {
104        let mut result = TestResult::new();
105
106        match self.engine.evaluate(code) {
107            Ok(eval_result) => {
108                let result_str = eval_result.to_string();
109                if result_str == expected {
110                    result.add_passed();
111                } else {
112                    result.add_failed();
113                }
114            }
115            Err(_) => {
116                result.add_skipped();
117            }
118        }
119
120        result
121    }
122
123    pub fn run_tests(&mut self, tests: Vec<(&str, &str, &str)>) -> TestResult {
124        let mut total_result = TestResult::new();
125
126        for (test_name, code, expected) in tests {
127            let test_result = self.run_test(test_name, code, expected);
128            total_result.passed += test_result.passed;
129            total_result.failed += test_result.failed;
130            total_result.skipped += test_result.skipped;
131        }
132
133        total_result
134    }
135}
136
137#[derive(Default)]
138pub struct E2ETestRunner {
139    pub engine: crate::api::Engine,
140}
141
142impl E2ETestRunner {
143    pub fn new() -> Self {
144        Self::default()
145    }
146
147    pub fn run_e2e_test(&mut self, _test_name: &str, code: &str, expected: &str) -> TestResult {
148        let mut result = TestResult::new();
149
150        match self.engine.evaluate(code) {
151            Ok(eval_result) => {
152                let result_str = eval_result.to_string();
153                if result_str == expected {
154                    result.add_passed();
155                } else {
156                    result.add_failed();
157                }
158            }
159            Err(_error) => {
160                result.add_failed();
161            }
162        }
163
164        result
165    }
166
167    pub fn run_e2e_tests(&mut self, tests: Vec<(&str, &str, &str)>) -> TestResult {
168        let mut total_result = TestResult::new();
169
170        for (test_name, code, expected) in tests {
171            let test_result = self.run_e2e_test(test_name, code, expected);
172            total_result.passed += test_result.passed;
173            total_result.failed += test_result.failed;
174            total_result.skipped += test_result.skipped;
175        }
176
177        total_result
178    }
179}
180
181pub fn get_test_header(title: &str) -> String {
182    format!("\n=== {title} ===")
183}
184
185pub fn get_test_summary(result: &TestResult) -> String {
186    format!("{result}")
187}