tailwind_rs_testing/
theme_testing.rs

1//! Theme testing utilities for tailwind-rs
2
3use tailwind_rs_core::theme::{BorderRadius, BoxShadow, Color, Spacing, Theme};
4
5/// Result of a theme test
6#[derive(Debug, Clone)]
7pub struct ThemeTestResult {
8    pub success: bool,
9    pub message: String,
10    pub expected_value: Option<String>,
11    pub actual_value: Option<String>,
12}
13
14impl ThemeTestResult {
15    pub fn success(message: impl Into<String>) -> Self {
16        Self {
17            success: true,
18            message: message.into(),
19            expected_value: None,
20            actual_value: None,
21        }
22    }
23
24    pub fn failure(message: impl Into<String>) -> Self {
25        Self {
26            success: false,
27            message: message.into(),
28            expected_value: None,
29            actual_value: None,
30        }
31    }
32
33    pub fn with_values(mut self, expected: impl Into<String>, actual: impl Into<String>) -> Self {
34        self.expected_value = Some(expected.into());
35        self.actual_value = Some(actual.into());
36        self
37    }
38}
39
40/// Test theme color values
41pub fn test_theme_color(
42    theme: &Theme,
43    color_name: &str,
44    expected_color: &Color,
45) -> ThemeTestResult {
46    match theme.get_color(color_name) {
47        Ok(actual_color) => {
48            if actual_color == expected_color {
49                ThemeTestResult::success(format!("Color '{}' matches expected value", color_name))
50            } else {
51                ThemeTestResult::failure(format!(
52                    "Color '{}' does not match expected value",
53                    color_name
54                ))
55                .with_values(expected_color.to_css(), actual_color.to_css())
56            }
57        }
58        Err(e) => ThemeTestResult::failure(format!("Failed to get color '{}': {}", color_name, e)),
59    }
60}
61
62/// Test theme spacing values
63pub fn test_theme_spacing(
64    theme: &Theme,
65    spacing_name: &str,
66    expected_spacing: &Spacing,
67) -> ThemeTestResult {
68    match theme.get_spacing(spacing_name) {
69        Ok(actual_spacing) => {
70            if actual_spacing == expected_spacing {
71                ThemeTestResult::success(format!(
72                    "Spacing '{}' matches expected value",
73                    spacing_name
74                ))
75            } else {
76                ThemeTestResult::failure(format!(
77                    "Spacing '{}' does not match expected value",
78                    spacing_name
79                ))
80                .with_values(expected_spacing.to_css(), actual_spacing.to_css())
81            }
82        }
83        Err(e) => {
84            ThemeTestResult::failure(format!("Failed to get spacing '{}': {}", spacing_name, e))
85        }
86    }
87}
88
89/// Test theme border radius values
90pub fn test_theme_border_radius(
91    theme: &Theme,
92    radius_name: &str,
93    expected_radius: &BorderRadius,
94) -> ThemeTestResult {
95    match theme.get_border_radius(radius_name) {
96        Ok(actual_radius) => {
97            if actual_radius == expected_radius {
98                ThemeTestResult::success(format!(
99                    "Border radius '{}' matches expected value",
100                    radius_name
101                ))
102            } else {
103                ThemeTestResult::failure(format!(
104                    "Border radius '{}' does not match expected value",
105                    radius_name
106                ))
107                .with_values(expected_radius.to_css(), actual_radius.to_css())
108            }
109        }
110        Err(e) => ThemeTestResult::failure(format!(
111            "Failed to get border radius '{}': {}",
112            radius_name, e
113        )),
114    }
115}
116
117/// Test theme box shadow values
118pub fn test_theme_box_shadow(
119    theme: &Theme,
120    shadow_name: &str,
121    expected_shadow: &BoxShadow,
122) -> ThemeTestResult {
123    match theme.get_box_shadow(shadow_name) {
124        Ok(actual_shadow) => {
125            if actual_shadow == expected_shadow {
126                ThemeTestResult::success(format!(
127                    "Box shadow '{}' matches expected value",
128                    shadow_name
129                ))
130            } else {
131                ThemeTestResult::failure(format!(
132                    "Box shadow '{}' does not match expected value",
133                    shadow_name
134                ))
135                .with_values(expected_shadow.to_css(), actual_shadow.to_css())
136            }
137        }
138        Err(e) => {
139            ThemeTestResult::failure(format!("Failed to get box shadow '{}': {}", shadow_name, e))
140        }
141    }
142}
143
144/// Assert theme value matches expected
145pub fn assert_theme_value(theme: &Theme, value_type: &str, value_name: &str, expected_value: &str) {
146    let result = match value_type {
147        "color" => {
148            if let Ok(expected_color) = parse_color(expected_value) {
149                test_theme_color(theme, value_name, &expected_color)
150            } else {
151                ThemeTestResult::failure(format!("Invalid color format: {}", expected_value))
152            }
153        }
154        "spacing" => {
155            if let Ok(expected_spacing) = parse_spacing(expected_value) {
156                test_theme_spacing(theme, value_name, &expected_spacing)
157            } else {
158                ThemeTestResult::failure(format!("Invalid spacing format: {}", expected_value))
159            }
160        }
161        "border_radius" => {
162            if let Ok(expected_radius) = parse_border_radius(expected_value) {
163                test_theme_border_radius(theme, value_name, &expected_radius)
164            } else {
165                ThemeTestResult::failure(format!(
166                    "Invalid border radius format: {}",
167                    expected_value
168                ))
169            }
170        }
171        "box_shadow" => {
172            if let Ok(expected_shadow) = parse_box_shadow(expected_value) {
173                test_theme_box_shadow(theme, value_name, &expected_shadow)
174            } else {
175                ThemeTestResult::failure(format!("Invalid box shadow format: {}", expected_value))
176            }
177        }
178        _ => ThemeTestResult::failure(format!("Unknown value type: {}", value_type)),
179    };
180
181    if !result.success {
182        panic!("Theme assertion failed: {}", result.message);
183    }
184}
185
186/// Parse color from string
187fn parse_color(s: &str) -> Result<Color, String> {
188    if s.starts_with('#') {
189        Ok(Color::hex(s))
190    } else if s.starts_with("rgb(") {
191        // Simple RGB parsing
192        Ok(Color::hex(s))
193    } else {
194        Ok(Color::named(s))
195    }
196}
197
198/// Parse spacing from string
199fn parse_spacing(s: &str) -> Result<Spacing, String> {
200    if s.ends_with("px") {
201        let value = s
202            .trim_end_matches("px")
203            .parse::<f32>()
204            .map_err(|_| "Invalid pixel value")?;
205        Ok(Spacing::px(value))
206    } else if s.ends_with("rem") {
207        let value = s
208            .trim_end_matches("rem")
209            .parse::<f32>()
210            .map_err(|_| "Invalid rem value")?;
211        Ok(Spacing::rem(value))
212    } else if s.ends_with("em") {
213        let value = s
214            .trim_end_matches("em")
215            .parse::<f32>()
216            .map_err(|_| "Invalid em value")?;
217        Ok(Spacing::em(value))
218    } else if s.ends_with("%") {
219        let value = s
220            .trim_end_matches("%")
221            .parse::<f32>()
222            .map_err(|_| "Invalid percentage value")?;
223        Ok(Spacing::percent(value))
224    } else {
225        Ok(Spacing::named(s))
226    }
227}
228
229/// Parse border radius from string
230fn parse_border_radius(s: &str) -> Result<BorderRadius, String> {
231    if s.ends_with("px") {
232        let value = s
233            .trim_end_matches("px")
234            .parse::<f32>()
235            .map_err(|_| "Invalid pixel value")?;
236        Ok(BorderRadius::px(value))
237    } else if s.ends_with("rem") {
238        let value = s
239            .trim_end_matches("rem")
240            .parse::<f32>()
241            .map_err(|_| "Invalid rem value")?;
242        Ok(BorderRadius::rem(value))
243    } else if s.ends_with("%") {
244        let value = s
245            .trim_end_matches("%")
246            .parse::<f32>()
247            .map_err(|_| "Invalid percentage value")?;
248        Ok(BorderRadius::percent(value))
249    } else {
250        Ok(BorderRadius::named(s))
251    }
252}
253
254/// Parse box shadow from string
255fn parse_box_shadow(_s: &str) -> Result<BoxShadow, String> {
256    // Simple box shadow parsing - just create a basic shadow
257    Ok(BoxShadow::new(
258        0.0,
259        1.0,
260        2.0,
261        0.0,
262        Color::hex("#000000"),
263        false,
264    ))
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270
271    #[test]
272    fn test_theme_color_testing() {
273        let theme = tailwind_rs_core::theme::create_default_theme();
274        let expected_color = Color::hex("#3b82f6");
275
276        let result = test_theme_color(&theme, "primary", &expected_color);
277        assert!(result.success);
278    }
279
280    #[test]
281    fn test_theme_spacing_testing() {
282        let theme = tailwind_rs_core::theme::create_default_theme();
283        let expected_spacing = Spacing::rem(1.0);
284
285        let result = test_theme_spacing(&theme, "md", &expected_spacing);
286        assert!(result.success);
287    }
288
289    #[test]
290    fn test_theme_border_radius_testing() {
291        let theme = tailwind_rs_core::theme::create_default_theme();
292        let expected_radius = BorderRadius::rem(0.375);
293
294        let result = test_theme_border_radius(&theme, "md", &expected_radius);
295        assert!(result.success);
296    }
297
298    #[test]
299    fn test_theme_box_shadow_testing() {
300        let theme = tailwind_rs_core::theme::create_default_theme();
301        let expected_shadow = BoxShadow::new(0.0, 1.0, 2.0, 0.0, Color::hex("#000000"), false);
302
303        let result = test_theme_box_shadow(&theme, "sm", &expected_shadow);
304        assert!(result.success);
305    }
306}