tailwind_rs_testing/
mock_components.rs1use std::collections::HashMap;
4
5#[derive(Debug, Clone)]
7pub struct MockComponent {
8 pub name: String,
9 pub html: String,
10 pub classes: std::collections::HashSet<String>,
11 pub custom_properties: HashMap<String, String>,
12 pub props: HashMap<String, String>,
13}
14
15impl MockComponent {
16 pub fn new(name: impl Into<String>) -> Self {
18 Self {
19 name: name.into(),
20 html: String::new(),
21 classes: std::collections::HashSet::new(),
22 custom_properties: HashMap::new(),
23 props: HashMap::new(),
24 }
25 }
26
27 pub fn with_html(mut self, html: impl Into<String>) -> Self {
29 self.html = html.into();
30 self
31 }
32
33 pub fn with_class(mut self, class: impl Into<String>) -> Self {
35 self.classes.insert(class.into());
36 self
37 }
38
39 pub fn with_classes(mut self, classes: impl IntoIterator<Item = String>) -> Self {
41 for class in classes {
42 self.classes.insert(class);
43 }
44 self
45 }
46
47 pub fn with_custom_property(
49 mut self,
50 property: impl Into<String>,
51 value: impl Into<String>,
52 ) -> Self {
53 self.custom_properties.insert(property.into(), value.into());
54 self
55 }
56
57 pub fn with_prop(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
59 self.props.insert(key.into(), value.into());
60 self
61 }
62
63 pub fn to_html(&self) -> String {
65 let mut html = self.html.clone();
66
67 if html.contains("{{classes}}") || html.contains("{{style}}") {
69 if !self.classes.is_empty() {
70 let mut classes_vec: Vec<String> = self.classes.iter().cloned().collect();
71 classes_vec.sort(); let classes_str = classes_vec.join(" ");
73 html = html.replace("{{classes}}", &format!("class=\"{}\"", classes_str));
74 }
75
76 if !self.custom_properties.is_empty() {
77 let style_parts: Vec<String> = self
78 .custom_properties
79 .iter()
80 .map(|(k, v)| format!("--{}: {}", k, v))
81 .collect();
82 let style_str = format!("style=\"{}\"", style_parts.join("; "));
83 html = html.replace("{{style}}", &style_str);
84 }
85
86 for (key, value) in &self.props {
87 html = html.replace(&format!("{{{{{}}}}}", key), value);
88 }
89 } else {
90 let mut attributes = Vec::new();
92
93 if !self.classes.is_empty() {
94 let mut classes_vec: Vec<String> = self.classes.iter().cloned().collect();
95 classes_vec.sort(); let classes_str = classes_vec.join(" ");
97 attributes.push(format!("class=\"{}\"", classes_str));
98 }
99
100 if !self.custom_properties.is_empty() {
101 let style_parts: Vec<String> = self
102 .custom_properties
103 .iter()
104 .map(|(k, v)| format!("--{}: {}", k, v))
105 .collect();
106 attributes.push(format!("style=\"{}\"", style_parts.join("; ")));
107 }
108
109 let mut processed_html = html;
111 for (key, value) in &self.props {
112 processed_html = processed_html.replace(&format!("{{{{{}}}}}", key), value);
113 }
114
115 if !attributes.is_empty() {
116 html = format!("<div {}>{}</div>", attributes.join(" "), processed_html);
118 } else {
119 html = processed_html;
120 }
121 }
122
123 html
124 }
125}
126
127pub fn create_mock_button() -> MockComponent {
129 MockComponent::new("button")
130 .with_html("<button {{classes}} {{style}}>{{text}}</button>")
131 .with_class("bg-blue-500")
132 .with_class("text-white")
133 .with_class("px-4")
134 .with_class("py-2")
135 .with_class("rounded")
136 .with_prop("text", "Click me")
137}
138
139pub fn create_mock_card() -> MockComponent {
141 MockComponent::new("card")
142 .with_html("<div {{classes}} {{style}}>{{content}}</div>")
143 .with_class("bg-white")
144 .with_class("shadow-lg")
145 .with_class("rounded-lg")
146 .with_class("p-6")
147 .with_prop("content", "Card content")
148}
149
150pub fn create_mock_input() -> MockComponent {
152 MockComponent::new("input")
153 .with_html("<input {{classes}} {{style}} placeholder=\"{{placeholder}}\" />")
154 .with_class("w-full")
155 .with_class("px-3")
156 .with_class("py-2")
157 .with_class("border")
158 .with_class("border-gray-300")
159 .with_class("rounded-md")
160 .with_prop("placeholder", "Enter text")
161}
162
163pub fn create_mock_responsive_grid() -> MockComponent {
165 MockComponent::new("responsive_grid")
166 .with_html("<div {{classes}} {{style}}>{{items}}</div>")
167 .with_class("grid")
168 .with_class("gap-4")
169 .with_class("sm:grid-cols-1")
170 .with_class("md:grid-cols-2")
171 .with_class("lg:grid-cols-3")
172 .with_prop("items", "Grid items")
173}
174
175pub fn create_mock_themed_component() -> MockComponent {
177 MockComponent::new("themed_component")
178 .with_html("<div {{classes}} {{style}}>{{content}}</div>")
179 .with_class("p-4")
180 .with_class("rounded-lg")
181 .with_class("border")
182 .with_custom_property("primary-color", "#3b82f6")
183 .with_custom_property("spacing", "1rem")
184 .with_prop("content", "Themed content")
185}
186
187pub fn create_mock_component(name: impl Into<String>) -> MockComponent {
189 MockComponent::new(name)
190}
191
192pub fn create_mock_component_for_test(test_name: &str) -> MockComponent {
194 match test_name {
195 "button" => create_mock_button(),
196 "card" => create_mock_card(),
197 "input" => create_mock_input(),
198 "responsive_grid" => create_mock_responsive_grid(),
199 "themed_component" => create_mock_themed_component(),
200 _ => create_mock_component(test_name),
201 }
202}
203
204pub fn create_mock_components() -> Vec<MockComponent> {
206 vec![
207 create_mock_button(),
208 create_mock_card(),
209 create_mock_input(),
210 create_mock_responsive_grid(),
211 create_mock_themed_component(),
212 ]
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_mock_component_creation() {
221 let component = MockComponent::new("test");
222 assert_eq!(component.name, "test");
223 assert!(component.html.is_empty());
224 assert!(component.classes.is_empty());
225 assert!(component.custom_properties.is_empty());
226 assert!(component.props.is_empty());
227 }
228
229 #[test]
230 fn test_mock_component_with_html() {
231 let component = MockComponent::new("test").with_html("<div>Test</div>");
232 assert_eq!(component.html, "<div>Test</div>");
233 }
234
235 #[test]
236 fn test_mock_component_with_classes() {
237 let component = MockComponent::new("test")
238 .with_class("bg-blue-500")
239 .with_class("text-white");
240
241 assert!(component.classes.contains("bg-blue-500"));
242 assert!(component.classes.contains("text-white"));
243 }
244
245 #[test]
246 fn test_mock_component_with_custom_properties() {
247 let component = MockComponent::new("test")
248 .with_custom_property("primary-color", "#3b82f6")
249 .with_custom_property("spacing", "1rem");
250
251 assert_eq!(
252 component.custom_properties.get("primary-color"),
253 Some(&"#3b82f6".to_string())
254 );
255 assert_eq!(
256 component.custom_properties.get("spacing"),
257 Some(&"1rem".to_string())
258 );
259 }
260
261 #[test]
262 fn test_mock_component_with_props() {
263 let component = MockComponent::new("test")
264 .with_prop("text", "Hello World")
265 .with_prop("count", "42");
266
267 assert_eq!(
268 component.props.get("text"),
269 Some(&"Hello World".to_string())
270 );
271 assert_eq!(component.props.get("count"), Some(&"42".to_string()));
272 }
273
274 #[test]
275 fn test_mock_component_to_html() {
276 let component = MockComponent::new("test")
277 .with_html("<button {{classes}} {{style}}>{{text}}</button>")
278 .with_class("bg-blue-500")
279 .with_class("text-white")
280 .with_custom_property("primary-color", "#3b82f6")
281 .with_prop("text", "Click me");
282
283 let html = component.to_html();
284 assert!(html.contains("class=\"bg-blue-500 text-white\""));
285 assert!(html.contains("style=\"--primary-color: #3b82f6\""));
286 assert!(html.contains("Click me"));
287 }
288
289 #[test]
290 fn test_mock_button_component() {
291 let button = create_mock_button();
292 let html = button.to_html();
293
294 assert!(html.contains("<button"));
295 assert!(html.contains("bg-blue-500"));
296 assert!(html.contains("text-white"));
297 assert!(html.contains("Click me"));
298 }
299
300 #[test]
301 fn test_mock_card_component() {
302 let card = create_mock_card();
303 let html = card.to_html();
304
305 assert!(html.contains("<div"));
306 assert!(html.contains("bg-white"));
307 assert!(html.contains("shadow-lg"));
308 assert!(html.contains("Card content"));
309 }
310
311 #[test]
312 fn test_mock_input_component() {
313 let input = create_mock_input();
314 let html = input.to_html();
315
316 assert!(html.contains("<input"));
317 assert!(html.contains("w-full"));
318 assert!(html.contains("border"));
319 assert!(html.contains("Enter text"));
320 }
321
322 #[test]
323 fn test_mock_responsive_grid_component() {
324 let grid = create_mock_responsive_grid();
325 let html = grid.to_html();
326
327 assert!(html.contains("<div"));
328 assert!(html.contains("grid"));
329 assert!(html.contains("sm:grid-cols-1"));
330 assert!(html.contains("md:grid-cols-2"));
331 assert!(html.contains("lg:grid-cols-3"));
332 }
333
334 #[test]
335 fn test_mock_themed_component() {
336 let themed = create_mock_themed_component();
337 let html = themed.to_html();
338
339 assert!(html.contains("<div"));
340 assert!(html.contains("p-4"));
341 assert!(html.contains("rounded-lg"));
342 assert!(html.contains("--primary-color: #3b82f6"));
343 assert!(html.contains("--spacing: 1rem"));
344 }
345
346 #[test]
347 fn test_create_mock_components() {
348 let components = create_mock_components();
349 assert_eq!(components.len(), 5);
350
351 let names: Vec<String> = components.iter().map(|c| c.name.clone()).collect();
352 assert!(names.contains(&"button".to_string()));
353 assert!(names.contains(&"card".to_string()));
354 assert!(names.contains(&"input".to_string()));
355 assert!(names.contains(&"responsive_grid".to_string()));
356 assert!(names.contains(&"themed_component".to_string()));
357 }
358}