fluent_test/
lib.rs

1//! FluentTest: A fluent, Jest-like testing library for Rust
2//!
3//! This crate provides a more expressive way to write tests in Rust,
4//! inspired by JavaScript testing frameworks like Jest.
5//!
6//! By default, assertions behave like standard Rust assertions.
7//! Enable enhanced output with:
8//!
9//! ```
10//! // In your test code:
11//! use fluent_test::prelude::*;
12//!
13//! fn my_test() {
14//!     // Enable enhanced output for this test
15//!     fluent_test::config().enhanced_output(true).apply();
16//!     
17//!     expect!(2 + 2).to_equal(4);
18//! }
19//! ```
20//!
21//! Or set the FLUENT_TEST_ENHANCED_OUTPUT=true environment variable.
22
23// Allow explicit return statements as part of the coding style
24#![allow(clippy::needless_return)]
25
26// Initialization constants and utilities
27
28// Import Once for initialization
29use std::sync::Once;
30
31// Initialization for tests
32static TEST_INIT: Once = Once::new();
33
34pub mod backend;
35pub mod config;
36pub mod events;
37pub mod frontend;
38mod reporter;
39
40// Auto-initialize for tests if enhanced output is enabled
41pub fn auto_initialize_for_tests() {
42    TEST_INIT.call_once(|| {
43        // Check environment variable to enable enhanced output
44        let config = config::Config::new();
45        if config.enhanced_output {
46            // Apply the config which will initialize the event system
47            config.apply();
48        }
49    });
50}
51
52// Re-export the initialize function
53pub use config::initialize;
54
55/// Matcher traits module for bringing the traits into scope
56pub mod matchers {
57    pub use crate::backend::matchers::boolean::BooleanMatchers;
58    pub use crate::backend::matchers::collection::{CollectionExtensions, CollectionMatchers};
59    pub use crate::backend::matchers::equality::EqualityMatchers;
60    pub use crate::backend::matchers::hashmap::HashMapMatchers;
61    pub use crate::backend::matchers::numeric::NumericMatchers;
62    pub use crate::backend::matchers::option::OptionMatchers;
63    pub use crate::backend::matchers::result::ResultMatchers;
64    pub use crate::backend::matchers::string::StringMatchers;
65}
66
67/// Main prelude module containing everything needed for fluent testing
68pub mod prelude {
69    pub use crate::backend::Assertion;
70    pub use crate::expect;
71    pub use crate::expect_not;
72
73    // Import all matcher traits
74    pub use crate::matchers::*;
75
76    // Import modifiers
77    pub use crate::backend::modifiers::*;
78
79    // Import configuration and initialization
80    pub use crate::config;
81    pub use crate::initialize;
82
83    #[cfg(test)]
84    pub use crate::auto_initialize_for_tests;
85}
86
87// Re-exports
88pub use crate::config::Config;
89pub use crate::reporter::Reporter;
90
91/// Creates a new test configuration
92pub fn config() -> Config {
93    Config::new()
94}
95
96/// Main entry point for fluent assertions
97#[macro_export]
98macro_rules! expect {
99    ($expr:expr) => {{
100        // Always auto-initialize
101        $crate::auto_initialize_for_tests();
102
103        $crate::backend::Assertion::new($expr, stringify!($expr))
104    }};
105}
106
107/// Shorthand for creating a negated expectation
108/// This provides a more natural way to write assertions with not
109#[macro_export]
110macro_rules! expect_not {
111    ($expr:expr) => {{
112        // Always auto-initialize
113        $crate::auto_initialize_for_tests();
114
115        use $crate::backend::modifiers::NotModifier;
116        $crate::backend::Assertion::new($expr, stringify!($expr)).not()
117    }};
118}
119
120/// Run all FluentTest tests in a module
121///
122/// This can be used as a test harness to handle initialization
123/// and reporting.
124#[macro_export]
125macro_rules! fluent_test {
126    () => {
127        #[test]
128        fn _fluent_test_runner() {
129            // Auto-initialize if enhanced output is enabled
130            $crate::auto_initialize_for_tests();
131
132            // Check if enhanced output is enabled
133            let enhanced_output = $crate::config::is_enhanced_output_enabled();
134
135            // Setup reporting guard only if enhanced output is enabled
136            if enhanced_output {
137                // Use a drop guard to ensure reporter runs at the end
138                struct ReportOnDrop;
139                impl Drop for ReportOnDrop {
140                    fn drop(&mut self) {
141                        $crate::Reporter::summarize();
142                    }
143                }
144
145                let _guard = ReportOnDrop;
146            }
147
148            // The test itself runs normally
149        }
150    };
151}
152
153// Special module for test support
154#[cfg(test)]
155pub mod test_utils {
156    // Just re-export all the traits for easy importing in tests
157    pub use crate::backend::matchers::boolean::BooleanMatchers;
158    pub use crate::backend::matchers::collection::{CollectionExtensions, CollectionMatchers};
159    pub use crate::backend::matchers::equality::EqualityMatchers;
160    pub use crate::backend::matchers::hashmap::HashMapMatchers;
161    pub use crate::backend::matchers::numeric::NumericMatchers;
162    pub use crate::backend::matchers::option::OptionMatchers;
163    pub use crate::backend::matchers::result::ResultMatchers;
164    pub use crate::backend::matchers::string::StringMatchers;
165
166    // Helper function to set up testing
167    pub fn setup_tests() {
168        // Force enhanced output for internal tests
169        crate::config().enhanced_output(true).apply();
170    }
171
172    // When tests run, make sure we have enhanced output enabled
173    // by default for internal library tests
174    #[test]
175    #[ignore]
176    fn _setup_for_tests() {
177        setup_tests();
178    }
179}