tailwind_rs_core/utilities/
enhanced_validation.rs

1//! Enhanced Validation utilities for tailwind-rs
2//!
3//! This module provides utilities for enhanced validation features.
4//! It includes support for class validation, property validation, and validation rules.
5
6use crate::classes::ClassBuilder;
7use serde::{Deserialize, Serialize};
8use std::fmt;
9use std::collections::HashMap;
10
11/// Validation rule types
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub enum ValidationRule {
14    /// Required validation
15    Required,
16    /// Pattern validation
17    Pattern(String),
18    /// Length validation
19    Length(usize, usize),
20    /// Range validation
21    Range(f64, f64),
22    /// Custom validation
23    Custom(String),
24}
25
26impl fmt::Display for ValidationRule {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match self {
29            ValidationRule::Required => write!(f, "required"),
30            ValidationRule::Pattern(pattern) => write!(f, "pattern:{}", pattern),
31            ValidationRule::Length(min, max) => write!(f, "length:{}-{}", min, max),
32            ValidationRule::Range(min, max) => write!(f, "range:{}-{}", min, max),
33            ValidationRule::Custom(rule) => write!(f, "{}", rule),
34        }
35    }
36}
37
38impl ValidationRule {
39    /// Get the CSS class name for this validation rule
40    pub fn to_class_name(&self) -> String {
41        match self {
42            ValidationRule::Required => "validation-required".to_string(),
43            ValidationRule::Pattern(pattern) => format!("validation-pattern-{}", pattern.replace(":", "-").replace("(", "").replace(")", "").replace("*", "star").replace("+", "plus").replace("?", "question").replace("^", "caret").replace("$", "dollar").replace("|", "pipe").replace("\\", "backslash").replace("/", "slash").replace(" ", "-")),
44            ValidationRule::Length(min, max) => format!("validation-length-{}-{}", min, max),
45            ValidationRule::Range(min, max) => format!("validation-range-{}-{}", min, max),
46            ValidationRule::Custom(rule) => format!("validation-{}", rule.replace(":", "-").replace("(", "").replace(")", "").replace("*", "star").replace("+", "plus").replace("?", "question").replace("^", "caret").replace("$", "dollar").replace("|", "pipe").replace("\\", "backslash").replace("/", "slash").replace(" ", "-")),
47        }
48    }
49
50    /// Get the CSS value for this validation rule
51    pub fn to_css_value(&self) -> String {
52        self.to_string()
53    }
54}
55
56/// Validation severity levels
57#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
58pub enum ValidationSeverity {
59    /// Error level
60    Error,
61    /// Warning level
62    Warning,
63    /// Info level
64    Info,
65    /// Success level
66    Success,
67    /// Custom severity
68    Custom(String),
69}
70
71impl fmt::Display for ValidationSeverity {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        match self {
74            ValidationSeverity::Error => write!(f, "error"),
75            ValidationSeverity::Warning => write!(f, "warning"),
76            ValidationSeverity::Info => write!(f, "info"),
77            ValidationSeverity::Success => write!(f, "success"),
78            ValidationSeverity::Custom(severity) => write!(f, "{}", severity),
79        }
80    }
81}
82
83impl ValidationSeverity {
84    /// Get the CSS class name for this validation severity
85    pub fn to_class_name(&self) -> String {
86        match self {
87            ValidationSeverity::Error => "validation-error".to_string(),
88            ValidationSeverity::Warning => "validation-warning".to_string(),
89            ValidationSeverity::Info => "validation-info".to_string(),
90            ValidationSeverity::Success => "validation-success".to_string(),
91            ValidationSeverity::Custom(severity) => format!("validation-{}", severity),
92        }
93    }
94
95    /// Get the CSS value for this validation severity
96    pub fn to_css_value(&self) -> String {
97        self.to_string()
98    }
99}
100
101/// Validation scope types
102#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
103pub enum ValidationScope {
104    /// Global validation
105    Global,
106    /// Local validation
107    Local,
108    /// Component validation
109    Component,
110    /// Page validation
111    Page,
112    /// Custom scope
113    Custom(String),
114}
115
116impl fmt::Display for ValidationScope {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        match self {
119            ValidationScope::Global => write!(f, "global"),
120            ValidationScope::Local => write!(f, "local"),
121            ValidationScope::Component => write!(f, "component"),
122            ValidationScope::Page => write!(f, "page"),
123            ValidationScope::Custom(scope) => write!(f, "{}", scope),
124        }
125    }
126}
127
128impl ValidationScope {
129    /// Get the CSS class name for this validation scope
130    pub fn to_class_name(&self) -> String {
131        match self {
132            ValidationScope::Global => "validation-global".to_string(),
133            ValidationScope::Local => "validation-local".to_string(),
134            ValidationScope::Component => "validation-component".to_string(),
135            ValidationScope::Page => "validation-page".to_string(),
136            ValidationScope::Custom(scope) => format!("validation-{}", scope),
137        }
138    }
139
140    /// Get the CSS value for this validation scope
141    pub fn to_css_value(&self) -> String {
142        self.to_string()
143    }
144}
145
146/// Validation mode types
147#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
148pub enum ValidationMode {
149    /// Strict validation
150    Strict,
151    /// Loose validation
152    Loose,
153    /// Custom validation
154    Custom,
155    /// Disabled validation
156    Disabled,
157    /// Custom mode
158    CustomMode(String),
159}
160
161impl fmt::Display for ValidationMode {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        match self {
164            ValidationMode::Strict => write!(f, "strict"),
165            ValidationMode::Loose => write!(f, "loose"),
166            ValidationMode::Custom => write!(f, "custom"),
167            ValidationMode::Disabled => write!(f, "disabled"),
168            ValidationMode::CustomMode(mode) => write!(f, "{}", mode),
169        }
170    }
171}
172
173impl ValidationMode {
174    /// Get the CSS class name for this validation mode
175    pub fn to_class_name(&self) -> String {
176        match self {
177            ValidationMode::Strict => "validation-strict".to_string(),
178            ValidationMode::Loose => "validation-loose".to_string(),
179            ValidationMode::Custom => "validation-custom".to_string(),
180            ValidationMode::Disabled => "validation-disabled".to_string(),
181            ValidationMode::CustomMode(mode) => format!("validation-{}", mode),
182        }
183    }
184
185    /// Get the CSS value for this validation mode
186    pub fn to_css_value(&self) -> String {
187        self.to_string()
188    }
189}
190
191/// Validation result types
192#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
193pub enum ValidationResult {
194    /// Valid result
195    Valid,
196    /// Invalid result
197    Invalid(String),
198    /// Warning result
199    Warning(String),
200    /// Info result
201    Info(String),
202    /// Custom result
203    Custom(String),
204}
205
206impl fmt::Display for ValidationResult {
207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208        match self {
209            ValidationResult::Valid => write!(f, "valid"),
210            ValidationResult::Invalid(message) => write!(f, "invalid:{}", message),
211            ValidationResult::Warning(message) => write!(f, "warning:{}", message),
212            ValidationResult::Info(message) => write!(f, "info:{}", message),
213            ValidationResult::Custom(result) => write!(f, "{}", result),
214        }
215    }
216}
217
218impl ValidationResult {
219    /// Get the CSS class name for this validation result
220    pub fn to_class_name(&self) -> String {
221        match self {
222            ValidationResult::Valid => "validation-valid".to_string(),
223            ValidationResult::Invalid(_) => "validation-invalid".to_string(),
224            ValidationResult::Warning(_) => "validation-warning".to_string(),
225            ValidationResult::Info(_) => "validation-info".to_string(),
226            ValidationResult::Custom(result) => format!("validation-{}", result),
227        }
228    }
229
230    /// Get the CSS value for this validation result
231    pub fn to_css_value(&self) -> String {
232        self.to_string()
233    }
234}
235
236/// Trait for adding enhanced validation to ClassBuilder
237pub trait EnhancedValidationUtilities {
238    /// Set validation rule
239    fn validation_rule(self, rule: ValidationRule) -> Self;
240    /// Set validation severity
241    fn validation_severity(self, severity: ValidationSeverity) -> Self;
242    /// Set validation scope
243    fn validation_scope(self, scope: ValidationScope) -> Self;
244    /// Set validation mode
245    fn validation_mode(self, mode: ValidationMode) -> Self;
246    /// Set validation result
247    fn validation_result(self, result: ValidationResult) -> Self;
248    /// Set validation with custom options
249    fn validation_custom(self, name: &str, options: HashMap<String, String>) -> Self;
250}
251
252impl EnhancedValidationUtilities for ClassBuilder {
253    fn validation_rule(self, rule: ValidationRule) -> Self {
254        self.class(&rule.to_class_name())
255    }
256
257    fn validation_severity(self, severity: ValidationSeverity) -> Self {
258        self.class(&severity.to_class_name())
259    }
260
261    fn validation_scope(self, scope: ValidationScope) -> Self {
262        self.class(&scope.to_class_name())
263    }
264
265    fn validation_mode(self, mode: ValidationMode) -> Self {
266        self.class(&mode.to_class_name())
267    }
268
269    fn validation_result(self, result: ValidationResult) -> Self {
270        self.class(&result.to_class_name())
271    }
272
273    fn validation_custom(self, name: &str, _options: HashMap<String, String>) -> Self {
274        let validation_class = format!("validation-{}", name);
275        self.class(&validation_class)
276    }
277}
278
279/// Convenience methods for common validation patterns
280pub trait EnhancedValidationConvenience {
281    /// Set required validation
282    fn validation_required(self) -> Self;
283    /// Set pattern validation
284    fn validation_pattern(self, pattern: &str) -> Self;
285    /// Set length validation
286    fn validation_length(self, min: usize, max: usize) -> Self;
287    /// Set range validation
288    fn validation_range(self, min: f64, max: f64) -> Self;
289    /// Set error severity
290    fn validation_error(self) -> Self;
291    /// Set warning severity
292    fn validation_warning(self) -> Self;
293    /// Set info severity
294    fn validation_info(self) -> Self;
295    /// Set success severity
296    fn validation_success(self) -> Self;
297    /// Set global scope
298    fn validation_global(self) -> Self;
299    /// Set local scope
300    fn validation_local(self) -> Self;
301    /// Set component scope
302    fn validation_component(self) -> Self;
303    /// Set page scope
304    fn validation_page(self) -> Self;
305    /// Set strict mode
306    fn validation_strict(self) -> Self;
307    /// Set loose mode
308    fn validation_loose(self) -> Self;
309    /// Set custom mode
310    fn validation_custom_mode(self) -> Self;
311    /// Set disabled mode
312    fn validation_disabled(self) -> Self;
313    /// Set valid result
314    fn validation_valid(self) -> Self;
315    /// Set invalid result
316    fn validation_invalid(self, message: &str) -> Self;
317    /// Set warning result
318    fn validation_warning_result(self, message: &str) -> Self;
319    /// Set info result
320    fn validation_info_result(self, message: &str) -> Self;
321}
322
323impl EnhancedValidationConvenience for ClassBuilder {
324    fn validation_required(self) -> Self {
325        self.validation_rule(ValidationRule::Required)
326    }
327
328    fn validation_pattern(self, pattern: &str) -> Self {
329        self.validation_rule(ValidationRule::Pattern(pattern.to_string()))
330    }
331
332    fn validation_length(self, min: usize, max: usize) -> Self {
333        self.validation_rule(ValidationRule::Length(min, max))
334    }
335
336    fn validation_range(self, min: f64, max: f64) -> Self {
337        self.validation_rule(ValidationRule::Range(min, max))
338    }
339
340    fn validation_error(self) -> Self {
341        self.validation_severity(ValidationSeverity::Error)
342    }
343
344    fn validation_warning(self) -> Self {
345        self.validation_severity(ValidationSeverity::Warning)
346    }
347
348    fn validation_info(self) -> Self {
349        self.validation_severity(ValidationSeverity::Info)
350    }
351
352    fn validation_success(self) -> Self {
353        self.validation_severity(ValidationSeverity::Success)
354    }
355
356    fn validation_global(self) -> Self {
357        self.validation_scope(ValidationScope::Global)
358    }
359
360    fn validation_local(self) -> Self {
361        self.validation_scope(ValidationScope::Local)
362    }
363
364    fn validation_component(self) -> Self {
365        self.validation_scope(ValidationScope::Component)
366    }
367
368    fn validation_page(self) -> Self {
369        self.validation_scope(ValidationScope::Page)
370    }
371
372    fn validation_strict(self) -> Self {
373        self.validation_mode(ValidationMode::Strict)
374    }
375
376    fn validation_loose(self) -> Self {
377        self.validation_mode(ValidationMode::Loose)
378    }
379
380    fn validation_custom_mode(self) -> Self {
381        self.validation_mode(ValidationMode::Custom)
382    }
383
384    fn validation_disabled(self) -> Self {
385        self.validation_mode(ValidationMode::Disabled)
386    }
387
388    fn validation_valid(self) -> Self {
389        self.validation_result(ValidationResult::Valid)
390    }
391
392    fn validation_invalid(self, message: &str) -> Self {
393        self.validation_result(ValidationResult::Invalid(message.to_string()))
394    }
395
396    fn validation_warning_result(self, message: &str) -> Self {
397        self.validation_result(ValidationResult::Warning(message.to_string()))
398    }
399
400    fn validation_info_result(self, message: &str) -> Self {
401        self.validation_result(ValidationResult::Info(message.to_string()))
402    }
403}
404
405#[cfg(test)]
406mod tests {
407    use super::*;
408    use crate::classes::ClassBuilder;
409
410    #[test]
411    fn test_validation_rule_enum_values() {
412        assert_eq!(ValidationRule::Required.to_string(), "required");
413        assert_eq!(ValidationRule::Pattern("test".to_string()).to_string(), "pattern:test");
414        assert_eq!(ValidationRule::Length(1, 10).to_string(), "length:1-10");
415        assert_eq!(ValidationRule::Range(0.0, 100.0).to_string(), "range:0-100");
416        assert_eq!(ValidationRule::Custom("custom".to_string()).to_string(), "custom");
417    }
418
419    #[test]
420    fn test_validation_rule_class_names() {
421        assert_eq!(ValidationRule::Required.to_class_name(), "validation-required");
422        assert_eq!(ValidationRule::Pattern("test".to_string()).to_class_name(), "validation-pattern-test");
423        assert_eq!(ValidationRule::Length(1, 10).to_class_name(), "validation-length-1-10");
424        assert_eq!(ValidationRule::Range(0.0, 100.0).to_class_name(), "validation-range-0-100");
425        assert_eq!(ValidationRule::Custom("custom".to_string()).to_class_name(), "validation-custom");
426    }
427
428    #[test]
429    fn test_validation_severity_enum_values() {
430        assert_eq!(ValidationSeverity::Error.to_string(), "error");
431        assert_eq!(ValidationSeverity::Warning.to_string(), "warning");
432        assert_eq!(ValidationSeverity::Info.to_string(), "info");
433        assert_eq!(ValidationSeverity::Success.to_string(), "success");
434        assert_eq!(ValidationSeverity::Custom("custom".to_string()).to_string(), "custom");
435    }
436
437    #[test]
438    fn test_validation_severity_class_names() {
439        assert_eq!(ValidationSeverity::Error.to_class_name(), "validation-error");
440        assert_eq!(ValidationSeverity::Warning.to_class_name(), "validation-warning");
441        assert_eq!(ValidationSeverity::Info.to_class_name(), "validation-info");
442        assert_eq!(ValidationSeverity::Success.to_class_name(), "validation-success");
443        assert_eq!(ValidationSeverity::Custom("custom".to_string()).to_class_name(), "validation-custom");
444    }
445
446    #[test]
447    fn test_validation_scope_enum_values() {
448        assert_eq!(ValidationScope::Global.to_string(), "global");
449        assert_eq!(ValidationScope::Local.to_string(), "local");
450        assert_eq!(ValidationScope::Component.to_string(), "component");
451        assert_eq!(ValidationScope::Page.to_string(), "page");
452        assert_eq!(ValidationScope::Custom("custom".to_string()).to_string(), "custom");
453    }
454
455    #[test]
456    fn test_validation_scope_class_names() {
457        assert_eq!(ValidationScope::Global.to_class_name(), "validation-global");
458        assert_eq!(ValidationScope::Local.to_class_name(), "validation-local");
459        assert_eq!(ValidationScope::Component.to_class_name(), "validation-component");
460        assert_eq!(ValidationScope::Page.to_class_name(), "validation-page");
461        assert_eq!(ValidationScope::Custom("custom".to_string()).to_class_name(), "validation-custom");
462    }
463
464    #[test]
465    fn test_validation_mode_enum_values() {
466        assert_eq!(ValidationMode::Strict.to_string(), "strict");
467        assert_eq!(ValidationMode::Loose.to_string(), "loose");
468        assert_eq!(ValidationMode::Custom.to_string(), "custom");
469        assert_eq!(ValidationMode::Disabled.to_string(), "disabled");
470        assert_eq!(ValidationMode::CustomMode("custom".to_string()).to_string(), "custom");
471    }
472
473    #[test]
474    fn test_validation_mode_class_names() {
475        assert_eq!(ValidationMode::Strict.to_class_name(), "validation-strict");
476        assert_eq!(ValidationMode::Loose.to_class_name(), "validation-loose");
477        assert_eq!(ValidationMode::Custom.to_class_name(), "validation-custom");
478        assert_eq!(ValidationMode::Disabled.to_class_name(), "validation-disabled");
479        assert_eq!(ValidationMode::CustomMode("custom".to_string()).to_class_name(), "validation-custom");
480    }
481
482    #[test]
483    fn test_validation_result_enum_values() {
484        assert_eq!(ValidationResult::Valid.to_string(), "valid");
485        assert_eq!(ValidationResult::Invalid("message".to_string()).to_string(), "invalid:message");
486        assert_eq!(ValidationResult::Warning("message".to_string()).to_string(), "warning:message");
487        assert_eq!(ValidationResult::Info("message".to_string()).to_string(), "info:message");
488        assert_eq!(ValidationResult::Custom("custom".to_string()).to_string(), "custom");
489    }
490
491    #[test]
492    fn test_validation_result_class_names() {
493        assert_eq!(ValidationResult::Valid.to_class_name(), "validation-valid");
494        assert_eq!(ValidationResult::Invalid("message".to_string()).to_class_name(), "validation-invalid");
495        assert_eq!(ValidationResult::Warning("message".to_string()).to_class_name(), "validation-warning");
496        assert_eq!(ValidationResult::Info("message".to_string()).to_class_name(), "validation-info");
497        assert_eq!(ValidationResult::Custom("custom".to_string()).to_class_name(), "validation-custom");
498    }
499
500    #[test]
501    fn test_enhanced_validation_utilities() {
502        let classes = ClassBuilder::new()
503            .validation_rule(ValidationRule::Required)
504            .validation_severity(ValidationSeverity::Error)
505            .validation_scope(ValidationScope::Global)
506            .validation_mode(ValidationMode::Strict)
507            .validation_result(ValidationResult::Valid);
508
509        let result = classes.build();
510        assert!(result.classes.contains("validation-required"));
511        assert!(result.classes.contains("validation-error"));
512        assert!(result.classes.contains("validation-global"));
513        assert!(result.classes.contains("validation-strict"));
514        assert!(result.classes.contains("validation-valid"));
515    }
516
517    #[test]
518    fn test_enhanced_validation_convenience() {
519        let classes = ClassBuilder::new()
520            .validation_required()
521            .validation_pattern("test")
522            .validation_length(1, 10)
523            .validation_range(0.0, 100.0)
524            .validation_error()
525            .validation_warning()
526            .validation_info()
527            .validation_success()
528            .validation_global()
529            .validation_local()
530            .validation_component()
531            .validation_page()
532            .validation_strict()
533            .validation_loose()
534            .validation_custom_mode()
535            .validation_disabled()
536            .validation_valid()
537            .validation_invalid("message")
538            .validation_warning_result("message")
539            .validation_info_result("message");
540
541        let result = classes.build();
542        assert!(result.classes.contains("validation-required"));
543        assert!(result.classes.contains("validation-pattern-test"));
544        assert!(result.classes.contains("validation-length-1-10"));
545        assert!(result.classes.contains("validation-range-0-100"));
546        assert!(result.classes.contains("validation-error"));
547        assert!(result.classes.contains("validation-warning"));
548        assert!(result.classes.contains("validation-info"));
549        assert!(result.classes.contains("validation-success"));
550        assert!(result.classes.contains("validation-global"));
551        assert!(result.classes.contains("validation-local"));
552        assert!(result.classes.contains("validation-component"));
553        assert!(result.classes.contains("validation-page"));
554        assert!(result.classes.contains("validation-strict"));
555        assert!(result.classes.contains("validation-loose"));
556        assert!(result.classes.contains("validation-custom"));
557        assert!(result.classes.contains("validation-disabled"));
558        assert!(result.classes.contains("validation-valid"));
559        assert!(result.classes.contains("validation-invalid"));
560        assert!(result.classes.contains("validation-warning"));
561        assert!(result.classes.contains("validation-info"));
562    }
563
564    #[test]
565    fn test_enhanced_validation_serialization() {
566        let validation_rule = ValidationRule::Required;
567        let serialized = serde_json::to_string(&validation_rule).unwrap();
568        let deserialized: ValidationRule = serde_json::from_str(&serialized).unwrap();
569        assert_eq!(validation_rule, deserialized);
570
571        let validation_severity = ValidationSeverity::Error;
572        let serialized = serde_json::to_string(&validation_severity).unwrap();
573        let deserialized: ValidationSeverity = serde_json::from_str(&serialized).unwrap();
574        assert_eq!(validation_severity, deserialized);
575
576        let validation_scope = ValidationScope::Global;
577        let serialized = serde_json::to_string(&validation_scope).unwrap();
578        let deserialized: ValidationScope = serde_json::from_str(&serialized).unwrap();
579        assert_eq!(validation_scope, deserialized);
580
581        let validation_mode = ValidationMode::Strict;
582        let serialized = serde_json::to_string(&validation_mode).unwrap();
583        let deserialized: ValidationMode = serde_json::from_str(&serialized).unwrap();
584        assert_eq!(validation_mode, deserialized);
585
586        let validation_result = ValidationResult::Valid;
587        let serialized = serde_json::to_string(&validation_result).unwrap();
588        let deserialized: ValidationResult = serde_json::from_str(&serialized).unwrap();
589        assert_eq!(validation_result, deserialized);
590    }
591
592    #[test]
593    fn test_enhanced_validation_comprehensive_usage() {
594        let classes = ClassBuilder::new()
595            .validation_rule(ValidationRule::Required)
596            .validation_severity(ValidationSeverity::Error)
597            .validation_scope(ValidationScope::Global)
598            .validation_mode(ValidationMode::Strict)
599            .validation_result(ValidationResult::Valid)
600            .validation_required()
601            .validation_pattern("test")
602            .validation_length(1, 10)
603            .validation_range(0.0, 100.0)
604            .validation_error()
605            .validation_warning()
606            .validation_info()
607            .validation_success()
608            .validation_global()
609            .validation_local()
610            .validation_component()
611            .validation_page()
612            .validation_strict()
613            .validation_loose()
614            .validation_custom_mode()
615            .validation_disabled()
616            .validation_valid()
617            .validation_invalid("message")
618            .validation_warning_result("message")
619            .validation_info_result("message");
620
621        let result = classes.build();
622        assert!(result.classes.contains("validation-required"));
623        assert!(result.classes.contains("validation-error"));
624        assert!(result.classes.contains("validation-global"));
625        assert!(result.classes.contains("validation-strict"));
626        assert!(result.classes.contains("validation-valid"));
627        assert!(result.classes.contains("validation-pattern-test"));
628        assert!(result.classes.contains("validation-length-1-10"));
629        assert!(result.classes.contains("validation-range-0-100"));
630        assert!(result.classes.contains("validation-warning"));
631        assert!(result.classes.contains("validation-info"));
632        assert!(result.classes.contains("validation-success"));
633        assert!(result.classes.contains("validation-local"));
634        assert!(result.classes.contains("validation-component"));
635        assert!(result.classes.contains("validation-page"));
636        assert!(result.classes.contains("validation-loose"));
637        assert!(result.classes.contains("validation-custom"));
638        assert!(result.classes.contains("validation-disabled"));
639        assert!(result.classes.contains("validation-invalid"));
640    }
641}