tailwind_rs_testing/
responsive_testing.rs

1//! Responsive testing utilities for tailwind-rs
2
3use tailwind_rs_core::responsive::{Breakpoint, ResponsiveConfig, ResponsiveValue};
4
5/// Result of a responsive test
6#[derive(Debug, Clone)]
7pub struct ResponsiveTestResult {
8    pub success: bool,
9    pub message: String,
10    pub expected_value: Option<String>,
11    pub actual_value: Option<String>,
12}
13
14impl ResponsiveTestResult {
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 responsive breakpoint values
41pub fn test_responsive_breakpoint(
42    breakpoint: Breakpoint,
43    expected_min_width: u32,
44) -> ResponsiveTestResult {
45    let actual_min_width = breakpoint.min_width();
46
47    if actual_min_width == expected_min_width {
48        ResponsiveTestResult::success(format!("Breakpoint {:?} has correct min width", breakpoint))
49    } else {
50        ResponsiveTestResult::failure(format!(
51            "Breakpoint {:?} has incorrect min width",
52            breakpoint
53        ))
54        .with_values(expected_min_width.to_string(), actual_min_width.to_string())
55    }
56}
57
58/// Test responsive value at specific breakpoint
59pub fn test_responsive_value<T: PartialEq + std::fmt::Debug>(
60    responsive_value: &ResponsiveValue<T>,
61    breakpoint: Breakpoint,
62    expected_value: &T,
63) -> ResponsiveTestResult {
64    let actual_value = responsive_value.get_breakpoint(breakpoint);
65
66    if actual_value == expected_value {
67        ResponsiveTestResult::success(format!(
68            "Responsive value at {:?} matches expected",
69            breakpoint
70        ))
71    } else {
72        ResponsiveTestResult::failure(format!(
73            "Responsive value at {:?} does not match expected",
74            breakpoint
75        ))
76        .with_values(
77            format!("{:?}", expected_value),
78            format!("{:?}", actual_value),
79        )
80    }
81}
82
83/// Test responsive configuration
84pub fn test_responsive_config(
85    config: &ResponsiveConfig,
86    expected_breakpoints: &[(&str, u32)],
87) -> ResponsiveTestResult {
88    let mut missing_breakpoints = Vec::new();
89    let mut incorrect_breakpoints = Vec::new();
90
91    for (name, expected_value) in expected_breakpoints {
92        match config.get_breakpoint(name) {
93            Ok(actual_value) => {
94                if actual_value != *expected_value {
95                    incorrect_breakpoints.push(format!(
96                        "{}: expected {}, got {}",
97                        name, expected_value, actual_value
98                    ));
99                }
100            }
101            Err(_) => {
102                missing_breakpoints.push(name.to_string());
103            }
104        }
105    }
106
107    if missing_breakpoints.is_empty() && incorrect_breakpoints.is_empty() {
108        ResponsiveTestResult::success("All breakpoints match expected values")
109    } else {
110        let mut message = String::new();
111        if !missing_breakpoints.is_empty() {
112            message.push_str(&format!("Missing breakpoints: {:?}", missing_breakpoints));
113        }
114        if !incorrect_breakpoints.is_empty() {
115            if !message.is_empty() {
116                message.push_str("; ");
117            }
118            message.push_str(&format!(
119                "Incorrect breakpoints: {:?}",
120                incorrect_breakpoints
121            ));
122        }
123        ResponsiveTestResult::failure(message)
124    }
125}
126
127/// Test responsive classes
128pub fn test_responsive_classes(
129    classes: &std::collections::HashSet<String>,
130    expected_responsive: &[(&str, &str)],
131) -> ResponsiveTestResult {
132    let mut missing_classes = Vec::new();
133    let mut found_classes = Vec::new();
134
135    for (breakpoint, class) in expected_responsive {
136        let responsive_class = format!("{}:{}", breakpoint, class);
137        if classes.contains(&responsive_class) {
138            found_classes.push(responsive_class);
139        } else {
140            missing_classes.push(responsive_class);
141        }
142    }
143
144    if missing_classes.is_empty() {
145        ResponsiveTestResult::success(format!("All responsive classes found: {:?}", found_classes))
146    } else {
147        ResponsiveTestResult::failure(format!("Missing responsive classes: {:?}", missing_classes))
148    }
149}
150
151/// Assert responsive value matches expected
152pub fn assert_responsive_value<T: PartialEq + std::fmt::Debug>(
153    responsive_value: &ResponsiveValue<T>,
154    breakpoint: Breakpoint,
155    expected_value: &T,
156) {
157    let result = test_responsive_value(responsive_value, breakpoint, expected_value);
158    if !result.success {
159        panic!("Responsive assertion failed: {}", result.message);
160    }
161}
162
163/// Assert responsive breakpoint has correct min width
164pub fn assert_responsive_breakpoint(breakpoint: Breakpoint, expected_min_width: u32) {
165    let result = test_responsive_breakpoint(breakpoint, expected_min_width);
166    if !result.success {
167        panic!("Responsive assertion failed: {}", result.message);
168    }
169}
170
171/// Assert responsive configuration matches expected
172pub fn assert_responsive_config(config: &ResponsiveConfig, expected_breakpoints: &[(&str, u32)]) {
173    let result = test_responsive_config(config, expected_breakpoints);
174    if !result.success {
175        panic!("Responsive assertion failed: {}", result.message);
176    }
177}
178
179/// Assert responsive classes are present
180pub fn assert_responsive_classes(
181    classes: &std::collections::HashSet<String>,
182    expected_responsive: &[(&str, &str)],
183) {
184    let result = test_responsive_classes(classes, expected_responsive);
185    if !result.success {
186        panic!("Responsive assertion failed: {}", result.message);
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn test_responsive_breakpoint_testing() {
196        let result = test_responsive_breakpoint(Breakpoint::Sm, 640);
197        assert!(result.success);
198
199        let result = test_responsive_breakpoint(Breakpoint::Sm, 768);
200        assert!(!result.success);
201    }
202
203    #[test]
204    fn test_responsive_value_testing() {
205        let responsive_value = ResponsiveValue::new(10);
206        let result = test_responsive_value(&responsive_value, Breakpoint::Base, &10);
207        assert!(result.success);
208
209        let result = test_responsive_value(&responsive_value, Breakpoint::Base, &20);
210        assert!(!result.success);
211    }
212
213    #[test]
214    fn test_responsive_config_testing() {
215        let config = ResponsiveConfig::new();
216        let expected_breakpoints = [
217            ("sm", 640),
218            ("md", 768),
219            ("lg", 1024),
220            ("xl", 1280),
221            ("2xl", 1536),
222        ];
223
224        let result = test_responsive_config(&config, &expected_breakpoints);
225        assert!(result.success);
226    }
227
228    #[test]
229    fn test_responsive_classes_testing() {
230        let classes: std::collections::HashSet<String> =
231            ["sm:text-sm".to_string(), "md:text-md".to_string()]
232                .iter()
233                .cloned()
234                .collect();
235
236        let expected_responsive = [("sm", "text-sm"), ("md", "text-md")];
237
238        let result = test_responsive_classes(&classes, &expected_responsive);
239        assert!(result.success);
240    }
241}